原创 STM32开发板上音频播放程序的设计之二

2010-4-10 12:37 7156 15 17 分类: MCU/ 嵌入式

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

 

 


二、音频播放程序设计


1、程序实现的方案


1)目的


添加一个命令msplay,后带一个参数,就是要播放的WAV文件的名称。


支持播放PCM格式的文件,其它编码方式暂不支持。


2)实现方案


参照<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" />李宁教授书上的《简易声波播放器的设计与实现》那一篇。


利用TIM4通道1定期产生的中断,从缓冲期中读取声音数据。这个中断频率就是WAV文件的采样频率。


声音数据通过TIM3的通道3产生不同频率方波输出。我这个板上的PB0接着扬声器,可以复用为TIM3_CH3


为了声音播放的连续性,采用两个数据缓冲区。在中断处理程序中,当一个缓冲区的数据读完后,自动切换到下一个缓冲区开头。而在主程序中,当发现一个缓冲区空以后,又很快读入声音数据。


 


3)程序执行流程分析


串口输入命令“msplay ***.wav”,进入音频播放命令处理程序。


首先读入文件头的44个字节,获取wav文件头信息,对比标志“RIFF”,“WAVE”“fmt ”,获取编码方式、声道、采样频率、位数,每次采样的字节数、每秒采样的字节数,根据“fmt ”后面的size字段定位 “DATA CHUNK”,获得数据区开始偏移。暂时不考虑有“fact CHUCK”的情况。


数据的读取通过设置几个结构体,定义一个指针让它指向缓冲区头部,将该指针前者类型转换成结构指针,取出缓冲区对应的属性数据。


若对应位置的数据不符合要求,则程序退出。


 


将重要的数据都保存到WavInfo结构体中。


根据采样率的要求,初始化TIM4。然后将TIM3进行初始化设置,并设置CH3输出通道。填充0号缓冲区,使能TIM4中断。置位播放标志。播放实际上就开始了。


 


2、程序代码设计


1)重点是先要把定时器的配置弄清楚


 


首先TIM2TIM3TIM4都位于APB1,最高频率36MHzTIM2已经被我使用,利用通道3产生方波,为液晶提供稳压电源驱动脉冲。它的输出是采用了输出比较模式。下面先回顾一下一个重要寄存器和工作模式。


 


预分频PSC控制计数的实际频率,定时器并不直接对36MHz的脉冲计数,而要先分频。计数频率 = 36MHz 除以 PSC +1)。


自动重装载ARR实际控制计数周期,相当于定时器的提前溢出值,比如设置成0x100,则当从0计数到0x100时,定时器自动复位到初始值。(我的理解不知道对不对)。或者在向下计数模式时,每次都从自动装载值开始计数,向下计数到0,然后又重新装载ARR的值。


 


其它的再看一下参考资料:


时钟选择:我现在是采用内部时钟源,所以要禁止从模式。CR1CEN使能内部时钟,开启定时器,DIR控制计数方向。


比较通道的预装载含义:如果不使能预装载,比较值总是立即写入当前比较寄存器。如果使能,则设定的比较值现在不起作用,等到一个更新事件发生时,才写入当前比较寄存器。


自动重装载寄存器也有同样的预装载的含义。


 


向上计数PWM模式1:初始化时,计数值为0,比较输出参考电平为高。当计数值大于比较值时,输出电平为低。所以ARR是控制PWM的频率,写CCR是控制脉冲的占空比。但是要特别注意OCREFOC输出极性之间的关系,要不然有可能你想增加占空比的时候反而减小了。


比较模式是在匹配时产生动作,如果利用比较匹配、输出翻转模式产生波形,只能产生方波。而且输出频率在除以周期值以后还得除以2


PWM才是真正的像是在比较大小,计数值小于比较值时输出高电平,大于比较值时输出低电平(OCREF引脚)。PWM中心对称模式是指所有输出引脚的波形中心对称,它内部实现是通过先向上计数、再向下计数实现的。其输出频率在相同周期值的情况下,为向上计数模式的一半


 


2)添加命令msplay,先设计出框架


1要使用定时器4,先要使能时钟。


2要使用定时器4的更新中断,必须设置NVIC


3)让定时器3工作于PWM1模式下,其频率设置为72000 000/256大概为280kHz,这就是PWM频率(要工作于高频下,我试着将频率降低,结果就只能听到噪音)。


