試實(shí)戰(zhàn):TLV320AIC31 Codec驅(qū)動鏈路與排錯指南)
簡介本資源是一套面向嵌入式音頻開發(fā)工程師與硬件驗(yàn)證人員的TLV320AIC31立體聲音頻編解碼器裸機(jī)測試代碼專用于無操作系統(tǒng)環(huán)境下對芯片核心功能進(jìn)行底層驗(yàn)證解決I2C配置、寄存器初始化、IIS音頻流傳輸、DMA數(shù)據(jù)搬運(yùn)及中斷響應(yīng)等關(guān)鍵開發(fā)痛點(diǎn)。壓縮包共22個文件85KB含6個C源文件如main.c、apb_i2c_module.c、iis.c實(shí)現(xiàn)驅(qū)動邏輯與主控交互6個H頭文件如iis.h、hw_lib.h定義寄存器映射與接口規(guī)范5個.o目標(biāo)文件便于快速集成另有MCP工程配置、AXF可執(zhí)行鏡像及STG調(diào)試腳本結(jié)構(gòu)完整、即拿即用。已有213人學(xué)習(xí)下載代碼覆蓋采樣率設(shè)置、麥克風(fēng)/耳機(jī)通路測試、電源管理及錯誤狀態(tài)檢測等典型場景結(jié)合www.pudn.com.txt提供的參考來源可高效支撐硬件Bring-up、音頻鏈路調(diào)試與TI平臺二次開發(fā)。 上周整理舊項(xiàng)目檔案翻出一個名字很長很隨意的壓縮包TLV320AIC31_test.rar。這類東西在嵌入式音頻開發(fā)里屬于寶藏級的存在——TLV320AIC31是TI的低功耗立體聲編解碼器Audio Codectest說明這是當(dāng)年調(diào)音頻鏈路時留下的測試工程沒有配套文檔注釋也稀疏但拿到新板子上比對著跑一遍比從零翻幾十頁數(shù)據(jù)手冊效率高得多。而且這種test工程往往是前人踩過坑之后沉淀下來的里面藏著大量手冊不會告訴你的細(xì)節(jié)。這篇文章不打算按數(shù)據(jù)手冊的章節(jié)給你翻譯寄存器而是以這份test代碼為線索講清楚驅(qū)動TLV320AIC31的關(guān)鍵鏈路I2C控制通道怎么驗(yàn)證、時鐘和PLL怎么配、音頻數(shù)據(jù)通路怎么搭、test代碼里的幾種驗(yàn)證方法各自能發(fā)現(xiàn)什么問題最后是我?guī)状握{(diào)試踩坑后總結(jié)的排錯順序。適合手里拿著AIC31系列芯片要調(diào)驅(qū)動的人也適合剛接觸I2S接口音頻芯片的MCU工程師。1. 先看懂壓縮包里的TLV320AIC31測試工程骨架1.1 解壓后最先該翻哪幾個文件一個典型的TLV320AIC31_test工程文件結(jié)構(gòu)大概是這樣TLV320AIC31.c和TLV320AIC31.h管寄存器定義與初始化調(diào)用i2c_helper或者i2c_soft.c管底層I2C讀寫test_main.c/app.c里是正弦波播放、回環(huán)測試、串口交互命令外面套一層Makefile或者Keil/IAR工程文件。偶爾還會有一個readme.txt里面畫著板子接法、I2C地址和主時鐘頻率這是全壓縮包里最值錢的文件解壓后建議第一眼先看它。拿到這樣的工程我的習(xí)慣是不要急著編譯燒錄先讀三個文件寄存器初始化數(shù)組。能直接看出作者用的采樣率、音頻數(shù)據(jù)格式、模擬通路是怎么接的這等于把整個芯片的接線圖攤開在眼前。底層I2C讀寫函數(shù)。看I2C地址是寫死還是可配是否支持讀操作。很多test代碼只寫了配置函數(shù)沒有讀回校驗(yàn)這種工程適配起來要加一層保險。主測試流程??此趺磁袛鄿y試通過有的靠串口打印寄存器值有的靠DMA搬運(yùn)后聽聲音有的靠板載LED變化。理解了判定標(biāo)準(zhǔn)你才能復(fù)現(xiàn)它的驗(yàn)證邏輯。1.2 芯片選型確認(rèn)AIC31不是一個芯片是一族芯片TLV320AIC31系列包含了AIC3100、AIC3101、AIC3104、AIC3106等多個型號核心架構(gòu)一致I2C/SPI負(fù)責(zé)控制I2S接口負(fù)責(zé)傳輸音頻數(shù)據(jù)內(nèi)置ADC/DAC和模擬輸入輸出差別主要在模擬輸入輸出通道數(shù)量和封裝。這串型號看著像孿生兄弟但引腳和部分寄存器定義并不完全相同。我見過有人把AIC3104的配置直接套到AIC3101上折騰一整天左聲道死活不出聲最后查到是模擬開關(guān)寄存器在不同型號上的映射不一樣。所以拿到test代碼的第一步不是看代碼而是確認(rèn)你板子上焊的到底是哪一顆再決定哪些寄存器配置可以照抄哪些必須對著datasheet重新核對。1.3 從初始化序列反向推斷作者的時鐘設(shè)計如果代碼完全沒有文檔最快的理解方式是看初始化時序。初始化數(shù)組里先寫哪個寄存器、后寫哪個寄存器往往反映著datasheet推薦的上電順序。配置完時鐘之后作者才去打開各路電源和模擬通路這個順序本身就有信息量。還有一個判斷技巧看MCLK和采樣率的關(guān)系。如果MCLK剛好是采樣率的256倍大概率沒開PLL如果初始化里出現(xiàn)PLL編程寄存器0x04下面幾個字節(jié)那說明作者在前面的宏定義或者M(jìn)akefile里寫了MCLK和采樣率的計算關(guān)系向前翻代碼就能找到。一個我反復(fù)用的通訊自檢動作軟件復(fù)位寫完后回讀0x00寄存器。如果回讀的值是0x00而不是0xFF說明I2C物理鏈路已經(jīng)通了后面配置才有討論前提。這個動作看著簡單但很多調(diào)試在寫不進(jìn)去和配置錯誤之間折騰半天根源就是沒先驗(yàn)證這一步。2. 先打通控制通道I2C地址、頁面機(jī)制、寄存器回讀驗(yàn)證2.1 地址不是想當(dāng)然的0x18TLV320AIC31的I2C地址由ADR0和ADR1兩個引腳的電平共同決定不同引腳接法組合出不同地址。最常見的接法是兩者都接地此時7位I2C地址為0x18換算成8位寫地址是0x30。但如果你在I2C總線上掃不到設(shè)備先別急著懷疑芯片壞了用萬用表量一下ADR0/ADR1有沒有被板子上的其它信號拉錯。調(diào)試時最實(shí)用的工具是一個I2C地址掃描函數(shù)從0x03到0x77依次發(fā)地址試探讀看哪個地址返回ACK。這個套路在沒有示波器的場景下非常管用。但要小心如果總線上掛了多個器件且地址重疊掃描結(jié)果會有多個響應(yīng)那就需要斷開其它器件或者用GPIO單獨(dú)模擬I2C只掛這一顆芯片把問題隔離出來。2.2 頁面機(jī)制寫0x00切頁這是個隱形的坑AIC31的寄存器總數(shù)超過255個所以芯片內(nèi)部做了分頁管理。0x00和0x7F都是頁選擇寄存器具體看型號和手冊比如page 0和page 1。往I2C寫數(shù)據(jù)時要先寫頁選擇寄存器的值再寫目標(biāo)寄存器切到別的頁之前也要先切回去。這里有個隱蔽的坑調(diào)試過程中你在page 1設(shè)置了麥克風(fēng)PGA增益然后切回page 0配置DAC如果某一段代碼在切頁后忘了重新指定page后續(xù)所有配置就被寫到了page 1里你完全沒想到的位置。這個問題極難查因?yàn)檫壿嬌厦總€寄存器地址都是對的但物理上頁不對。我后來養(yǎng)成一個習(xí)慣把寫頁寫寄存器封裝成一個底層函數(shù)應(yīng)用層永遠(yuǎn)不直接操作頁選擇寄存器。這樣即使配置再多也不會出現(xiàn)頁錯亂的隱性bug。2.3 寄存器讀寫驗(yàn)證代碼怎么寫才不容易翻車以下是一個典型的I2C讀寫函數(shù)骨架無論是Linux的i2c-dev還是MCU的HAL庫都能快速套用static int aic31_write_reg(uint8_t reg, uint8_t val) { uint8_t buf[2]; buf[0] reg; buf[1] val; return i2c_master_write(AIC31_I2C_ADDR, buf, 2); } static int aic31_read_reg(uint8_t reg, uint8_t *val) { int ret; uint8_t addr reg; ret i2c_master_write(AIC31_I2C_ADDR, addr, 1); if (ret 0) return ret; return i2c_master_read(AIC31_I2C_ADDR, val, 1); }有兩個細(xì)節(jié)要提醒。第一讀操作要先發(fā)寄存器地址再發(fā)一個重復(fù)起始信號或stop后start然后讀這是I2C協(xié)議的標(biāo)準(zhǔn)做法但有些平臺的I2C驅(qū)動對write then read without stop支持得不好需要單獨(dú)實(shí)現(xiàn)combined transaction。第二I2C速率建議從100kHz起步芯片手冊給的上限可能更高但高波特率會放大板級抗干擾問題。我遇到過一顆芯片在400kHz下偶爾回讀失敗、降到100kHz就完全穩(wěn)定最后查到是上拉電阻阻值跟總線電容不匹配。3. 時鐘樹和PLL無聲、爆音、斷續(xù)多數(shù)根源在時鐘3.1 時鐘從哪來MCLK和主從模式TLV320AIC31需要一路主時鐘MCLK才能工作。這路時鐘可以來自外部有源晶振、主控SoC的MCLK引腳也可以在某些模式下由I2S的BCLK轉(zhuǎn)換而來。MCLK的頻率直接決定了編解碼器能輸出什么采樣率所以畫原理圖的時候就要想清楚而不是等軟件調(diào)試時再補(bǔ)救。MCLK和采樣率之間如果有整數(shù)關(guān)系調(diào)試會輕松很多。比如MCLK等于12.288MHz、采樣率48kHz時MCLK正好等于256倍的fs這時候大多數(shù)情況下可以直接旁路PLL讓CODEC_CLKIN直接使用MCLK。如果MCLK是13MHz這種不干凈的頻率那就必須把PLL打開把它合成到想要的時鐘頻率上。3.2 用PLL還是不用PLL別陷入必須開的誤區(qū)我見過很多參考代碼一上來就寫PLL寄存器好像不開PLL這芯片就廢了。實(shí)際上TLV320AIC31的PLL完全可以旁路旁路模式還少一個故障點(diǎn)。我的建議是先算你的MCLK是不是目標(biāo)采樣率的256倍、512倍等整數(shù)倍是就優(yōu)先旁路PLL不是才去配PLL。真要開PLL時計算過程通常是這樣確認(rèn)PLL_CLKIN也就是外部進(jìn)來的MCLK比如13MHz。設(shè)定目標(biāo)輸出時鐘通常取12.288MHz代表48kHz采樣率家族11.2896MHz代表44.1kHz采樣率家族。按數(shù)據(jù)手冊的公式求出R、P、N、K參數(shù)填入PLL編程寄存器。配置時鐘分頻寄存器確保CODEC_CLKIN信號源指向PLL輸出而不是直接MCLK。不同子型號的分頻器范圍不一樣所以不存在一個萬能公式能替所有頻率算好參數(shù)。我的做法是在PC上先用Excel把PLL參數(shù)算清楚再手動填到寄存器里同時把MCLK、目標(biāo)采樣率、PLL輸出三個值寫在代碼注釋里。這樣下次換板子、換晶振頻率直接改宏定義就能重新計算。3.3 用示波器量什么MCLK、BCLK、WCLK三者的關(guān)系I2S跑起來之后示波器上應(yīng)該看到三個時鐘信號協(xié)同工作。WCLK也叫LRCK是幀時鐘頻率等于采樣率。48kHz采樣率時WCLK應(yīng)該是48kHzBCLK是位時鐘頻率取決于幀格式和位寬標(biāo)準(zhǔn)I2S 32位幀格式下大概是48kHz乘以32也就是1.536MHz。MCLK不要求一定跟BCLK同步但在絕大多數(shù)主控配置里它們同源這樣最省事。調(diào)試時如果寄存器配置完、DMA已經(jīng)搬運(yùn)數(shù)據(jù)但喇叭就是不出聲先量這三組時鐘。WCLK是0說明I2S主控側(cè)沒有產(chǎn)生幀時鐘多半是DMA沒觸發(fā)或主控音頻時鐘輸出沒打開。WCLK頻率是預(yù)期的兩倍或一半大概率是I2S格式位設(shè)置錯誤比如16bit和32bit幀不匹配或者采樣率寄存器跟MCLK的比例算錯。4. 從寄存器序列到真實(shí)發(fā)聲test代碼里該有的幾件事4.1 初始化順序四步走別跳步我把AIC31的初始化順序總結(jié)為四步軟件復(fù)位配置硬件接口格式和時鐘配置模擬輸入輸出通路最后解除靜音和設(shè)置音量。順序亂了后面就是無休止的為什么沒聲。很多無聲問題的根源都在第四步默認(rèn)配置下DAC處于靜音狀態(tài)輸出音量可能是最小值。寄存器全部看起來對但沒聲音先查靜音位和音量寄存器再懷疑硬件。這個低級錯誤在真實(shí)的開發(fā)中出現(xiàn)頻率非常高。一個精簡的初始化配置示例寄存器地址和位定義不同型號有差異務(wù)必對照數(shù)據(jù)手冊static const struct { uint8_t reg; uint8_t val; } aic31_init[] { { 0x00, 0x00 }, // page 0 { 0x01, 0x80 }, // software reset // 復(fù)位后延時100ms再繼續(xù)配置 { 0x02, 0x40 }, // 采樣率48kHz具體值查手冊 { 0x03, 0x00 }, // DAC數(shù)據(jù)通路解除靜音 { 0x04, 0x00 }, // DAC左聲道音量 0dB { 0x05, 0x00 }, // DAC右聲道音量 0dB // 模擬通路把DAC輸出連到耳機(jī)放大器 { 0x0B, 0x0F }, // HPL輸出路由DAC_L - HPL { 0x0C, 0x0F }, // HPR輸出路由DAC_R - HPR { 0x0D, 0x00 }, // HPL音量 0dB { 0x0E, 0x00 }, // HPR音量 0dB // 如果要用麥克風(fēng)/line in按需配置page 1的PGA { 0x00, 0x01 }, // 切到page 1 { 0x01, 0x40 }, // 左聲道MIC PGA增益具體值查手冊 { 0x00, 0x00 }, // 切回page 0 };寫完后別急著播放逐個回讀關(guān)鍵寄存器把I2C的log打印出來和期望值對比一遍。這一步能杜絕90%的寫錯位、寫錯頁、寫錯值問題。4.2 最直接的驗(yàn)證I2S播放1kHz正弦波判斷一顆音頻Codec通不通最不過腦子的測試是播放1kHz正弦波。生成一個正弦表通過DMA循環(huán)送給I2S耳機(jī)或喇叭里出現(xiàn)穩(wěn)定的單音說明DAC到輸出級的模擬通路基本正常。如果這個測試都不過就不要急著做錄音回環(huán)了。#define FS 48000 #define TONE_FREQ 1000 #define SIN_BUF_LEN 480 int16_t sine_buf[SIN_BUF_LEN]; for (int i 0; i SIN_BUF_LEN; i) { sine_buf[i] (int16_t)(32767.0f * 0.5f * sinf(2.0f * M_PI * TONE_FREQ * i / FS)); }這一段緩沖正好10msDMA循環(huán)發(fā)送非常穩(wěn)定。播放的時候用示波器看耳機(jī)座輸出應(yīng)該能看到一個等幅度的正弦波頻率精確度取決于主時鐘精度。如果用手摸一下輸出引腳有嗡聲說明模擬通路已經(jīng)通了只是信號路徑上某個環(huán)節(jié)沒開或沒接對。4.3 回環(huán)測試怎么判斷出聲了但聲音不對1kHz正弦波能正常播放才建議做ADC/DAC回環(huán)測試?;丨h(huán)有兩種芯片內(nèi)部的數(shù)字回環(huán)也就是DAC的數(shù)據(jù)直接從內(nèi)部連到ADC路徑不做模擬往返另一種是物理回環(huán)把DAC輸出用跳線接到LINE IN再讓ADC采集。數(shù)字回環(huán)驗(yàn)證的是I2S收發(fā)和寄存器通路物理回環(huán)能額外驗(yàn)證模擬輸入級。我通常的做法是通過串口命令切換回環(huán)模式讓主控把ADC采集到的數(shù)據(jù)通過串口發(fā)回PC存成WAV文件直接打開聽。能聽到原始正弦的回聲說明ADC通路正常聽到明顯的諧波或底噪那就是模擬電源和PCB布局的問題寄存器怎么調(diào)都救不回來?;丨h(huán)測試還有一層價值在沒有音頻分析儀的情況下播放1kHz正弦、回采后做FFT粗看基波能量和噪聲底能對THDN有個大致判斷。5. 實(shí)測排錯test代碼跑不通時我按這個順序鎖定根因5.1 現(xiàn)象一I2C讀回全是0xFF或0x00這一條通常不是寄存器配置問題而是物理鏈路或地址問題按下面順序排查先做I2C地址掃描確認(rèn)總線上到底有沒有這顆設(shè)備返回ACK的地址和你預(yù)期是否一致。量SCL/SDA有沒有上拉上拉到哪個電源域。TLV320AIC31的I2C引腳電平和DVDD有關(guān)如果主控I2C是1.8V而芯片DVDD接了3.3V回讀可能全是0xFF。確認(rèn)DVDD和AVDD都穩(wěn)定之后再發(fā)I2C。有些板子用GPIO控制電源軟件上電后立刻初始化芯片還沒起來自然沒有ACK。如果讀回0x00但寫不進(jìn)去多半是I2C時序不滿足芯片要求把速率降到100kHz重試。5.2 現(xiàn)象二寄存器讀寫正常耳機(jī)一點(diǎn)聲音都沒有I2C通了寄存器也能回讀但就是無聲按這個順序排查先查靜音。DAC數(shù)據(jù)通路寄存器里的靜音位、耳機(jī)音量寄存器是不是0或Mute狀態(tài)。查I2S三根時鐘。示波器看WCLK頻率是否等于采樣率BCLK是否正確。查DIN有沒有數(shù)據(jù)。邏輯分析儀掛到DIN上確認(rèn)DMA搬運(yùn)正常數(shù)據(jù)格式和時鐘邊沿跟芯片配置一致。查模擬電源。HPVDD有沒有供上如果有獨(dú)立耳機(jī)功放使能腳檢查有沒有被拉低。最后一招示波器直接測量DAC輸出引腳看有沒有直流偏置或微弱信號痕跡把問題定位在芯片內(nèi)部還是外部鏈路。5.3 現(xiàn)象三有聲但噪聲大或者音量調(diào)不上去底噪大和爆音是兩回事。底噪大大多數(shù)來自模擬電源和地環(huán)路寄存器解決不了比較有效的三板斧是AVDD用LDO單獨(dú)供電模擬地和數(shù)字地單點(diǎn)連接DAC輸出到耳機(jī)座的耦合電容選低ESR型號并注意極性。音量調(diào)不上去則回頭查PGA和混音寄存器別在DAC主音量上死磕——有時候是前級增益把動態(tài)范圍吃掉了后面怎么加音量都破音。5.4 把test代碼遷移到新平臺時的三個適配點(diǎn)一份別人調(diào)通的測試代碼放到你自己板子上至少有三個地方要改底層I2C接口把函數(shù)指針或宏替換成你平臺的實(shí)現(xiàn)最好保持接口名不變這樣上層代碼完全不用動。等待延時原工程可能用for循環(huán)空轉(zhuǎn)或者沒有考慮不同主頻下延時偏差最好換成毫秒級的系統(tǒng)延時。軟件復(fù)位后、PLL鎖定后都需要延時這塊省不了。I2S主控配置Codec側(cè)的I2S設(shè)置通常不用改但主控的DMA描述符深度、FIFO閾值、采樣率和Codec要對齊。很多有雜音、聲音斷續(xù)的問題不是Codec配錯而是DMA緩沖太小導(dǎo)致下溢。另外一個建議拿到工程后先查一下TI官網(wǎng)對應(yīng)型號的勘誤表。TLV320AIC31系列有些寄存器存在已知問題比如某些位必須按特定順序?qū)懟蛘呱想娗氨仨毐3帜硞€狀態(tài)。這些信息在datasheet正文里不顯眼但踩過一次就能記一輩子。最后說點(diǎn)個人體會。我調(diào)音頻Codec的頭兩年遇到不出聲總以為是寄存器配錯反復(fù)翻手冊、改配置折騰幾天沒結(jié)果。后來養(yǎng)成先量時鐘、先確認(rèn)I2C鏈路、再動寄存器的習(xí)慣問題基本都能在半天內(nèi)定位。test代碼這種東西不是標(biāo)準(zhǔn)答案而是前人踩坑之后留下的路標(biāo)——順著路標(biāo)走再結(jié)合示波器、邏輯分析儀和一對靠譜的耳機(jī)比什么都管用。本文還有配套的精品資源點(diǎn)擊獲取