<?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)重点是先要把定时器的配置弄清楚
首先TIM2、TIM3和TIM4都位于APB1,最高频率36MHz,TIM2已经被我使用,利用通道3产生方波,为液晶提供稳压电源驱动脉冲。它的输出是采用了输出比较模式。下面先回顾一下一个重要寄存器和工作模式。
预分频PSC:控制计数的实际频率,定时器并不直接对36MHz的脉冲计数,而要先分频。计数频率 = 36MHz 除以 (PSC +1)。
自动重装载ARR:实际控制计数周期,相当于定时器的提前溢出值,比如设置成0x100,则当从0计数到0x100时,定时器自动复位到初始值。(我的理解不知道对不对)。或者在向下计数模式时,每次都从自动装载值开始计数,向下计数到0,然后又重新装载ARR的值。
其它的再看一下参考资料:
时钟选择:我现在是采用内部时钟源,所以要禁止从模式。CR1中CEN使能内部时钟,开启定时器,DIR控制计数方向。
比较通道的预装载含义:如果不使能预装载,比较值总是立即写入当前比较寄存器。如果使能,则设定的比较值现在不起作用,等到一个更新事件发生时,才写入当前比较寄存器。
自动重装载寄存器也有同样的预装载的含义。
向上计数PWM模式1:初始化时,计数值为0,比较输出参考电平为高。当计数值大于比较值时,输出电平为低。所以写ARR是控制PWM的频率,写CCR是控制脉冲的占空比。但是要特别注意OCREF与OC输出极性之间的关系,要不然有可能你想增加占空比的时候反而减小了。
比较模式是在匹配时产生动作,如果利用比较匹配、输出翻转模式产生波形,只能产生方波。而且输出频率在除以周期值以后还得除以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 CH3,PWM脉冲输出,该输出控制扬声器 */
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);
}
程序下载,已经可以听到音乐了,但是声音比较小。
用户1358696 2011-9-29 12:11
tengjingshu_112148725 2010-4-11 01:28