返回列表 发新帖我要提问本帖赏金: 30.00元(功能说明)

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

[复制链接]
2363|14
 楼主| yuyy1989 发表于 2023-7-14 10:00 | 显示全部楼层 |阅读模式
#申请原创# @21小跑堂  
9.利用DAC实现音频输出
DAC数模转换器DAC与ADC模数转换相反,是将数字量转换为模拟量输出,APM32F407提供了2个12位的DAC,可配置为输入8位或12位数据
QQ截图20230713232912.png
先利用DAC和定时器输出方波三角波正弦波等各种波形,能用于DAC输出的只有这两个IO
QQ截图20230712232206.png
选用PA4,用按键切换波形
代码实现

  1. uint8_t dacvalue = 0;
  2. uint8_t wavetype = 0;
  3. uint16_t wavestep = 0;
  4. 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};
  5. void tim3_init()
  6. {
  7.     TMR_BaseConfig_T TMR_TimeBaseStruct;
  8.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
  9.     TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;
  10.     TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;
  11.     TMR_TimeBaseStruct.division = 41;
  12.     TMR_TimeBaseStruct.repetitionCounter = 0;
  13.     TMR_TimeBaseStruct.period = 999;
  14.     TMR_ConfigTimeBase(TMR3, &TMR_TimeBaseStruct);
  15.     TMR_EnableInterrupt(TMR3,TMR_INT_UPDATE);
  16.     NVIC_EnableIRQRequest(TMR3_IRQn, 0, 0);
  17.     TMR_Enable(TMR3);
  18. }

  19. void dac_init()
  20. {
  21.     GPIO_Config_T   gpioConfig;
  22.     DAC_Config_T    dacConfig;

  23.     /* Enable GPIOA clock */
  24.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA);

  25.     /* DAC out PA4 pin configuration */
  26.     GPIO_ConfigStructInit(&gpioConfig);
  27.     gpioConfig.mode    = GPIO_MODE_AN;
  28.     gpioConfig.pupd    = GPIO_PUPD_NOPULL;
  29.     gpioConfig.pin     = GPIO_PIN_4;
  30.     GPIO_Config(GPIOA, &gpioConfig);

  31.     /* Enable DAC clock */
  32.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_DAC);

  33.     /* DAC channel 1 configuration */
  34.     DAC_ConfigStructInit(&dacConfig);
  35.     dacConfig.trigger             = DAC_TRIGGER_NONE;
  36.     dacConfig.waveGeneration      = DAC_WAVE_GENERATION_NONE;
  37.     dacConfig.maskAmplitudeSelect = DAC_LFSR_MASK_BIT11_1;
  38.     dacConfig.outputBuffer        = DAC_OUTPUT_BUFFER_ENABLE;
  39.     DAC_Config(DAC_CHANNEL_1, &dacConfig);

  40.     /* Enable DAC channel 1 */
  41.     DAC_Enable(DAC_CHANNEL_1);
  42. }

  43. void proc_dac()
  44. {
  45.     uint8_t changed = 0;
  46.     switch(wavetype)
  47.     {
  48.         case 0:
  49.             if(wavestep > 0)
  50.                 wavestep -= 1;
  51.             else
  52.             {
  53.                 if(dacvalue == 0)
  54.                     dacvalue = 255;
  55.                 else
  56.                     dacvalue = 0;
  57.                 changed = 1;
  58.                 wavestep = 99;
  59.             }
  60.             break;
  61.         case 1:
  62.             dacvalue+=1;
  63.             changed = 1;
  64.             break;
  65.         case 2:
  66.             if(wavestep > 0)
  67.                 wavestep -= 1;
  68.             else
  69.             {
  70.                 wavestep = 10;
  71.                 dacvalue+=15;
  72.                 changed = 1;
  73.             }
  74.             break;
  75.         case 3:
  76.             if(wavestep < 255)
  77.             {
  78.                 dacvalue=wavestep;
  79.                 changed = 1;
  80.             }
  81.             else if(wavestep > 300 && wavestep < 556)
  82.             {
  83.                 dacvalue= 555 - wavestep;
  84.                 changed = 1;
  85.             }
  86.             if(wavestep < 600)
  87.                 wavestep++;
  88.             else
  89.                 wavestep = 0;
  90.             break;
  91.         case 4:
  92.             if(wavestep < 90)
  93.             {
  94.                 dacvalue = sinvalues[wavestep];
  95.                 changed = 1;
  96.             }
  97.             else
  98.             {
  99.                 dacvalue = sinvalues[180 - wavestep];
  100.                 changed = 1;
  101.             }
  102.             if(wavestep < 179)
  103.                 wavestep++;
  104.             else
  105.                 wavestep = 0;
  106.             break;
  107.         case 5:
  108.             if(wavestep < 90)
  109.             {
  110.                 dacvalue = 127 + (sinvalues[wavestep]/2);
  111.                 changed = 1;
  112.             }
  113.             else
  114.             {
  115.                 dacvalue = 128 - (sinvalues[180 - wavestep]/2);
  116.                 changed = 1;
  117.             }
  118.             if(wavestep < 179)
  119.                 wavestep++;
  120.             else
  121.                 wavestep = 0;
  122.             break;
  123.         case 6:
  124.             if(wavestep < 90)
  125.             {
  126.                 dacvalue = 127 + (sinvalues[wavestep]/2);
  127.                 changed = 1;
  128.             }
  129.             else if(wavestep < 180)
  130.             {
  131.                 dacvalue = 127 + (sinvalues[180 - wavestep]/2);
  132.                 changed = 1;
  133.             }
  134.             else if(wavestep < 270)
  135.             {
  136.                 dacvalue = 128 - (sinvalues[wavestep - 180]/2);
  137.                 changed = 1;
  138.             }
  139.             else
  140.             {
  141.                 dacvalue = 128 - (sinvalues[360 - wavestep]/2);
  142.                 changed = 1;
  143.             }
  144.             if(wavestep < 359)
  145.                 wavestep++;
  146.             else
  147.                 wavestep = 0;
  148.             break;
  149.         default:
  150.             break;
  151.     }
  152.     if(changed > 0)
  153.         DAC_ConfigChannel1Data(DAC_ALIGN_8BIT_R,dacvalue);
  154. }

  155. int main(void)
  156. {
  157.     APM_TINY_PBInit(BUTTON_KEY1,BUTTON_MODE_GPIO);
  158.     APM_TINY_PBInit(BUTTON_KEY2,BUTTON_MODE_GPIO);
  159.     dac_init();
  160.     tim3_init();
  161.     while (1)
  162.     {
  163.         if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
  164.         {
  165.             yuyy_delay_ms(30);
  166.             if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
  167.             {
  168.                 while(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET);
  169.                 wavetype += 1;
  170.                 wavestep = 0;
  171.                 if(wavetype > 6)
  172.                     wavetype = 0;
  173.             }
  174.         }
  175.     }
  176. }

  177. void TMR3_IRQHandler(void)
  178. {
  179.     if(TMR_ReadIntFlag(TMR3, TMR_INT_UPDATE) == SET)
  180.     {
  181.         proc_dac();
  182.         TMR_ClearIntFlag(TMR3, TMR_INT_UPDATE);
  183.     }
  184. }
