发新帖本帖赏金 30.00元(功能说明)我要提问
返回列表
打印
[活动]

【极海APM32F407IG Tiny Board开发板测评】9.利用DAC实现音频输出

[复制链接]
1861|14
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
#申请原创# @21小跑堂  
9.利用DAC实现音频输出
DAC数模转换器DAC与ADC模数转换相反,是将数字量转换为模拟量输出,APM32F407提供了2个12位的DAC,可配置为输入8位或12位数据

先利用DAC和定时器输出方波三角波正弦波等各种波形,能用于DAC输出的只有这两个IO

选用PA4,用按键切换波形
代码实现

uint8_t dacvalue = 0;
uint8_t wavetype = 0;
uint16_t wavestep = 0;
uint8_t sinvalues[91] = {0,4,9,13,18,22,27,31,35,40,44,49,53,57,62,66,70,75,79,83,87,91,96,100,104,108,112,116,120,124,127,131,135,139,143,146,150,153,157,160,164,167,171,174,177,180,183,186,190,192,195,198,201,204,206,209,211,214,216,219,221,223,225,227,229,231,233,235,236,238,240,241,243,244,245,246,247,248,249,250,251,252,253,253,254,254,254,255,255,255,255};
void tim3_init()
{
    TMR_BaseConfig_T TMR_TimeBaseStruct;
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
    TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;
    TMR_TimeBaseStruct.division = 41;
    TMR_TimeBaseStruct.repetitionCounter = 0;
    TMR_TimeBaseStruct.period = 999;
    TMR_ConfigTimeBase(TMR3, &TMR_TimeBaseStruct);
    TMR_EnableInterrupt(TMR3,TMR_INT_UPDATE);
    NVIC_EnableIRQRequest(TMR3_IRQn, 0, 0);
    TMR_Enable(TMR3);
}

void dac_init()
{
    GPIO_Config_T   gpioConfig;
    DAC_Config_T    dacConfig;

    /* Enable GPIOA clock */
    RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);

    /* DAC out PA4 pin configuration */
    GPIO_ConfigStructInit(&gpioConfig);
    gpioConfig.mode    = GPIO_MODE_AN;
    gpioConfig.pupd    = GPIO_PUPD_NOPULL;
    gpioConfig.pin     = GPIO_PIN_4;
    GPIO_Config(GPIOA, &gpioConfig);

    /* Enable DAC clock */
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);

    /* DAC channel 1 configuration */
    DAC_ConfigStructInit(&dacConfig);
    dacConfig.trigger             = DAC_TRIGGER_NONE;
    dacConfig.waveGeneration      = DAC_WAVE_GENERATION_NONE;
    dacConfig.maskAmplitudeSelect = DAC_LFSR_MASK_BIT11_1;
    dacConfig.outputBuffer        = DAC_OUTPUT_BUFFER_ENABLE;
    DAC_Config(DAC_CHANNEL_1, &dacConfig);

    /* Enable DAC channel 1 */
    DAC_Enable(DAC_CHANNEL_1);
}

