首頁 > 評(píng)測 > 第二篇 嵌入式系統(tǒng)硬件輸出系統(tǒng)簡介及播放實(shí)驗(yàn)(播放篇之一)

第二篇 嵌入式系統(tǒng)硬件輸出系統(tǒng)簡介及播放實(shí)驗(yàn)(播放篇之一)

  • 作者:Zhanzr21
  • 來源:21ic
  • [導(dǎo)讀]
  • Everyboard Can Sing

21ic打算攜手資(tu)深(ding)直男癌晚期工程師zhanzr21,來給大家講一講嵌入式系統(tǒng)與音頻處理的故事。

關(guān)于zhanzr21

曾經(jīng)混跡于兩岸三地,摸爬滾打在前端后端,搞過學(xué)術(shù)上過班,F(xiàn)在創(chuàng)業(yè)中,歡迎各種撩

點(diǎn)擊鏈接加入群【嵌入式音頻信號(hào)處理】:https://jq.qq.com/?_wv=1027&k=45wk8Ks

嵌入式音頻專用資料代碼分享:https://pan.baidu.com/s/1dFh5pWd

C2.jpg

前言

此章僅為最基本的播放原理介紹, 還有不少關(guān)於播放的內(nèi)容由于篇幅與活動(dòng)配套安排等原因沒有包含在此, 所以在標(biāo)題上加 播放篇之一, 后面的章節(jié)中也會(huì)有對(duì)這個(gè)話題的繼續(xù)討論.

1.基本的播放原理與硬件組成

從原理上講,數(shù)字系統(tǒng)播放聲音就是把聲音的樣本數(shù)據(jù)轉(zhuǎn)換成人耳能感覺到的變化.上一章的文章中我們已經(jīng)體驗(yàn)過聲音的數(shù)字形式.那么將這種數(shù)據(jù)如何轉(zhuǎn)換為最終能感覺到的變化呢.理論上的功能Block如下:

音頻播放的理論Block.jpg

圖 音頻播放的理論Block

這里依次說明.

1. 控制系統(tǒng),負(fù)責(zé)控制生成數(shù)據(jù)或者讀取數(shù)據(jù)(可能需要解壓或其他操作),并將其交給下一級(jí)的DAC,一般來講這個(gè)指的是MCU/DSP/CPU/FPGA這樣的器件.

2. 數(shù)據(jù)即是音頻數(shù)據(jù),可以是控制系統(tǒng)即時(shí)生成的也可以是讀取其它系統(tǒng)處理好的數(shù)據(jù).一般而言,除了實(shí)驗(yàn)性質(zhì)或者創(chuàng)作性質(zhì)的系統(tǒng),數(shù)據(jù)一般是其它系統(tǒng)生成存放在存儲(chǔ)器中的數(shù)據(jù).

3. DAC負(fù)責(zé)把數(shù)字轉(zhuǎn)換成模擬.這個(gè)DAC可以是內(nèi)部DAC也可以是外部DAC.甚至可能是個(gè)假的DAC(數(shù)字口+低通濾波器).

4. 功率級(jí)將DAC的輸出進(jìn)行功率放大,功率級(jí)視乎后面的輸出設(shè)備需求來決定其性質(zhì).可以是管子(FET,BJT,電子管),也可能是集成電路. 現(xiàn)代音頻系統(tǒng)中集成電路放大器居多.如果后一級(jí)的負(fù)載足夠小, 這一級(jí)可以省掉, 后面的實(shí)驗(yàn)會(huì)進(jìn)一步詳細(xì)解釋.

5. 輸出設(shè)備一般而言就是揚(yáng)聲器(音箱或者耳機(jī)中的揚(yáng)聲器).

2.實(shí)際硬件原理分析 (硬件DAC直接輸出)

這里給出我搭建的幾個(gè)音頻播放的硬件例子以助理解.

1.系統(tǒng)之一:Nucleo板子+有源音箱

首先來看看DAC+音箱形式.

DAC+有源音響.jpg

圖 DAC+有源音箱形式

