排查實(shí)戰(zhàn):從時(shí)鐘樹到引腳復(fù)用全鏈路解析)
1. SPI3 沒信號(hào)送出的現(xiàn)場形態(tài)先搞清楚問題到底長什么樣先交代一下背景。最近在調(diào)一塊板子主控是某款 Cortex-A 系列處理器板載一個(gè) SPI3 接口外接到一顆 NOR Flash 和一片 LCD 驅(qū)動(dòng)芯片。同事反饋說接口“完全沒信號(hào)”我拎著示波器過去一看CLK、MOSI、CS 三個(gè)引腳全部平得像心電圖停跳一根毛刺都看不到。這里要插一句“SPI3 接口沒有信號(hào)送出”這個(gè)描述在實(shí)際工程里至少能拆成好幾種完全不同的現(xiàn)象排查方向完全不一樣?,F(xiàn)象 ACLK、MOSI、CS 全部無波形像沒初始化過一樣?,F(xiàn)象 BCLK 有時(shí)鐘輸出但 MOSI 上什么都沒有CS 也不動(dòng)?,F(xiàn)象 CCLK、MOSI 都有但 CS 一直是高電平從機(jī)根本沒被選中。現(xiàn)象 D主控這邊的信號(hào)都有但到了連接器/排線那一端就沒了中間鏈路斷了?,F(xiàn)象 E所有信號(hào)都有但電平幅度不對(duì)比如 1.8V 的主控接 3.3V 的外設(shè)中間電平轉(zhuǎn)換沒工作。我遇到的屬于現(xiàn)象 A但下面的排查思路對(duì) B、C、D、E 同樣適用。先說一個(gè)最容易犯的認(rèn)知錯(cuò)誤很多人一聽到“SPI3”就默認(rèn)它一定存在但 SPI3 這個(gè)編號(hào)的含義在不同平臺(tái)上有兩種常見解釋。一種解釋是 SoC 內(nèi)部的第 3 個(gè) SPI 控制器比如 i.MX6 的 ECSPI3、STM32H7 的 SPI3、樹莓派 BCM2711 的 SPI3 控制器另一種解釋是 SPI 工作在三線制模式即只有 CLK、MOSI雙向和 CS沒有獨(dú)立的 MISO。這兩種情況下的排查手法差別很大。我先按“第 3 個(gè) SPI 控制器”來展開因?yàn)檫@是絕大多數(shù)嵌入式項(xiàng)目里遇到的情況三線制模式會(huì)在后面單獨(dú)說。在動(dòng)手之前先把問題域收窄。SPI 是一個(gè)同步串行接口主控要發(fā)出完整的傳輸事務(wù)必須同時(shí)滿足時(shí)鐘線有翻轉(zhuǎn)、數(shù)據(jù)線有正確的電平變化、片選線有有效的拉低動(dòng)作。這三根線只要有一根不對(duì)從設(shè)備就不會(huì)響應(yīng)而工程師往往只看到“沒信號(hào)”這個(gè)籠統(tǒng)現(xiàn)象。所以第一步不是去懷疑某個(gè)寄存器而是用示波器把四根線CLK、MOSI、MISO、CS全部掛上看看到底哪根沒有、哪根有、哪根波形不對(duì)。如果四根線全部平直那問題幾乎可以肯定出在主控側(cè)要么外設(shè)時(shí)鐘沒使能要么引腳復(fù)用沒配要么控制器根本沒被初始化。如果 CLK 有而其他沒有往往是傳輸事務(wù)沒有真正啟動(dòng)比如片選極性配置錯(cuò)誤導(dǎo)致 CS 一直處于無效電平或者發(fā)送緩沖區(qū)為空。如果主控側(cè)波形完整但下游沒有那就是鏈路中間的問題比如電平轉(zhuǎn)換方向控制腳沒拉對(duì)、連接器虛焊、排線斷了。我習(xí)慣用一個(gè)比喻來給剛?cè)胄械耐陆忉孲PI 就像一場有主持人的電話會(huì)議CLK 是主持人打節(jié)拍的拍子CS 是“現(xiàn)在開始點(diǎn)名”的信號(hào)MOSI 是主持人說的話MISO 是參會(huì)者的回答。拍子沒響、沒點(diǎn)名、沒人說話任何一個(gè)環(huán)節(jié)斷了會(huì)議都開不起來。排查 SPI3 沒信號(hào)本質(zhì)上就是沿著這條“會(huì)議鏈路”逐段檢查看是主持人的問題、接線的問題還是話筒的問題。2. 從引腳到時(shí)鐘樹SPI3 信號(hào)鏈路到底包含哪些環(huán)節(jié)確定現(xiàn)象之后下一步是把 SPI3 從“軟件寄存器”到“物理引腳”的完整鏈路畫在腦子里。很多人排查時(shí)喜歡一上來就翻代碼但我建議先打開原理圖從主控芯片的引腳一路看到對(duì)外連接器把每個(gè)環(huán)節(jié)都過一遍。大多數(shù) SPI3 無聲問題根源不是 SPI 控制器本身而是鏈路中間的某個(gè)“隱形開關(guān)”沒打開。2.1 引腳復(fù)用最高頻的翻車點(diǎn)現(xiàn)代 SoC 的引腳基本都是多功能引腳同一個(gè)物理引腳可能同時(shí)掛著 UART、I2C、PWM、GPIO、SPI 等多組功能。芯片上電默認(rèn)狀態(tài)往往是 GPIO 模式而且經(jīng)常是帶上拉或下拉的 GPIO。如果軟件里沒有把該引腳切換成 SPI3 的復(fù)用功能那么即使 SPI 控制器已經(jīng)在跑時(shí)鐘也翻轉(zhuǎn)了信號(hào)也到了引腳內(nèi)部但引腳仍然以 GPIO 模式輸出表現(xiàn)為“寄存器看起來在工作示波器上看不到波形”。這里有個(gè)特別容易踩的坑復(fù)用功能寄存器寫入的值必須在具體芯片手冊(cè)里查不能想當(dāng)然。不同芯片對(duì)相同外設(shè)的 ALT 編號(hào)可能完全不同。比如某顆芯片上 SPI3_MOSI 的復(fù)用功能是 ALT2另一顆同系列的芯片卻可能變成 ALT5。我見過不止一次有同事拿著上一顆芯片的設(shè)備樹直接改型號(hào)就上結(jié)果 SPI3 引腳全部處于 GPIO 模式死活沒波形。檢查方法很簡單讀芯片手冊(cè)的“Pin Multiplexing”章節(jié)找到 SPI3 相關(guān)引腳確認(rèn)當(dāng)前代碼里寫的 mux 值對(duì)應(yīng)的是 SPI3 功能。如果用的是 Linux 下的設(shè)備樹重點(diǎn)檢查 pinctrl 節(jié)點(diǎn)里的pinctrl-0和pinctrl-names是否真的被 SPI3 驅(qū)動(dòng)引用到了。2.2 SPI 控制器時(shí)鐘樹不開時(shí)鐘寄存器全是空氣引腳復(fù)用配對(duì)了接下來要看時(shí)鐘樹。SPI 控制器本身是一個(gè)數(shù)字外設(shè)它需要兩路時(shí)鐘一路是總線接口時(shí)鐘用來訪問寄存器另一路是外設(shè)功能時(shí)鐘用來產(chǎn)生 SPI 的 SCK 信號(hào)。在很多 SoC 上這兩路時(shí)鐘是獨(dú)立的門控位也可能分開。外設(shè)功能時(shí)鐘沒打開是“SPI3 完全沒有信號(hào)”的第二大常見原因。這時(shí)候寄存器能讀寫控制器狀態(tài)寄存器也顯示 enabled但 SCK 引腳就是沒有任何輸出因?yàn)椴ㄌ芈拾l(fā)生器沒有時(shí)鐘源產(chǎn)生的 SCK 頻率是 0。排查手段是在驅(qū)動(dòng)里讀取時(shí)鐘狀態(tài)寄存器確認(rèn) SPI3 的CKEN或類似門控位已經(jīng)置 1。如果是 Linux 環(huán)境檢查設(shè)備樹里 SPI3 節(jié)點(diǎn)的clocks屬性和assigned-clock-rates確保時(shí)鐘源頻率設(shè)置正確。用clk_summary或者/sys/kernel/debug/clk/clk_summary可以快速確認(rèn) SPI3 的時(shí)鐘樹是否 enable、頻率是多少。我遇到的這顆主控SPI3 的時(shí)鐘源來自一個(gè) PLL 分頻PLL 默認(rèn)狀態(tài)是關(guān)閉的必須由 bootloader 或內(nèi)核初始化時(shí)打開。這就導(dǎo)致很多人只改了 SPI 驅(qū)動(dòng)配置沒動(dòng)時(shí)鐘樹結(jié)果 SPI3 寄存器訪問正常但沒有 SCK 輸出。檢查時(shí)鐘樹時(shí)我習(xí)慣先看/sys/kernel/debug/clk/clk_summary里 SPI3 相關(guān)節(jié)點(diǎn)的prepare_count和enable_count如果都是 0那基本可以斷定就是時(shí)鐘門控的問題。2.3 SPI 模式參數(shù)不是“有配置”就一定能通設(shè)好引腳和時(shí)鐘之后還要檢查 SPI 模式參數(shù)。SPI 有四種工作模式由 CPOL時(shí)鐘極性和 CPHA時(shí)鐘相位兩個(gè)參數(shù)組合而成。CPOL 決定空閑時(shí) SCK 是高電平還是低電平CPHA 決定數(shù)據(jù)是在第一個(gè)邊沿還是第二個(gè)邊沿采樣。主從雙方必須匹配否則從機(jī)可能完全不響應(yīng)甚至數(shù)據(jù)全錯(cuò)。如果從機(jī)是 NOR Flash大多數(shù)器件支持 Mode 0 或 Mode 3。如果是從機(jī)是 LCD 驅(qū)動(dòng)芯片不同型號(hào)支持的時(shí)序可能完全不同。如果 SPI3 接口接了多個(gè)從機(jī)一定要確認(rèn)每個(gè)從機(jī)事務(wù)都用了正確的 mode而不是用一個(gè)全局配置通吃所有從機(jī)。在 Linux 下每個(gè)從設(shè)備節(jié)點(diǎn)都可以單獨(dú)配置spi-max-frequency和模式位別偷懶。另一個(gè)參數(shù)是數(shù)據(jù)位寬。STM32 的 SPI 外設(shè)默認(rèn)是 8 位但有的 SoC 支持 4 位、16 位、32 位甚至可編程位寬。如果驅(qū)動(dòng)里配置了 16 位而總線上的從機(jī)只支持 8 位波形看起來是有的但數(shù)據(jù)完全對(duì)不上。這次“沒信號(hào)”的現(xiàn)場里如果示波器上能看到 CLK 和數(shù)據(jù)線都有動(dòng)作但數(shù)據(jù)內(nèi)容不對(duì)先從位寬和模式檢查起。3. 硬件側(cè)排查實(shí)操示波器該夾哪里量什么波形軟件配置檢查完如果還是沒信號(hào)就該上硬件手段了。很多工程師在硬件排查時(shí)有個(gè)壞毛病拿起萬用表亂點(diǎn)一通點(diǎn)不出問題就一臉茫然。硬件排查必須按信號(hào)路徑逐段推進(jìn)每推進(jìn)一步都要能回答“這個(gè)節(jié)點(diǎn)上的信號(hào)應(yīng)該長什么樣”。3.1 上電靜態(tài)測量先確認(rèn)供電和電平域第一步是萬用表量電壓。量三個(gè)東西SPI3 相關(guān)引腳所在的電源域電壓是否正常。很多 SoC 有多個(gè) VDDIO 域SPI3 可能在 1.8V 域也可能在 3.3V 域電壓不對(duì)引腳電平自然不對(duì)。引腳對(duì)地阻抗是否正常。SPI3 引腳如果對(duì)地短路信號(hào)會(huì)被直接拉低示波器上就是平的。量阻抗時(shí)最好在斷電狀態(tài)下測避免誤判。外部上拉/下拉電阻是否焊接正確。SPI 的 CS 引腳在系統(tǒng)里通常有上拉電阻保證空閑時(shí)處于高電平。如果這個(gè)上拉電阻沒焊或者焊到了錯(cuò)誤的位置CS 可能一直懸空狀態(tài)不定。靜態(tài)測量能排除大概三成問題剩下的要看動(dòng)態(tài)波形。3.2 動(dòng)態(tài)波形測量探頭的掛法有講究我習(xí)慣用四通道示波器同時(shí)掛 CLK、MOSI、MISO、CS四根線一起看。如果沒有四通道至少也要保證 CLK 和 CS 同時(shí)看因?yàn)镃S 的有效拉低是判斷一個(gè) SPI 事務(wù)是否啟動(dòng)的最直觀標(biāo)志。探頭的接地夾要盡量靠近被測點(diǎn)最好用彈簧地針不要用長地線夾子。SPI 的時(shí)鐘頻率通常在幾 MHz 到幾十 MHz長地線會(huì)引入寄生電感導(dǎo)致波形上出現(xiàn)過沖和振鈴影響判斷。我見過有人拿著 60MHz 帶寬的示波器去量 50MHz 的 SPI 時(shí)鐘量出來的波形嚴(yán)重失真還以為是信號(hào)質(zhì)量問題。觸發(fā)方式建議用 CS 下降沿觸發(fā)。CS 平時(shí)是高電平事務(wù)開始時(shí)拉低用這個(gè)沿觸發(fā)能穩(wěn)定抓到整個(gè)事務(wù)的波形。如果 CS 一直沒有低電平出現(xiàn)說明事務(wù)根本沒有啟動(dòng)如果 CS 有低電平而 CLK 沒有翻轉(zhuǎn)說明控制器認(rèn)為自己在傳輸?shù)ㄌ芈蕰r(shí)鐘或分頻配置有問題如果 CS、CLK 都有而 MOSI 沒有說明發(fā)送數(shù)據(jù)寄存器是空的或者 DMA 沒有正確觸發(fā)。3.3 回環(huán)測試一句話區(qū)分“沒發(fā)出”和“沒收到”在排查 SPI3 沒信號(hào)的現(xiàn)場最快的一個(gè)定位手段是回環(huán)測試。直接把 MOSI 和 MISO 短接或者在軟件里把 SPI 配置成 loopback 模式然后發(fā)一串已知數(shù)據(jù)看能不能收回來。如果回環(huán)能收到說明 SPI 控制器的發(fā)送和接收路徑都正常問題在外部的從機(jī)設(shè)備、PCB 走線或連接器。如果回環(huán)收不到說明控制器本身可能就沒正常工作回到軟件配置和時(shí)鐘樹檢查。Loopback 有兩種實(shí)現(xiàn)方式一種是 SoC 自帶的硬件 loopback 模式在 SPI 控制器的配置寄存器里打開信號(hào)在芯片內(nèi)部直接回環(huán)不經(jīng)過引腳另一種是外部物理回環(huán)用導(dǎo)線或者 PCB 測試點(diǎn)把 MOSI 和 MISO 短接。外部回環(huán)比內(nèi)部回環(huán)更能反映問題因?yàn)樗岩_復(fù)用、PCB 走線、連接器全鏈路都覆蓋了。我做外部回環(huán)時(shí)習(xí)慣在連接器的從機(jī)端做短接而不是在主控芯片引腳附近做。這樣如果回環(huán)成功說明從主控引腳到連接器這一段鏈路都是通的問題可以進(jìn)一步縮小到從機(jī)端。3.4 電平轉(zhuǎn)換芯片的方向控制腳一個(gè)特別隱蔽的坑如果主控和從機(jī)的電壓域不同中間需要加電平轉(zhuǎn)換芯片比如 TXS0108、SN74LVC4245 之類。這類芯片的方向控制腳DIR、OE如果沒接對(duì)信號(hào)根本過不去。常見的坑是OE 腳應(yīng)該拉低使能結(jié)果被懸空了DIR 腳方向接反導(dǎo)致數(shù)據(jù)從從機(jī)流向主機(jī)而不是從主機(jī)流向從機(jī)或者方向控制腳接到了某個(gè) GPIO而該 GPIO 在軟件里沒有初始化。表現(xiàn)為 CPU 這邊的 SPI3 引腳有完整波形但電平轉(zhuǎn)換芯片輸出端什么都量不到。這種問題用示波器一夾就能發(fā)現(xiàn)但前提是你知道要在電平轉(zhuǎn)換芯片的輸入端和輸出端同時(shí)掛探頭對(duì)比。很多人在主控引腳上看到信號(hào)正常就以為整條鏈路都正常跳過了中間環(huán)節(jié)的測量這是排查效率低下的主要原因之一。4. 軟件配置里最容易埋雷的幾個(gè)位置如果說硬件排查是“看得見摸得著”的部分軟件配置就是“藏在代碼里”的部分。而且軟件配置的錯(cuò)誤往往比硬件錯(cuò)誤更隱蔽因?yàn)樗粫?huì)在產(chǎn)品上留下任何物理痕跡只會(huì)在運(yùn)行時(shí)表現(xiàn)為功能失效。4.1 設(shè)備樹或初始化結(jié)構(gòu)體每個(gè)字段都得較真在 Linux 環(huán)境下SPI3 沒信號(hào)最常見的原因是設(shè)備樹節(jié)點(diǎn)沒有正確匹配到驅(qū)動(dòng)。檢查的順序很重要我列一下我常用的核對(duì)清單檢查項(xiàng)典型錯(cuò)誤排查方式compatible 字符串與驅(qū)動(dòng)不匹配導(dǎo)致 probe 不執(zhí)行dmesg里看是否有 spi3 相關(guān) probe 信息reg 屬性控制器基地址寫錯(cuò)訪問到無關(guān)寄存器對(duì)照芯片手冊(cè)確認(rèn)基地址interrupts中斷號(hào)配置錯(cuò)誤傳輸沒完成時(shí)無法觸發(fā)回調(diào)cat /proc/interrupts確認(rèn)中斷是否產(chǎn)生pinctrl-0引腳復(fù)用配置未被引用引腳處于 GPIO 模式讀/sys/kernel/debug/pinctrl確認(rèn)引腳狀態(tài)spi-max-frequency頻率設(shè)太高從機(jī)跟不上導(dǎo)致無響應(yīng)先降到 1MHz 以下嘗試mode 位CPOL/CPHA 與從機(jī)不匹配對(duì)照從機(jī)數(shù)據(jù)手冊(cè)確認(rèn)很多人看到設(shè)備樹里 SPI3 節(jié)點(diǎn)存在就默認(rèn)驅(qū)動(dòng)已經(jīng)正常工作。但dmesg里如果出現(xiàn)spi3 supply spi not found或者failed to get clock說明還有依賴資源沒就緒。我排查時(shí)有個(gè)習(xí)慣先看dmesg | grep -i spi再看/dev/spidev3.0是否存在最后直接跑spidev_test工具發(fā)數(shù)據(jù)。4.2 時(shí)鐘使能與 GPIO 復(fù)用兩個(gè)最隱蔽的失敗點(diǎn)前面硬件部分已經(jīng)提過時(shí)鐘但軟件側(cè)還要再強(qiáng)調(diào)一次很多時(shí)候不是芯片不支持而是驅(qū)動(dòng)里的時(shí)鐘框架沒有正確使能 SPI3 的時(shí)鐘。在設(shè)備樹里配置了clocks屬性不代表時(shí)鐘已經(jīng)打開驅(qū)動(dòng)必須在probe函數(shù)里調(diào)用clk_prepare_enable()或者依賴運(yùn)行時(shí) PM 框架自動(dòng)開時(shí)鐘。如果驅(qū)動(dòng)本身的pm_runtime_enable沒調(diào)用或者時(shí)鐘的enable計(jì)數(shù)不對(duì)SPI3 就會(huì)處于“時(shí)鐘未使能”的狀態(tài)。GPIO 復(fù)用也一樣。設(shè)備樹里 pinctrl 節(jié)點(diǎn)存在不代表它生效。如果 SPI3 驅(qū)動(dòng)和某個(gè) GPIO 驅(qū)動(dòng)同時(shí)請(qǐng)求了同一個(gè)物理引腳后加載的驅(qū)動(dòng)可能把復(fù)用配置覆蓋掉。我在項(xiàng)目中遇到過一次某個(gè)按鍵驅(qū)動(dòng)把 SPI3 的 CS 引腳當(dāng)成了 GPIO 輸入加載順序剛好晚于 SPI 驅(qū)動(dòng)結(jié)果 CS 引腳被切成了 GPIO 模式SPI3 的 CS 永遠(yuǎn)無法拉低。排查這類問題需要查看/sys/kernel/debug/pinctrl/下各個(gè)引腳當(dāng)前的 mux 狀態(tài)確認(rèn) SPI3 引腳沒有被別的驅(qū)動(dòng)搶占。4.3 DMA 與中斷配置傳輸啟動(dòng)了但數(shù)據(jù)是空的SPI 控制器支持 DMA 模式時(shí)數(shù)據(jù)搬運(yùn)依賴 DMA 通道。如果 DMA 通道配置失敗或者 DMA 請(qǐng)求信號(hào)沒有被正確映射到 SPI3 的事件輸出那么發(fā)送時(shí)寄存器里可能只寫入了第一個(gè)字節(jié)后面的數(shù)據(jù)全部丟失?,F(xiàn)象表現(xiàn)為CLK 有時(shí)鐘MOSI 上只有零星幾個(gè)脈沖然后就沒有然后了。檢查手段是看 DMA 引擎的狀態(tài)。Linux 下可以查看/sys/kernel/debug/dmaengine/summary確認(rèn) SPI3 的 DMA 通道是否注冊(cè)成功。如果驅(qū)動(dòng)里使用的是dma_request_chan卻在dmesg里看到failed to get dma channel那就要檢查設(shè)備樹里dmas和dma-names屬性是否配了正確的 DMA 請(qǐng)求 ID。中斷配置問題也類似。SPI3 在沒有 DMA 的情況下依賴 TX FIFO 為空和 RX FIFO 非空這兩類中斷。如果中斷號(hào)配錯(cuò)或者中斷處理函數(shù)里沒有正確清除標(biāo)志位傳輸會(huì)卡死在某個(gè)狀態(tài)。這時(shí)候示波器上能看到 CLK 只翻轉(zhuǎn)了幾下就停了這是因?yàn)榘l(fā)送端在等 TX FIFO 有空位而中斷沒有觸發(fā)驅(qū)動(dòng)認(rèn)為 FIFO 還是滿的。4.4 從機(jī)沒響應(yīng)導(dǎo)致的“假性無輸出”還有一種容易被誤判為“SPI3 沒信號(hào)”的情況主控確實(shí)發(fā)出了完整的事務(wù)CLK、MOSI、CS 波形全部正常但 MISO 上什么都沒有整條讀取的數(shù)據(jù)全是 0xFF。從軟件角度往回查會(huì)看到 SPI 驅(qū)動(dòng)超時(shí)返回-ETIMEDOUT。這其實(shí)是從機(jī)設(shè)備沒有正常工作而不是主控沒有發(fā)送信號(hào)。遇到這種情況先查從機(jī)供電、復(fù)位引腳、使能引腳是否正常。很多從機(jī)芯片都有硬件復(fù)位腳或使能腳如果這些引腳被 GPIO 控制但 GPIO 初始化順序不對(duì)從機(jī)可能一直處于復(fù)位狀態(tài)或者掉電狀態(tài)。我遇到過一顆 LCD 驅(qū)動(dòng)芯片它的 TE撕裂效應(yīng)引腳被復(fù)用成了 GPIO 且被拉高導(dǎo)致從機(jī)認(rèn)為數(shù)據(jù)輸入被暫停SPI 傳輸?shù)臄?shù)據(jù)全部被丟棄。這種問題看起來像是“SPI3 沒信號(hào)”實(shí)際上鏈路全通只是從機(jī)不干活。5. 一次真實(shí)案例的完整復(fù)現(xiàn)從現(xiàn)象到根因的排查路徑講了這么多理論拿一個(gè)實(shí)際案例串一下整個(gè)排查思路。這個(gè)案例來自我最近調(diào)試的板子過程比較典型希望能幫你建立一條可復(fù)用的排查路徑。5.1 現(xiàn)象確認(rèn)量到的波形同事反饋 SPI3 外接的 NOR Flash 讀不出來flash_erase命令直接報(bào)錯(cuò)。我用示波器掛上四根線觸發(fā)方式設(shè)成 CS 下降沿結(jié)果 CS 上的下降沿都看不到四根線全部平直。當(dāng)時(shí)第一反應(yīng)是軟件根本沒初始化 SPI3。5.2 排查過程從 dmesg 到時(shí)鐘樹先看dmesg | grep -i spi3輸出顯示spi3 spi3.0: setup mode 0, 8 bits, 10000000 Hz max說明驅(qū)動(dòng) probe 成功也正確配置了模式。再看/sys/kernel/debug/clk/clk_summary | grep spi3發(fā)現(xiàn) spi3 的時(shí)鐘節(jié)點(diǎn)enable_count是 1看起來也正常。這時(shí)候陷入了僵局軟件配置看起來正常引腳復(fù)用也查過沒有沖突但就是沒有波形。于是回頭再看了一遍時(shí)鐘樹發(fā)現(xiàn)clk_summary里有一個(gè)父時(shí)鐘節(jié)點(diǎn)的prepare_count是 0。這個(gè)父節(jié)點(diǎn)正好是 SPI3 的源時(shí)鐘雖然是直連的分頻節(jié)點(diǎn)但父時(shí)鐘沒 prepare子時(shí)鐘 enable 了也沒用。查驅(qū)動(dòng)代碼發(fā)現(xiàn)有一個(gè)時(shí)鐘是通過devm_clk_get_optional()獲取的驅(qū)動(dòng)認(rèn)為它是可選的獲取失敗也不報(bào)錯(cuò)但這個(gè)時(shí)鐘恰恰是外設(shè)功能時(shí)鐘的父時(shí)鐘。解決辦法是在設(shè)備樹的 SPI3 節(jié)點(diǎn)里補(bǔ)上缺失的時(shí)鐘引用同時(shí)在驅(qū)動(dòng)里把devm_clk_get_optional()改成必需的devm_clk_get()獲取失敗直接返回-EPROBE_DEFER。改完重新編譯燒錄示波器上立刻出現(xiàn)了完整波形。5.3 復(fù)盤要點(diǎn)為什么一開始沒發(fā)現(xiàn)回頭看這個(gè)問題的隱蔽性在于clk_enable_count已經(jīng)置 1但父級(jí)時(shí)鐘沒有 prepare導(dǎo)致時(shí)鐘根本沒有真正到達(dá) SPI3 外設(shè)。如果你只檢查 SPI3 節(jié)點(diǎn)本身的時(shí)鐘狀態(tài)永遠(yuǎn)發(fā)現(xiàn)不了問題。必須沿著時(shí)鐘樹往上追確認(rèn)每一個(gè)父節(jié)點(diǎn)都處于可工作狀態(tài)。排查這種問題我的經(jīng)驗(yàn)是在clk_summary里找到 SPI3 時(shí)鐘節(jié)點(diǎn)后一路往根節(jié)點(diǎn)方向看任何prepare_count為 0 的中間節(jié)點(diǎn)都有可能是問題所在。很多時(shí)候 SoC 內(nèi)部的時(shí)鐘樹比你想的要復(fù)雜多一個(gè) mux、多一個(gè) divider就多一個(gè)開關(guān)。5.4 另一個(gè)案例外部回環(huán)定位鏈路斷點(diǎn)另一個(gè)案例是和 SPI3 無關(guān)但思路相同。某板子的 SPI3 連接器測不到信號(hào)但主控引腳上波形正常。我在連接器的從機(jī)端把 MOSI 和 MISO 短接主機(jī)發(fā)送 0xAA結(jié)果收不到任何數(shù)據(jù)說明從主控引腳到連接器這一段鏈路是斷的。然后拿萬用表量連接器到主控引腳的走線導(dǎo)通性發(fā)現(xiàn)某個(gè)過孔虛焊補(bǔ)焊后回環(huán)測試通過。整個(gè)過程用了不到十分鐘比盲改代碼高效得多。6. 幾個(gè)容易忽略但非常實(shí)用的補(bǔ)充細(xì)節(jié)前面算是一條主線的排查思路但實(shí)際項(xiàng)目里還有幾個(gè)零散但很實(shí)用的細(xì)節(jié)值得單獨(dú)拿出來說省得你在現(xiàn)場重復(fù)踩坑。6.1 三線制 SPI3 模式的特殊性開頭提到過有些語境下 SPI3 指的是三線制模式。三線制 SPI 只有 SCK、CS、DATA 三根線數(shù)據(jù)線是雙向的主控發(fā)送時(shí)需要把方向切到輸出接收時(shí)需要切到輸入。這種模式下如果驅(qū)動(dòng)里的 GPIO 方向切換時(shí)機(jī)不對(duì)就會(huì)出現(xiàn)“發(fā)送正常、接收全零”或者反過來“發(fā)送時(shí)數(shù)據(jù)線上的波形是亂的”。三線制 SPI 的排查重點(diǎn)是數(shù)據(jù)線的方向切換時(shí)序。用示波器量數(shù)據(jù)線發(fā)送階段應(yīng)該有主控驅(qū)動(dòng)的電平變化接收階段應(yīng)該變成高阻或從機(jī)驅(qū)動(dòng)。如果在接收階段數(shù)據(jù)線上還是主控的高電平或者低電平說明方向沒有切換成功數(shù)據(jù)沖突了。這類問題在 STM32 上開啟三線制模式時(shí)尤其常見因?yàn)樗蕾囉布詣?dòng)切換方向一旦引腳復(fù)用配置成普通推挽輸出方向切換就失效了。6.2 CS 片選的“假正常”狀態(tài)CS 是 SPI 信號(hào)里最容易被忽略又最容易出問題的一根線。很多人看到 CS 在示波器上是高電平就認(rèn)為它正?!?SPI 空閑時(shí) CS 本來就是高電平。要確認(rèn) CS 是否正常必須在觸發(fā)事務(wù)時(shí)看它有沒有拉低。CS 的常見問題有三個(gè)一是極性配置反了設(shè)備樹里配成spi-cs-high導(dǎo)致 CS 在傳輸時(shí)是高電平從機(jī)永遠(yuǎn)處于未選中狀態(tài)二是 CS 被某個(gè)驅(qū)動(dòng)當(dāng)成了普通 GPIO 控制導(dǎo)致 SPI 控制器無法驅(qū)動(dòng)它三是 CS 線上下拉電阻沒接在 EMI 干擾下電平抖動(dòng)從機(jī)誤動(dòng)作。排查 CS 最直接的辦法是在/sys/kernel/debug/pinctrl里查看該引腳的當(dāng)前狀態(tài)如果是 GPIO 模式而設(shè)備樹里沒配置 CS 為 GPIO 控制那就說明 pinctrl 配置有問題。6.3 時(shí)序余量的判斷不要被“有波形”騙了有時(shí)候示波器上波形完整但系統(tǒng)就是不工作。這時(shí)候要關(guān)注信號(hào)的時(shí)序參數(shù)包括建立時(shí)間、保持時(shí)間、上升沿/下降沿時(shí)間。SPI 主控輸出的信號(hào)經(jīng)過 PCB 走線、電平轉(zhuǎn)換、連接器之后到達(dá)從機(jī)引腳時(shí)可能已經(jīng)變差。如果從機(jī)要求的建立時(shí)間余量不足傳輸就會(huì)偶發(fā)失敗。判斷方法是看數(shù)據(jù)線的跳變沿和 CLK 采樣沿之間的關(guān)系。在 Mode 0 下數(shù)據(jù)在 CLK 上升沿被采樣數(shù)據(jù)線的跳變應(yīng)該發(fā)生在 CLK 下降沿附近給采樣留出完整的建立時(shí)間。如果數(shù)據(jù)線的跳變沿幾乎和采樣沿重合說明時(shí)序余量已經(jīng)很緊張需要考慮降低 SPI 時(shí)鐘頻率、優(yōu)化 PCB 走線長度、或者換用壓擺率更高的電平轉(zhuǎn)換芯片。6.4 邏輯分析儀還是示波器排查 SPI3 信號(hào)工具選擇上有講究。示波器適合看模擬特性比如電平、邊沿、振鈴邏輯分析儀適合看時(shí)序關(guān)系和數(shù)據(jù)內(nèi)容尤其是需要解碼一串很長的數(shù)據(jù)時(shí)。如果只是確認(rèn)“有沒有信號(hào)”示波器就夠如果要確認(rèn)“數(shù)據(jù)內(nèi)容對(duì)不對(duì)”邏輯分析儀更方便。實(shí)際操作中我通常是示波器先掛上確認(rèn)物理層正常再用邏輯分析儀抓一段完整的事務(wù)解碼驗(yàn)證。兩者配合排查效率最高。7. 最后再分享一點(diǎn)個(gè)人體會(huì)SPI3 接口沒有信號(hào)送出這類問題的排查過程說到底就是“沿著信號(hào)鏈路走一遍”的過程。軟件配置查完查硬件硬件查完再回頭看軟件每一步都要有明確證據(jù)而不是靠猜。我在實(shí)際工作中見過太多人一上來就反復(fù)改寄存器、改設(shè)備樹改完一測還是不行又改回去一個(gè)下午就這么耗掉了。我更推薦的做法是先花十分鐘把示波器掛好量清每一根線的實(shí)際狀態(tài)。波形是最好的證據(jù)它不會(huì)騙人。CLK 有沒有、CS 有沒有拉低、MOSI 有沒有數(shù)據(jù)這三個(gè)信息一出來問題范圍立刻縮到很小。剩下的就是對(duì)癥下藥沒有時(shí)鐘查時(shí)鐘樹沒有片選查復(fù)用配置有波形但數(shù)據(jù)錯(cuò)查時(shí)序和模式參數(shù)。排查過程中養(yǎng)成記錄的習(xí)慣也很有價(jià)值。每次改動(dòng)什么參數(shù)、波形有什么變化、最終根因是什么記下來之后下次遇到類似問題可以快速對(duì)照。我自己就維護(hù)了一份“SPI 接口排查筆記”里面記錄了每個(gè)平臺(tái) SPI3 的引腳復(fù)用編號(hào)、時(shí)鐘樹路徑、常見坑點(diǎn)。遇到新項(xiàng)目時(shí)直接翻出來對(duì)照比重新踩一遍坑高效得多。希望這篇經(jīng)驗(yàn)分享能幫你少走一些彎路。如果你在排查 SPI3 時(shí)遇到的情況和上面說的都不一樣也歡迎交流畢竟嵌入式世界里的“沒信號(hào)”千奇百怪多一個(gè)案例就多一分經(jīng)驗(yàn)。