运行效果
WeChat_20230713163701 00_00_00-00_00_30~1.gif
这样就实现了可控的电压输出,我们生活中所用的耳机音箱等最终也是向喇叭输出模拟量,所以理论上是能够利用这个DAC来输出音频的,接下来尝试一下
先选一段音频,用Adobe Audition转换为WAV,为了少占用点空间加之DAC最大只支持12位,这里按如下参数导出
QQ截图20230713170003.png
之所以要转成这个格式是因为这个格式下文件内存储的就是数字电平信息
用winhex打开转换后的wav文件,复制中间的一段内容,用这个选项可以直接以数组的形式复制,把复制的数组粘贴到代码中
微信图片_20230713173743.png
音频的采样频率是6000Hz,定时器这里也要调整参数以匹配采样率

  1. uint16_t wavestep = 0;
  2. void tim3_init()
  3. {
  4.     TMR_BaseConfig_T TMR_TimeBaseStruct;
  5.     RCM_EnableAPB1PeriphClock(RCM_APB1_PERIPH_TMR3);
  6.     TMR_TimeBaseStruct.clockDivision = TMR_CLOCK_DIV_1;
  7.     TMR_TimeBaseStruct.countMode = TMR_COUNTER_MODE_UP;
  8.     TMR_TimeBaseStruct.division = 13;
  9.     TMR_TimeBaseStruct.repetitionCounter = 0;
  10.     TMR_TimeBaseStruct.period = 999;
  11.     TMR_ConfigTimeBase(TMR3, &TMR_TimeBaseStruct);
  12.     TMR_EnableInterrupt(TMR3,TMR_INT_UPDATE);
  13.     NVIC_EnableIRQRequest(TMR3_IRQn, 0, 0);
  14.     TMR_Enable(TMR3);
  15. }