左邊是一個(gè)ST的Nucleo開發(fā)板子,具體型號(hào)是STM32F722ZE. 但是絕大多數(shù)的類似板子都能替代. 音頻信號(hào)通過芯片內(nèi)部的DAC輸出到音箱的輸入級(jí). 音箱須為有源音箱(帶電源的就是有源音箱, 本文例子使用的就是網(wǎng)上買的幾十塊錢包郵的USB音箱, 如果是無源音箱, 需要另外增加功率分大級(jí). 根據(jù)作者的經(jīng)驗(yàn), 你能找到的音箱絕大多數(shù)是有源音箱).

我們看看連接的細(xì)節(jié):

earjack_detail.jpg

圖 耳機(jī)接口細(xì)節(jié)

這是個(gè)耳機(jī)接口座子+杜邦線改裝出來的連接器, 左右兩根線是左右聲道信號(hào), 當(dāng)中那個(gè)是共地. 本文所討論的音頻系統(tǒng)除非另外說明都為單聲道, 故此只會(huì)使用左右兩通道中的之一加下面那根地線.

大多數(shù)的MCU自帶DAC為兩個(gè)或以上的通道, 足夠完成簡單的立體聲輸出了. 為討論簡便, 先只用單聲道做實(shí)驗(yàn), 所以只會(huì)用到一個(gè)DAC輸出. 這是Nucleo板上的連接, 一個(gè)DAC輸出(PA5,DAC通道2), 一個(gè)地.

DAC連接細(xì)節(jié).jpg

圖 DAC連接細(xì)節(jié)

2.系統(tǒng)之二: Nucleo板子+耳機(jī)

DAC+耳機(jī).png

圖 DAC+耳機(jī)形式

按照數(shù)據(jù)手冊(cè),ST的DAC是無法直接驅(qū)動(dòng)耳機(jī)的.

f7_dac_load.png

圖 F722數(shù)據(jù)手冊(cè)中關(guān)于DAC的負(fù)載參數(shù)

這里發(fā)揚(yáng)(不怕燒壞板子燒壞耳機(jī)的)探索精神, 使用STM32F722的DAC直接驅(qū)動(dòng)32 Ohm耳機(jī), 經(jīng)試驗(yàn)是可以進(jìn)行播放的. 當(dāng)然當(dāng)做產(chǎn)品長時(shí)間運(yùn)行這樣是有點(diǎn)吊兒郎當(dāng)?shù)? 此處只是做實(shí)驗(yàn). 如果你沒有心理準(zhǔn)備或者接受不了這種冒險(xiǎn)行為, 請(qǐng)勿模仿!

3.系統(tǒng)之三: Nucleo板子+功放板+揚(yáng)聲器

這個(gè)系統(tǒng)應(yīng)該是屬于本章目前為止分析的最正統(tǒng), 最教科書式的系統(tǒng)構(gòu)造了.

DAC+功放+揚(yáng)聲器.jpg

圖 DAC+功放板子+揚(yáng)聲器

DAC輸出信號(hào)到功放板,功放板驅(qū)動(dòng)揚(yáng)聲器. 其實(shí)這個(gè)系統(tǒng)跟第一個(gè)有音箱的系統(tǒng)在架構(gòu)上是一模一樣的. 只是把音箱拆開了開膛破肚, 把功放板與揚(yáng)聲器拿出來, 大家更容易看明白信號(hào)的走向流程. 圖片中的功放板子是用的Adafruit做的一個(gè)板子, 作者自己打的板子將在后文中介紹.

D類音頻功放揚(yáng)聲器.jpg

圖 D類音頻功放板子

這里補(bǔ)一句, 一般而言音頻功放分為AB類和D類. 當(dāng)然還有許多其他類以及變種, 但是用得最多的就是AB類和D類. 兩者的差別用一句話概括:

AB類效果好,D類效率高.

具體關(guān)于兩種放大器的分析與仿真作者打算使用自制的板子再開一篇來講. 這里先跳過去.

至于揚(yáng)聲器,這里是作者從舊的筆記本上拆的,我想大家應(yīng)該都能在家中找到這樣的舊的揚(yáng)聲器吧. 揚(yáng)聲器上標(biāo)的+-跟馬達(dá)/無源蜂鳴器/燈絲燈拋一樣, 只是個(gè)接線參考, 兩極從電路原理上講是對(duì)稱的.

