硬件——STM32 , 录音
戰艦V3的錄音程序解析
上一章,我們實現了一個簡單的音樂播放器,本章我們將在上一章的基礎上,實現一個簡單的錄音機,實現WAV錄音。本章分為如下幾個部:
50.1 WAV簡介
50.2 硬件設計
50.3 軟件設計
50.4 下載驗證
?
50.1 WAV簡介?
?
WAV即WAVE文件,WAV是計算機領域最常用的數字化聲音文件格式之一,它是微軟專門為Windows系統定義的波形文件格式(Waveform Audio),由于其擴展名為"*.wav"。它符合RIFF(Resource Interchange File Format)文件規范,用于保存Windows平臺的音頻信息資源,被Windows平臺及其應用程序所廣泛支持,該格式也支持MSADPCM,CCITT A LAW等多種壓縮運算法,支持多種音頻數字,取樣頻率和聲道,標準格式化的WAV文件和CD格式一樣,也是44.1K的取樣頻率,16位量化數字,因此在聲音文件質量和CD相差無幾!
?
ALIENTEK戰艦STM32開發板板載的VS1053支持2種格式的WAV錄音:PCM格式或者IMA ADPCM格式,其中PCM(脈沖編碼調制)是最基本的WAVE文件格式,這種文件直接存儲采樣的聲音數據沒有經過任何的壓縮。而IAM ADPCM則是使用了壓縮算法,壓縮比率為4:1。
?
本章,我們主要討論PCM,因為這個最簡單。我們將利用VS1053實現16位,8Khz采樣率的單聲道WAV錄音(PCM格式)。要想實現WAV錄音得先了解一下WAV文件的格式,WAVE文件是由若干個Chunk組成的。按照在文件中的出現位置包括:RIFF WAVE Chunk、 Format Chunk、 Fact Chunk(可選)和 Data Chunk。每個Chunk由塊標識符、數據大小和數據三部分組成,如圖50.1.1所示:
其中塊標識符由4個ASCII碼構成,數據大小則標出緊跟其后的數據的長度(單位為字節),注意這個長度不包含塊標識符和數據大小的長度,即不包含最前面的8個字節。所以實際Chunk的大小為數據大小加8。
?
首先,我們來看看RIFF塊(RIFF WAVE Chunk),該塊以“RIFF”作為標示,緊跟wav文件大小(該大小是wav文件的總大小-8),然后數據段為“WAVE”,表示是wav文件。RIFF塊的Chunk結構如下:
?
//RIFF塊
?
typedef __packed struct
?
{
?
??? u32 ChunkID;??????? ?? ? //chunk id;這里固定為"RIFF",即0X46464952
?
??? u32 ChunkSize ;??????????? //集合大小;文件總大小-8
?
??? u32 Format;??? ?? ???????? //格式;WAVE,即0X45564157
?
}ChunkRIFF ;
接著,我們看看Format塊(Format Chunk),該塊以“fmt ”作為標示(注意有個空格!),一般情況下,該段的大小為16個字節,但是有些軟件生成的wav格式,該部分可能有18個字節,含有2個字節的附加信息。Format塊的Chunk結構如下:
?
//fmt塊
?
typedef __packed struct
?
{
?
??? u32 ChunkID;??????? ?? ? //chunk id;這里固定為"fmt ",即0X20746D66
?
??? u32 ChunkSize ;??????????? //子集合大小(不包括ID和Size);這里為:20.
?
??? u16 AudioFormat;? ? ??? //音頻格式;0X10,表示線性PCM;0X11表示IMA ADPCM
?
?????? u16 NumOfChannels;??? //通道數量;1,表示單聲道;2,表示雙聲道;
?
?????? u32 SampleRate;?????????? //采樣率;0X1F40,表示8Khz
?
?????? u32 ByteRate;?????????????? //字節速率;
?
?????? u16 BlockAlign;??????????? //塊對齊(字節);
?
?????? u16 BitsPerSample;????????????? //單個采樣數據大小;4位ADPCM,設置為4
?
}ChunkFMT;?
接下來,我們再看看Fact塊(Fact Chunk),該塊為可選塊,以“fact”作為標示,不是每個WAV文件都有,在非PCM格式的文件中,一般會在Format結構后面加入一個Fact塊,該塊Chunk結構如下:
?
//fact塊
?
typedef __packed struct
?
{
?
??? u32 ChunkID;??????? ?? ? ?????? //chunk id;這里固定為"fact",即0X74636166;
?
??? u32 ChunkSize ;??????????? ?? ? //子集合大小(不包括ID和Size);這里為:4.
?
??? u32 DataFactSize;? ? ??? ?????? //數據轉換為PCM格式后的大小
?
}ChunkFACT;
DataFactSize是這個Chunk中最重要的數據,如果這是某種壓縮格式的聲音文件,那么從這里就可以知道他解壓縮后的大小。對于解壓時的計算會有很大的好處!不過本章我們使用的是PCM格式,所以不存在這個塊。
最后,我們來看看數據塊(Data Chunk),該塊是真正保存wav數據的地方,以“data”'作為該Chunk的標示。然后是數據的大小。緊接著就是wav數據。根據Format Chunk中的聲道數以及采樣bit數,wav數據的bit位置可以分成如表50.1.1所示的幾種形式:
本章,我們采用的是16位,單聲道,所以每個取樣為2個字節,低字節在前,高字節在后。數據塊的Chunk結構如下:
//data塊
typedef __packed struct
{
??? u32 ChunkID;??????? ?? ? //chunk id;這里固定為"data",即0X61746164
??? u32 ChunkSize ;??????????? //子集合大小(不包括ID和Size);文件大小-60.
}ChunkDATA;
通過以上學習,我們對WAVE文件有了個大概了解。接下來,我們看看如何使用VS1053實現WAV(PCM格式)錄音。
激活PCM錄音
VS1053激活PCM錄音需要設置的寄存器和相關位如表50.1.2所示:
通過設置SCI_MODE寄存器的2、12、14位,來激活PCM錄音,SCI_MODE的各位描述見表49.1.4(也可以參考VS1053的數據手冊)。SCI_AICTRL0寄存器用于設置采樣率,我們本章用的是8K的采樣率,所以設置這個值為8000即可。SCI_AICTRL1寄存器用于設置AGC,1024相當于數字增加1,這里建議大家設置AGC在4(4*1024)左右比較合適。SCI_AICTRL2用于設置自動AGC的時候的最大值,當設置為0的時候表示最大64(65536),這個大家按自己的需要設置即可。最后,SCI_AICTRL3,我們本章用到的是咪頭線性PCM單聲道錄音,所以設置該寄存器值為6。
通過這幾個寄存器的設置,我們就激活VS1053的PCM錄音了。不過,VS1053的PCM錄音有一個小BUG,必須通過加載patch才能解決,如果不加載patch,那么VS1053是不輸出PCM數據的,VLSI提供了我們這個patch,只需要通過軟件加載即可。
?
讀取PCM數據
?
在激活了PCM錄音之后,SCI_HDAT0和SCI_HDAT1有了新的功能。VS1053的PCM采樣緩沖區由1024個16位數據組成,如果SCI_HDAT1大于0,則說明可以從SCI_HDAT0讀取至少SCI_HDAT1個16位數據,如果數據沒有被及時讀取,那么將溢出,并返回空的狀態。
?
注意,如果SCI_HDAT1≥896,最好等待緩沖區溢出,以免數據混疊。所以,對我們來說,只需要判斷SCI_HDAT1的值非零,然后從SCI_HDAT0讀取對應長度的數據,即完成一次數據讀取,以此循環,即可實現PCM數據的持續采集。
?
最后,我們看看本章實現WAV錄音需要經過哪些步驟:
?
1)設置VS1053 PCM采樣參數
?
這一步,我們要設置PCM的格式(線性PCM)、采樣率(8K)、位數(16位)、通道數(單聲道)等重要參數,同時還要選擇采樣通道(咪頭),還包括AGC設置等。可以說這里的設置直接決定了我們wav文件的性質。
?
2)激活VS1053的PCM模式,加載patch
?
通過激活VS1053的PCM格式,讓其開始PCM數據采集,同時,由于VS1053的BUG,我們需要加載patch,以實現正常的PCM數據接收。
?
3)創建WAV文件,并保存wav頭
?
在前兩部設置成功之后,我們即可正常的從SCI_HDAT0讀取我們需要的PCM數據了,不過在這之前,我們需要先在創建一個新的文件,并寫入wav頭,然后才能開始寫入我們的PCM數據。
?
4)讀取PCM數據
?
經過前面幾步的處理,這一步就比較簡單了,只需要不停的從SCI_HDAT0讀取數據,然后存入wav文件即可,不過這里我們還需要做文件大小統計,在最后的時候寫入wav頭里面。
?
5)計算整個文件大小,重新保存wav頭并關閉文件
?
在結束錄音的時候,我們必須知道本次錄音的大小(數據大小和整個文件大小),然后更新wav頭,重新寫入文件,最后因為FATFS,在文件創建之后,必須調用f_close,文件才會真正體現在文件系統里面,否則是不會寫入的!所以最后還需要調用f_close,以保存文件。
?
50.2 硬件設計
?
本章實驗功能簡介:開機的時候先檢測字庫,然后初始化VS1053,進行RAM測試和正弦測試,之后,檢測SD卡根目錄是否存在RECORDER文件夾,如果不存在則創建,如果創建失敗,則報錯。在找到SD卡的RECORDER文件夾后,即設置VS1053進入錄音模式,此時可以在耳機聽到VS1053采集的音頻。KEY0用于開始/暫停錄音,KEY2用于保存并停止錄音,WK_UP用于AGC增加、KEY1用于AGC減小,TPAD用于播放最近一次的錄音。當我們按下KEY0的時候,可以在屏幕上看到錄音文件的名字,以及錄音時間,然后通過KEY2可以保存該文件,同時停止錄音(文件名和時間也都將清零),在完成一個錄音后,我們可以通過按TPAD按鍵,來試聽剛剛的錄音。DS0用于提示程序正在運行,DS1用于指示當前是否處于錄音暫停狀態。
?
本實驗用到的資源如下:
?
1)? 指示燈DS0和DS1
?
2)? 五個按鍵(WK_UP/KEY0/KEY1/KEY2/TPAD)
?
3)? 串口
?
4)? TFTLCD模塊
?
5)? SD卡
?
6)? SPI FLASH
?
7)? VS1053
?
8)? 74HC4052
?
9)? TDA1308
?
本章用到的硬件資源同上一章基本一樣,就多了一個TPAD按鍵,用于播放最近一次錄音。
?
本實驗,大家需要準備1個SD卡和一個耳機,分別插入SD卡接口和耳機接口,然后下載本實驗就可以實現一個簡單的錄音機了。
?
50.3 軟件設計
?
打開上一章的工程,首先在APP文件夾下面新建recorder.c和recorder.h兩個文件,然后將recorder.c加入到工程的APP組下。
?
因為recorder.c代碼比較多,我們這里僅介紹其中的三個函數,首先是設置VS1053進入PCM模式的函數:recoder_enter_rec_mode,該函數代碼如下:
?
//進入PCM 錄音模式
?
//agc:0,自動增益.1024相當于1倍,512相當于0.5倍,最大值65535=64倍?????????? ?
?
void recoder_enter_rec_mode(u16 agc)
?
{
?
?????? //如果是IMA ADPCM,采樣率計算公式如下:
?
????? //采樣率=CLKI/256*d;?
?
?????? //假設d=0,并2倍頻,外部晶振為12.288M.那么Fc=(2*12288000)/256*6=16Khz
?
?????? //如果是線性PCM,采樣率直接就寫采樣值
?
?? ? VS_WR_Cmd(SPI_BASS,0x0000);???
?
????? VS_WR_Cmd(SPI_AICTRL0,8000);?? //設置采樣率,設置為8Khz
?
????? VS_WR_Cmd(SPI_AICTRL1,agc);????????????
?
//設置增益,0,自動增益.1024相當于1倍,512相當于0.5倍,最大值65535=64倍?
?
????? VS_WR_Cmd(SPI_AICTRL2,0);???????? //設置增益最大值,0,代表最大值65536=64X
?
????? VS_WR_Cmd(SPI_AICTRL3,6);???????? //左通道(MIC單聲道輸入)
?
?????? VS_WR_Cmd(SPI_CLOCKF,0X2000);??????
?
//設置VS10XX的時鐘,MULT:2倍頻;ADD:不允許;CLK:12.288Mhz
?
?????? VS_WR_Cmd(SPI_MODE,0x1804);??? //MIC,錄音激活???
?
????? delay_ms(5);?????????????????????????????? ?????? //等待至少1.35ms
?
????? VS_Load_Patch((u16*)wav_plugin,40);//VS1053的WAV錄音需要patch
?
}
該函數就是用我們前面介紹的方法,激活VS1053的PCM模式,本章,我們使用的是8Khz采樣率,16位單聲道線性PCM模式,AGC通過函數參數設置。最后加載patch(用于修復VS1053錄音BUG)。
第二個函數是初始化wav頭的函數:recoder_wav_init,該函數代碼如下:
//初始化WAV頭.
void recoder_wav_init(__WaveHeader* wavhead) //初始化WAV頭??????????????? ??
{
?????? wavhead->riff.ChunkID=0X46464952; ?????? //"RIFF"
?????? wavhead->riff.ChunkSize=0;?????????????? ?????? //還未確定,最后需要計算
?????? wavhead->riff.Format=0X45564157; ? ?????? //"WAVE"
?????? wavhead->fmt.ChunkID=0X20746D66; ???? //"fmt "
?????? wavhead->fmt.ChunkSize=16; ????????????????? //大小為16個字節
?????? wavhead->fmt.AudioFormat=0X01; ????????? //0X01,表示PCM;0X01,表示IMA ADPCM
????? wavhead->fmt.NumOfChannels=1;???????????? //單聲道
????? wavhead->fmt.SampleRate=8000;??????? ?????? //8Khz采樣率 采樣速率
????? wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*2;//16位,即2個字節
????? wavhead->fmt.BlockAlign=2;???????????????????? //塊大小,2個字節為一個塊
????? wavhead->fmt.BitsPerSample=16;????????????? ?????? //16位PCM
?? ? wavhead->data.ChunkID=0X61746164;?????? //"data"
????? wavhead->data.ChunkSize=0;???????????????????? //數據大小,還需要計算?
}
該函數初始化wav頭的絕大部分數據,這里我們設置了該wav文件為8Khz采樣率,16位線性PCM格式,另外由于錄音還未真正開始,所以文件大小和數據大小都還是未知的,要等錄音結束才能知道。該函數__WaveHeader結構體就是由前面介紹的三個Chunk組成,結構為:
//wav頭
typedef __packed struct
{
?????? ChunkRIFF riff;???? //riff塊
?????? ChunkFMT fmt;? //fmt塊
?????? //ChunkFACT fact; //fact塊 線性PCM,沒有這個結構體?
?????? ChunkDATA data;?? //data塊????????
}__WaveHeader;
最后,我們介紹recoder_play函數,是錄音機實現的主循環函數,該函數代碼如下:
//錄音機
//所有錄音文件,均保存在SD卡RECORDER文件夾內.
u8 recoder_play(void)
{
?????? u8 res, key, rval=0;
?????? __WaveHeader *wavhead=0;
?????? u32 sectorsize=0;u16 w; u16 idx=0;????
?????? FIL* f_rec=0;???????????????????????????? //文件??????????? ???
????? DIR recdir;???? ????????????????????????? //目錄
?????? u8 *recbuf;???????????????????????????????? //數據內存????
?????? u8 rec_sta=0;????????????????????????????? //錄音狀態
??????????????????????????????????????????????????????? //[7]:0,沒有錄音;1,有錄音;
??????????????????????????????????????????????????????? //[6:1]:保留
??????????????????????????????????????????????????????? //[0]:0,正在錄音;1,暫停錄音;
????? u8 *pname=0;
?????? u8 timecnt=0;???????????????????????????? //計時器??
?????? u32 recsec=0;????????????????????????????? //錄音時間
????? u8 recagc=4;?????????????????????????????? //默認增益為4
? ??? while(f_opendir(&recdir,"0:/RECORDER"))//打開錄音文件夾
????? {????
????????????? Show_Str(60,230,240,16,"RECORDER文件夾錯誤!",16,0); delay_ms(200);???????? ?
????????????? LCD_Fill(60,230,240,246,WHITE); delay_ms(200);????????? //清除顯示????
????????????? f_mkdir("0:/RECORDER");//創建該目錄??
?????? }
? ??? f_rec=(FIL *)mymalloc(SRAMIN,sizeof(FIL));?? //開辟FIL字節的內存區域
?????? if(f_rec==NULL)rval=1;?????? //申請失敗
????? wavhead=(__WaveHeader*)mymalloc(SRAMIN,sizeof(__WaveHeader));
//開辟__WaveHeader字節的內存區域
?????? if(wavhead==NULL)rval=1;
?????? recbuf=mymalloc(SRAMIN,512); ?????
?????? if(recbuf==NULL)rval=1;???? ? ?????????? ???
?????? pname=mymalloc(SRAMIN,30);?
//申請30個字節內存,存放路徑+名字,類似"0:RECORDER/REC00001.wav"
?????? if(pname==NULL)rval=1;
????? if(rval==0)????????????????????????????????????????????????????? //內存申請OK
?????? {?????
???????????? recoder_enter_rec_mode(1024*recagc);???????????????????????????
?? ???????? while(VS_RD_Reg(SPI_HDAT1)>>8);??????? //等到buf 較為空閑再開始?
? ?????????? recoder_show_time(recsec);??????????????????????? //顯示時間
????????????? recoder_show_agc(recagc);???????????????????????? //顯示agc
????????????? pname[0]=0;???????????????????????????????????????????? //pname沒有任何文件名????????????
????? ?? ? while(rval==0)
????????????? {
???????????????????? key=KEY_Scan(0);
???????????????????? switch(key)
???????????????????? {???????????
??????????????????????????? case KEY_LEFT:?? //STOP&SAVE
?????????????????????????????????? if(rec_sta&0X80)//有錄音
?????????????????????????????????? {
????????????????????????????????????????? wavhead->riff.ChunkSize=sectorsize*512+36;???? //文件大小-8;
??????????????????????????? ?? ???????? wavhead->data.ChunkSize=sectorsize*512;???????? //數據大小
????????????????????????????????????????? f_lseek(f_rec,0);?????????????????????????????????????????????? //偏移到文件頭.
??????????????????????????? ? ?????????? f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),
&bw);//寫入頭數據
????????????????????????????????????????? f_close(f_rec); sectorsize=0;
?????????????????????????????????? }
?????????????????????????????????? rec_sta=0; recsec=0; LED1=1; //關閉DS1?????????
?????????????????????????????????? LCD_Fill(60,230,240,246,WHITE);????
//清除顯示,清除之前顯示的錄音文件名??? ????
?????????????????????????????????? recoder_show_time(recsec);????????? //顯示時間
?????????????????????????????????? break;????
??????????????????????????? case KEY_RIGHT: //REC/PAUSE
?????????????????????????????????? if(rec_sta&0X01)//原來是暫停,繼續錄音
?????????????????????????????????? {
????????????????????????????????????????? rec_sta&=0XFE;//取消暫停
?????????????????????????????????? }else if(rec_sta&0X80)//已經在錄音了,暫停
?????????????????????????????????? {
????????????????????????????????????????? rec_sta|=0X01;?????? //暫停
?????????????????????????????????? }else??????????????????????????? //還沒開始錄音
?????????????????????????????????? {
?????? ????????????????????????????????? rec_sta|=0X80;?????? //開始錄音???? ???? ?
????????????????????????????????????????? recoder_new_pathname(pname);???????????????? //得到新的名字
????????????????????????????????????????? Show_Str(60,230,240,16,pname+11,16,0);? //顯示錄音文件名字
??????????????????????????? ???????????? recoder_wav_init(wavhead);?????????????????????? //初始化wav數據
?????? ????????????????????????????????? res=f_open(f_rec,(const TCHAR*)pname,FA_CREATE_ALWAYS
|FA_WRITE);
????????????????????????????????????????? if(res)//文件創建失敗
????????????????????????????????????????? {
???????????????????????????????????????????????? rec_sta=0;?????? //創建文件失敗,不能錄音
???????????????????? ??????????????????????????? rval=0XFE;??? //提示是否存在SD卡
????????????????????????????????????????? }else res=f_write(f_rec,(const void*)wavhead,
sizeof(__WaveHeader),&bw);//寫入頭數據
?????? ?????????????????????????? }
?????????????????????????????????? if(rec_sta&0X01)LED1=0;??? //提示正在暫停
?????????????????????????????????? else LED1=1;
?????????????????????????????????? break;
??????????????????????????? case KEY_UP:?????? //AGC+??
??????????????????????????? case KEY_DOWN: //AGC-
?????????????????????????????????? if(key==KEY_UP)recagc++;
?????????????????????????????????? else if(recagc)recagc--;
?????????????????????????????????? if(recagc>15)recagc=15;?????? //范圍限定為0~15,自動AGC.其他AGC倍數
?????????????????????????????????? recoder_show_agc(recagc);
?????????????????????????????????? VS_WR_Cmd(SPI_AICTRL1,1024*recagc);??????
//設置增益,0,自動增益.1024相當于1倍,512相當于0.5倍
?????????????????????????????????? break;
???????????????????? }
???????????????????? if(rec_sta==0X80)//已經在錄音了
?????? ????????????? {
????????????? ? ?????????? w=VS_RD_Reg(SPI_HDAT1);????
??????????????????????????? if((w>=256)&&(w<896))
??????????????????????????? {
?????? ?????????????????????????? idx=0;????????????????????????? ?? ? ?
????????????? ? ????????????????? while(idx<512) ??? //一次讀取512字節
?????????????????????????????????? {????
???????????????????? ??????????????????? w=VS_RD_Reg(SPI_HDAT0);????????????????????????? ?? ? ????
????????????? ?????????????????????????? recbuf[idx++]=w&0XFF; recbuf[idx++]=w>>8;?????????????????????
?????????????????????????????????? }???? ? ?????????? ?
?????? ?????????????????????????? res=f_write(f_rec,recbuf,512,&bw);//寫入文件
?????????????????????????????????? if(res) break;//寫入出錯.??????
?????????????????????????????????? sectorsize++;//扇區數增加1,約為32ms?????
??????????????????????????? }??????????????????
???????????????????? }else//沒有開始錄音,則檢測TPAD按鍵
???????????????????? {????????????????????????????????????????????????????? ?
??????????????????????????? if(TPAD_Scan(0)&&pname[0])//如果觸摸按鍵被按下,且pname不為空
??????????????????????????? {?????????????????????????
?????????????????????????????????? Show_Str(60,230,240,16,"播放:",16,0);???????????? ??
?????????????????????????????????? Show_Str(60+40,230,240,16,pname+11,16,0);//顯示播放的文件名字??
?????????????????????????????????? rec_play_wav(pname);??????????????????????? 播放pname
?????????????????????????????????? LCD_Fill(60,230,240,246,WHITE);???? /清除之前顯示的錄音文件名?
?????????????????????????????????? recoder_enter_rec_mode(1024*recagc);?????? //重新進入錄音模式????
???????????????????? ?? ???????? while(VS_RD_Reg(SPI_HDAT1)>>8); //等到buf 較為空閑再開始?
???????????????????? ? ?????????? recoder_show_time(recsec);???????????????? //顯示時間
???????????????????? ????????????? recoder_show_agc(recagc);????????????????? //顯示agc
?????????????????????????? }
??????????????????????????? delay_ms(5); timecnt++;
??????????????????????????? if((timecnt%20)==0)LED0=!LED0;//DS0閃爍
???????????????????? }
??????????????????? if(recsec!=(sectorsize*4/125))//錄音時間顯示
???????????????????? {???? ??
??????????????????????????? LED0=!LED0;//DS0閃爍
??????????????????????????? recsec=sectorsize*4/125;
??????????????????????????? recoder_show_time(recsec);//顯示時間
???????????????????? }
????????????? }???????????????????????????????? ??
?????? }???? ?? ???????? ????????????????????????? ????
?????? myfree(SRAMIN,wavhead); myfree(SRAMIN,recbuf);????? ?????? ?
????? myfree(SRAMIN,f_rec);?????? myfree(SRAMIN,pname);
?????? return rval;
}
該函數實現了我們在硬件設計時介紹的功能,我們就不詳細介紹了。recorder.c的其他代碼和recorder.h的代碼我們這里就不再貼出了,請大家參考光盤本實驗的源碼。保存recorder.c,最后,我們在test.c里面修改main函數如下:
int main(void)
{???????????
?? ? Stm32_Clock_Init(9);??? //系統時鐘設置
?????? delay_init(72);????????????? ?????? //延時初始化
?????? uart_init(72,9600); ????? //串口1初始化? ? ??
?????? LCD_Init();????????????????? //初始化液晶
?????? LED_Init();???????? ? //LED初始化
?????? KEY_Init();????????????????? //按鍵初始化
?????? TPAD_Init(72);???????????? //初始化觸摸按鍵? ?
?????? Audiosel_Init();???? ?????? //初始化音源選擇
?????? usmart_dev.init(72);????? //usmart初始化????
????? mem_init(SRAMIN);???? //初始化內部內存池????
????? VS_Init();????? ?
????? exfuns_init();??????????????? //為fatfs相關變量申請內存?
? ??? f_mount(0,fs[0]); ??????? //掛載SD卡
????? f_mount(1,fs[1]); ??????? //掛載FLASH.
?????? POINT_COLOR=RED;?????
????? while(font_init()) ??????? //檢查字庫
?????? {???? ???
????????????? LCD_ShowString(60,50,200,16,16,"Font Error!"); delay_ms(200);??????????????????????? ?
????????????? LCD_Fill(60,50,240,66,WHITE);//清除顯示????? ????
?????? }
????? Show_Str(60,50,200,16,"戰艦 STM32開發板",16,0);?????????????????????????? ??? ?????? ?
?????? Show_Str(60,70,200,16,"WAV錄音機實驗",16,0);???????????????????????? ??? ?????? ?
?????? Show_Str(60,90,200,16,"廣州星翼電子",16,0);?????????????????????? ??? ?????? ?
?????? Show_Str(60,110,200,16,"2012年9月20日",16,0);
?????? Show_Str(60,130,200,16,"KEY0:REC/PAUSE",16,0);
?????? Show_Str(60,150,200,16,"KEY2:STOP&SAVE",16,0);
?????? Show_Str(60,170,200,16,"KEY_UP:AGC+ KEY1:AGC-",16,0);
?????? Show_Str(60,190,200,16,"TPADlay The File",16,0);
?????? while(1)
?????? {
????????????? Audiosel_Set(0); ? //MP3通道
????????????? Show_Str(60,210,200,16,"存儲器測試...",16,0);
????????????? VS_Ram_Test();???? ???
????????????? Show_Str(60,210,200,16,"正弦波測試...",16,0); ????? ????? ?
???????????? VS_Sine_Test();???? ??
????????????? Show_Str(60,210,200,16,"<<WAV錄音機>>",16,0);
????????????? recoder_play();
?????? }???? ?????????????????????????????????????????????????????????????????? ???????????????????????? ????
}
??? 該函數代碼同上一章的main函數代碼幾乎一樣,只是我們這里增加了TPAD初始化,然后修改了一些顯示內容,其他兩者就都差不多了,我們就不再細說了。
至此,本實驗的軟件設計部分結束。
?
50.4 下載驗證
?
在代碼編譯成功之后,我們下載代碼到ALIENTEK戰艦STM32開發板上,程序先檢測字庫,然后對VS1053進行RAM測試和正弦測試,之后檢測SD卡的RECORDER文件夾,一切順利通過之后,激活VS1053的PCM錄音模式,得到,如圖50.4.1所示:
?
圖50.4.1 錄音機界面
?
此時,我們按下KEY0就開始錄音了,此時看到屏幕顯示錄音文件的名字以及錄音時長,如圖50.4.2所示:
?
圖50.4.2 錄音進行中
?
?????? 在錄音的時候按下KEY0則執行暫停/繼續錄音的切換,通過DS1指示錄音暫停,按WK_UP和KEY1可以調節AGC,AGC越大,越靈敏,不過不建議設置太大,因為這可能導致失真。通過按下KEY2,可以停止當前錄音,并保存錄音文件。在完成一次錄音文件保存之后,我們可以通過按TPAD按鍵,來實現播放這個錄音文件(即播放最近一次的錄音文件),實現試聽。
?
?????? 我們將開發板的錄音文件放到電腦上面,可以通過屬性查看錄音文件的屬性,如圖50.4.3所示:
?
圖50.4.3 錄音文件屬性
?
?????? 這和我們預期的效果一樣,通過電腦端的播放器(winamp/千千靜聽等)可以直接播放我們所錄的音頻。經實測,效果還是非常不錯的。
?
?
?
?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/chulin/p/7908498.html
總結
以上是生活随笔為你收集整理的硬件——STM32 , 录音的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【软件构造】第一章 软件构造基础(1)
- 下一篇: pfsense 2.2RC版本应用