首頁(yè) > 評(píng)測(cè) > 第二篇 嵌入式系統(tǒng)硬件輸出系統(tǒng)簡(jiǎn)介及播放實(shí)驗(yàn)(播放篇之一)
第二篇 嵌入式系統(tǒng)硬件輸出系統(tǒng)簡(jiǎn)介及播放實(shí)驗(yàn)(播放篇之一)
- [導(dǎo)讀]
- Everyboard Can Sing
21ic打算攜手資(tu)深(ding)直男癌晚期工程師zhanzr21,來(lái)給大家講一講嵌入式系統(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
前言
此章僅為最基本的播放原理介紹, 還有不少關(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
這里依次說(shuō)明.
1. 控制系統(tǒng),負(fù)責(zé)控制生成數(shù)據(jù)或者讀取數(shù)據(jù)(可能需要解壓或其他操作),并將其交給下一級(jí)的DAC,一般來(lái)講這個(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è)備需求來(lái)決定其性質(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板子+有源音箱
首先來(lái)看看DAC+音箱形式.
圖 DAC+有源音箱形式
左邊是一個(gè)ST的Nucleo開發(fā)板子,具體型號(hào)是STM32F722ZE. 但是絕大多數(shù)的類似板子都能替代. 音頻信號(hào)通過芯片內(nèi)部的DAC輸出到音箱的輸入級(jí). 音箱須為有源音箱(帶電源的就是有源音箱, 本文例子使用的就是網(wǎng)上買的幾十塊錢包郵的USB音箱, 如果是無(wú)源音箱, 需要另外增加功率分大級(jí). 根據(jù)作者的經(jīng)驗(yàn), 你能找到的音箱絕大多數(shù)是有源音箱).
我們看看連接的細(xì)節(jié):
圖 耳機(jī)接口細(xì)節(jié)
這是個(gè)耳機(jī)接口座子+杜邦線改裝出來(lái)的連接器, 左右兩根線是左右聲道信號(hào), 當(dāng)中那個(gè)是共地. 本文所討論的音頻系統(tǒng)除非另外說(shuō)明都為單聲道, 故此只會(huì)使用左右兩通道中的之一加下面那根地線.
大多數(shù)的MCU自帶DAC為兩個(gè)或以上的通道, 足夠完成簡(jiǎn)單的立體聲輸出了. 為討論簡(jiǎn)便, 先只用單聲道做實(shí)驗(yàn), 所以只會(huì)用到一個(gè)DAC輸出. 這是Nucleo板上的連接, 一個(gè)DAC輸出(PA5,DAC通道2), 一個(gè)地.
圖 DAC連接細(xì)節(jié)
2.系統(tǒng)之二: Nucleo板子+耳機(jī)
圖 DAC+耳機(jī)形式
按照數(shù)據(jù)手冊(cè),ST的DAC是無(wú)法直接驅(qū)動(dòng)耳機(jī)的.
圖 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)品長(zhǎng)時(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)聲器
DAC輸出信號(hào)到功放板,功放板驅(qū)動(dòng)揚(yáng)聲器. 其實(shí)這個(gè)系統(tǒng)跟第一個(gè)有音箱的系統(tǒng)在架構(gòu)上是一模一樣的. 只是把音箱拆開了開膛破肚, 把功放板與揚(yáng)聲器拿出來(lái), 大家更容易看明白信號(hào)的走向流程. 圖片中的功放板子是用的Adafruit做的一個(gè)板子, 作者自己打的板子將在后文中介紹.
圖 D類音頻功放板子
這里補(bǔ)一句, 一般而言音頻功放分為AB類和D類. 當(dāng)然還有許多其他類以及變種, 但是用得最多的就是AB類和D類. 兩者的差別用一句話概括:
AB類效果好,D類效率高.
具體關(guān)于兩種放大器的分析與仿真作者打算使用自制的板子再開一篇來(lái)講. 這里先跳過去.
至于揚(yáng)聲器,這里是作者從舊的筆記本上拆的,我想大家應(yīng)該都能在家中找到這樣的舊的揚(yáng)聲器吧. 揚(yáng)聲器上標(biāo)的+-跟馬達(dá)/無(wú)源蜂鳴器/燈絲燈拋一樣, 只是個(gè)接線參考, 兩極從電路原理上講是對(duì)稱的.
圖 舊筆記本上拆的揚(yáng)聲器
以上三個(gè)系統(tǒng)都是本人為了說(shuō)明音頻播放原理而搭建的簡(jiǎn)化型的系統(tǒng). 這些系統(tǒng)便于說(shuō)明原理, 也能夠用作大家自己動(dòng)手的參考. 事實(shí)上實(shí)際的音頻系統(tǒng)中很少直接使用處理器的ADC/DAC來(lái)播放音頻, 而經(jīng)常使用專用的Codecs來(lái)處理音頻. 這些Codecs都是專門為音頻應(yīng)用優(yōu)化過的ADC/DAC, 有的還加入了運(yùn)放電路,DSP處理模塊等等. 在成本與性能上都大大優(yōu)于處理器自帶ADC/DAC. 本文在介紹基本原理時(shí)使用這些通用ADC/DAC, 在此之后將回到使用專用Codecs的更加主流的道路上來(lái). 如果看過第一篇的讀者, 應(yīng)該會(huì)記得曾經(jīng)介紹過的F769-Discovery板子上的音頻系統(tǒng),那就是使用的專用Codecs: WM8994. 作者也會(huì)做一些測(cè)試板子來(lái)配合文中的內(nèi)容.
4.播放實(shí)驗(yàn)之一(DAC+定時(shí)器播放生成音頻)
硬件介紹差不多了, 現(xiàn)在開始寫代碼了. 讀者如果要試驗(yàn)本章配套代碼, 需要搭建的硬件就是上面所述的三種之一. 從軟件角度來(lái)說(shuō), 這三種硬件中控制器所需要做的工作就是將數(shù)據(jù)從DAC中發(fā)出去就行了. 具體而言, 就是將數(shù)據(jù)以目標(biāo)采樣率的速度把數(shù)據(jù)發(fā)到DAC上, 就是這么簡(jiǎn)單. 為求最迅捷的展示原理, 這里使用的音頻參數(shù)為:
8bit PCM * 單通道 * 8K采樣率
這個(gè)參數(shù)今天看來(lái)很寒酸,可是退回一二十年, 這樣參數(shù)的硬件可都是高端人士才用的起的數(shù)字音頻系統(tǒng)哦.作為嵌入式系統(tǒng),實(shí)現(xiàn)這種參數(shù)也是能接受的.
程序的結(jié)構(gòu)很容易構(gòu)思:
圖 最簡(jiǎn)單的音頻播放程序結(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, 也就是都能得到無(wú)更新誤差的輸出精度.但是使用11.025K/22.05K/44.1K這樣的采樣率時(shí)得不到整數(shù)的更新Period, 也就是播放這種采樣率的音頻的時(shí), 需要重新配置時(shí)鐘源才能得到無(wú)更新誤差的回放精度. 在音頻系統(tǒng)中經(jīng)常會(huì)有這樣的問題, 切換采樣率需要重新配置時(shí)鐘. 一旦主頻時(shí)鐘定了,某些采樣率是怎么也配置不了無(wú)更新誤差的. 在使用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)容. 還說(shuō)一點(diǎn)就是增益設(shè)置, 如果是音箱或者功放板子那倒無(wú)所謂, 如果用耳機(jī)收聽建議先將增益調(diào)小一點(diǎn)試驗(yàn)(本文實(shí)驗(yàn)中設(shè)置的1/7), 覺得聲音小了, 慢慢往上調(diào)整以保護(hù)你的耳朵.
圖 DAC實(shí)際輸出波形
5.播放實(shí)驗(yàn)之二(DAC+定時(shí)器播放raw音頻)
上一章與上一節(jié)所講的播放這種計(jì)算出來(lái)的數(shù)據(jù)大致有兩種用途:
· 可以測(cè)試我們的系統(tǒng)是否工作,頻率響應(yīng)等等參數(shù)的測(cè)量都需要生成周期性的信號(hào).
· 有藝術(shù)家這樣創(chuàng)作音樂.
除此之外, 大多數(shù)情況下音頻系統(tǒng)還是應(yīng)用在播放其他人已經(jīng)錄制/處理好的聲音. 這里播放一個(gè)作者處理好的8K/8bit的一段raw音頻為例作實(shí)驗(yàn). 至于如何處理這樣的文件下節(jié)馬上介紹. 這里先使用本文附帶資源中的文件作實(shí)驗(yàn).
共享文件夾\ Chapter2\code\resource\ 8k8bit_mono_raw_sample.bin
這個(gè)文件大小為: 125548, hex表示是0x1EA6C, 實(shí)驗(yàn)使用的STM32F722ZE的Flash大小是0x80000, 這里將此文件燒錄在Flash的末端以0x08060000開始的位置.
首先打開STM32 ST-LINK Utility.exe,連接板子:
圖STM32 ST-LINK Utility.exe打開bin文件
點(diǎn)燒錄,注意選地址: 0x08060000.
圖 選地址燒錄
點(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)系圖:
圖 音頻數(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格式或其他軟件自身支持的格式也是一樣操作.
圖 打開你要轉(zhuǎn)的文件
一般而言電腦上的音樂文件一般為雙通道的立體聲歌曲,這里為了后面簡(jiǎn)單處理,將其合并為單通道.(立體聲的處理以后會(huì)講到.)
圖 立體聲至單聲道
重采樣,44.1KHz->8KHz,再將工程采樣率改成8KHz:
圖 重采樣
截取一段音頻,導(dǎo)出,因?yàn)橹环峙淞?28K的空間給音頻數(shù)據(jù),所以最多只能截取:
128K/(8K*8/8) = 16秒
的片段.(以后我們會(huì)講壓縮,以及外部存儲(chǔ)的.)
圖 選取并導(dǎo)出
注意選擇格式,最好把采樣率與位寬反映在文件名中,以便以后使用.
圖 選取格式
至此就可以燒錄播放了, Enjoy!
如果與上次生成的音頻數(shù)據(jù)長(zhǎng)度不同的話,注意代碼中文件長(zhǎng)度相應(yīng)的定義也需要改一下子,否則播放的后段會(huì)有以前的殘余數(shù)據(jù).
此篇至此為止,懇請(qǐng)讀者多多反饋指教.
后記
這篇介紹了最基本的音頻播放原理, 關(guān)于播放這個(gè)話題還有很多內(nèi)容沒有覆蓋, 比如Arduino開發(fā)板子播放音頻, PWM+LPF播放原理仿真, I2S接口信號(hào)介紹, SAI簡(jiǎn)介,作者自制的音頻播放板子也沒有登場(chǎng)等等等. 暫時(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
- 我要投稿
-
歡迎入駐,開放投稿
- NRF52810藍(lán)牙數(shù)字耳機(jī)找人定制
預(yù)算:¥30005天前
- 125KW模塊式PCS軟硬件外包開發(fā)
預(yù)算:¥1100000015小時(shí)前
- 12V汽車啟動(dòng)電源項(xiàng)目BMS設(shè)計(jì)
預(yù)算:¥50000023小時(shí)前
- 數(shù)據(jù)可視化軟件 開發(fā)
預(yù)算:¥5000023小時(shí)前
- PLC項(xiàng)目調(diào)試修改
預(yù)算:¥100001天前
- 起動(dòng)電機(jī)控制器開發(fā)
預(yù)算:¥1100001天前