pos機(jī)成功錄音,阿波羅 STM32F767 開(kāi)發(fā)板資料連載第五十三章 錄音機(jī)實(shí)驗(yàn)

 新聞資訊  |   2023-04-23 10:01  |  投稿人:pos機(jī)之家

網(wǎng)上有很多關(guān)于pos機(jī)成功錄音,阿波羅 STM32F767 開(kāi)發(fā)板資料連載第五十三章 錄音機(jī)實(shí)驗(yàn)的知識(shí),也有很多人為大家解答關(guān)于pos機(jī)成功錄音的問(wèn)題,今天pos機(jī)之家(www.tjfsxbj.com)為大家整理了關(guān)于這方面的知識(shí),讓我們一起來(lái)看下吧!

本文目錄一覽:

1、pos機(jī)成功錄音

pos機(jī)成功錄音

1)實(shí)驗(yàn)平臺(tái):alientek 阿波羅 STM32F767 開(kāi)發(fā)板2)摘自《STM32F7 開(kāi)發(fā)指南(HAL 庫(kù)版)》關(guān)注官方微信號(hào)公眾號(hào),獲取更多資料:正點(diǎn)原子

第五十三章 錄音機(jī)實(shí)驗(yàn)

上一章,我們實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的音樂(lè)播放器,本章我們將在上一章的基礎(chǔ)上,實(shí)現(xiàn)一個(gè)簡(jiǎn)

單的錄音機(jī),實(shí)現(xiàn) WAV 錄音。本章分為如下幾個(gè)部:

53.1 SAI 錄音簡(jiǎn)介

53.2 硬件設(shè)計(jì)

53.3 軟件設(shè)計(jì)

53.4 下載驗(yàn)證

53.1 SAI 錄音簡(jiǎn)介

本章涉及的知識(shí)點(diǎn)基本上在上一章都有介紹。本章要實(shí)現(xiàn) WAV 錄音,還是和上一章一樣,

要了解:WAV 文件格式、WM8978 和 SAI 接口。WAV 文件格式,我們?cè)谏弦徽乱呀?jīng)做了詳細(xì)

介紹了,這里就不作介紹了。

ALIENTEK 阿波羅 STM32F767 開(kāi)發(fā)板將板載的一個(gè) MIC 分別接入到了 WM8978 的 2 個(gè)

差分輸入通道(LIP/LIN 和 RIP/RIN,原理圖見(jiàn):圖 52.2.1)。代碼上,我們采用立體聲 WAV

錄音,不過(guò),左右聲道的音源都是一樣的,錄音出來(lái)的 WAV 文件,聽(tīng)起來(lái)就是個(gè)單聲道效果。

WM8978 上一章也做了比較詳細(xì)的介紹,本章我們主要看一下要進(jìn)行 MIC 錄音,WM8978

的配置步驟:

1,寄存器 R0(00h),該寄存器用于控制 WM8978 的軟復(fù)位,寫(xiě)任意值到該寄存器地址,

即可實(shí)現(xiàn)軟復(fù)位 WM8978。

2,寄存器 R1(01h),該寄存器主要要設(shè)置 MICBEN(bit4)和 BIASEN(bit3)兩個(gè)位為 1,

開(kāi)啟麥克風(fēng)(MIC)偏置,以及使能模擬部分放大器。

3,寄存器 R2(02h),該寄存器要設(shè)置 SLEEP(bit6)、INPGAENR(bit3)、INPGAENL(bit2)、

ADCENR(bit1)和ADCENL(bit0)等五個(gè)位。SLEEP設(shè)置為0,進(jìn)入正常工作模式;INPGAENR

和 INPGAENL 設(shè)置為 1,使能 IP PGA 放大器;ADCENL 和 ADCENR 設(shè)置為 1,使能左

右通道 ADC。

4,寄存器 R4(04h),該寄存器要設(shè)置 WL(bit6:5)和 FMT(bit4:3)等 4 個(gè)位。WL(bit6:5)用

于設(shè)置字長(zhǎng)(即設(shè)置音頻數(shù)據(jù)有效位數(shù)),00 表示 16 位音頻,10 表示 24 位音頻;FMT(bit4:3)

用于設(shè)置 I2S 音頻數(shù)據(jù)格式(模式),我們一般設(shè)置為 10,表示 I2S 格式,即飛利浦模式。

5,寄存器 R6(06h),該寄存器我們直接全部設(shè)置為 0 即可,設(shè)置 MCLK 和 BCLK 都來(lái)

自外部,即由 STM32F767 提供。

6,寄存器 R14(0Eh),該寄存器要設(shè)置 ADCOSR128(bit3)為 1,ADC 得到最好的 SNR。

7,寄存器 R44(2Ch),該寄存器我們要設(shè)置 LIP2INPPGA(bit0)、LIN2INPPGA(bit1)、

RIP2INPPGA(bit4)和 RIN2INPPGA(bit5)等 4 個(gè)位,將這 4 個(gè)位都設(shè)置為 1,將左右通道差