因为测试文件是单声道、8位采样的,所以采样值小的时候,PWM占空比就小,PWM输出平均幅度就与波形文件中的数据大小成正比,因此扬声器可以将声音还原


 


void Speaker_Start( WAVINFO *WavInfo )


{


  GPIO_InitTypeDef GPIO_InitStructure;  //进行GPIO端口设置的数据结构


  NVIC_InitTypeDef NVIC_InitStructure;


  TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; //时基单元配置数据结构


  TIM_OCInitTypeDef  TIM_OCInitStructure; //输出模式设置数据结构


 


  /* PB0脚接着定时器3 CH3PWM脉冲输出,该输出控制扬声器 */


  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;


  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;


  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;


  GPIO_Init(GPIOB, &GPIO_InitStructure);


 


  /* 时基单元配置:TIM3计数频率 =72MHz除以分频系数。*/


  TIM_TimeBaseStructure.TIM_Period = 255;    //写入自动装载寄存器,PWM频率大约36000000/256


  TIM_TimeBaseStructure.TIM_Prescaler = 0; //


  TIM_TimeBaseStructure.TIM_ClockDivision = 0;//与什么采样相关,这里用不到


  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;


  TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);


 


  /* TIM3 配置在PWM1模式, */


  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;


  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能


  TIM_OCInitStructure.TIM_OutputNState =  TIM_OutputNState_Disable;


  TIM_OCInitStructure.TIM_Pulse = 0;  /* 占空比初始化为0*/


  TIM_OCInitStructure.TIM_OCPolarity =    TIM_OCPolarity_High;//高电平有效,因为PWM输出接三极管基极。


  TIM_OCInitStructure.TIM_OCIdleState =      TIM_OCIdleState_Reset;


  TIM_OC3Init(TIM3, &TIM_OCInitStructure);


  TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);


 


  /* TIM4 配置为定时中断,其频率为WAV文件采样频率 */


  TIM_TimeBaseStructure.TIM_Period = 72000000/WavInfo->SampFreq;


  TIM_TimeBaseStructure.TIM_Prescaler = 0x0;   


  TIM_TimeBaseStructure.TIM_ClockDivision = 0;  


  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;      


  TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);


 


  /* Enable the TIM4 Interrupt */


  NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQChannel;


  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;


  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;


  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;


  NVIC_Init(&NVIC_InitStructure);  //刚开始时忘了这个。


  /* Start TIM3 */


  TIM_Cmd(TIM3, ENABLE);


  /* Start TIM4 */


  TIM_Cmd(TIM4, ENABLE);


  /* Enable TIM4 update interrupt */


  TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);


}


 


程序下载,已经可以听到音乐了,但是声音比较小。


 

 


 


 


 

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户1358696 2011-9-29 12:11

看了很久,不是很懂。想向博主请教一些问题。 我希望只使用一个定时器完成PWM输出。之前使用LPC213x的DAC资源时,也可以只使用一个定时器完成的。但现在改到STM32,我就不知道应该怎么办了。 看了楼主的文章后,我把 WAVE数据 用于 PWM的占空比,但周期设为8K是无法输出正常音频的。请问应该怎么设置呢? 应用环境如下: (1)WAVE数据都放在ROM中,可从ROM中直接读取。WAVE参数:8k采样率、8位、单声道。 (2)使用TIM4_CH3作PWM通道 (3)使用STM32F103VET芯片

tengjingshu_112148725 2010-4-11 01:28

博主辛苦了,写了这篇这么好的文章
相关推荐阅读
nthq2004 2010-05-08 20:04
USB自定义设备驱动02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  本来还想编写应用程序测试一下自定...
nthq2004 2010-05-07 21:35
USB自定义设备驱动01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、USB设备驱动入门1、学习目...
nthq2004 2010-05-04 21:01
智林开发板上实现自定义的USB HID设备
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />  一、自定义HID设备的相关概念1...
nthq2004 2010-05-01 21:58
U盘例程在智林开发板上的移植
 <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 一、移植前的准备工作1、有哪些操...
nthq2004 2010-04-30 19:19
U盘实现流程跟踪分析02
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   二、追踪USB大容量设备的实现...
nthq2004 2010-04-27 21:51
U盘实现流程跟踪分析01
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />   一、追踪USB大容量设备的实现...
我要评论
2
15
关闭 站长推荐上一条 /3 下一条