void proc_dac()
{
    uint8_t changed = 0;
    switch(wavetype)
    {
        case 0:
            if(wavestep > 0)
                wavestep -= 1;
            else
            {
                if(dacvalue == 0)
                    dacvalue = 255;
                else
                    dacvalue = 0;
                changed = 1;
                wavestep = 99;
            }
            break;
        case 1:
            dacvalue+=1;
            changed = 1;
            break;
        case 2:
            if(wavestep > 0)
                wavestep -= 1;
            else
            {
                wavestep = 10;
                dacvalue+=15;
                changed = 1;
            }
            break;
        case 3:
            if(wavestep < 255)
            {
                dacvalue=wavestep;
                changed = 1;
            }
            else if(wavestep > 300 && wavestep < 556)
            {
                dacvalue= 555 - wavestep;
                changed = 1;
            }
            if(wavestep < 600)
                wavestep++;
            else
                wavestep = 0;
            break;
        case 4:
            if(wavestep < 90)
            {
                dacvalue = sinvalues[wavestep];
                changed = 1;
            }
            else
            {
                dacvalue = sinvalues[180 - wavestep];
                changed = 1;
            }
            if(wavestep < 179)
                wavestep++;
            else
                wavestep = 0;
            break;
        case 5:
            if(wavestep < 90)
            {
                dacvalue = 127 + (sinvalues[wavestep]/2);
                changed = 1;
            }
            else
            {
                dacvalue = 128 - (sinvalues[180 - wavestep]/2);
                changed = 1;
            }
            if(wavestep < 179)
                wavestep++;
            else
                wavestep = 0;
            break;
        case 6:
            if(wavestep < 90)
            {
                dacvalue = 127 + (sinvalues[wavestep]/2);
                changed = 1;
            }
            else if(wavestep < 180)
            {
                dacvalue = 127 + (sinvalues[180 - wavestep]/2);
                changed = 1;
            }
            else if(wavestep < 270)
            {
                dacvalue = 128 - (sinvalues[wavestep - 180]/2);
                changed = 1;
            }
            else
            {
                dacvalue = 128 - (sinvalues[360 - wavestep]/2);
                changed = 1;
            }
            if(wavestep < 359)
                wavestep++;
            else
                wavestep = 0;
            break;
        default:
            break;
    }
    if(changed > 0)
        DAC_ConfigChannel1Data(DAC_ALIGN_8BIT_R,dacvalue);
}

int main(void)
{
    APM_TINY_PBInit(BUTTON_KEY1,BUTTON_MODE_GPIO);
    APM_TINY_PBInit(BUTTON_KEY2,BUTTON_MODE_GPIO);
    dac_init();
    tim3_init();
    while (1)
    {
        if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
        {
            yuyy_delay_ms(30);
            if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
            {
                while(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET);
                wavetype += 1;
                wavestep = 0;
                if(wavetype > 6)
                    wavetype = 0;
            }
        }
    }
}

void TMR3_IRQHandler(void)
{
    if(TMR_ReadIntFlag(TMR3, TMR_INT_UPDATE) == SET)
    {
        proc_dac();
        TMR_ClearIntFlag(TMR3, TMR_INT_UPDATE);
    }
}
运行效果

这样就实现了可控的电压输出,我们生活中所用的耳机音箱等最终也是向喇叭输出模拟量,所以理论上是能够利用这个DAC来输出音频的,接下来尝试一下
先选一段音频,用Adobe Audition转换为WAV,为了少占用点空间加之DAC最大只支持12位,这里按如下参数导出

之所以要转成这个格式是因为这个格式下文件内存储的就是数字电平信息
用winhex打开转换后的wav文件,复制中间的一段内容,用这个选项可以直接以数组的形式复制,把复制的数组粘贴到代码中

音频的采样频率是6000Hz,定时器这里也要调整参数以匹配采样率

uint16_t wavestep = 0;
void tim3_init()
{
    TMR_BaseConfig_T TMR_TimeBaseStruct;
    RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
    TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;
    TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;
    TMR_TimeBaseStruct.division = 13;
    TMR_TimeBaseStruct.repetitionCounter = 0;
    TMR_TimeBaseStruct.period = 999;
    TMR_ConfigTimeBase(TMR3, &TMR_TimeBaseStruct);
    TMR_EnableInterrupt(TMR3,TMR_INT_UPDATE);
    NVIC_EnableIRQRequest(TMR3_IRQn, 0, 0);
    TMR_Enable(TMR3);
}
DAC初始化代码和之前一样,播放代码
#define AUDIO_DATA_LEN 57552 
uint8_t play = 0;
const uint8_t audio_data[57552] = {/*音频数组*/};
int main(void)
{
    APM_TINY_PBInit(BUTTON_KEY1,BUTTON_MODE_GPIO);
    APM_TINY_PBInit(BUTTON_KEY2,BUTTON_MODE_GPIO);
    dac_init();
    tim3_init();
    while (1)
    {
        if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
        {
            yuyy_delay_ms(30);
            if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
            {
                while(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET);
                play = 1-play;
            }
        }
    }
}