分輸入接入 IN PGA。

8,寄存器 R45(2Dh)和 R46(2Eh),這兩個(gè)寄存器用于設(shè)置 PGA 增益(調(diào)節(jié)麥克風(fēng)增

益),一個(gè)用于設(shè)置左通道(R45),另外一個(gè)用于設(shè)置右通道(R46)。這兩個(gè)寄存器的

最高位(INPPGAUPDATE)用于設(shè)置是否更新左右通道的增益,最低 6 位用于設(shè)置左右

通道的增益,我們可以先設(shè)置好兩個(gè)寄存器的增益,最后設(shè)置其中一個(gè)寄存器最高位為 1,

即可更新增益設(shè)置。

9,寄存器 R47(2Fh)和 R48(30h),這兩個(gè)寄存器也類(lèi)似,我們只關(guān)心其最高位(bit8),

都設(shè)置為 1,可以讓左右通道的 MIC 各獲得 20dB 的增益。

10,寄存器 R49(31h),該寄存器我們要設(shè)置 TSDEN(bit1)這個(gè)位,設(shè)置為 1,開(kāi)啟過(guò)熱

保護(hù)。

以上,就是我們用 WM8978 錄音時(shí)的設(shè)置,按照以上所述,對(duì)各個(gè)寄存器進(jìn)行相應(yīng)的配置,

即可使用 WM8978 正常錄音了。不過(guò)我們本章還要用到播放錄音的功能,WM8978 的播放配置

在 50.1.2 節(jié)已經(jīng)介紹過(guò)了,請(qǐng)大家參考這個(gè)章節(jié)。

上一章我們向大家介紹了 STM32F767 的 SAI 放音,通過(guò)上一章的了解,我們知道:

STM32F767 SAI 的全雙工通信,需要用到 SAI 的兩個(gè)子模塊(SAI_A 和 SAI_B)),一個(gè)工作

在主模式,產(chǎn)生 FS、SCK 和 MCLK,一個(gè)工作在從模式,通過(guò) SD 引腳接收數(shù)據(jù)。

本章我們必須向 WM8978 提供 WS(FS),CK(SCK)和 MCK(MCLK)等時(shí)鐘,同時(shí)又要錄音,

所以只能使用全雙工模式。工作在主模式的 SAI 子模塊循環(huán)發(fā)送數(shù)據(jù) 0X0000,給 WM8978,

以產(chǎn)生 CK、WS 和 MCK 等信號(hào),工作在從模式的 SAI 子模塊,則接收來(lái)自 WM8978 的 ADC

數(shù)據(jù)(ADCDAT),并保存到 SD 卡,實(shí)現(xiàn)錄音。

本章我們將同時(shí)使用 SAI 的兩個(gè)子模塊,以實(shí)現(xiàn)錄音功能,SAI 的相關(guān)寄存器,我們?cè)谏?/p>

一章已經(jīng)介紹的差不多了,這里就不再進(jìn)行寄存器介紹,大家可以參考《STM32F7 中文參考手

冊(cè).pdf》第 33.5 小節(jié)。

要實(shí)現(xiàn)錄音功能,我們根據(jù)上一章,圖 50.2.1 的連接關(guān)系可知,SAI_A 子模塊必須工作在

主模式,循環(huán)發(fā)送 0X0000,以提供 FS、SCK 和 MCLK 等時(shí)鐘信號(hào),SAI_B 子模塊則工作在從

模式,讀取 ADCDAT 輸出的數(shù)據(jù)流(SAI_SD_B),從而實(shí)現(xiàn)錄音功能。

最后,我們看看要通過(guò) STM32F767 的 SAI,驅(qū)動(dòng) WM8978 實(shí)現(xiàn) WAV 錄音的簡(jiǎn)要步驟,

如下:

1)初始化 WM8978

這個(gè)過(guò)程就是前面所講的 WM8978 MIC 錄音配置步驟,讓 WM8978 的 ADC 以及其模擬部

分工作起來(lái)。

2)初始化 SAI_A 和 SAI_B

本章要用到 SAI 的全雙工模式,所以,SAI_A 和 SAI_B 都需要配置,其中 SAI_A 配置為

主模式,SAI 設(shè)置為從模式,且與 SAI_A 同步。他們的其他配置(協(xié)議、時(shí)鐘電平特性、slot

相關(guān)參數(shù))基本一樣,只是一個(gè)是發(fā)送一個(gè)是接收,且都要使能 DMA。同時(shí),還需要設(shè)置音

頻采樣率,不過(guò)這個(gè)只需要設(shè)置 SAI_A 的即可,還是通過(guò)上一章介紹的查表法設(shè)置。

3)設(shè)置發(fā)送和接收 DMA

放音和錄音都是采用 DMA 傳輸數(shù)據(jù)的,本章放音其實(shí)就是個(gè)幌子,不過(guò)也得設(shè)置 DMA

