参考战舰板 USB声卡例程,实现PWM+RC滤波播放WAV语音失败,求指导

2019-08-17 06:25发布

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 中断源


请专家不吝赐教,感谢

友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
5条回答
hujianggao
1楼-- · 2019-08-17 08:53
 精彩回答 2  元偷偷看……
正点原子
2楼-- · 2019-08-17 13:40
回复【2楼】hujianggao:
---------------------------------
直接测试我们usb声卡,效果如何?
hujianggao
3楼-- · 2019-08-17 15:42
USB声卡的效果还不错,接“酷狗音乐”,音质都还不错,但用来播放FLASH中的语音文件就出问题。
其实我想原理不复杂啊,就是在定时器TIM7中,往TIM4的CCR1放WAV头文件后的数据就可以啊,硬件和软件配件都不用改,怎么就会有问题呢
正点原子
4楼-- · 2019-08-17 17:41
回复【4楼】hujianggao:
---------------------------------
看是不是你的转换有问题
xueshawu
5楼-- · 2019-08-17 19:41
楼主能把修改后的代码给我参考下么,我也在做这个功能

一周热门 更多>