void TMR3_IRQHandler(void)
{
    if(TMR_ReadIntFlag(TMR3, TMR_INT_UPDATE) == SET)
    {
        if(play > 0)
        {
            DAC_ConfigChannel1Data(DAC_ALIGN_8BIT_R,audio_data[wavestep]);
            wavestep += 1;
            if(wavestep == AUDIO_DATA_LEN)
                wavestep = 0;
        }
        TMR_ClearIntFlag(TMR3, TMR_INT_UPDATE);
    }
}
运行效果,没有放大电路直接接喇叭几乎听不到声音,接到音箱上还可以

成功用DAC播放了音频,开发板上还有USB和网口,可以尝试结合USB做个USB声卡,或者通过USB读取U盘中的WAV文件进行播放,或者利用以太网口通过网络播放,或者还可以用ADC连接MIC实现录音再用DAC播放,这里就不展开了,有兴趣的可以去尝试一下


使用特权

评论回复

打赏榜单

Gfan 打赏了 30.00 元 2023-08-17
理由:APM32F407IG Tiny Board精选测评

沙发
caizhiwei| | 2023-7-14 19:14 | 只看该作者
解锁各种**,厉害的

使用特权

评论回复
板凳
tpgf| | 2023-8-8 15:34 | 只看该作者
外部的驱动器件在选型上有什么讲究吗

使用特权

评论回复
地板
yuyy1989|  楼主 | 2023-8-8 15:38 | 只看该作者
tpgf 发表于 2023-8-8 15:34
外部的驱动器件在选型上有什么讲究吗

我这个就是个简单的演示,没用其它外部器件

使用特权

评论回复
5
nawu| | 2023-8-8 16:04 | 只看该作者
看视频中的波形 好像不是很平滑啊

使用特权

评论回复
6
aoyi| | 2023-8-8 16:20 | 只看该作者
看波形的话 跟音频输出的关系是什么呢

使用特权

评论回复
7
zljiu| | 2023-8-8 16:44 | 只看该作者
楼主只是通过输出波形来推测 并没有实际接音频输出设备是吗

使用特权

评论回复
8
gwsan| | 2023-8-8 17:03 | 只看该作者
实际的音频输出是这些所有波形的组合吧

使用特权

评论回复
9
yuyy1989|  楼主 | 2023-8-8 17:25 | 只看该作者
gwsan 发表于 2023-8-8 17:03
实际的音频输出是这些所有波形的组合吧

实际的声音可能是各种频率各种波形的混合

使用特权

评论回复
10
yuyy1989|  楼主 | 2023-8-8 17:26 | 只看该作者
aoyi 发表于 2023-8-8 16:20
看波形的话 跟音频输出的关系是什么呢

看波形只是验证DAC的模拟量输出,音频输出也是模拟量是各种波形的混合

使用特权

评论回复
11
yuyy1989|  楼主 | 2023-8-8 17:27 | 只看该作者
zljiu 发表于 2023-8-8 16:44
楼主只是通过输出波形来推测 并没有实际接音频输出设备是吗

最后不是有个视频么,里面不是接了吗

使用特权

评论回复
12
tfqi| | 2023-8-8 17:34 | 只看该作者
我们实际应用的音频输出信号是什么种类的信号啊

使用特权

评论回复
13
yuyy1989|  楼主 | 2023-8-8 18:15 | 只看该作者
tfqi 发表于 2023-8-8 17:34
我们实际应用的音频输出信号是什么种类的信号啊

输出到喇叭那都是模拟信号,中间的过程可能是模拟量(例如3.5毫米的耳机音箱)也可能是数字量(例如HDMI传输音频)

使用特权

评论回复
14
yangxiaor520| | 2023-8-9 07:59 | 只看该作者
12位DAC还是不错了

使用特权

评论回复
发新帖 本帖赏金 30.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

149

主题

708

帖子

7

粉丝