(使用 DMA2 數(shù)據(jù)流 3 的通道 0),配置同上一章一模一樣,不過(guò)不需要開(kāi)啟 DMA 傳輸完成

中斷。對(duì)于錄音,則使用的是 DMA2 數(shù)據(jù)流 5 的通道 0 實(shí)現(xiàn)的 DMA 數(shù)據(jù)接收,我們需要配置

DMA2 的數(shù)據(jù)流 5,本章將 DMA2 數(shù)據(jù)流 5 設(shè)置為:雙緩沖循環(huán)模式,外設(shè)和存儲(chǔ)器都是 16

位寬,并開(kāi)啟傳輸完成中斷(方便接收數(shù)據(jù))。

4)編寫(xiě)接收通道 DMA 傳輸完成中斷服務(wù)函數(shù)

為了方便接收音頻數(shù)據(jù),我們使用 DMA 傳輸完成中斷,每當(dāng)一個(gè)緩沖接數(shù)據(jù)滿(mǎn)了,硬件

自動(dòng)切換為下一個(gè)緩沖,同時(shí)進(jìn)入中斷服務(wù)函數(shù),將已滿(mǎn)緩沖的數(shù)據(jù)寫(xiě)入 SD 卡的 wav 文件。

過(guò)程如圖 53.1.1 所示:

圖 53.1.1 DMA 雙緩沖接收音頻數(shù)據(jù)流框圖

5)創(chuàng)建 WAV 文件,并保存 wav 頭

前面 4 步完成,其實(shí)就可以開(kāi)始讀取音頻數(shù)據(jù)了,不過(guò)在錄音之前,我們需要先在創(chuàng)建一

個(gè)新的文件,并寫(xiě)入 wav 頭,然后才能開(kāi)始寫(xiě)入我們讀取到的的 PCM 音頻數(shù)據(jù)。

6)開(kāi)啟 DMA 傳輸,接收數(shù)據(jù)

然后,我們就只需要開(kāi)啟 DMA 傳輸,然后及時(shí)將 SAI_SD_B 讀到的數(shù)據(jù)寫(xiě)入到 SD 卡之

前新建的 wav 文件里面,就可以實(shí)現(xiàn)錄音了。

7)計(jì)算整個(gè)文件大小,重新保存 wav 頭并關(guān)閉文件

在結(jié)束錄音的時(shí)候,我們必須知道本次錄音的大?。〝?shù)據(jù)大小和整個(gè)文件大?。?,然后更新

wav 頭,重新寫(xiě)入文件,最后因?yàn)?FATFS,在文件創(chuàng)建之后,必須調(diào)用 f_close,文件才會(huì)真正

體現(xiàn)在文件系統(tǒng)里面,否則是不會(huì)寫(xiě)入的!所以最后還需要調(diào)用 f_close,以保存文件。

53.2 硬件設(shè)計(jì)

本章實(shí)驗(yàn)功能簡(jiǎn)介:開(kāi)機(jī)后,先初始化各外設(shè),然后檢測(cè)字庫(kù)是否存在,如果檢測(cè)無(wú)問(wèn)題,

再檢測(cè) SD 卡根目錄是否存在 RECORDER 文件夾,如果不存在則創(chuàng)建,如果創(chuàng)建失敗,則報(bào)

錯(cuò)。在找到 SD 卡的 RECORDER 文件夾后,即進(jìn)入錄音模式(包括配置 WM8978 和 SAI 等),

此時(shí)可以在耳機(jī)(或喇叭)聽(tīng)到采集到的音頻。KEY0 用于開(kāi)始/暫停錄音,KEY2 用于保存并

停止錄音,KEY_UP 用于播放最近一次的錄音。

當(dāng)我們按下 KEY0 的時(shí)候,可以在屏幕上看到錄音文件的名字、碼率以及錄音時(shí)間等,然

后通過(guò) KEY2 可以保存該文件,同時(shí)停止錄音(文件名和時(shí)間也都將清零),在完成一段錄音

后,我們可以通過(guò)按 KEY_UP 按鍵,來(lái)試聽(tīng)剛剛的錄音。DS0 用于提示程序正在運(yùn)行,DS1

用于提示是否處于暫停錄音狀態(tài)。

本實(shí)驗(yàn)用到的資源如下:

1) 指示燈 DS0,DS1

2) 三個(gè)按鍵(KEY_UP/KEY0/KEY2)

3) 串口

4) LCD 模塊

5) SD 卡

6) SPI FLASH

7) WM8978

8) SAI

這些前面都已介紹過(guò)。本實(shí)驗(yàn),大家需要準(zhǔn)備 1 個(gè) SD 卡和一個(gè)耳機(jī),分別插入 SD 卡接

口和耳機(jī)接口(PHONE),然后下載本實(shí)驗(yàn)就可以實(shí)現(xiàn)一個(gè)簡(jiǎn)單的錄音機(jī)了。

