搜索
bottom↓
回复: 16

利用stm32的pwm还原wav格式声音的疑问

[复制链接]

出0入0汤圆

发表于 2020-6-6 10:35:49 | 显示全部楼层 |阅读模式
本帖最后由 cssbkj88 于 2020-6-6 10:39 编辑

我的思路是这样的:把wav格式的声音文件转成bin格式后存到25q64中,然后stm32读出来,利用pwm来模式da(我用的型号不带da)发声,现在不清楚的有2点。
(1)Bin文件是比较大的,有50K左右,然后在程序定义一个这么大的Buff是不合理的。
(2)跟声音有关的最重要的2个特点就是采样率和采样值,我用的是16K ,8Bit。我网上搜索了下,有些朋友是采用2个定时器来实现,一个实现频率,一个实现占空比
,但是具体怎么跟Bin文件对应起来?还望有经验的大牛指点下迷津,多谢!

出0入4汤圆

发表于 2020-6-6 11:47:56 来自手机 | 显示全部楼层
最好用2个定时器,逻辑上简单。注意乒乓操作就行。

出0入0汤圆

 楼主| 发表于 2020-6-7 16:17:38 | 显示全部楼层
zqf441775525 发表于 2020-6-6 11:47
最好用2个定时器,逻辑上简单。注意乒乓操作就行。

谢谢zqf441775525,能麻烦你详细说下具体的思路吗?一个定时器负责频率,另外一个定时器用wav的数据作为TIM1->CCR1  .为什么需要乒乓操作?谢谢!

出0入0汤圆

发表于 2020-6-7 20:11:29 | 显示全部楼层
要两个定时器,例如16KHZ 8bit

一个定时器每 62.5us(1/16KHZ)产生中断, 在中断里更新另外一个定时器的占空比。

PWM输出->RC滤波,就是音频波形了

出0入0汤圆

发表于 2020-6-7 20:16:30 | 显示全部楼层
cssbkj88 发表于 2020-6-7 16:17
谢谢zqf441775525,能麻烦你详细说下具体的思路吗?一个定时器负责频率,另外一个定时器用wav的数据作为T ...

乒乓操作就是两个缓冲区轮流输出音频数据,音频数据不能中断,中断就会有杂音

出870入263汤圆

发表于 2020-6-7 20:43:57 | 显示全部楼层
为什么楼上都说要两个定时器呢?PWM播放wav太简单了,用STM32F030C8T6就能做到啊。还可以同时实现IO驱动VGA显示器呢。
https://www.amobbs.com/thread-5663166-1-1.html
实现的原理就是TIM_UP触发DMA,双缓冲传输。TIMER播放BUF1时,软件准备好BUF2待用;播放BUF2时,又准备好BUF1,如此更替下去。
由于SPI-FLASH的带宽远大于WAV播放,所以丝毫不用担心BUF来不及准备。每个BUF容量能维持播放20毫秒就够了,多了也是浪费RAM。

出0入0汤圆

 楼主| 发表于 2020-6-8 09:35:38 | 显示全部楼层
谢谢上面的几位兄弟,真的帮大忙了!

出870入263汤圆

发表于 2020-6-8 10:27:33 | 显示全部楼层
cssbkj88 发表于 2020-6-8 09:35
谢谢上面的几位兄弟,真的帮大忙了!