DAC初始化代码和之前一样,播放代码
  1. #define AUDIO_DATA_LEN 57552
  2. uint8_t play = 0;
  3. const uint8_t audio_data[57552] = {/*音频数组*/};
  4. int main(void)
  5. {
  6.     APM_TINY_PBInit(BUTTON_KEY1,BUTTON_MODE_GPIO);
  7.     APM_TINY_PBInit(BUTTON_KEY2,BUTTON_MODE_GPIO);
  8.     dac_init();
  9.     tim3_init();
  10.     while (1)
  11.     {
  12.         if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
  13.         {
  14.             yuyy_delay_ms(30);
  15.             if(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET)
  16.             {
  17.                 while(APM_TINY_PBGetState(BUTTON_KEY1) == BIT_RESET);
  18.                 play = 1-play;
  19.             }
  20.         }
  21.     }
  22. }

  23. void TMR3_IRQHandler(void)
  24. {
  25.     if(TMR_ReadIntFlag(TMR3, TMR_INT_UPDATE) == SET)
  26.     {
  27.         if(play > 0)
  28.         {
  29.             DAC_ConfigChannel1Data(DAC_ALIGN_8BIT_R,audio_data[wavestep]);
  30.             wavestep += 1;
  31.             if(wavestep == AUDIO_DATA_LEN)
  32.                 wavestep = 0;
  33.         }
  34.         TMR_ClearIntFlag(TMR3, TMR_INT_UPDATE);
  35.     }
  36. }
运行效果,没有放大电路直接接喇叭几乎听不到声音,接到音箱上还可以

成功用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
外部的驱动器件在选型上有什么讲究吗

我这个就是个简单的演示,没用其它外部器件
nawu 发表于 2023-8-8 16:04 | 显示全部楼层
看视频中的波形 好像不是很平滑啊
aoyi 发表于 2023-8-8 16:20 | 显示全部楼层
看波形的话 跟音频输出的关系是什么呢
zljiu 发表于 2023-8-8 16:44 | 显示全部楼层
楼主只是通过输出波形来推测 并没有实际接音频输出设备是吗
gwsan 发表于 2023-8-8 17:03 | 显示全部楼层
实际的音频输出是这些所有波形的组合吧
 楼主| yuyy1989 发表于 2023-8-8 17:25 | 显示全部楼层
gwsan 发表于 2023-8-8 17:03
实际的音频输出是这些所有波形的组合吧

实际的声音可能是各种频率各种波形的混合
 楼主| yuyy1989 发表于 2023-8-8 17:26 | 显示全部楼层
aoyi 发表于 2023-8-8 16:20
看波形的话 跟音频输出的关系是什么呢

看波形只是验证DAC的模拟量输出,音频输出也是模拟量是各种波形的混合
 楼主| yuyy1989 发表于 2023-8-8 17:27 | 显示全部楼层
zljiu 发表于 2023-8-8 16:44
楼主只是通过输出波形来推测 并没有实际接音频输出设备是吗

最后不是有个视频么,里面不是接了吗
tfqi 发表于 2023-8-8 17:34 | 显示全部楼层
我们实际应用的音频输出信号是什么种类的信号啊
 楼主| yuyy1989 发表于 2023-8-8 18:15 | 显示全部楼层
tfqi 发表于 2023-8-8 17:34
我们实际应用的音频输出信号是什么种类的信号啊

输出到喇叭那都是模拟信号,中间的过程可能是模拟量(例如3.5毫米的耳机音箱)也可能是数字量(例如HDMI传输音频)
yangxiaor520 发表于 2023-8-9 07:59 来自手机 | 显示全部楼层
12位DAC还是不错了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:同飞软件研发工程师
简介:制冷系统单片机软件开发,使用PID控制温度

168

主题

826

帖子

10

粉丝
快速回复 在线客服 返回列表 返回顶部