53.3 軟件設(shè)計(jì)

打開(kāi)本章實(shí)驗(yàn)工程可以看到我們?cè)?APP 分組下新增了 recorder.c 文件,用來(lái)存放錄音相關(guān)

源碼。因?yàn)?recorder.c 代碼比較多,我們這里僅介紹其中幾個(gè)重要的函數(shù),代碼如下:

u8 *sairecbuf1; //SAI1 DMA 接收 BUF1u8 *sairecbuf2; //SAI1 DMA 接收 BUF2 //REC 錄音 FIFO 管理參數(shù).//由于 FATFS 文件寫(xiě)入時(shí)間的不確定性,如果直接在接收中斷里面寫(xiě)文件,可能導(dǎo)致某次寫(xiě)//入時(shí)間過(guò)長(zhǎng)從而引起數(shù)據(jù)丟失,故加入 FIFO 控制,以解決此問(wèn)題.vu8 sairecfifordpos=0; //FIFO 讀位置vu8 sairecfifowrpos=0; //FIFO 寫(xiě)位置u8 *sairecfifobuf[SAI_RX_FIFO_SIZE]; //定義 10 個(gè)錄音接收 FIFOFIL* f_rec=0; //錄音文件u32 wavsize; //wav 數(shù)據(jù)大小(字節(jié)數(shù),不包括文件頭!!)u8 rec_sta=0; //錄音狀態(tài)//[7]:0,沒(méi)有開(kāi)啟錄音;1,已經(jīng)開(kāi)啟錄音;//[6:1]:保留//[0]:0,正在錄音;1,暫停錄音; //讀取錄音 FIFO//buf:數(shù)據(jù)緩存區(qū)首地址//返回值:0,沒(méi)有數(shù)據(jù)可讀;// 1,讀到了 1 個(gè)數(shù)據(jù)塊u8 rec_sai_fifo_read(u8 **buf){if(sairecfifordpos==sairecfifowrpos)return 0;sairecfifordpos++; //讀位置加 1if(sairecfifordpos>=SAI_RX_FIFO_SIZE)sairecfifordpos=0;//歸零*buf=sairecfifobuf[sairecfifordpos];return 1;}//寫(xiě)一個(gè)錄音 FIFO//buf:數(shù)據(jù)緩存區(qū)首地址//返回值:0,寫(xiě)入成功;// 1,寫(xiě)入失敗u8 rec_sai_fifo_write(u8 *buf){u16 i;u8 temp=sairecfifowrpos;//記錄當(dāng)前寫(xiě)位置sairecfifowrpos++; //寫(xiě)位置加 1if(sairecfifowrpos>=SAI_RX_FIFO_SIZE)sairecfifowrpos=0;//歸零 if(sairecfifordpos==sairecfifowrpos){sairecfifowrpos=temp;//還原原來(lái)的寫(xiě)位置,此次寫(xiě)入失敗 return 1;}for(i=0;i<SAI_RX_DMA_BUF_SIZE;i++)sairecfifobuf[sairecfifowrpos][i]=buf[i];//拷貝return 0;} //錄音 SAI_DMA 接收中斷服務(wù)函數(shù).在中斷里面寫(xiě)入數(shù)據(jù)void rec_sai_dma_rx_callback(void) { if(rec_sta==0X80)//錄音模式{ if(DMA2_Stream5->CR&(1<<19))rec_sai_fifo_write(sairecbuf1);//sairecbuf1 寫(xiě) FIFOelse rec_sai_fifo_write(sairecbuf2);//sairecbuf2 寫(xiě)入 FIFO } } const u16 saiplaybuf[2]={0X0000,0X0000};//2 個(gè)數(shù)據(jù),用于錄音時(shí) SAI_A 主機(jī)循環(huán)發(fā)送 0.//進(jìn)入 PCM 錄音模式 void recoder_enter_rec_mode(void){WM8978_ADDA_Cfg(0,1); //開(kāi)啟 ADCWM8978_Input_Cfg(1,1,0); //開(kāi)啟輸入通道(MIC&LINE IN)WM8978_Output_Cfg(0,1); //開(kāi)啟 BYPASS 輸出WM8978_MIC_Gain(46); //MIC 增益設(shè)置WM8978_SPKvol_Set(0); //關(guān)閉喇叭.WM8978_I2S_Cfg(2,0); //飛利浦標(biāo)準(zhǔn),16 位數(shù)據(jù)長(zhǎng)度SAIA_Init(SAI_MODEMASTER_TX,SAI_CLOCKSTROBING_RISINGEDGE,SAI_DATASIZE_16); //SAI1 Block A,主發(fā)送,16 位數(shù)據(jù)SAIB_Init(SAI_MODESLAVE_RX,SAI_CLOCKSTROBING_RISINGEDGE,SAI_DATASIZE_16);//SAI1 Block B 從模式接收,16 位SAIA_SampleRate_Set(REC_SAMPLERATE);//設(shè)置采樣率SAIA_TX_DMA_Init((u8*)&saiplaybuf[0],(u8*)&saiplaybuf[1],1,1);//TX DMA,16 位 __HAL_DMA_DISABLE_IT(&SAI1_TXDMA_Handler,DMA_IT_TC);//關(guān)閉傳輸完成中斷(這里不用中斷送數(shù)據(jù)) SAIA_RX_DMA_Init(sairecbuf1,sairecbuf2,SAI_RX_DMA_BUF_SIZE/2,1);//配置 RX DMA sai_rx_callback=rec_sai_dma_rx_callback;//初始化回調(diào)函數(shù)指 sai_rx_callbackSAI_Play_Start(); //開(kāi)始 SAI 數(shù)據(jù)發(fā)送(主機(jī))SAI_Rec_Start(); //開(kāi)始 SAI 數(shù)據(jù)接收(從機(jī))recoder_remindmsg_show(0); } //初始化 WAV 頭.void recoder_wav_init(__WaveHeader* wavhead) //初始化 WAV 頭 {wavhead->riff.ChunkID=0X46464952; //"RIFF"wavhead->riff.ChunkSize=0; //還未確定,最后需要計(jì)算wavhead->riff.Format=0X45564157; //"WAVE"wavhead->fmt.ChunkID=0X20746D66; //"fmt "wavhead->fmt.ChunkSize=16; //大小為 16 個(gè)字節(jié)wavhead->fmt.AudioFormat=0X01; //0X01,表示 PCM;0X01,表示 IMA ADPCMwavhead->fmt.NumOfChannels=2; //雙聲道wavhead->fmt.SampleRate=REC_SAMPLERATE;//設(shè)置采樣速率wavhead->fmt.ByteRate=wavhead->fmt.SampleRate*4;//采樣率*通道數(shù)*(ADC 位數(shù)/8)wavhead->fmt.BlockAlign=4; //塊大小=通道數(shù)*(ADC 位數(shù)/8)wavhead->fmt.BitsPerSample=16; //16 位 PCM wavhead->data.ChunkID=0X61746164; //"data"wavhead->data.ChunkSize=0; //數(shù)據(jù)大小,還需要計(jì)算 } //WAV 錄音void wav_recorder(void){ u8 res,i; u8 key; u8 rval=0;__WaveHeader *wavhead=0; DIR recdir; //目錄 u8 *pname=0; u8 *pdatabuf;u8 timecnt=0; //計(jì)時(shí)器 u32 recsec=0; //錄音時(shí)間 while(f_opendir(&recdir,"0:/RECORDER"))//打開(kāi)錄音文件夾{Show_Str(30,230,240,16,"RECORDER 文件夾錯(cuò)誤!",16,0); delay_ms(200);LCD_Fill(30,230,240,246,WHITE); delay_ms(200); //清除顯示 f_mkdir("0:/RECORDER"); //嘗試創(chuàng)建該目錄 } sairecbuf1=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE); //SAI 錄音內(nèi)存 1 申請(qǐng)sairecbuf2=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE); //SAI 錄音內(nèi)存 2 申請(qǐng) for(i=0;i<SAI_RX_FIFO_SIZE;i++){sairecfifobuf[i]=mymalloc(SRAMIN,SAI_RX_DMA_BUF_SIZE);//FIFO 內(nèi)存申請(qǐng)if(sairecfifobuf[i]==NULL)break; //申請(qǐng)失敗} f_rec=(FIL *)mymalloc(SRAMIN,sizeof(FIL)); //開(kāi)辟 FIL 字節(jié)的內(nèi)存區(qū)域 wavhead=(__WaveHeader*)mymalloc(SRAMIN,sizeof(__WaveHeader));//申請(qǐng)內(nèi)存pname=mymalloc(SRAMIN,30);//申請(qǐng)30字節(jié)內(nèi)存,類(lèi)似"0:RECORDER/REC00001.wav" if(!sairecbuf1||!sairecbuf2||!f_rec||!wavhead||!pname||i!=SAI_RX_FIFO_SIZE)rval=1; if(rval==0){recoder_enter_rec_mode(); //進(jìn)入錄音模式,此時(shí)耳機(jī)可以聽(tīng)到咪頭采集到的音頻 pname[0]=0; //pname 沒(méi)有任何文件名 while(rval==0){key=KEY_Scan(0);switch(key){case KEY2_PRES: //STOP&SAVEif(rec_sta&0X80)//有錄音{rec_sta=0; //關(guān)閉錄音wavhead->riff.ChunkSize=wavsize+36; //整個(gè)文件的大小-8; wavhead->data.ChunkSize=wavsize; //數(shù)據(jù)大小f_lseek(f_rec,0); //偏移到文件頭. f_write(f_rec,(const void*)wavhead,sizeof(__WaveHeader),&bw); f_close(f_rec);wavsize=0;sairecfifordpos=0; //FIFO 讀寫(xiě)位置重新歸零sairecfifowrpos=0;}rec_sta=0; recsec=0;LED1(1); //關(guān)閉 DS1LCD_Fill(30,190,lcddev.width="360px",height="auto" />