用STM32F030C8T6的TIM16【PB8管脚】播放PCM音频流,11K/s,8bit;仅供参考:

  1. #define AUDIO_MUTE_LEN  (118)   /* 118个样本大约播放10毫秒 */

  2. static uint8_t const audio_mute[AUDIO_MUTE_LEN] = {
  3.   128,128,128,128,128,128,128,128,
  4.   128,128,128,128,128,128,128,128,
  5.   128,128,128,128,128,128,128,128,
  6.   128,128,128,128,128,128,128,128,
  7.   128,128,128,128,128,128,128,128,
  8.   128,128,128,128,128,128,128,128,
  9.   128,128,128,128,128,128,128,128,
  10.   128,128,128,128,128,128,128,128,
  11.   128,128,128,128,128,128,128,128,
  12.   128,128,128,128,128,128,128,128,
  13.   128,128,128,128,128,128,128,128,
  14.   128,128,128,128,128,128,128,128,
  15.   128,128,128,128,128,128,128,128,
  16.   128,128,128,128,128,128,128,128,
  17.   128,128,128,128,128,128
  18. };

  19. static volatile struct {
  20.   const uint8_t *buf_addr1;
  21.   uint32_t buf_len1;
  22.   
  23.   const uint8_t *buf_addr2;
  24.   uint32_t buf_len2;
  25.   
  26.   uint8_t trig_req;
  27. } audio_dev = { .buf_len1 = 0, .buf_len2 = 0, .trig_req = 0 };

  28. void Drv_AudioOpen(void)
  29. {
  30.   GPIO_InitTypeDef GPIO_InitStructure;

  31.   /**************************************************************************/
  32.   /* Timer16用作音频输出,管脚为PB8(AF2=TIM16_CH1),PB6(AF2=TIM16_CH1N) */
  33.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
  34.   GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_6|GPIO_Pin_8;
  35.   GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
  36.   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  37.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  38.   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  39.   GPIO_Init(GPIOB, &GPIO_InitStructure);
  40.   GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_2);
  41.   GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_2);
  42.   
  43.   /* 开启Timer16的系统时钟,并初始化Timer16 */
  44.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, ENABLE);

  45.   /* 关闭Timer计数器 */
  46.   TIM16->CR1 = TIM_CR1_ARPE|TIM_CR1_URS;
  47.   TIM16->CR2 = 0;
  48.   /* 关闭Timer中断 */
  49.   TIM16->DIER = 0;
  50.   TIM16->SR   = 0;
  51.   /* 预分频使CK_CNT=12MHZ,同时配置RCR=3且ARR=254,
  52.      就可以得到11718.75HZ脉冲,
  53.      与标准音频采样频率11025HZ近似 */
  54.   TIM16->PSC = (4-1);
  55.   /* 每个音频脉冲输出4次 */
  56.   TIM16->RCR = (4-1);
  57.   /* 配置PWM脉宽 */
  58.   TIM16->ARR = (uint16_t)(255-1);
  59.   
  60.   /* 配置比较器工作在PWM方式,并配置其电平逻辑 */
  61.   TIM16->CCMR1 = ((6<<4)|TIM_CCMR1_OC1PE|TIM_CCMR1_OC1FE|(0<<0));
  62.   TIM16->CCER  = ((0<<3)|TIM_CCER_CC1NE|(0<<1)|TIM_CCER_CC1E);
  63.   TIM16->BDTR  = (TIM_BDTR_MOE|TIM_BDTR_AOE|(0<<12));
  64.   /* 配置PWM的有效电平脉宽 */
  65.   TIM16->CCR1 = (uint16_t)128;
  66.   TIM16->EGR  = TIM_EGR_UG; /* 手动更新设定值 */
  67.   /* 配置Timer模块的DMA功能 */
  68.   TIM16->DCR  = TIM_DMABurstLength_1Transfer|TIM_DMABase_CCR1;
  69.   TIM16->DIER = TIM_DIER_CC1DE; /* 使能CC1的DMA请求 */
  70.   TIM16->CR2 |= TIM_CR2_CCDS;   /* Update时产生CCx的DMA请求 */
  71.   TIM16->CR1 |= TIM_CR1_CEN;
  72.   
  73.   /* 配置DMA1_Channel4模块,用于Timer */
  74.   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
  75.   /* 注意:要使SYSCFG_CFGR1寄存器起作用,必须开启SYSCFG模块时钟! */
  76.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
  77.   SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_TIM16, ENABLE);
  78.   
  79.   /* 使用DMA1通道4响应TIM16_UP的传输请求 */
  80.   DMA1->IFCR = DMA_IFCR_CGIF4;
  81.   /* 源端口8位宽,目的端口16位宽,NDT是指源端口读取次数 */
  82.   DMA1_Channel4->CCR   = ((0<<12)|(1<<8)|DMA_CCR_MINC|DMA_CCR_DIR);
  83.   DMA1_Channel4->CCR  |= (DMA_CCR_TEIE|DMA_CCR_TCIE);
  84.   DMA1_Channel4->CPAR  = (uint32_t)&TIM16->DMAR;
  85.   DMA1_Channel4->CMAR  = (uint32_t)audio_mute;
  86.   DMA1_Channel4->CNDTR = (uint16_t)AUDIO_MUTE_LEN;
  87.   /* DMA1_Channel4->CCR  |= DMA_CCR_EN; */
  88.   NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
  89.   audio_dev.trig_req = 1;
  90. }

  91. int Drv_AudioPlay(uint8_t const stream[], int num)
  92. {
  93.   if(audio_dev.buf_len1 == 0){
  94.     audio_dev.buf_addr1 = stream;
  95.     audio_dev.buf_len1 = num;
  96.     return num;
  97.   }else if(!audio_dev.buf_len2){
  98.     audio_dev.buf_addr2 = stream;
  99.     audio_dev.buf_len2 = num;
  100.     return num;
  101.   }
  102.   return 0;
  103. }

  104. void Drv_AudioClose(void)
  105. {
  106.   audio_dev.trig_req = 0;
  107.   TIM16->CR1 &= ~TIM_CR1_CEN;
  108.   NVIC_DisableIRQ(DMA1_Channel4_5_IRQn);
  109.   DMA1_Channel4->CCR &= ~DMA_CCR_EN;
  110.   RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM16, DISABLE);
  111. }

  112. void Drv_AudioAppHook(void)
  113. {
  114.   if(audio_dev.trig_req){
  115.     if(audio_dev.buf_len1){
  116.       audio_dev.trig_req = 0;
  117.       DMA1_Channel4->CCR &= ~DMA_CCR_EN;
  118.       DMA1_Channel4->CMAR  = (uint32_t)audio_dev.buf_addr1;
  119.       DMA1_Channel4->CNDTR = (uint16_t)audio_dev.buf_len1;
  120.       DMA1_Channel4->CCR |= DMA_CCR_EN;
  121.       audio_dev.buf_len1 = 0;
  122.     }else if(audio_dev.buf_len2){
  123.       audio_dev.trig_req = 0;
  124.       DMA1_Channel4->CCR &= ~DMA_CCR_EN;
  125.       DMA1_Channel4->CMAR  = (uint32_t)audio_dev.buf_addr2;
  126.       DMA1_Channel4->CNDTR = (uint16_t)audio_dev.buf_len2;
  127.       DMA1_Channel4->CCR |= DMA_CCR_EN;
  128.       audio_dev.buf_len2 = 0;
  129.     }
  130.   }
  131. }

  132. /* PWM音频引擎控制中断 */
  133. void TIM16_IRQHandler(void)
  134. {
  135.   TIM16->SR = 0;
  136. }

  137. /* PWM音频引擎输出中断 */
  138. void DMA1_Channel4_5_IRQHandler(void)
  139. {
  140.   uint32_t isr = DMA1->ISR;
  141.   
  142.   if(isr & DMA_ISR_GIF4){
  143.    
  144.     DMA1->IFCR = DMA_IFCR_CGIF4;
  145.    
  146.     if(isr & DMA_ISR_TEIF4){
  147.       /* A transfer error (TE) occurred */
  148.       audio_dev.trig_req = 0;
  149.       DMA1_Channel4->CCR &= ~DMA_CCR_EN;
  150.       DMA1_Channel4->CMAR  = (uint32_t)audio_mute;
  151.       DMA1_Channel4->CNDTR = (uint16_t)AUDIO_MUTE_LEN;
  152.       DMA1_Channel4->CCR |= DMA_CCR_EN;
  153.     }
  154.    
  155.     if(isr & DMA_ISR_TCIF4){
  156.       /* A transfer complete (TC) event occurred */
  157.       DMA1_Channel4->CCR &= ~DMA_CCR_EN;
  158.       if(audio_dev.buf_len1){
  159.         audio_dev.trig_req = 0;
  160.         DMA1_Channel4->CMAR  = (uint32_t)audio_dev.buf_addr1;
  161.         DMA1_Channel4->CNDTR = (uint16_t)audio_dev.buf_len1;
  162.         DMA1_Channel4->CCR |= DMA_CCR_EN;
  163.         audio_dev.buf_len1 = 0;
  164.       }else if(audio_dev.buf_len2){
  165.         audio_dev.trig_req = 0;
  166.         DMA1_Channel4->CMAR  = (uint32_t)audio_dev.buf_addr2;
  167.         DMA1_Channel4->CNDTR = (uint16_t)audio_dev.buf_len2;
  168.         DMA1_Channel4->CCR |= DMA_CCR_EN;
  169.         audio_dev.buf_len2 = 0;
  170.       }else{
  171.         audio_dev.trig_req = 1;
  172.       }
  173.     }
  174.   }
  175. }