speaker_3.jpg

圖 舊筆記本上拆的揚(yáng)聲器

以上三個(gè)系統(tǒng)都是本人為了說明音頻播放原理而搭建的簡化型的系統(tǒng). 這些系統(tǒng)便于說明原理, 也能夠用作大家自己動(dòng)手的參考. 事實(shí)上實(shí)際的音頻系統(tǒng)中很少直接使用處理器的ADC/DAC來播放音頻, 而經(jīng)常使用專用的Codecs來處理音頻. 這些Codecs都是專門為音頻應(yīng)用優(yōu)化過的ADC/DAC, 有的還加入了運(yùn)放電路,DSP處理模塊等等. 在成本與性能上都大大優(yōu)于處理器自帶ADC/DAC. 本文在介紹基本原理時(shí)使用這些通用ADC/DAC, 在此之后將回到使用專用Codecs的更加主流的道路上來. 如果看過第一篇的讀者, 應(yīng)該會(huì)記得曾經(jīng)介紹過的F769-Discovery板子上的音頻系統(tǒng),那就是使用的專用Codecs: WM8994. 作者也會(huì)做一些測試板子來配合文中的內(nèi)容.

4.播放實(shí)驗(yàn)之一(DAC+定時(shí)器播放生成音頻)

硬件介紹差不多了, 現(xiàn)在開始寫代碼了. 讀者如果要試驗(yàn)本章配套代碼, 需要搭建的硬件就是上面所述的三種之一. 從軟件角度來說, 這三種硬件中控制器所需要做的工作就是將數(shù)據(jù)從DAC中發(fā)出去就行了. 具體而言, 就是將數(shù)據(jù)以目標(biāo)采樣率的速度把數(shù)據(jù)發(fā)到DAC上, 就是這么簡單. 為求最迅捷的展示原理, 這里使用的音頻參數(shù)為:

 

8bit PCM * 單通道 * 8K采樣率

 

這個(gè)參數(shù)今天看來很寒酸,可是退回一二十年, 這樣參數(shù)的硬件可都是高端人士才用的起的數(shù)字音頻系統(tǒng)哦.作為嵌入式系統(tǒng),實(shí)現(xiàn)這種參數(shù)也是能接受的.

程序的結(jié)構(gòu)很容易構(gòu)思:

simplest_audio_playback_prog_archi.png

圖 最簡單的音頻播放程序結(jié)構(gòu)圖

以STM32F722ZE板子為例,主系統(tǒng)跑216MHz, 使用定時(shí)器6作8KHz的更新中斷, 那么Period設(shè)定為

 

P = (216000000/2)/8000 = 13500

 

這里順便碎碎念一下子采樣率與主頻率的關(guān)系. 當(dāng)使用定時(shí)器6的時(shí), 采樣率8K/16K/32K/48K/96K/144K都能計(jì)算得到整數(shù)的更新Period, 也就是都能得到無更新誤差的輸出精度.但是使用11.025K/22.05K/44.1K這樣的采樣率時(shí)得不到整數(shù)的更新Period, 也就是播放這種采樣率的音頻的時(shí), 需要重新配置時(shí)鐘源才能得到無更新誤差的回放精度. 在音頻系統(tǒng)中經(jīng)常會(huì)有這樣的問題, 切換采樣率需要重新配置時(shí)鐘. 一旦主頻時(shí)鐘定了,某些采樣率是怎么也配置不了無更新誤差的. 在使用I2S接口這樣的外部Codecs時(shí)候原理也跟這個(gè)類似. 關(guān)于這個(gè)采樣率定時(shí)誤差后面的章節(jié)會(huì)再次詳敘.

在定時(shí)器ISR中做個(gè)標(biāo)記,表示125us的節(jié)點(diǎn)到了,主函數(shù)該更新輸出了:

/* USER CODE BEGIN TIM6_DAC_IRQn 1 */

g_Tim6_Flag = true;