這里總共 6 個(gè)函數(shù),接下來(lái),我們分別介紹。

1,rec_sai_fifo_read 和 rec_sai_fifo_write 函數(shù)

這兩個(gè)函數(shù)用于我們構(gòu)建的 FIFO 里面的數(shù)據(jù)讀取和寫(xiě)入,SAI 采集到的數(shù)據(jù),通過(guò) FATFS

寫(xiě)入 SD 卡的時(shí)候,因?yàn)?FATFS 寫(xiě)入時(shí)間不確定(有時(shí)候短,有時(shí)候長(zhǎng)),可能導(dǎo)致數(shù)據(jù)寫(xiě)入

不及時(shí),出現(xiàn)數(shù)據(jù)丟失,從而錄音會(huì)有間隔(丟失一部分)。所以,我們構(gòu)建了一個(gè) FIFO,SAI

采集的數(shù)據(jù),通過(guò) rec_sai_fifo_write 函數(shù)寫(xiě)入 FIFO 里面,在主循環(huán)里面,我們通過(guò)

rec_sai_fifo_read 函數(shù)不停的讀取 FIFO 里面的數(shù)據(jù),并將數(shù)據(jù)通過(guò) FATFS 寫(xiě)入 SD 卡里面,只

要 rec_sai_fifo_read 的速度,不小于 rec_sai_fifo_write 的速度,就可以保證數(shù)據(jù)不丟失,這個(gè)

