1. 由于产品需要,需要使用MCU的PWM功能实现语音播放
2.WAV文件存放于外部FLASH中
3.目前现状,效果很差,杂音非常多,“叮叮”这种声音稍好,但像”谢谢“ “请重新输入”这种语音,只能模糊的听个大概! 而且杂音非常大
以下是跟语音有关的函数
/*******************************************************************************
* Function Name : Speaker_Timer_Config
* Description : Configure and enable the timer
* Input : None.
* Return : None.
*******************************************************************************/
void Speaker_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能PB端口时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM7,ENABLE); //TIM4,TIM7时钟使能
//GPIO配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; ////PB6输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_SetBits(GPIOB,GPIO_Pin_6);
//TIM7配置
TIM_TimeBaseStructure.TIM_Period = 3273; //设定计数器自动重装值3273 中断频率=72M/3273=11Khz
// TIM_TimeBaseStructure.TIM_Period = 1636; //22k
// TIM_TimeBaseStructure.TIM_Period = 818; //44k
TIM_TimeBaseStructure.TIM_Prescaler =0; //预分频器为0.
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM7,TIM_IT_Update, ENABLE ); //使能或者失能指定的TIM中断
TIM_Cmd(TIM7, ENABLE); //使能TIMx外设
//TIM4配置
TIM_TimeBaseStructure.TIM_Period = 0XFF; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 80K
TIM_TimeBaseStructure.TIM_Prescaler =0; //设置用来作为TIMx时钟频率除数的预分频值 不分频
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //CH1 PWM2模式
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //OC1 低电平有效
TIM_OC1Init(TIM4, &TIM_OCInitStructure); //根据TIM_OCInitStruct中指定的参数初始化外设TIMx
TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable); //CH1 预装载使能
TIM_ARRPreloadConfig(TIM4, ENABLE); //使能TIMx在ARR上的预装载寄存器
TIM_Cmd(TIM4, ENABLE); //使能TIMx外设
//NVIC中断配置
NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; //组2,优先级次之
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/********************************************************************************************************************8
函数功能 : 提取WAV文件头
输入参数 :
输出参数 :
操作说明 :使用的为硬件PWM+DAC+RC滤波
*********************************************************************************************************************/
void Wav_HeadChange(u8* source, WAVE_HEAD* h){
u8 i;
u32 m3,m2,m1,m0;
for(i=0;i<4;i++) h->RIFF
=source;
m0=0;m1=0;m2=0;m3=0;
m0=source[4];
m1=source[5];
m1=m1<<8;
m2=source[6];
m2=m2<16;
m3=source[7];
m3=m3<24;
h->ALL_DATA=m3|m2|m1|m0;
for(i=0;i<4;i++) h->WAVE=source[i+0x08];
for(i=0;i<4;i++) h->FMT=source[i+0x0c];
h->Filtrate[0]=source[0x13];
h->Filtrate[1]=source[0x12];
h->Filtrate[2]=source[0x11];
h->Filtrate[3]=source[0x10];
h->CM=source[0x14]+source[0x15]*256;
h->Track=source[0x16]+source[0x17]*256;
m0=0;m1=0;m2=0;m3=0;
m0=source[0x18];
m1=source[0x19];
m1=m1<<8;
m2=source[0x1a];
m2=m2<16;
m3=source[0x1b];
m3=m3<24;
h->Frequency=m3|m2|m1|m0;
m0=0;m1=0;m2=0;m3=0;
m0=source[0x1c];
m1=source[0x1d];
m1=m1<<8;
m2=source[0x1e];
m2=m2<16;
m3=source[0x1f];
m3=m3<24;
h->Speed=m3|m2|m1|m0;
h->Data_Block=source[0x20]+source[0x21]*256;
h->Wide=source[0x22]+source[0x23]*256;
for(i=0;i<4;i++) h->Data=source[i+0x24];
m0=0;m1=0;m2=0;m3=0;
m0=source[0x28];
m1=source[0x29];
m1=m1<<8;
m2=source[0x2a];
m2=m2<16;
m3=source[0x2b];
m3=m3<24;
h->Data_Size=m3|m2|m1|m0;
}
/********************************************************************************************************************8
函数功能 : WAV文件播放驱动
输入参数 :路径(含文件名)
输出参数 :0,正常播放完成
1,下一曲
2,上一曲
0XFF,出现错误了
操作说明 :使用的为硬件PWM+DAC+RC滤波
WAV头文件格式:(小端结构,低字节在前)
00~03 ASCLL码 "RIFF" 固定格式,表示这是WAVE文件头
04~07 文件大小 4字节 低字节在前 后续数据包的大小
08~0B WAV文件标志 ASCLL "WAVE"
0c~0f 波形格式 ASCLL "fmt "最后一个为空格
10~13 过滤字节 通常为“00000010”,此低字节在前
14~15 格式类型 “0001”表未PCM编码
16~17 通道数 “0001”单声道 “0002”双声道
18~1B 采样率 本例为“00002B11” 转换为11025采样率
1C~1F 波形数据传输速率 本例为"00002B11"
20~21 数据块的长度(字节)本例为“0001" 表明单字节数据
22~23  CM位宽 本例为“0008” 表示8位宽
24~27 ASCLL “data"表示头结束,开始数据部分
28~2B 数据部分长度
WAV文件通道为2种格式
1.单声道 11.025K采样率 8位宽
2.双声道 44.1K采样率 16位宽
*********************************************************************************************************************/
u8 WAV_play_song(u8 *pname)
{
FIL* fmp3;
u8 *databuf;
WAVE_HEAD* wav_head;
u16 br;
u8 res,rval;
rval=0;
fmp3=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //申请内存
wav_head=(WAVE_HEAD*)mymalloc(SRAMIN,sizeof(WAVE_HEAD));
databuf=(u8*)mymalloc(SRAMIN,4096); //开辟4096字节的内存区域
if(wav_head==NULL)rval=0XFF ; //内存申请失败.
if(fmp3==NULL)rval=0XFF ; //内存申请失败.
if(rval==0) //内存足够
{
res=f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开文件
if(res==0) //打开成功.
{
res=f_read(fmp3,databuf,44,(UINT*)&br); //读出44字节的包头
if(res==0){ //数据头读取正确
Wav_HeadChange(databuf,wav_head);
if((wav_head->RIFF[0]=='R')&&
(wav_head->RIFF[1]=='I')&&
(wav_head->RIFF[2]=='F')&&
(wav_head->RIFF[3]=='F')){
Wav_Buf.Flag=0; //清播放标志
while(wav_head->Data_Size){ //还有数据
while(Wav_Buf.Flag); //等待数据转换完成
if(wav_head->Data_Size>4096){ //数据够4096
res=f_read(fmp3,(u8 *)Wav_Buf.Buf,4096,(UINT*)&br);//读出4096个字节
if(res==0){ //正常读取
Wav_Buf.Size=4096;
Wav_Buf.Num=0;
Wav_Buf.Flag=1;
wav_head->Data_Size-=4096;
}
else{ //异常
rval=0xff;break;
}
}
else{ //数据不够4096
res=f_read(fmp3,(u8 *)Wav_Buf.Buf,wav_head->Data_Size,(UINT*)&br);//读出剩余字节
if(res==0){ //正常读取
Wav_Buf.Size=wav_head->Data_Size;
Wav_Buf.Num=0;
Wav_Buf.Flag=1;
wav_head->Data_Size=0;
}
else{ //异常
rval=0xff;break;
}
}
}
}
}
f_close(fmp3);
}
else rval=0XFF; //文件打开出现错误
}
myfree(SRAMIN,fmp3);
myfree(SRAMIN,wav_head);
myfree(SRAMIN,databuf);
return rval;
}
//播放一曲指定的歌曲
/********************************************************************************************************************8
函数功能 : 文件播放驱动
输入参数 :路径(含文件名)
输出参数 :0,正常播放完成
1,下一曲
2,上一曲
0XFF,出现错误了
操作说明 :
*********************************************************************************************************************/
u8 mp3_play_song(u8 *pname)
{
FIL* fmp3;
u16 br;
u8 res,rval;
u8 *databuf;
u16 i=0;
u8 key;
rval=0;
fmp3=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //申请内存
databuf=(u8*)mymalloc(SRAMIN,4096); //开辟4096字节的内存区域
if(databuf==NULL||fmp3==NULL)rval=0XFF ; //内存申请失败.
if(rval==0) //内存足够
{
VS_Restart_Play(); //重启播放
VS_Set_All(); //设置音量等信息
VS_Reset_DecodeTime(); //复位解码时间
res=f_typetell(pname); //得到文件后缀
if(res==0x4c) //如果是flac,加载patch
{
VS_Load_Patch((u16*)vs1053b_patch,VS1053B_PATCHLEN);
}
res=f_open(fmp3,(const TCHAR*)pname,FA_READ);//打开文件
if(res==0) //打开成功.
{
VS_SPI_SpeedHigh(); //高速
while(rval==0)
{
res=f_read(fmp3,databuf,4096,(UINT*)&br);//读出4096个字节
i=0;
do//主播放循环
{
if(VS_Send_MusicData(databuf+i)==0)//给VS10XX发送音频数据
{
i+=32;
}else
{
key=KEY_Scan(0);
switch(key)
{
case KEY_RIGHT:
rval=1; //下一曲
break;
case KEY_LEFT:
rval=2; //上一曲
break;
case KEY_UP: //音量增加
if(vsset.mvol<250)
{
vsset.mvol+=5;
VS_Set_Vol(vsset.mvol);
}else vsset.mvol=250;
mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
break;
case KEY_DOWN: //音量减
if(vsset.mvol>100)
{
vsset.mvol-=5;
VS_Set_Vol(vsset.mvol);
}else vsset.mvol=100;
mp3_vol_show((vsset.mvol-100)/5); //音量限制在:100~250,显示的时候,按照公式(vol-100)/5,显示,也就是0~30
break;
}
mp3_msg_show(fmp3->fsize);//显示信息
}
}while(i<4096);//循环发送4096个字节
if(br!=4096||res!=0)
{
rval=0;
break;//读完了.
}
}
f_close(fmp3);
}
else rval=0XFF; //文件打开出现错误
}
myfree(SRAMIN,databuf);
myfree(SRAMIN,fmp3);
return rval;
}
//用示波器测试过,中断频率为11.05K
void TIM7_IRQHandler(void)
{
if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
LED1=!LED1;
if(Wav_Buf.Flag){
TIM4->CCR1=Wav_Buf.Buf[Wav_Buf.Num];
Wav_Buf.Num+=1;
if(Wav_Buf.Num==Wav_Buf.Size){Wav_Buf.Num=0;Wav_Buf.Flag=0;}
}
}
TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIMx的中断待处理位:TIM 中断源
}
请专家不吝赐教,感谢
---------------------------------
直接测试我们usb声卡,效果如何?
其实我想原理不复杂啊,就是在定时器TIM7中,往TIM4的CCR1放WAV头文件后的数据就可以啊,硬件和软件配件都不用改,怎么就会有问题呢
---------------------------------
看是不是你的转换有问题
一周热门 更多>