計與時序可靠性實踐)
做 IC 卡讀寫控制器這個選題放在今天的硬件圈里聽起來有點復(fù)古但它一直在真實產(chǎn)線上活著。現(xiàn)場一批老終端用了十幾年主板燒了買不到備件原廠早就停止維護能做的只有照原邏輯重畫一塊板、重寫一份固件然后塞進原來的機柜里接口、時序、卡型一樣不能變。這類活兒的技術(shù)難度不在算法而在于IC 卡讀寫控制器要在 51 單片機這種資源緊到摳門的平臺上把供電、時序、協(xié)議、掉電保護、插拔耐受全部做穩(wěn)一塊板子裝上去得扛住三年不斷電、卡座插拔十萬次不虛接。這篇就按我實際交付過的思路從前期的架構(gòu)選型、硬件原理圖與 PCB 的關(guān)鍵網(wǎng)絡(luò)、軟件分層怎么切、時序參數(shù)怎么算一直講到聯(lián)調(diào)時示波器該看哪幾根線、量產(chǎn)前必須補的校驗和與老化測試。適合手上正接這類改造項目、或者剛接觸 IC 卡底層通信的同行參考讀完之后你至少能畫出一版能過 EMC 預(yù)測試的板子同時寫出一份半年后自己還看得懂的固件。1. IC卡讀寫控制器的整體設(shè)計與選型拆解1.1 為什么這個場景下還在用51內(nèi)核先把為什么不用 ARM這個問題正面回答掉因為每次評審都會被問。答案是三件事BOM 成本、存量生態(tài)、供貨確定性。一塊控制器的核心成本里MCU 只占很小一部分但如果你換成 32 位平臺隨之而來的穩(wěn)壓要求、PCB 層數(shù)、固件工具鏈、燒錄工裝、產(chǎn)線檢測治具全部要重做一次改造攤到幾百臺設(shè)備上單價立刻失控。51 內(nèi)核的 8 位機在 5V 供電下 IO 直接兼容絕大多數(shù) IC 卡接口芯片和邏輯器件省掉一堆電平轉(zhuǎn)換這在老機柜里是巨大優(yōu)勢。第二個理由是代碼規(guī)模和實時性需求匹配。一個 IC 卡讀寫控制器要做的事情其實很少上電自檢、輪詢卡座、識別卡型、讀寫指定扇區(qū)或地址、串口上報、異常記錄。這些邏輯加起來也就幾 KB 代碼中斷響應(yīng)要求是毫秒級不是微秒級。用 51 內(nèi)核跑起來主循環(huán)加一個定時器節(jié)拍足夠干凈。反而是上 32 位平臺之后很多人會不自覺地引入 RTOS、文件系統(tǒng)、動態(tài)內(nèi)存最后把一件簡單的事做復(fù)雜穩(wěn)定性還不如原來。第三個理由最實在舊固件的時序特征是和舊硬件綁定的。上位機軟件里可能硬編碼了命令響應(yīng)時間、超時重試次數(shù)、上電延時你換了主頻和控制周期上位機會偶發(fā)判超時。沿用同主頻的 51 內(nèi)核平臺比如經(jīng)典的 89C52 系列配 11.0592MHz 晶振串口波特率誤差、定時器節(jié)拍、延時循環(huán)都能對上原來的量級改造風(fēng)險最低。我一般會明確告訴對方這次改造的目標(biāo)是無縫替換不是技術(shù)升級兩條路線不要混著走。提示如果項目是從零開始的新設(shè)計沒有存量上位機約束那選擇面可以放寬。但只要涉及替換舊板主頻、晶振、串口參數(shù)這三項盡量與舊方案保持一致能省掉大量的聯(lián)調(diào)扯皮。1.2 控制器要解決的四類核心問題把需求拆開看一個 IC 卡讀寫控制器本質(zhì)上要同時解決四類問題任何一類沒處理好現(xiàn)場都會表現(xiàn)為偶發(fā)讀不出卡。類別具體內(nèi)容典型失效表現(xiàn)供電與保護卡座供電、限流、熱插拔浪涌、反接保護插卡瞬間復(fù)位、芯片發(fā)熱時序與協(xié)議時鐘、數(shù)據(jù)建立保持時間、應(yīng)答超時偶發(fā) NAK、讀到全 FF 或全 00數(shù)據(jù)可靠性校驗、寫周期等待、掉電保護寫一半斷電后數(shù)據(jù)塊損壞人機與上報指示燈、蜂鳴、串口協(xié)議、日志現(xiàn)場無法判斷故障歸因這張表是我做方案評審時必擺的一頁。很多人一上來就盯著協(xié)議層摳時序結(jié)果板子插拔幾十次之后接觸電阻變化波形已經(jīng)歪了還在懷疑代碼。供電和接插件永遠(yuǎn)應(yīng)該排在第一優(yōu)先級因為它們決定了后面所有軟件努力能不能生效。1.3 軟硬件一體的分層模型我把整個控制器切成四層這個分層從畫原理圖那一天就開始生效而不是等寫代碼時才想起來。最底層是硬件抽象層只做一件事把引腳操作封裝成函數(shù)比如bus_start()、bus_write_byte()、card_power_on()、card_power_off()。這一層不允許出現(xiàn)任何協(xié)議語義只允許出現(xiàn)電平。往上是器件驅(qū)動層負(fù)責(zé)具體卡型的時序比如存儲卡的頁寫、邏輯加密卡的密碼校驗、非接觸卡的尋卡與防沖突。這一層只知道怎么和這顆芯片說話不知道業(yè)務(wù)要存什么。再往上是業(yè)務(wù)邏輯層負(fù)責(zé)卡型識別、權(quán)限判斷、余額或工號的讀出寫入、異常計數(shù)。這一層是純邏輯理論上可以脫離硬件在 PC 上跑單元測試。最頂上是通信與運維層負(fù)責(zé)串口或總線上報、命令解析、日志落盤。這樣切的好處很直接換卡座供應(yīng)商只改第一層換卡型只改第二層上位機改協(xié)議只改第四層。我見過太多把四個層揉成一個main.c的項目改一個引腳定義要翻八百行最后沒人敢動只能推倒重寫。2. 硬件設(shè)計從電源到卡座的每一個坑2.1 電源架構(gòu)與去耦的實操取舍IC 卡控制器一般是 5V 系統(tǒng)卡座供電也吃 5V但卡座是熱插拔部件插拔瞬間的浪涌和靜電是最大的敵人。我的常規(guī)做法是主板輸入 5V 先過一顆自恢復(fù)保險絲再進 LC 濾波然后分兩路——一路直供 MCU一路經(jīng)限流電阻供卡座卡座這路再并一顆 TVS 管到地。限流電阻的取值要算不能拍腦袋。假設(shè)卡型工作電流最大 10mA卡座接觸瞬間可能短路你希望短路電流不超過 100mA那么 R 5V/100mA 50Ω。但電阻串在供電回路里會產(chǎn)生壓降10mA 時壓降 0.5V卡端只剩 4.5V多數(shù)卡還能工作但已經(jīng)在邊緣。所以更穩(wěn)的做法是用 22Ω 到 33Ω 之間配合自恢復(fù)保險絲讓保險絲承擔(dān)長時間短路的保護電阻只負(fù)責(zé)削掉瞬態(tài)尖峰。去耦這塊我固定放三顆MCU 電源腳旁邊 0.1uF 瓷片卡座電源腳旁邊 0.1uF 加 10uF 鉭電容另外整板入口放一顆 100uF 電解。這里有個常被忽略的細(xì)節(jié)——卡座的 10uF 電容一定要靠近卡座焊盤不要放在板子另一頭。我遇到過一次插卡復(fù)位查了兩天最后發(fā)現(xiàn)是 10uF 電容離卡座 4 厘米插卡瞬間的電流突變在走線電感上產(chǎn)生的壓降直接把 MCU 拉進欠壓復(fù)位。挪到卡座旁邊 5 毫米問題消失。注意TVS 管的選型要看結(jié)電容高速數(shù)據(jù)線上串的 TVS 結(jié)電容過大會把上升沿拖慢導(dǎo)致時序不滿足。電源線用大結(jié)電容的沒關(guān)系數(shù)據(jù)線一定要選低結(jié)電容型號或者干脆只在電源側(cè)做保護。2.2 卡座選型與接觸可靠性設(shè)計卡座是這類項目里故障率最高的機械件沒有之一。選型時我會重點關(guān)注四項觸點鍍層、插拔壽命標(biāo)稱值、防插反結(jié)構(gòu)、檢測開關(guān)。觸點鍍層優(yōu)先選鍍金厚度至少要 0.3 微米以上鍍錫的便宜但氧化快南方潮濕環(huán)境下半年就開始出問題。插拔壽命標(biāo)稱值一般寫 10 萬次實際現(xiàn)場能撐到 5 萬次就算不錯所以軟件上必須做讀到異常就重試的兜底硬件上則要在結(jié)構(gòu)上給它留出更換空間——別把卡座焊死在板子正中間四周留出鑷子能伸進去的間隙維修時能單獨換。防插反這件事光靠卡座自帶的塑料限位不夠。我的做法是在 PCB 上把卡座的插入方向做成非對稱布局卡座一側(cè)緊貼板邊另一側(cè)留出元件區(qū)用戶反插時物理上插不到底。這個細(xì)節(jié)在打樣階段用 3D 打印一個假卡套試一下就能驗證成本幾乎為零但能省掉大量現(xiàn)場投訴。帶檢測開關(guān)的卡座貴一點點但非常值。檢測開關(guān)接一個 IO 并在外部加 10k 上拉插卡時產(chǎn)生電平跳變MCU 用中斷或輪詢捕捉。這樣控制器平時可以不給卡座供電插卡才上電既省電又減少觸點氧化。沒有檢測開關(guān)的方案只能靠一直上電一直輪詢觸點長期帶電氧化速度明顯更快。2.3 電平匹配、上拉與熱插拔保護51 內(nèi)核單片機的 IO 是準(zhǔn)雙向口輸出高電平時實際上是靠內(nèi)部弱上拉灌電流能力很弱。這意味著凡是需要輸出高電平驅(qū)動的地方都不能直接接。驅(qū)動指示燈、蜂鳴器、卡座電源開關(guān)一律用三極管或者專用的驅(qū)動芯片IO 只負(fù)責(zé)給控制信號。I2C 這類開漏總線上拉電阻必須外部加不能指望內(nèi)部弱上拉。上拉電阻的取值要算我在第 4 章會給出完整計算過程這里先說結(jié)論5V 系統(tǒng)、總線電容 200pF 以內(nèi)、速率 100kHz 的條件下4.7k 是最穩(wěn)妥的通用值2.2k 更快但功耗高10k 在長走線時上升沿會明顯變緩。熱插拔保護除了前面說的 TVS 和限流還有一個細(xì)節(jié)所有接到卡座的信號線串一顆 100Ω 到 220Ω 的電阻。這顆電阻的作用不是限流而是隔離防止卡座上積累的靜電通過信號線打進 MCU 內(nèi)部保護二極管。串了這顆電阻之后上升沿會變緩所以要重新核算時序余量通常 100kHz 的 I2C 完全沒問題400kHz 就要謹(jǐn)慎。2.4 時鐘、復(fù)位與看門狗的配置取舍晶振選 11.0592MHz 還是 12MHz這是老話題但仍然值得說清楚。11.0592MHz 的優(yōu)點是串口波特率可以做到零誤差——11059200 除以 12 得 921600再除以 16 和 6 正好是 9600。12MHz 在 9600 波特率下誤差約 8.5%短幀還能忍長幀連續(xù)通信就容易出問題。所以只要涉及串口我一律選 11.0592MHz。代價是定時器節(jié)拍不是整數(shù)。12MHz 時機器周期正好 1 微秒算延時非常舒服11.0592MHz 時機器周期是 1.085 微秒做 1 毫秒定時器要裝 922 而不是 1000。這個換算必須提前算好寫進注釋否則后人接手會以為初值算錯了。復(fù)位電路我用經(jīng)典的阻容復(fù)位加一顆專用復(fù)位芯片的組合。純阻容復(fù)位在電源緩慢上升時可能不可靠加一顆帶延時的復(fù)位監(jiān)控芯片成本幾毛錢能保證上電時 MCU 一定在穩(wěn)定電壓之后才開始跑。看門狗必須開而且要養(yǎng)成主循環(huán)里定時喂狗、任何可能阻塞的地方都要評估喂狗時機的習(xí)慣。我踩過一次坑某個等待卡應(yīng)答的循環(huán)里用了長延時結(jié)果看門狗把板子復(fù)位了現(xiàn)場表現(xiàn)為插卡就重啟查了很久才發(fā)現(xiàn)是喂狗點設(shè)計不合理。2.5 非接觸方案的線圈與鋪地注意事項如果卡型是非接觸的硬件部分還要多考慮一層讀卡芯片的天線匹配和鋪地。常見做法是天線和芯片之間串匹配電容和阻尼電阻具體參數(shù)必須按芯片手冊給的參考值來不要自己算因為天線電感跟 PCB 走線形狀強相關(guān)算出來往往對不上。鋪地上有一條硬規(guī)矩天線投影區(qū)域內(nèi)層和底層不允許鋪銅也不允許走任何信號線。鋪了銅相當(dāng)于給天線并聯(lián)了一個短路線圈讀卡距離會斷崖式下降。我見過一塊板子讀卡距離只有 1 厘米翻來覆去改代碼最后發(fā)現(xiàn)是底層鋪了一整片地。把天線區(qū)域的地挖空距離立刻回到 5 厘米。另外讀卡芯片和 MCU 之間的 SPI 線要盡量短超過 10 厘米就要考慮串電阻做阻抗匹配。51 內(nèi)核跑 SPI 速率不高一般 1MHz 以內(nèi)這個速率下短走線問題不大但走線長了之后時鐘和數(shù)據(jù)之間的偏移會引起偶發(fā)讀錯寄存器表現(xiàn)是讀出來的版本號偶爾不對。3. 軟件分層從能跑到能維護3.1 四個層次的職責(zé)邊界與目錄結(jié)構(gòu)軟件分層最怕的是名義上分了實際上還在互相調(diào)。我的做法是用目錄結(jié)構(gòu)強制約束每個目錄只允許向上依賴不允許橫向亂調(diào)。/project /bsp 硬件抽象引腳、延時、電源控制 /driver 器件驅(qū)動各類卡的時序?qū)崿F(xiàn) /app 業(yè)務(wù)邏輯卡型識別、數(shù)據(jù)讀寫、權(quán)限 /comm 通信協(xié)議串口幀解析、上報 /cfg 配置與常量時序參數(shù)、地址映射 main.c 只做初始化和調(diào)度/bsp里只有寄存器和引腳操作絕不允許出現(xiàn)if (card_type xxx)這種判斷。/driver里可以有卡型相關(guān)的時序差異但不能出現(xiàn)余額不足這種業(yè)務(wù)詞匯。/app里全部是純邏輯理論上可以編譯到 PC 上跑測試。這套約束聽起來很教條實際收益很大。有一次客戶臨時要求把接觸式卡換成另一種邏輯加密卡我只改了/driver下一個文件、/cfg里幾個參數(shù)/app一行沒動半天就交付了。3.2 時間基準(zhǔn)用一個節(jié)拍器替掉所有長延時新手寫 51 程序最容易犯的錯是到處delay_ms(5)。這在 demo 里沒問題在真實控制器里是災(zāi)難按鍵響應(yīng)變慢、串口丟字節(jié)、看門狗被餓死。正確做法是建立一個 1 毫秒的定時器節(jié)拍所有跟時間相關(guān)的邏輯都掛在節(jié)拍上。定時器初值的算法要寫清楚/* 11.0592MHz 晶振定時器0 方式11ms 節(jié)拍 機器周期 12 / 11.0592MHz ≈ 1.085us 1ms 需要計數(shù) 1000 / 1.085 ≈ 922 初值 65536 - 922 64614 0xFC66 */ TH0 0xFC; TL0 0x66;節(jié)拍中斷里只做一件事給若干個軟件計數(shù)器加一。主循環(huán)里判斷計數(shù)器到?jīng)]到到了就執(zhí)行任務(wù)沒到就去干別的。這樣整個系統(tǒng)沒有阻塞點插卡響應(yīng)、串口收發(fā)、指示燈閃爍全部并行推進。對于確實需要等待的場合比如等卡內(nèi)部寫周期完成不要用死等而是設(shè)一個超時計數(shù)器每毫秒減一減到零就判失敗。這個模式統(tǒng)一之后代碼里幾乎看不到while(!flag);這種死循環(huán)穩(wěn)定性立刻上一個臺階。3.3 命令-響應(yīng)模型與超時重試策略卡和 MCU 之間的交互本質(zhì)上是發(fā)命令、等響應(yīng)。這中間有三個參數(shù)必須明確命令發(fā)出后的等待上限、單次響應(yīng)失敗后的重試次數(shù)、整體失敗的判定條件。等待上限我是這么定的查器件手冊給出的最大響應(yīng)時間乘以 2 作為軟件超時。比如某類卡寫的最大內(nèi)部寫周期是 5 毫秒那軟件超時設(shè) 10 毫秒。乘 2 是留余量因為實際器件的響應(yīng)時間會隨溫度、電壓、批次波動。重試次數(shù)不要多3 次足夠。重試太多會掩蓋真實故障讓偶發(fā)問題變成看起來能用。3 次全敗就上報錯誤碼讓上位機或者運維去處理。這里有個反直覺的經(jīng)驗重試次數(shù)多的系統(tǒng)現(xiàn)場故障率反而高因為它把早期征兆全部吞掉了等到徹底壞的時候已經(jīng)積累了一堆劣化數(shù)據(jù)。重試之間要有間隔一般 2 到 5 毫秒讓總線電容放電、讓卡內(nèi)部狀態(tài)機復(fù)位。間隔太短等于連續(xù)發(fā)同樣的命令卡還沒準(zhǔn)備好必然繼續(xù)失敗。3.4 掉電保護與關(guān)鍵數(shù)據(jù)的寫入策略這類控制器經(jīng)常要存一些關(guān)鍵數(shù)據(jù)卡的黑名單、設(shè)備參數(shù)、累計交易筆數(shù)。一旦寫到一半斷電數(shù)據(jù)塊就可能損壞。51 內(nèi)核沒有硬件 ECC必須靠軟件策略。我的策略是雙備份 序號 校驗和。同一份數(shù)據(jù)在存儲區(qū)里占兩個槽位每個槽位包含數(shù)據(jù)本體、一個遞增的寫入序號、一個 CRC8 校驗和。啟動時讀兩個槽位校驗通過的才算有效兩個都有效就取序號大的那個一個有效就用那個都無效就加載默認(rèn)值并置故障標(biāo)志。寫入順序也要講究先寫備用槽寫完校驗通過再把主槽標(biāo)記為舊。任何一個時刻斷電至少有一個槽是完整的。這套機制占用的空間不多但能救命。我之前有個項目現(xiàn)場經(jīng)常直接拉閘斷電用單槽方案時一個月壞了三次數(shù)據(jù)換成雙槽之后半年零故障。另外累加類數(shù)據(jù)比如總計數(shù)不要每次都寫存儲。改成在 RAM 里累加每滿 10 次或者每 10 秒落盤一次能把寫入次數(shù)降低一個數(shù)量級。存儲器的擦寫壽命是有限的典型存儲芯片標(biāo)稱 100 萬次但那是理想條件實際算上溫度和電壓偏差留 10 倍余量比較穩(wěn)妥。4. 關(guān)鍵時序與協(xié)議實現(xiàn)細(xì)節(jié)4.1 I2C總線上拉電阻的完整計算過程很多資料直接給用 4.7k但不給理由導(dǎo)致?lián)Q了個板子就不靈。這里把計算過程寫全。上拉電阻有上限和下限兩個約束。上限由上升沿時間決定。I2C 標(biāo)準(zhǔn)模式規(guī)定上升沿最長時間 1000 納秒上升時間近似為tr ≈ 0.8473 × R × Cb其中 Cb 是總線總電容包括走線電容、器件引腳電容、卡座電容。一塊小板子上Cb 一般在 100 到 300pF取 200pF 計算R ≤ tr / (0.8473 × Cb) ≤ 1000ns / (0.8473 × 200pF) ≈ 5900 Ω所以上拉不能大于約 5.9k4.7k 剛好在這個范圍內(nèi)。下限由灌電流能力決定。I2C 規(guī)范要求低電平時器件能拉低到 0.4V 以下標(biāo)準(zhǔn)模式灌電流按 3mA 算R ≥ (Vcc - Vol) / Iol ≥ (5 - 0.4) / 0.003 ≈ 1533 Ω所以 1.5k 到 5.9k 都合規(guī)取中間值 4.7k 兼顧功耗和速度。如果要跑 400kHz 快速模式上升沿要求收緊到 300 納秒上限變成約 1.8k這時候就得用 2.2k 甚至 1.5k同時要嚴(yán)格控制總線電容。注意很多人只加一組上拉結(jié)果總線上掛了多個器件后等效上拉變小。器件內(nèi)部如果自帶弱上拉外部再并 4.7k實際等效可能只有 2k 出頭功耗上去了但沒人注意。做低功耗產(chǎn)品時要逐個確認(rèn)。4.2 存儲類卡的頁寫與寫周期等待存儲卡比如常見的串行 EEPROM 類有兩個關(guān)鍵參數(shù)必須搞準(zhǔn)頁大小和內(nèi)部寫周期。頁大小決定了單次連續(xù)寫不能跨頁。以頁大小 8 字節(jié)的型號為例如果你從地址 0x06 開始連寫 4 個字節(jié)寫到 0x08 時會回卷到頁首 0x00把原本的數(shù)據(jù)覆蓋掉。這個坑極其隱蔽因為讀回來的前兩個字節(jié)是對的你以為寫成功了。穩(wěn)妥寫法是先算頁內(nèi)剩余空間/* 從 addr 開始寫 len 字節(jié)自動處理頁邊界 */ while (len 0) { uint8_t page_left PAGE_SIZE - (addr % PAGE_SIZE); uint8_t chunk (len page_left) ? len : page_left; bus_start(); bus_write_byte(DEV_ADDR_W); bus_write_byte(addr 8); bus_write_byte(addr 0xFF); for (i 0; i chunk; i) { bus_write_byte(buf[i]); } bus_stop(); wait_write_done(10); /* 超時 10ms手冊典型 5ms */ addr chunk; buf chunk; len - chunk; }寫周期等待有兩種做法固定延時和應(yīng)答輪詢。固定延時簡單但要按最壞情況取值浪費時間應(yīng)答輪詢更快但實現(xiàn)稍復(fù)雜。控制器的場景下一次操作省幾毫秒沒有意義我一般直接用固定延時代碼簡單不容易出錯只在批量寫的場合才用輪詢。4.3 邏輯加密卡的密碼校驗與錯誤計數(shù)處理邏輯加密卡這類器件有一個非常危險的特性密碼連續(xù)錯誤若干次后會永久鎖死鎖死之后卡就廢了不可恢復(fù)。這個特性必須寫進軟件設(shè)計的第一條注釋。防護措施有三層。第一層密碼在本地先做格式校驗長度、字符集不對直接拒絕不發(fā)到卡上。第二層每次校驗前先讀卡內(nèi)的剩余嘗試次數(shù)多數(shù)型號支持這個讀取剩余次數(shù)低于 2 就拒絕操作并上報需管理員介入。第三層錯誤次數(shù)在本地做一份影子記錄存到掉電保存區(qū)這樣即使換一臺設(shè)備也能知道這張卡已經(jīng)錯過幾次?,F(xiàn)場最常見的誤操作是運維人員反復(fù)試密碼。所以人機界面上必須有明確提示不能只亮個紅燈。我的做法是錯誤一次就蜂鳴長響、指示燈快閃兩次之后直接鎖定界面要求走管理流程。4.4 非接觸卡的尋卡流程與防沖突實現(xiàn)非接觸方案里MCU 和讀卡芯片之間是對接讀卡芯片和卡之間是射頻。MCU 側(cè)的關(guān)鍵是寄存器讀寫時序和命令流控制。尋卡的典型流程是軟復(fù)位、配置定時器和調(diào)制參數(shù)、發(fā)送尋卡命令、判斷是否有卡、有多張卡則進入防沖突讀序列號、選卡、認(rèn)證、讀寫。防沖突這一步經(jīng)常被簡化掉因為現(xiàn)場通常一次只放一張卡。但只要有兩張卡同時進入場區(qū)沒有防沖突邏輯的固件就會讀到混亂的序列號表現(xiàn)為讀到一張不存在的卡。穩(wěn)妥做法是保留完整的防沖突流程即使最終只取第一張卡。還有一個細(xì)節(jié)尋卡命令的間隔。如果主循環(huán)里每圈都發(fā)一次尋卡命令讀卡芯片會一直處于發(fā)射狀態(tài)功耗高、發(fā)熱大長期運行還可能影響芯片壽命。正確做法是每 100 到 200 毫秒發(fā)一次中間留出空閑。5. 聯(lián)調(diào)實錄示波器上該看什么5.1 三個必須上示波器的測量點軟件寫完燒進去能跑不代表跑得穩(wěn)。我聯(lián)調(diào)時必看三個點每次都能提前發(fā)現(xiàn)隱患。第一個點是卡座供電腳的上電波形??磧杉律仙赜袥]有臺階有臺階說明限流電阻和電容形成了 RC 拐點可能讓卡上電時序不滿足、插卡瞬間有沒有跌落超過 0.3V跌太多會導(dǎo)致卡內(nèi)部復(fù)位。這個波形只有在實際插拔時才能抓到用單次觸發(fā)模式觸發(fā)條件設(shè)成下降沿。第二個點是時鐘線的上升沿。把時間基準(zhǔn)放到 200 納秒一格看上升時間。如果超過 1 微秒說明上拉偏大或者總線電容偏大長線場合必然出問題。第三個點是應(yīng)答等待時間。觸發(fā)設(shè)在命令發(fā)出的最后一個時鐘下降沿看響應(yīng)回來的延遲。這個延遲如果在臨界值附近晃說明軟件超時設(shè)置得太緊換個批次或者溫度變了就會失敗。5.2 通信失敗的排查路徑表現(xiàn)場出問題的時候最忌諱上來就改代碼。我一般按這張表往下走八成的問題在前三步就能定位。現(xiàn)象首要懷疑快速驗證方法完全無響應(yīng)供電、卡座接觸、復(fù)位萬用表量卡座電壓插拔看是否跳變有時能讀有時不能卡座氧化、限流電阻偏大換一張新卡對比看是否與卡有關(guān)讀到全 FF數(shù)據(jù)線未接、上拉缺失示波器看數(shù)據(jù)線是否有拉低動作讀到全 00數(shù)據(jù)線被拉死、器件未上電斷電測數(shù)據(jù)線對地阻抗寫入后讀回錯亂頁邊界跨界、寫周期等待不足單字節(jié)寫測試逐字節(jié)驗證長線通信偶發(fā)錯上升沿過緩、串阻過大縮短走線或減小串阻后復(fù)測插卡就復(fù)位電源跌落、地彈示波器看 MCU 電源腳瞬態(tài)這張表我一般打印出來貼在工位上新來的同事按著走能省掉大量瞎猜的時間。要強調(diào)的是先物理后邏輯確認(rèn)供電、接觸、地線沒問題再去懷疑協(xié)議和代碼。我見過太多人抱著邏輯分析儀摳時序最后發(fā)現(xiàn)是卡座彈片變形。5.3 一次典型的偶發(fā)讀卡失敗定位過程講一個實際案例過程比結(jié)論有價值。某批控制器在現(xiàn)場出現(xiàn)偶發(fā)讀卡失敗比例大概 5%重啟就好過幾天又犯。第一步先確認(rèn)是不是卡的問題。拿十張同型號卡在新板子上跑全部正常說明卡沒問題。第二步把故障板拿回來在實驗室里連續(xù)插拔 500 次復(fù)現(xiàn)了兩次失敗。第三步示波器掛在數(shù)據(jù)線上做長時間錄制抓到失敗瞬間的波形——數(shù)據(jù)線的低電平只到 1.2V沒到 0.4V 以下。低電平下不去無非三個原因上拉太強、灌電流能力不足、地線阻抗大。量了一下上拉是 4.7k正常。查器件手冊低電平灌電流能力 3mA符合。最后量地線發(fā)現(xiàn)卡座的地是通過一段細(xì)走線連到主地的這段走線電阻有 0.4Ω加上插拔后接觸電阻上升到 2Ω 左右形成分壓把低電平抬起來了。處理方式很簡單卡座地改成大面積鋪銅直連主地另外在卡座地腳旁邊加一個過孔到地平面。改完之后連續(xù)插拔 2000 次零失敗。這個案例的教訓(xùn)是低速總線上的問題八成不是時序算錯而是回流路徑不通。6. 常見問題速查與工程避坑清單6.1 硬件類高頻問題問題一插卡時指示燈閃一下就滅。大概率是電源跌落導(dǎo)致 MCU 復(fù)位。檢查卡座電容位置、限流電阻是否過大、地線是否夠?qū)挕?梢韵扰R時把限流電阻短接驗證如果現(xiàn)象消失就是供電能力問題。問題二同一張卡在 A 板能用 B 板不能用。先對比兩塊板的上拉電阻和串阻再對比卡座型號。不同批次卡座的彈片壓力和鍍層不一樣接觸電阻能差好幾倍。問題三板子放一段時間就壞。潮濕環(huán)境下的電化學(xué)遷移。檢查是否有長期帶電的密集走線、是否有未做阻焊開窗的裸銅。三防漆能解決大部分問題但要避開卡座觸點區(qū)域。問題四讀卡距離忽遠(yuǎn)忽近。非接觸方案專屬。檢查天線附近有沒有金屬件、電池、螺絲。人體靠近也會影響測試時要模擬實際安裝狀態(tài)。6.2 軟件類高頻問題問題一串口偶爾收到亂碼。先算波特率誤差。11.0592MHz 配 9600 是零誤差如果用的是 12MHz誤差 8.5%連續(xù)收長幀必錯。另外檢查晶振負(fù)載電容是否匹配負(fù)載電容不對會導(dǎo)致實際頻率偏移。問題二寫入成功但讀回是舊數(shù)據(jù)。緩存沒刷新。很多驅(qū)動里讀操作會走緩存寫完必須主動清緩存或者延時后重讀。我一般寫完立刻讀回校驗校驗不過就重寫一次。問題三長時間運行后死機??撮T狗配置問題或者棧溢出。51 內(nèi)核的??臻g很小中斷嵌套加上局部數(shù)組很容易溢出。檢查原則中斷服務(wù)函數(shù)里不放數(shù)組、不調(diào)用多層函數(shù)。問題四換一批芯片就不工作。時序余量不足。不同批次的器件響應(yīng)速度有差異如果你的超時是卡著手冊最小值設(shè)的換批次必然出問題。所有時序參數(shù)至少留 2 倍余量。6.3 量產(chǎn)前必須補的三件事第一件每塊板做一次全流程自檢。上電后自動跑讀卡座空載檢測、寫一個測試字節(jié)到保留區(qū)、讀回校驗、清測試標(biāo)志。這個自檢可以在產(chǎn)線治具上觸發(fā)也可以在每次上電時跑成本很低但能攔住大部分焊接不良。第二件固件里加版本號和構(gòu)建時間。通過一條專用命令讀出來。現(xiàn)場排查時第一句問的就是這塊板子跑的哪個版本沒有版本號根本沒法定位。第三件做高低溫循環(huán)和反復(fù)插拔老化。抽 5% 的板子在 0 度和 60 度之間循環(huán) 10 次每次循環(huán)后跑一遍自檢插拔測試做 5000 次。這一步能提前暴露九成的接觸和溫漂問題。我吃過一次虧一批板子在常溫下全部合格到了北方冬天現(xiàn)場低溫下晶振起振慢上電復(fù)位失敗幾百臺返修。從那之后老化測試變成了必做項。6.4 關(guān)于代碼可維護性的一點私貨最后說點不那么技術(shù)但很重要的。這類改造項目的生命周期往往比預(yù)期長交付三年后可能還有人來找你改一個功能。所以代碼里一定要留夠信息函數(shù)命名要能自解釋card_power_on()比pwr(1)強一百倍。所有魔數(shù)時序參數(shù)、地址、密碼位置必須定義成宏并寫明來源手冊第幾頁、實測值。每個驅(qū)動文件頭部寫清楚適用的器件型號范圍和已知限制。關(guān)鍵分支加注釋說明為什么這么寫而不是寫了什么。我接手過一個前人留下的項目里面有一行delay_us(37);沒有任何注釋。查了三天最后通過對比手冊才知道那是某個器件的等待時間而且 37 這個值是因為當(dāng)時用的晶振是 12MHz 算出來的后來晶振換成了 11.0592MHz這個延時其實已經(jīng)不準(zhǔn)了只是恰好還在容差范圍內(nèi)。這種坑全靠注釋才能避免重演。我個人在這些項目里最深的體會是IC 卡讀寫控制器這類東西技術(shù)難度從來不是門檻把每一個偶發(fā)當(dāng)成必然會發(fā)生去設(shè)計才是門檻。現(xiàn)場插拔十萬次5% 的失效概率就是五千次投訴溫度跨 60 度任何卡著臨界值的參數(shù)都會翻車。所以每次做方案我都會問自己一句這個余量如果是最差批次、最差溫度、最臟的卡座還夠不夠。答案不夠就再加一倍。