使用vs1053在stm32上录音播放(不使用文件系统、SD卡)

前言:

最近在做毕设的过程中,需要用到录音播放方面的知识。所以,在借鉴了正点原子例程之后,成功修改出了16k录音+播放的代码。在录音之后马上接着播放音频是为了验证录制的音频是否正常。在修改的过程中也遇到了不少bug,记录于此,以表警示,希望今后不再重蹈覆辙。本文仅挑关键性代码进行分析:

工具:

keil5,stm32F103ZET6战舰开发板、蓝牙模块,ST-LINK烧录器

(开发板上的USB_232坏了,所以使用蓝牙模块仅仅是为了验证一些关键性数据是否正确,当然你也可以使用其他手段)

代码分析:

//初始化VS1053 ,使用SPI1进行通信,速度设置为最低,Fpclk之后256分频(Fpclk=72Mhz)

VS_Init();                      

while(1)

{

//在循环中检查到有按键按键就调用函数,开始录音->播放录音,返回音频数据地址

dstbuf=recoder_play();

}


SIZEWAVD, SIZEWAVH 在头文件中定义如下

#define SIZEWAVD 64                //wav音频数据大小
#define SIZEWAVH sizeof(__WaveHeader)            //wav文件头大小

以下是recoder_play()函数分析:

/*函数名:
**功能:生成录音数据,并保存在外部SRAM的特定地址内.
**返回值:(成功)返回存放wav音频数据的位置     (失败)分配失败
*/
u8* recoder_play(void)
{
    u32 sectorsize=0;
    u8 *recbuf_old;
    u8 *recbuf;                        //数据内存     
     u16 w;
    u32 idx=0;        
    __WaveHeader *wavhead=0;

    LCD_ShowString(30,210,200,16,16,"RAM...");
    u3_printf("Ram Test:0X%04X\r\n",VS_Ram_Test()); //打印RAM测试结果,VS1053为0X83FF表明完好;
    LCD_ShowString(30,210,200,20,16,"sintest");
    VS_Sine_Test();               //可以听到“嘟”一声
    LCD_ShowString(30,210,200,16,16,"<>");          
    

    //申请 SIZEWAVD k音频数据空间 + SIZEWAVH 字节wav文件头空间

    //录音码率为16000*16*1=256000kbps 即1秒可以传32000byte
    recbuf_old=mymalloc(SRAMEX,SIZEWAVD*1024+SIZEWAVH);    
    if(recbuf_old==NULL) return NULL;      
    u3_printf("recbuf_old=%x\n",recbuf_old); //通过蓝牙模块返回数据到手机
    
    recbuf=recbuf_old+SIZEWAVH;        //指向存放wav音频数据的位置
    wavhead=(__WaveHeader*)recbuf_old;

 

    //初始化wav文件头,修改:

    //wavhead->riff.ChunkSize=SIZEWAVD*1024+36;           (wavhead->data.ChunkSize+36)

    //wavhead->fmt.SampleRate=16000;        (16Khz采样率 采样速率)     

    //wavhead->data.ChunkSize=SIZEWAVD*1024;            (数据大小byte)
    recoder_wav_init(wavhead);             
    recoder_enter_rec_mode();              //进入录音模式,采样频率改为16k  
  while(VS_RD_Reg(SPI_HDAT1)>>8);            //等到buf 较为空闲再开始录音  


    LCD_ShowString(30+30,250,200,16,16,"START.");          //可以开始录音               
    while(sectorsize < SIZEWAVD*2)
    {
            w=VS_RD_Reg(SPI_HDAT1);    
        if((w>=256)&&(w<896))
        {
                                 
            while(idx             {     
                w=VS_RD_Reg(SPI_HDAT0);            
                recbuf[idx++]=w&0XFF;
                recbuf[idx++]=w>>8;                        
                if((idx&0x1FF)==0) break;    //相当于idx%512
            }               
            sectorsize++;    //扇区数增加1
        }        
        LED1=!LED1;//DS0闪烁 
    }                
    rec_play_wav(recbuf_old);
    return recbuf;
}

注意!!在循环读取HDAT0 寄存器内容的时候,不要使用打印调试语句,或者其他比较耗时的语句,这样会造成寄存器中的音频数据漏读,这个bug调了好久,一直以为是spi的读取速度太慢了导致的,就跑去提高vs1053的内部时钟CLKI,从而提高spi的读取速度(不能超过CLKI/7)。来回折腾好久才发现问题所在,同时,即使你后面的播放函数是正确的,也会产生一些奇怪的问题:比如播放语速加快,播放不了等等。


以下是rec_play_wav()函数分析:

/*函数名:rec_play_wav
**功能:播放音频
**参数:u8 *类型的指针,指向wav音频文件头+音频数据
*/
void rec_play_wav(u8 *databuf)
{     
    u32 i=0;                
    if(databuf == NULL)
    {
        while(1)
                {u3_printf("in recoder_play,mymalloc fail!\n"); delay_ms(1000);}
    }
    VS_HD_Reset();               //硬复位
    VS_Soft_Reset();          //软复位 
    VS_Set_All();            //设置音量等参数              
    VS_Reset_DecodeTime();    //复位解码时间            
    VS_SPI_SpeedHigh();    //高速                           
     
    do//主播放循环
        {      
        if(VS_Send_MusicData(databuf+i)==0)
        {
            i+=32;//给VS10XX发送音频数据        
        }
        else 
        {
            recoder_show_time(VS_Get_DecodeTime());//显示播放时间     
        }
                
    }while(i                 
    VS_SPK_Set(0);    //关闭板载喇叭         

}     

若需要播放成功,不能单单将音频数据发送给vs1053,还要包含wav文件头,这样vs1053才会帮你自动去解析播放音频。

你可能感兴趣的:(单片机)