FIFO 起到了一個(gè)緩沖的作用,從而保證錄音文件的流暢性。

2,rec_sai_dma_rx_callback 函數(shù)

該函數(shù)用于 SAI_B 的 DMA 接收完成中斷回調(diào)函數(shù)(通過(guò) sai_rx_callback 指向該函數(shù)實(shí)現(xiàn)),

在該函數(shù)里面調(diào)用 rec_sai_fifo_write 函數(shù),將采集到的音頻數(shù)據(jù),寫(xiě)入 FIFO。

3,recoder_enter_rec_mode 函數(shù)

該函數(shù)用于設(shè)置 WM8978 進(jìn)入錄音模式,并設(shè)置 SAI_A 和 SAI_B 的工作模式和位數(shù)等信

息,然后配置 DMA 和回調(diào)函數(shù)的指向,最后開(kāi)啟錄音。調(diào)用該函數(shù)后,就可以開(kāi)始錄音了。

4,recoder_wav_init 函數(shù)

該函數(shù)初始化 wav 頭的絕大部分?jǐn)?shù)據(jù),采樣率通過(guò) REC_SAMPLERATE 宏定義修改,默

認(rèn)是 44.1Khz,位數(shù)為 16 位,線性 PCM 格式,另外由于錄音還未真正開(kāi)始,所以文件大小和

數(shù)據(jù)大小都還是未知的,要等錄音結(jié)束才能知道。該函數(shù)__WaveHeader 結(jié)構(gòu)體就是由上一章

(50.1.1 節(jié))介紹的三個(gè) Chunk 組成,結(jié)構(gòu)為:

//wav 頭

typedef __packed struct

{

ChunkRIFF riff;

//riff 塊

ChunkFMT fmt; //fmt 塊

//

ChunkFACT fact; //fact 塊 線性 PCM,沒(méi)有這個(gè)結(jié)構(gòu)體

ChunkDATA data; //data 塊

}__WaveHeader;

5,wav_recorder 函數(shù)

該函數(shù)實(shí)現(xiàn)了我們?cè)谟布O(shè)計(jì)時(shí)介紹的功能(開(kāi)始/暫停錄音、保存錄音文件、播放最近一

次錄音等),實(shí)現(xiàn)方法請(qǐng)大家參考源碼理解。另外,該函數(shù)使用上一章實(shí)現(xiàn)的 audio_play_song

函數(shù),來(lái)播放最近一次錄音。

recorder.c 的其他代碼和 recorder.h 的代碼我們這里就不再貼出了,請(qǐng)大家參考光盤(pán)本實(shí)驗(yàn)

的源碼。然后,我們?cè)?sai.c 里面也增加了幾個(gè)函數(shù),如下:

//SAI Block B 初始化,I2S,飛利浦標(biāo)準(zhǔn)//mode:工作模式,可以設(shè)置:SAI_MODEMASTER_TX///SAI_MODEMASTER_RX/SAI_MODESLAVE_TX/SAI_MODESLAVE_RX//cpol:數(shù)據(jù)在時(shí)鐘的上升/下降沿選通,可以設(shè)置://SAI_CLOCKSTROBING_FALLINGEDGE/SAI_CLOCKSTROBING_RISINGEDGE//datalen:數(shù)據(jù)大小,可以設(shè)置:SAI_DATASIZE_8/10/16/20/24/32void SAIB_Init(u32 mode,u32 cpol,u32 datalen){ HAL_SAI_DeInit(&SAI1B_Handler); //清除以前的配置 SAI1B_Handler.Instance=SAI1_Block_B; //SAI1 Bock B SAI1B_Handler.Init.AudioMode=mode; //設(shè)置 SAI1 工作模式 SAI1B_Handler.Init.Synchro=SAI_SYNCHRONOUS; //音頻模塊同步 SAI1B_Handler.Init.OutputDrive=SAI_OUTPUTDRIVE_ENABLE; //立即驅(qū)動(dòng)輸出 SAI1B_Handler.Init.NoDivider=SAI_MASTERDIVIDER_ENABLE; //使能主時(shí)鐘分頻器 SAI1B_Handler.Init.FIFOThreshold=SAI_FIFOTHRESHOLD_1QF //設(shè)置 FIFO 閾值 SAI1B_Handler.Init.ClockSource=SAI_CLKSOURCE_PLLI2S; //SIA 時(shí)鐘源為 PLL2S SAI1B_Handler.Init.MonoStereoMode=SAI_STEREOMODE; //立體聲模式 SAI1B_Handler.Init.Protocol=SAI_FREE_PROTOCOL; //設(shè)置 SAI1 協(xié)議為自由協(xié)議 SAI1B_Handler.Init.DataSize=datalen; //設(shè)置數(shù)據(jù)大小 SAI1B_Handler.Init.FirstBit=SAI_FIRSTBIT_MSB; //數(shù)據(jù) MSB 位優(yōu)先 SAI1B_Handler.Init.ClockStrobing=cpol; //數(shù)據(jù)在時(shí)鐘的上升/下降沿選通 //幀設(shè)置 SAI1B_Handler.FrameInit.FrameLength=64; //設(shè)置幀長(zhǎng)度為 64,左/右通道各 32 個(gè) SCK, SAI1B_Handler.FrameInit.ActiveFrameLength=32; //設(shè)置幀同步有效電平長(zhǎng)度 SAI1B_Handler.FrameInit.FSDefinition=SAI_FS_CHANNEL_IDENTIFICATION;//FS 信號(hào)為 SOF 信號(hào)+通道識(shí)別信號(hào) SAI1B_Handler.FrameInit.FSPolarity=SAI_FS_ACTIVE_LOW; //FS 低電平有效(下降沿) SAI1B_Handler.FrameInit.FSOffset=SAI_FS_BEFOREFIRSTBIT; //在 slot0 的第一位的前一位使能 FS,以匹配飛利浦標(biāo)準(zhǔn) //SLOT 設(shè)置 SAI1B_Handler.SlotInit.FirstBitOffset=0; //slot 偏移(FBOFF)為 0 SAI1B_Handler.SlotInit.SlotSize=SAI_SLOTSIZE_32B; //slot 大小為 32 位 SAI1B_Handler.SlotInit.SlotNumber=2; //slot 數(shù)為 2 個(gè) SAI1B_Handler.SlotInit.SlotActive=SAI_SLOTACTIVE_0|SAI_SLOTACTIVE_1;//使能 slot0 和 slot1 HAL_SAI_Init(&SAI1B_Handler); SAIB_DMA_Enable(); //使能 SAI 的 DMA 功能 __HAL_SAI_ENABLE(&SAI1B_Handler); //使能 SAI }//SAIA TX DMA 配置//設(shè)置為雙緩沖模式,并開(kāi)啟 DMA 傳輸完成中斷//buf0:M0AR 地址.//buf1:M1AR 地址.//num:每次傳輸數(shù)據(jù)量//width="360px",height="auto" />

這里新增了5個(gè)函數(shù),SAIB_Init函數(shù)完成SAI_B子模塊的初始化,通過(guò)3個(gè)參數(shù)設(shè)置SAI_B

的詳細(xì)配置信息。SAIB_RX_DMA_Init 函數(shù),用于設(shè)置 SAI_B 的 DMA 接收,使用雙緩沖循環(huán)

模式,接收來(lái)自 WM8978 的數(shù)據(jù),并開(kāi)啟了傳輸完成中斷。而 DMA2_Stream5_IRQHandler 函

數(shù),則是 DMA2 數(shù)據(jù)流 5 傳輸完成中斷的服務(wù)函數(shù),該函數(shù)調(diào)用 sai_rx_callback 函數(shù)(函數(shù)指

針,使用前需指向特定函數(shù))實(shí)現(xiàn) DMA 數(shù)據(jù)接收保存。最后,SAI_ Rec_Start 和 SAI_ Rec_Stop,

用于開(kāi)啟和關(guān)閉 SAI_B 的 DMA 傳輸。

其他代碼,我們就不再介紹了,請(qǐng)大家參考開(kāi)發(fā)板光盤(pán)本例程源碼。最后我們看看 main

函數(shù)源碼:

int main(void){ Cache_Enable(); //打開(kāi) L1-Cache HAL_Init(); //初始化 HAL 庫(kù) Stm32_Clock_Init(432,25,2,9); //設(shè)置時(shí)鐘,216Mhz delay_init(216); //延時(shí)初始化uart_init(115200); //串口初始化 LED_Init(); //初始化KEY_Init(); //初始化按鍵 SDRAM_Init(); //初始化 SDRAM LCD_Init(); //初始化 LCDW25QXX_Init(); //初始化 W25Q256 WM8978_Init(); //初始化 WM8978WM8978_HPvol_Set(40,40); //耳機(jī)音量設(shè)置WM8978_SPKvol_Set(40); //喇叭音量設(shè)置 my_mem_init(SRAMIN); //初始化內(nèi)部?jī)?nèi)存池 my_mem_init(SRAMEX); //初始化外部 SDRAM 內(nèi)存池 my_mem_init(SRAMDTCM); //初始化內(nèi)部 DTCM 內(nèi)存池 exfuns_init(); //為 fatfs 相關(guān)變量申請(qǐng)內(nèi)存 f_mount(fs[0],"0:",1); //掛載 SD 卡f_mount(fs[1],"1:",1); //掛載 SPI FLASH. f_mount(fs[2],"2:",1); //掛載 NAND FLASH. POINT_COLOR=RED; while(font_init()) //檢查字庫(kù){ LCD_ShowString(30,50,200,16,16,"Font Error!");delay_ms(200); LCD_Fill(30,50,240,66,WHITE);//清除顯示 delay_ms(200); } POINT_COLOR=RED; Show_Str(30,40,200,16,"阿波羅 STM32F4/F7 開(kāi)發(fā)板",16,0); Show_Str(30,60,200,16,"錄音機(jī)實(shí)驗(yàn)",16,0); Show_Str(30,80,200,16,"正點(diǎn)原子@ALIENTEK",16,0); Show_Str(30,100,200,16,"2016 年 1 月 29 日",16,0); while(1){ wav_recorder(); } }

該函數(shù)代碼同上一章的 main 函數(shù)代碼幾乎一樣,十分簡(jiǎn)單,我們就不再多說(shuō)了。

至此,本實(shí)驗(yàn)的軟件設(shè)計(jì)部分結(jié)束。

53.4 下載驗(yàn)證

在代碼編譯成功之后,我們下載代碼到 ALIENTEK 阿波羅 STM32 開(kāi)發(fā)板上,程序先檢測(cè)

字庫(kù),然后檢測(cè) SD 卡的 RECORDER 文件夾,一切順利通過(guò)之后,進(jìn)入錄音模式,得到,如

圖 53.4.1 所示:

圖 53.4.1 錄音機(jī)界面

此時(shí),我們按下 KEY0 就開(kāi)始錄音了,此時(shí)看到屏幕顯示錄音文件的名字、碼率以及錄音

時(shí)長(zhǎng),如圖 53.4.2 所示:

圖 53.4.2 錄音進(jìn)行中

在錄音的時(shí)候按下 KEY0 則執(zhí)行暫停/繼續(xù)錄音的切換,通過(guò) DS1 指示錄音暫停。通過(guò)按

下 KEY2,可以停止當(dāng)前錄音,并保存錄音文件。在完成一次錄音文件保存之后,我們可以通

過(guò)按 KEY_UP 按鍵,來(lái)實(shí)現(xiàn)播放這個(gè)錄音文件(即播放最近一次的錄音文件),實(shí)現(xiàn)試聽(tīng)。

我們將開(kāi)發(fā)板的錄音文件放到電腦上面,可以通過(guò)屬性查看錄音文件的屬性,如圖 53.4.3

所示:

圖 53.4.3 錄音文件屬性

這和我們預(yù)期的效果一樣,通過(guò)電腦端的播放器(winamp/千千靜聽(tīng)等)可以直接播放我們

所錄的音頻。經(jīng)實(shí)測(cè),效果還是非常不錯(cuò)的。

以上就是關(guān)于pos機(jī)成功錄音,阿波羅 STM32F767 開(kāi)發(fā)板資料連載第五十三章 錄音機(jī)實(shí)驗(yàn)的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于pos機(jī)成功錄音的知識(shí),希望能夠幫助到大家!

轉(zhuǎn)發(fā)請(qǐng)帶上網(wǎng)址:http://www.tjfsxbj.com/news/22400.html

你可能會(huì)喜歡:

版權(quán)聲明:本文內(nèi)容由互聯(lián)網(wǎng)用戶(hù)自發(fā)貢獻(xiàn),該文觀點(diǎn)僅代表作者本人。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如發(fā)現(xiàn)本站有涉嫌抄襲侵權(quán)/違法違規(guī)的內(nèi)容, 請(qǐng)發(fā)送郵件至 babsan@163.com 舉報(bào),一經(jīng)查實(shí),本站將立刻刪除。