試指南:I2S配置、寄存器調(diào)優(yōu)與五大踩坑)
1. 從一塊“沒聲音”的板子說起ES7243 到底難在哪去年接手一個多路麥克風(fēng)陣列的采集板模擬前端用的是 ES7243 這顆音頻 ADC。板上電、驅(qū)動加載、arecord一跑波形是干凈的零。示波器量 MCLK 有、BCLK 有、LRCK 有、SDATA 也有翻轉(zhuǎn)看起來什么都是對的但數(shù)據(jù)就是不出來。折騰了兩天之后發(fā)現(xiàn)是寄存器初始化順序和主從模式配反了——這類問題沒有經(jīng)驗的人很難靠猜解決因為它不報錯所有信號都在“正常工作”只是工作在錯誤的模式下。這篇東西就是圍繞 ES7243 這顆芯片把 I2S 配置、寄存器調(diào)優(yōu)、以及我在實際項目里踩過的五個坑完整講一遍。ES7243 是一顆 24 位、支持最高 192kHz 采樣率的立體聲/多通道音頻 ADC通過 I2C 配置寄存器通過 I2S/PCM 接口把數(shù)字音頻送出去常用于會議終端、錄音筆、USB 聲卡、麥克風(fēng)陣列前端、車載語音采集這類場景。它的好處是集成度高——PGA、ALC、高通濾波、數(shù)字音量全在片內(nèi)主控只要給個時鐘就能收數(shù)據(jù)麻煩的地方也在這——可配置項多一旦某個位配錯癥狀往往不是“不工作”而是“工作得不對”比如音量小一半、左右聲道互換、底噪大、低頻有噗噗聲。適合誰看正在調(diào) ES7243 或者同類音頻 ADC 的嵌入式/驅(qū)動工程師做音頻硬件設(shè)計的同學(xué)以及拿到一塊采集板但不知道從哪里開始下手的開發(fā)者?;A(chǔ)差一點也沒關(guān)系我會把時鐘樹、主從模式這些容易繞暈的概念用生活化的方式講清楚有經(jīng)驗的可以直接跳到第 4、5、6 章看配置和踩坑。說明文中涉及的寄存器具體數(shù)值來自我手上那版手冊的典型配置。ES7243 存在不同版本和不同批次部分位域定義和默認值有差異抄之前一定用你手上的手冊逐位核對一遍。這是我踩過的最虧的一個坑后面會細說。2. 先把鏈路想清楚ES7243 在系統(tǒng)里干什么活2.1 芯片能力邊界與選型邏輯選一顆音頻 ADC本質(zhì)是在幾個維度上做權(quán)衡通道數(shù)、動態(tài)范圍、采樣率、接口復(fù)雜度、功耗、成本。ES7243 的定位很明確——它不是給專業(yè)錄音棚用的高精度器件而是給消費級和工業(yè)級語音采集用的高集成方案。它的核心能力大致是24 位分辨率雙通道差分或單端輸入內(nèi)置可調(diào)增益放大器PGA內(nèi)置自動電平控制ALC內(nèi)置數(shù)字高通濾波器I2S/PCM 輸出I2C 配置單電源供電加內(nèi)部偏置。為什么當(dāng)時選它而不是外掛一個獨立運放加 ADC 的方案我的判斷邏輯是這樣麥克風(fēng)陣列板空間緊通道多如果每一路都做獨立模擬前端布板面積和 BOM 成本會迅速上去。ES7243 把 PGA 和偏置做在片內(nèi)模擬側(cè)只需要幾個耦合電容和 RC 濾波PCB 上省了一大片另外 ALC 功能對語音場景很關(guān)鍵——說話人距離麥克風(fēng)遠近不一沒有自動增益的話遠場錄音小到聽不見近場又會削頂。這些特性如果在外面用分離器件實現(xiàn)調(diào)試工作量會翻好幾倍。代價是什么靈活性被鎖死在寄存器里。你沒法像調(diào)運放那樣隨手換一顆電阻改增益所有調(diào)整都得通過 I2C 寫寄存器。而且一旦寄存器配置和硬件設(shè)計比如輸入是差分還是單端、耦合電容多大不匹配就會出現(xiàn)各種“玄學(xué)”問題。所以我的習(xí)慣是動手寫代碼之前先把信號鏈和時鐘樹在紙上畫一遍標(biāo)清楚每一級的增益、阻抗、頻率響應(yīng)這一步花半小時后面能省兩天。2.2 信號鏈與時鐘樹兩條線必須同時通ES7243 的工作可以拆成兩條獨立的線理解這一點非常關(guān)鍵因為它直接決定了排查思路。第一條是模擬信號鏈麥克風(fēng)或線路輸入 → 耦合電容 → 片內(nèi) PGA → ADC 調(diào)制器 → 數(shù)字濾波器 → 數(shù)字音量 → 高通濾波 → 輸出。這條鏈上任何一級的偏置不對、增益過大、濾波截止頻率設(shè)置不當(dāng)都會反映成“聲音不對”。第二條是數(shù)字時鐘鏈MCLK主時鐘→ 內(nèi)部分頻 → 產(chǎn)生 BCLK位時鐘和 LRCK幀時鐘也就是采樣率→ 驅(qū)動 I2S 移位寄存器把數(shù)據(jù)推出去。這條鏈上任何一環(huán)缺失或比例不對結(jié)果就是“沒有數(shù)據(jù)”或者“數(shù)據(jù)全錯”。我見過太多人包括我自己把這兩條線混在一起排查?,F(xiàn)象是“錄音有噪聲”于是先去改增益改完發(fā)現(xiàn)還是噪聲再去改時鐘越改越亂。正確的做法是先分離拿邏輯分析儀抓 I2S 總線看數(shù)據(jù)是否在正常變化。如果數(shù)據(jù)在正常跳變但內(nèi)容不對那是模擬鏈或者格式問題如果數(shù)據(jù)完全不動或者周期性重復(fù)那是時鐘鏈問題。這一步定位能把排查范圍砍掉一半。用一個類比模擬鏈像是水管的粗細和水壓數(shù)字時鐘鏈像是水表的轉(zhuǎn)速和讀數(shù)方式。水壓正常但水表讀錯你去修水管是白費功夫。2.3 一個常被忽略的前置條件I2C 通不通在調(diào)任何寄存器之前先確認 I2C 能讀能寫。ES7243 的 I2C 從地址通常有兩種可選取決于地址引腳接法7 位地址常見是 0x20 或 0x40 這一類。別照抄別人的地址用i2cdetect掃一遍最穩(wěn)# 假設(shè)掛在 i2c-1 總線上 i2cdetect -y -r 1掃出來有響應(yīng)之后再讀一個已知的寄存器比如芯片 ID 或者復(fù)位寄存器確認讀回值合理。這一步如果跳過后面所有“配置不生效”的排查都會變成猜謎。我吃過這個虧代碼里地址寫錯一位寫操作全部成功返回因為 I2C 控制器不校驗從機是否 ACK 到正確的地址時可能超時但被忽略讀回來全是 0xFF我以為是芯片壞了換了兩片板子。3. 硬件落地原理圖上那幾個容易被忽視的細節(jié)3.1 電源與去耦底噪的一大半來自這里ES7243 一般需要模擬電源和數(shù)字電源很多設(shè)計會用同一路 3.3V 通過磁珠或者 LC 分隔。我的經(jīng)驗是模擬電源的紋波對底噪影響非常直接尤其是在 48kHz 采樣、PGA 增益開到 20dB 以上的時候。實測下來模擬電源上如果只掛一顆 100nF底噪會明顯比加一顆 10uF 鉭電容加 100nF 的方案高。具體的做法是AVDD 引腳旁邊就近放 100nF盡量靠近引腳走線短于 3mm再在電源入口放 10uFDVDD 同樣配置但可以放寬。地平面不要被分割得太碎模擬地和數(shù)字地單點連接的位置選在芯片下方附近而不是隨便找個角落。這些是老生常談但我在實際項目里見過好幾次因為磁珠選型不當(dāng)直流電阻太大導(dǎo)致壓降引起 ADC 工作不穩(wěn)定的情況——量一下 AVDD 實際電壓如果比 3.3V 低了 0.2V 以上就該換磁珠了。還有一個細節(jié)ES7243 的參考電壓和偏置是內(nèi)部產(chǎn)生的但如果芯片附近有大的數(shù)字走線經(jīng)過耦合進來的開關(guān)噪聲會讓底噪抬升。布板時把模擬輸入走線盡量短、包地、遠離 BCLK 和 LRCK 這類周期性信號成本幾乎為零收益很大。3.2 模擬輸入前端差分還是單端耦合電容怎么算ES7243 支持差分輸入和單端輸入兩種接法的配置不一樣這也是個常見的坑。差分輸入抗共模干擾好適合長線或者麥克風(fēng)陣列單端輸入簡單適合線路輸入。如果用交流耦合絕大多數(shù)音頻輸入都是耦合電容和片內(nèi)輸入阻抗構(gòu)成一個高通濾波器截止頻率fc 1 / (2 * π * R * C)假設(shè)片內(nèi)輸入阻抗約 20kΩ具體看手冊不同增益檔位下輸入阻抗會變想要 20Hz 以下的低頻也保留電容至少要到C 1 / (2 * π * 20000 * 20) ≈ 0.4μF實際取值一般用 1μF 或者 2.2μF 留余量。如果電容取太小比如 0.1μF截止頻率會到 80Hz 左右語音聽起來會“薄”低頻段被砍掉而且相位失真明顯。反過來如果用直流耦合就需要注意輸入共模電平必須落在芯片允許的范圍內(nèi)否則要么削頂要么完全沒信號。我建議新手一律先用交流耦合把偏置問題交給片內(nèi)解決等系統(tǒng)跑通了再考慮優(yōu)化。3.3 復(fù)位與上電時序別小看那根 PDN 引腳ES7243 通常有一根復(fù)位/掉電控制引腳。這根線的處理方式直接影響芯片能不能正常起來。常見錯誤有兩個一是直接拉高不控制靠上電復(fù)位二是拉低時間不夠。我的做法是上電后先保持復(fù)位有效至少 10ms然后釋放再延時 20ms 以上再開始 I2C 配置。為什么需要這么長因為內(nèi)部偏置電路和參考電壓需要建立時間如果一釋放就開始猛寫寄存器有些寄存器會寫不進去或者寫入后被內(nèi)部邏輯覆蓋。這一點在手冊的時序圖里通常有標(biāo)注但容易被跳過。另外如果板子上有多顆 ADC 共用復(fù)位線要注意釋放時刻的一致性避免某顆芯片在配置過程中被別的芯片干擾。4. I2S 配置時鐘、格式、主從三件事4.1 時鐘計算把所有頻率關(guān)系算清楚再動手I2S 配置的核心就三組關(guān)系算清楚了基本不會錯。第一組采樣率和幀時鐘LRCK 的頻率等于采樣率 fs。48kHz 采樣LRCK 就是 48kHz一個周期對應(yīng)一幀左右聲道各占半個周期。第二組位時鐘和幀時鐘BCLK fs × 每幀的位數(shù) × 通道數(shù)。如果每通道 32 位、雙聲道BCLK 48k × 64 3.072MHz如果每通道 16 位、雙聲道BCLK 48k × 32 1.536MHz。這里要注意的是“位數(shù)”指的是 I2S 幀長而不是有效數(shù)據(jù)位寬。很多主控配 24 位數(shù)據(jù)但用 32 位幀長這時候 BCLK 還是要按 32 算。第三組主時鐘和采樣率MCLK 一般是 fs 的 256 倍、384 倍或 512 倍。48kHz 下256fs 就是 12.288MHz384fs 是 18.432MHz512fs 是 24.576MHz。ES7243 支持這幾種比例但需要在寄存器里正確設(shè)置分頻比配錯了內(nèi)部就產(chǎn)生不了正確的 LRCK。把這三組關(guān)系整理成表格調(diào)時鐘的時候?qū)χ磪?shù)計算公式48kHz 下的常見取值LRCK幀時鐘 fs48 kHzBCLK位時鐘 fs × 幀長 × 通道數(shù)3.072 MHz32bit×2MCLK主時鐘 fs × 倍數(shù)12.288 MHz256fsMCLK/BCLK 倍數(shù) / (幀長×通道)4256fs / 64這張表最關(guān)鍵的作用是驗證用示波器或者邏輯分析儀量到的實際頻率能不能對上表里的值。如果 MCLK 是 12.288MHz但 BCLK 量出來是 1.536MHz那說明幀長被配成了 16 位而不是 32 位數(shù)據(jù)就會錯位。4.2 主從模式誰給誰時鐘一句話說清主從模式是新手最容易繞暈的地方。用一句話概括主模式是 ADC 自己產(chǎn)生 BCLK 和 LRCK從模式是主控給 ADC 提供 BCLK 和 LRCK。那什么時候選哪個如果主控的 I2S 控制器支持主模式絕大多數(shù) SoC 都支持Linux 下就是bitclock-master和frame-master屬性我強烈建議用從模式讓主控當(dāng)主。原因是主控的 I2S 時鐘通常由 PLL 分頻產(chǎn)生精度高、可調(diào)而且多通道場景下所有從設(shè)備能共用一套 BCLK/LRCK同步性好。ADC 當(dāng)從設(shè)備只需要外部給 MCLK 和 BCLK/LRCK自己專心做轉(zhuǎn)換。反過來如果主控的 I2S 只能當(dāng)從設(shè)備有些低端 MCU 是這樣那就讓 ES7243 當(dāng)主。這時候要注意 ES7243 內(nèi)部產(chǎn)生的 BCLK/LRCK 精度取決于 MCLK 的質(zhì)量MCLK 如果有抖動采樣率就會抖長期錄音會出現(xiàn)緩慢的漂移。主從配反的典型癥狀是示波器上 BCLK 和 LRCK 都沒有波形雙方都等著對方給或者兩方都在輸出導(dǎo)致電平?jīng)_突。這個坑我踩過后面第 6 章會詳細講。4.3 數(shù)據(jù)格式對齊I2S、左對齊、右對齊差在哪I2S 協(xié)議有三種常見的對齊方式名字容易混實際差異就在于“數(shù)據(jù)相對于 LRCK 邊沿的位置”。標(biāo)準(zhǔn) I2SLRCK 翻轉(zhuǎn)后延遲一個 BCLK 周期數(shù)據(jù)開始有效。這是最常見的。左對齊Left JustifiedLRCK 翻轉(zhuǎn)的同時數(shù)據(jù)就開始有效沒有那個一拍的延遲。右對齊Right Justified數(shù)據(jù)對齊到 LRCK 周期的末尾。如果主控配的是標(biāo)準(zhǔn) I2S而 ES7243 配成了左對齊結(jié)果就是數(shù)據(jù)整體左移一位聽起來可能是音量減半、噪聲增大或者左右聲道內(nèi)容錯位。判斷方法很簡單用一個已知的單頻正弦信號輸入比如 1kHz抓 I2S 數(shù)據(jù)出來看波形如果波形有臺階狀畸變或者幅度明顯偏低基本就是對齊方式錯了。我的經(jīng)驗是先在寄存器里把所有格式相關(guān)的位都配成“標(biāo)準(zhǔn) I2S、24 位數(shù)據(jù)、32 位幀長”主控端也配成一樣的跑通了再考慮要不要改。別一上來就嘗試各種組合容易把自己繞進去。4.4 驅(qū)動側(cè)落地設(shè)備樹怎么描述這塊 ADC在 Linux 平臺上ES7243 這類簡單音頻設(shè)備一般用simple-audio-card描述。核心是三個信息CPU 側(cè)的 I2S 控制器、Codec 側(cè)的 I2C 設(shè)備、以及主從關(guān)系。一個可參考的設(shè)備樹片段平臺無關(guān)的通用寫法具體節(jié)點名按你的 SoC 調(diào)整i2c1 { es7243: es724320 { compatible vendor,es7243; reg 0x20; clocks clk_mclk; clock-names mclk; /* 如果有復(fù)位引腳在這里聲明 */ reset-gpios gpio1 12 GPIO_ACTIVE_LOW; #sound-dai-cells 0; status okay; }; }; sound { compatible simple-audio-card; simple-audio-card,name es7243-capture; simple-audio-card,format i2s; /* 主控當(dāng)主ADC 當(dāng)從 */ simple-audio-card,bitclock-master dailink_master; simple-audio-card,frame-master dailink_master; simple-audio-card,cpu { sound-dai i2s0; }; dailink_master: simple-audio-card,codec { sound-dai es7243; /* 主控給 ADC 提供 MCLK */ clocks clk_mclk; }; };配好之后用arecord驗證# 列出采集設(shè)備 arecord -l # 采集 10 秒 48kHz/24bit 雙聲道 WAV arecord -D hw:0,0 -f S24_LE -r 48000 -c 2 -d 10 /tmp/test.wav # 看波形和頻譜用 sox 快速查看 sox /tmp/test.wav -n stat如果arecord報input/output error多半是時鐘沒起來或者主從配反如果能錄到文件但內(nèi)容全是零去看第 6 章的坑一和坑三。5. 寄存器調(diào)優(yōu)從“能出聲”到“好聽”5.1 初始化序列順序比內(nèi)容更重要寄存器配置最容易出錯的地方不是值寫錯而是順序?qū)戝e。ES7243 有幾個區(qū)域?qū)懭霑r機敏感時鐘管理、電源管理、以及模擬前端增益。我的一般順序是復(fù)位芯片延時等待穩(wěn)定。配置時鐘管理寄存器MCLK 分頻、主從模式。配置接口格式I2S 對齊方式、數(shù)據(jù)位寬、幀長。配置電源管理把需要工作的模塊上電。配置模擬前端PGA 增益、輸入模式。配置數(shù)字部分數(shù)字音量、高通濾波、ALC。最后使能輸出。為什么這個順序因為很多寄存器在模塊掉電狀態(tài)下寫入是無效的得先上電再配而時鐘如果沒配好接口格式的某些位可能寫不進去。第 4 步和第 5 步的順序尤其關(guān)鍵——先給模擬模塊上電再調(diào)增益否則增益設(shè)置會被復(fù)位。一個典型的 C 語言初始化骨架#include stdint.h #define ES7243_ADDR 0x20 struct reg_val { uint8_t reg; uint8_t val; }; static const struct reg_val init_seq[] { {0x00, 0x3F}, /* 軟復(fù)位寫完后需延時 */ {0x01, 0x00}, /* 時鐘管理MCLK 分頻與主從 */ {0x02, 0x00}, /* 接口格式I2S24bit從模式 */ {0x03, 0x00}, /* 幀長與 BCLK 分頻 */ {0x04, 0x00}, /* 時鐘使能 */ {0x05, 0x00}, /* 電源管理 */ {0x06, 0x00}, /* 模擬輸入選擇 */ {0x10, 0x00}, /* PGA 增益 */ {0x15, 0x00}, /* 數(shù)字音量 */ {0x1A, 0x00}, /* 高通濾波 */ {0x20, 0x00}, /* ALC 配置 */ }; int es7243_init(void) { int i; for (i 0; i sizeof(init_seq) / sizeof(init_seq[0]); i) { if (i2c_write_reg(ES7243_ADDR, init_seq[i].reg, init_seq[i].val) 0) return -1; /* 復(fù)位之后必須等待其他寄存器不需要 */ if (init_seq[i].reg 0x00) mdelay(50); } return 0; }注意上面代碼里的值我故意寫成了 0x00 占位因為不同版本的位域定義不一樣直接抄別人的值風(fēng)險很大。我的做法是準(zhǔn)備一張自己的配置表每一行寫清楚“這個寄存器我配的是什么、為什么這么配、對應(yīng)手冊第幾頁”。5.2 PGA 增益與數(shù)字音量兩級增益別疊加過頭ES7243 的增益鏈有兩級片內(nèi)模擬 PGA 和數(shù)字音量。這兩級的作用不一樣用錯了會出問題。模擬 PGA 在 ADC 之前放大的是模擬信號。它的優(yōu)點是提升信噪比——信號在進入 ADC 之前就被放大相對地 ADC 自身的量化噪聲占比降低。缺點是如果放太大輸入信號會超過 ADC 的滿量程直接削頂波形頂部變平聽起來是刺耳的失真。數(shù)字音量在 ADC 之后放大的是已經(jīng)量化的數(shù)字信號。它不會削頂內(nèi)部有飽和處理但會同時把量化噪聲放大信噪比不會提升。所以數(shù)字音量只適合做小幅微調(diào)比如 ±6dB 以內(nèi)。我的原則是需要的增益盡量放在模擬側(cè)數(shù)字側(cè)只做補償。比如麥克風(fēng)靈敏度偏低需要 20dB 增益那就把 PGA 設(shè)到 20dB數(shù)字音量保持 0dB。如果 PGA 的上限不夠再考慮用數(shù)字音量補剩下的但要接受信噪比下降的代價。調(diào)試方法是輸入一個已知幅度的 1kHz 正弦比如 -20dBFS逐步提高 PGA同時抓 I2S 數(shù)據(jù)看波形找到波形剛開始出現(xiàn)頂部削平的臨界點然后往下退 3~6dB 作為工作點。這個余量是給實際信號的峰值留的語音信號雖然平均電平低但瞬態(tài)峰值可能高出 10dB 以上。5.3 高通濾波與 ALC語音場景的兩個關(guān)鍵配置高通濾波的作用是濾掉低頻噪聲比如風(fēng)扇聲、空調(diào)聲、桌面震動。截止頻率設(shè)太低沒效果設(shè)太高人聲會變薄。語音場景我一般設(shè)在 80~120Hz這個區(qū)間既能濾掉大部分環(huán)境低頻噪聲又保留了人聲的基頻男聲基頻最低約 85Hz女聲更高。設(shè)置的時候要注意高通濾波的階數(shù)和截止頻率是兩回事。階數(shù)決定了滾降的陡峭程度一階濾波在截止頻率處只衰減 3dB實際上要衰減到有用程度得靠幾個倍頻程。如果噪聲主要集中在一個窄頻段可能需要更高階或者配合其他手段。ALC自動電平控制在會議和錄音場景非常有用但配置不當(dāng)會帶來“呼吸感”——聲音忽大忽小背景噪聲在說話間隙被抬起來。我的配置經(jīng)驗是參數(shù)建議值說明目標(biāo)電平-12dBFS 左右留 12dB 峰值余量避免削頂啟動時間10~50ms太快會把語音起始音吃掉釋放時間200~500ms太短會有明顯呼吸感最大增益20~30dB限制遠場噪聲被過度放大噪聲門閾值-55~-65dBFS靜音時別讓 ALC 把底噪推起來噪聲門這個參數(shù)特別容易被忽略。如果不設(shè)安靜環(huán)境下 ALC 會持續(xù)往上加增益把底噪推到和目標(biāo)電平差不多結(jié)果就是“安靜的時候反而更吵”。這個我在一個會議室項目里踩過甲方反饋“沒人的時候嗡嗡聲很大”最后就是噪聲門閾值沒設(shè)。5.4 低功耗與時鐘關(guān)斷別在不需要的時候還開著時鐘如果設(shè)備是電池供電或者有低功耗要求ES7243 支持在空閑時關(guān)斷部分模塊。做法是把 ADC 和 PGA 掉電只保留 I2C 接口和必要的偏置。喚醒的時候需要重新配置一遍關(guān)鍵寄存器因為掉電會復(fù)位部分狀態(tài)。這里有個坑某些寄存器在掉電再上電之后值會回到默認而不是保持掉電前的值。所以喚醒流程不能只寫一個“使能”位得把整個初始化序列的關(guān)鍵部分重跑一遍。我當(dāng)時的做法是封了一個es7243_resume()函數(shù)內(nèi)容和es7243_init()幾乎一樣只是跳過了軟復(fù)位那一步。多寫十幾行代碼換來的是喚醒后不用再查一遍“為什么聲音沒了”。6. 五大踩坑實錄每個坑都值兩天時間6.1 坑一MCLK 有波形但頻率不對數(shù)據(jù)全是零現(xiàn)象邏輯分析儀抓到 MCLK、BCLK、LRCK、SDATA 全都有波形BCLK 和 LRCK 的比例也正常但arecord錄出來全是零。排查過程一開始懷疑是格式配錯把 I2S、左對齊、右對齊全試了一遍沒變化。然后懷疑是從地址錯用i2cdetect確認有響應(yīng)讀回值也正常。最后量 MCLK 頻率發(fā)現(xiàn)是 24.576MHz而寄存器里配置的是 256fs——24.576MHz 對應(yīng)的是 512fs48kHz 下。芯片內(nèi)部按 256fs 去除算出來的采樣率變成了 96kHz而主控按 48kHz 收兩邊對不上數(shù)據(jù)自然全是亂的。根因時鐘源 PLL 分頻配置和 ADC 寄存器配置不一致。硬件上 MCLK 走的是 512fs軟件里卻按 256fs 寫。解決把寄存器里的 MCLK 分頻改成 512fs或者把主控的 MCLK 輸出改成 256fs。我選了后者因為 256fs 對大多數(shù) PLL 更容易分頻出來。教訓(xùn)量時鐘不要只量“有沒有”要量“是多少”。把量到的實際頻率和寄存器配置值做一次對賬這個動作應(yīng)該成為固定流程。6.2 坑二主從模式配反BCLK 和 LRCK 都沒有輸出現(xiàn)象板子上電MCLK 正常但 BCLK 和 LRCK 一點波形都沒有。SDATA 也沒有翻轉(zhuǎn)。排查過程先確認主控 I2S 控制器有沒有輸出。用cat /sys/kernel/debug/clk/clk_summary看 I2S 時鐘有沒有使能發(fā)現(xiàn)是使能的。那問題就在 ADC 側(cè)——如果 ADC 被配置成主模式它會等自己的內(nèi)部時鐘產(chǎn)生 BCLK但如果 MCLK 分頻參數(shù)不對內(nèi)部時鐘出不來如果 ADC 被配置成從模式它會等主控給 BCLK而主控也在等 ADC 給時鐘雙方僵持。根因設(shè)備樹里bitclock-master和frame-master指向了錯誤的節(jié)點導(dǎo)致主控沒把自己當(dāng)主ADC 也沒配對。解決把設(shè)備樹里這兩個屬性指向 CPU 側(cè) dai確認驅(qū)動加載后dmesg里沒有 “no master” 之類的告警。教訓(xùn)主從關(guān)系是 I2S 的第一性問題配錯了兩邊都不干活。排查時先看 BCLK 有沒有輸出沒有就查主從別急著改寄存器。6.3 坑三格式對齊差一位錄音音量小一半現(xiàn)象能錄到數(shù)據(jù)波形也有但幅度明顯低于預(yù)期測出來比理論值低 6dB 左右而且高次諧波變大。排查過程一開始以為是 PGA 增益沒配對反復(fù)調(diào)增益幅度確實上去了但失真也跟著上去了。后來抓了一段 I2S 原始數(shù)據(jù)用腳本按二進制打印出來發(fā)現(xiàn)數(shù)據(jù)位整體偏移了一位——最高位是 0實際數(shù)據(jù)從次高位開始。這就是標(biāo)準(zhǔn) I2S 和左對齊的差異。根因主控配的是標(biāo)準(zhǔn) I2S數(shù)據(jù)延遲一拍ADC 配的是左對齊數(shù)據(jù)不延遲結(jié)果 ADC 輸出的數(shù)據(jù)從主控的視角看整體右移了一位相當(dāng)于幅度除以 2。解決把 ADC 的接口格式寄存器改成標(biāo)準(zhǔn) I2S兩邊一致。教訓(xùn)幅度異常先懷疑格式別急著動增益。判斷方法很簡單——輸入一個滿量程附近的信號看波形頂部是不是有規(guī)律地缺一位。6.4 坑四寄存器寫入順序錯增益配置被復(fù)位覆蓋現(xiàn)象初始化函數(shù)里明明寫了 PGA 增益但錄出來的音量一直是最小讀回寄存器發(fā)現(xiàn)值變成了默認值。排查過程一開始以為是寫操作失敗加了返回值檢查發(fā)現(xiàn)每次寫都成功。后來在每次寫之后加一條讀回比對發(fā)現(xiàn)寫進去的值確實在但過了一段時間大概幾十毫秒就變回默認。懷疑是某個操作觸發(fā)了內(nèi)部復(fù)位。根因初始化序列里電源管理寄存器的配置放在了增益配置之后。電源管理里的某個位會讓模擬前端復(fù)位把之前配的增益清掉了。也就是說我先調(diào)了增益然后又把前端復(fù)位了一遍。解決調(diào)整順序先上電再配增益并且把電源管理里會觸發(fā)復(fù)位的操作全部前置到最前面。教訓(xùn)寄存器初始化不是“把值都寫一遍”就行順序是有語義的。每次調(diào)初始化序列都要問自己一句這一步會不會影響前面已經(jīng)配好的東西6.5 坑五模擬輸入偏置不對低頻段有噗噗聲現(xiàn)象錄音能聽到明顯的有規(guī)律的“噗噗”聲頻率很低大概 1~2Hz同時低頻段噪聲明顯偏高。排查過程先懷疑是電源紋波換了幾種電源方案沒改善。然后用示波器量模擬輸入引腳發(fā)現(xiàn)直流電平在緩慢漂移周期和噗噗聲一致。問題定位到耦合電容后面的偏置電路。根因輸入是單端接法但片內(nèi)偏置的配置位設(shè)成了差分模式導(dǎo)致輸入端沒有正確的直流偏置電容在緩慢充放電形成低頻振蕩。解決把輸入模式改成單端并且確認耦合電容另一側(cè)的偏置電阻取值合適。取值方法偏置電阻和耦合電容構(gòu)成的時間常數(shù)要遠大于最低頻率的周期20kΩ × 1μF 20ms對應(yīng) 8Hz 左右勉強夠用改成 2.2μF 后改善明顯。教訓(xùn)任何“周期性低頻異常”都應(yīng)該先懷疑偏置和耦合。這類問題的特征是頻率極低、規(guī)律性強和隨機噪聲很好區(qū)分。7. 排查手冊與實測數(shù)據(jù)把經(jīng)驗固化成表7.1 癥狀對照速查表調(diào)試的時候我習(xí)慣先查表再動手。下面這張表是這幾年積累下來的覆蓋了大部分常見癥狀癥狀首要懷疑點快速驗證方法錄出來全是零MCLK 頻率與配置不符量 MCLK 實際頻率和寄存器配置對賬BCLK/LRCK 無輸出主從模式配反查設(shè)備樹 master 屬性看兩側(cè)誰在等幅度偏低約一半I2S 格式對齊差一位抓原始數(shù)據(jù)看最高位是否有效左右聲道互換幀內(nèi)聲道順序配反輸入左右不同頻率信號看通道對應(yīng)底噪大模擬電源紋波/地處理短路輸入端接地看靜態(tài)底噪低頻噗噗聲偏置模式與輸入接法不匹配量輸入引腳直流電平是否漂移安靜時噪聲更大ALC 噪聲門未設(shè)關(guān)掉 ALC 對比底噪大音量削頂失真PGA 增益過高輸入滿量程信號看波形頂部寄存器讀回值不對寫入順序問題每次寫后立即讀回比對I2C 無響應(yīng)從地址或上拉電阻i2cdetect掃描量 SDA/SCL 電平7.2 我常用的兩個驗證動作第一個是接地測底噪。把模擬輸入端短接到地通過電容短接別直接短錄一段靜音測 RMS 電平。這個值代表了系統(tǒng)自身噪聲的天花板。如果底噪在 -85dBFS 以上說明模擬前端還有優(yōu)化空間如果在 -95dBFS 以下基本可以接受。這個動作能快速區(qū)分“噪聲來自外部干擾”還是“噪聲來自自身”。第二個是單頻正弦掃幅。輸入 1kHz 正弦幅度從 -60dBFS 逐步加到 0dBFS每一檔錄 2 秒測輸出幅度和 THD。這條曲線能同時看出增益線性度、削頂點和失真拐點。我在一個項目里就是靠這條曲線發(fā)現(xiàn) PGA 在 30dB 以上時 THD 急劇上升最后把工作點定在 24dB。# 用 sox 生成測試信號并采集需要聲卡支持播放和錄制 sox -n -r 48000 -c 2 -b 24 /tmp/sine.wav synth 5 sine 1000 vol -20dB aplay -D hw:0,0 /tmp/sine.wav arecord -D hw:0,0 -f S24_LE -r 48000 -c 2 -d 5 /tmp/rec.wav sox /tmp/rec.wav -n stat7.3 關(guān)于手冊版本的一個提醒這件事我必須單獨說。ES7243 的手冊存在多個版本我手上就有兩份寄存器 0x0A 的位域定義不一樣。第一份里某個位是“保留”第二份里同樣的位變成了“輸入模式選擇”。當(dāng)時我按第一份手冊配結(jié)果輸入模式一直是錯的排查了很久。所以我的建議是拿到芯片之后先確認手冊版本和芯片絲印批次是否對應(yīng)把關(guān)鍵寄存器時鐘、接口格式、電源、輸入模式的位域抄到自己的配置表里不要在不同項目之間直接復(fù)制舊代碼。這個習(xí)慣看起來麻煩但能省下大量排查時間。8. 最后說幾點個人體會調(diào)這類音頻 ADC我覺得最值錢的能力不是記住寄存器怎么配而是建立一套從現(xiàn)象到原因的映射。芯片本身不復(fù)雜難的是它出錯的方式太安靜——不報錯、不崩潰只是默默地輸出錯誤的數(shù)據(jù)。所以每次遇到問題我都會強迫自己先做分離是時鐘問題還是數(shù)據(jù)問題是模擬問題還是數(shù)字問題是配置問題還是硬件問題。這個二分法做上三五次排查范圍就會收縮到很小。另外一點別迷信“默認配置能跑”。ES7243 的默認值在實驗室環(huán)境下可能勉強能用但一旦換到有干擾的現(xiàn)場、換成不同靈敏度的麥克風(fēng)、換成不同長度的線纜就必須重新調(diào)。我現(xiàn)在的習(xí)慣是每個項目都重新走一遍增益標(biāo)定和底噪測量不復(fù)用上一版的值雖然多花半天但避免了后期返工。最后留一個可以繼續(xù)往下挖的方向如果你需要多顆 ES7243 做同步采集比如四麥陣列用兩顆芯片主從模式和時鐘分配會更復(fù)雜需要讓所有芯片共用同一路 MCLK 和 LRCK并且確保它們的 I2S 輸出在時間上對齊。這塊我在下一個項目里正在做等跑通了再單獨寫一篇。