复制代码

出0入0汤圆

发表于 2020-6-9 09:11:58 | 显示全部楼层
flash3g 发表于 2020-6-7 20:16
乒乓操作就是两个缓冲区轮流输出音频数据,音频数据不能中断,中断就会有杂音 ...

没做过,不过我想你的信息都是main points

出0入0汤圆

发表于 2020-6-9 18:13:37 | 显示全部楼层
armstrong 发表于 2020-6-8 10:27
用STM32F030C8T6的TIM16【PB8管脚】播放PCM音频流,11K/s,8bit;仅供参考:

用DMA的话,可以用DMA的半传输中断,这样一个缓冲区就行了

出0入0汤圆

 楼主| 发表于 2020-6-15 10:50:02 | 显示全部楼层
各位大佬,现在声音是有了,但是很小,而且声音块结束的时候伴随着刺耳的啸叫声,怎么破?

出0入149汤圆

发表于 2020-6-15 12:28:19 来自手机 | 显示全部楼层
大部分stm32有i2s接口,价格音频dac几毛钱,折腾啥pwm。

出0入0汤圆

发表于 2020-6-15 17:15:57 | 显示全部楼层
PWM做DAC,因为输出的滤波电路会使原本一样幅度的高频信号的幅度比低频信号的小

出10入284汤圆

发表于 2020-6-15 19:06:37 来自手机 | 显示全部楼层
tm8211才几毛钱,没必要pwm

出0入0汤圆

 楼主| 发表于 2020-6-16 08:14:41 | 显示全部楼层
已经搞好了,谢谢大家!

出0入0汤圆

发表于 2020-8-15 00:53:11 | 显示全部楼层
cssbkj88 发表于 2020-6-16 08:14
已经搞好了,谢谢大家!

你好,能不能放点料出来啊,谢谢!

出0入0汤圆

发表于 2021-4-20 17:15:19 | 显示全部楼层
cssbkj88 发表于 2020-6-16 08:14
已经搞好了,谢谢大家!

兄台。请教一下,PWM输出部分电路是怎样的?声音不够大呢。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 11:40

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表