/* USER CODE END TIM6_DAC_IRQn 1 */

main文件中定義如下幾個(gè)宏用于生成播放數(shù)據(jù):

#ifndef M_PI

#define M_PI 3.14159265358979323846

#endif

#define TEST_SAMPLE_RATE 8000

#define AUDIO_HZ 200

#define AUDIO_CYCLE (TEST_SAMPLE_RATE / AUDIO_HZ)

#define PULSE_HZ 4

#define PULSE_CYCLE (TEST_SAMPLE_RATE / PULSE_HZ)

主函數(shù)中的主循環(huán):

while (1)

{

if(true == g_Tim6_Flag)

{

//Play Next sample

//This demo play pulsed sine wave, if you want to play other shape of audio, please read the last article of this series,

//and modified the next line of data feeding

if(0==((time_index/PULSE_CYCLE)%2))

{

tmpSample = INT8_MAX * ((sin(M_PI*2*(time_index%AUDIO_CYCLE)/AUDIO_CYCLE)) + 1);

//Note: this macro is deliberately defined without parentness for simplication

#warning This gain is very important if you test the playback with an earphone,

too little you will not here audible sound, too loud your ears are in risk.

I suggest you start from a little value and adjust up.

#define TEST_GAIN 1/7

tmpSample = (uint8_t)(tmpSample * TEST_GAIN );

#undef TEST_GAIN

}

else

{

tmpSample = 0;

}

HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_8B_R, tmpSample);

time_index ++;

HAL_DAC_Start(&hdac, DAC_CHANNEL_2);

HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);

/* check the end of the file */

//No end for generated data, if you need to stop at some point, uncomment next block

//       if(SOME_POINT == time_index)

//       {

//          while(1);

//        }

           g_Tim6_Flag = false;

         }

}

完整代碼請(qǐng)參考共享文件夾. 這里播放的是2Hz脈沖(跳變頻率為4Hz)的200Hz正弦波. 聽到應(yīng)該是 嘟-嘟-嘟 這種效果. 關(guān)于如何生成該種波形, 請(qǐng)參考上一篇的內(nèi)容. 還說一點(diǎn)就是增益設(shè)置, 如果是音箱或者功放板子那倒無所謂, 如果用耳機(jī)收聽建議先將增益調(diào)小一點(diǎn)試驗(yàn)(本文實(shí)驗(yàn)中設(shè)置的1/7), 覺得聲音小了, 慢慢往上調(diào)整以保護(hù)你的耳朵.

 

dac_pulse_2hz_oscope.jpg

 

圖 DAC實(shí)際輸出波形

5.播放實(shí)驗(yàn)之二(DAC+定時(shí)器播放raw音頻)

上一章與上一節(jié)所講的播放這種計(jì)算出來的數(shù)據(jù)大致有兩種用途:

· 可以測試我們的系統(tǒng)是否工作,頻率響應(yīng)等等參數(shù)的測量都需要生成周期性的信號(hào).

· 有藝術(shù)家這樣創(chuàng)作音樂.

除此之外, 大多數(shù)情況下音頻系統(tǒng)還是應(yīng)用在播放其他人已經(jīng)錄制/處理好的聲音. 這里播放一個(gè)作者處理好的8K/8bit的一段raw音頻為例作實(shí)驗(yàn). 至于如何處理這樣的文件下節(jié)馬上介紹. 這里先使用本文附帶資源中的文件作實(shí)驗(yàn).

共享文件夾 Chapter2coderesource 8k8bit_mono_raw_sample.bin

這個(gè)文件大小為: 125548, hex表示是0x1EA6C, 實(shí)驗(yàn)使用的STM32F722ZE的Flash大小是0x80000, 這里將此文件燒錄在Flash的末端以0x08060000開始的位置.

首先打開STM32 ST-LINK Utility.exe,連接板子:

 

st_ut_openfile.png

 

圖STM32 ST-LINK Utility.exe打開bin文件

點(diǎn)燒錄,注意選地址: 0x08060000.

st_ut_flash_conf_addr_prog.png

圖 選地址燒錄

點(diǎn)Start開始燒錄.

再修改程序,上 一節(jié)的程序?yàn)楦翫AC前計(jì)算采樣值. 將其改為從Flash取數(shù)據(jù)即可:

關(guān)鍵代碼:

#define RAW_FILE_ADDRESS 0x08060000

#define RAW_FILE_SIZE 125548

uint8_t* PlaybackPosition = (uint8_t*)RAW_FILE_ADDRESS;

....

//Play Next sample

HAL_DAC_SetValue(&hdac, DAC_CHANNEL_2, DAC_ALIGN_8B_R, *((uint8_t *)(PlaybackPosition)));

PlaybackPosition ++;

完整代碼請(qǐng)參考共享文件夾工程.

編譯之后下載就可以聽音樂了. 歌曲是老版本TVB拍的聊齋的主題曲<<隔世情>>的片段,也不知道讀者是否喜歡這種風(fēng)格. 如果想播放你們想聽的歌曲, 請(qǐng)繼續(xù)閱讀下一節(jié)就能自己處理了.這里解釋一下子程序與音樂在Flash中的關(guān)系圖:

flash_space_audio_prog.png

圖 音頻數(shù)據(jù)與程序在Flash中的分布圖(非比例)

一瞬間有一種馮諾伊曼架構(gòu)的感覺, 不過這是開玩笑的, 實(shí)際上M7是哈佛結(jié)構(gòu).

6. 將任意音頻文件處理成8K/8bit的raw數(shù)據(jù)

任意找一個(gè)你喜歡的音頻文件,比如xyz.mp3.用Audacity打開,注意首次Audacity打開mp3的話需要另外安裝插件. 這個(gè)通過軟件提示應(yīng)該很容易完成. 這里篇幅原因不介紹這個(gè)了.如果不想裝插件包, 請(qǐng)找個(gè)ogg格式或其他軟件自身支持的格式也是一樣操作.

mp3_raw_1.png

圖 打開你要轉(zhuǎn)的文件

一般而言電腦上的音樂文件一般為雙通道的立體聲歌曲,這里為了后面簡單處理,將其合并為單通道.(立體聲的處理以后會(huì)講到.)

mp3_raw_2.png

圖 立體聲至單聲道

重采樣,44.1KHz->8KHz,再將工程采樣率改成8KHz:

mp3_raw_3.png

 

圖 重采樣

截取一段音頻,導(dǎo)出,因?yàn)橹环峙淞?28K的空間給音頻數(shù)據(jù),所以最多只能截取:

128K/(8K*8/8) = 16秒

的片段.(以后我們會(huì)講壓縮,以及外部存儲(chǔ)的.)

mp3_raw_4.png

圖 選取并導(dǎo)出

注意選擇格式,最好把采樣率與位寬反映在文件名中,以便以后使用.

mp3_raw_5.png

圖 選取格式

至此就可以燒錄播放了, Enjoy!

如果與上次生成的音頻數(shù)據(jù)長度不同的話,注意代碼中文件長度相應(yīng)的定義也需要改一下子,否則播放的后段會(huì)有以前的殘余數(shù)據(jù).

此篇至此為止,懇請(qǐng)讀者多多反饋指教.

后記

這篇介紹了最基本的音頻播放原理, 關(guān)于播放這個(gè)話題還有很多內(nèi)容沒有覆蓋, 比如Arduino開發(fā)板子播放音頻, PWM+LPF播放原理仿真, I2S接口信號(hào)介紹, SAI簡介,作者自制的音頻播放板子也沒有登場等等等. 暫時(shí)計(jì)劃將多余的內(nèi)容分成附錄形式在以后的章節(jié)順帶發(fā). 當(dāng)然還得聽編輯與讀者的意見.

代碼與資源共享地址: https://pan.baidu.com/s/1dFh5pWd

  • 本文系21ic原創(chuàng),未經(jīng)許可禁止轉(zhuǎn)載!

網(wǎng)友評(píng)論

  • 聯(lián)系人:巧克力娃娃
  • 郵箱:board@21ic.com
  • 我要投稿
  • 歡迎入駐,開放投稿

熱門標(biāo)簽
項(xiàng)目外包 more+