、電源到FAT文件系統(tǒng)的根治指南)
SD卡數(shù)據(jù)記錄這種活兒看起來簡單不就是開個文件、寫數(shù)據(jù)、關(guān)文件嘛。但真正在野外跑起來十個項目里有八個會栽在同一個坑上設(shè)備剛上電時一切正常日志一條條往里寫看著很順利。可過了幾分鐘、幾小時最多撐不過一天日志就悄悄停了。你翻SD卡前半段數(shù)據(jù)好好的后半段什么都沒有連個報錯殘留都沒有。要是重啟一下設(shè)備它又能正常記錄一段時間然后再次沉默。這種“只記錄短時間”的問題我這些年修過不知道多少次從Arduino級別的小記錄儀到工業(yè)級的采集盒子都遇到過。今天把排查思路和根因一次性講透。先說個反直覺的結(jié)論大部分“記錄中斷”根本不是SD卡壞了也不是程序邏輯里寫文件那行代碼出了問題。真正的原因是系統(tǒng)某個環(huán)節(jié)被堵死、被餓死、或者被電壓波動打了個措手不及。只是這些問題全都暴露在“SD卡寫入”這一步上所以看起來像是卡的錯。你要是不把思路從“寫文件”上挪開光盯著SD卡換卡、換格式化工具問題永遠復(fù)現(xiàn)、永遠修不好。下面對標我實際踩過的場景按“現(xiàn)象 - 根因 - 驗證 - 根治”的順序拆開講順帶附上可以直接抄作業(yè)的排查流程。1. 先還原現(xiàn)場短時記錄背后的三類典型表象排查之前先搞清楚你到底面對的是哪種“停”。同樣是記錄中斷表象不同指向的根因完全不同。我最怕收到的問題描述就是“它停了”沒別的了。你一定要去把SD卡拿回來看文件系統(tǒng)實際長什么樣這會直接砍掉一半的排查分支。1.1 表象A文件系統(tǒng)完好日志戛然而止最常見的一種。SD卡插到電腦上文件系統(tǒng)能正常識別目錄結(jié)構(gòu)也沒壞就是最后一個文件寫到某個時間點之后不再增長。文件大小停在幾十KB、幾MB打開看末尾是完整的半行數(shù)據(jù)沒有亂碼沒有截斷痕跡。這種表象往往指向邏輯層面的停止——程序還在跑或者已經(jīng)死了但無論如何都沒有再執(zhí)行寫入操作。常見原因包括緩沖區(qū)寫滿后主循環(huán)被阻塞系統(tǒng)進入死鎖或看門狗復(fù)位。采樣線程崩了或者FIFO隊列滿了之后寫入線程被暫停又沒有恢復(fù)機制。文件打開失敗后錯誤處理分支里有個while(1)死循環(huán)代碼就停在那了。判斷方法很簡單看最后一條數(shù)據(jù)的時間戳和程序預(yù)期行為是否對齊。如果最后一條數(shù)據(jù)正好卡在某個整點、某個采集周期邊界大概率是軟件邏輯問題而不是硬件問題。1.2 表象B文件損壞、目錄亂碼、卡上出現(xiàn)碎片文件SD卡拔下來插電腦系統(tǒng)提示需要格式化或者目錄里出現(xiàn)一堆“.$$$”臨時文件、文件名變成亂碼、文件大小為0。這種屬于寫入過程中突然掉電或復(fù)位FAT表沒來得及更新。注意一個容易被忽略的細節(jié)SD卡寫入不是直接改扇區(qū)而是先寫數(shù)據(jù)區(qū)再寫FAT表。如果中途斷電數(shù)據(jù)區(qū)可能已經(jīng)寫進去了但FAT表沒更新表現(xiàn)出來就是文件大小不對、數(shù)據(jù)在但文件打不開、或者殘留一堆臨時文件。這種情況在電池供電的設(shè)備上尤其常見。電池電壓隨著放電逐漸下降當電壓跌到SD卡或主控的最低工作電壓附近時系統(tǒng)會處于一種“半死不活”的狀態(tài)——主控還在運行SD卡卻開始丟響應(yīng)程序反復(fù)重試最終卡死或復(fù)位。復(fù)位瞬間如果正好在寫FAT表文件系統(tǒng)就爛了。1.3 表象CSD卡“消失”必須重新插拔才能恢復(fù)這種最詭異程序還在跑指示燈還閃著但SD卡就是讀不到、寫不進。你通過串口打印調(diào)試信息會看到類似“card init failed”或者“write timeout”的錯誤。重啟程序沒用必須物理斷電或者把卡拔出來重新插進去才能重新初始化。這種往往是SD卡進入了保護狀態(tài)或者電氣連接出現(xiàn)異常。常見原因SPI模式下SD卡被錯誤的電平信號干擾進入休眠或錯誤狀態(tài)。電源紋波過大導(dǎo)致卡內(nèi)部邏輯混亂但主控還能撐住。卡座接觸不良振動環(huán)境下偶發(fā)性斷路。更隱蔽的情況你的程序在檢測到寫錯誤后沒有做重新初始化而是直接放棄之后所有寫入嘗試都走錯誤分支表現(xiàn)為“卡消失了”。這三種表象經(jīng)常混合出現(xiàn)。比如先有電壓波動導(dǎo)致寫失敗程序處理不當又引發(fā)邏輯死循環(huán)最后表現(xiàn)出來既是B又是A。所以拿到卡之后先做一件最基礎(chǔ)的事把卡掛到電腦上用磁盤檢查工具掃一遍然后直接看卡里日志文件的十六進制末尾記錄它“死在哪個操作上”。2. 緩沖區(qū)與寫入速度的失衡數(shù)據(jù)記錄中斷的第一大元兇2.1 緩沖區(qū)為什么會滿采樣率與寫入速率的靜態(tài)計算很多記錄中斷根源就是一句話采樣速率 寫入速率緩沖區(qū)被填滿沒有退路。在MCU環(huán)境下SD卡的寫入速度遠低于你想象的數(shù)字。以常見的SPI模式SD卡為例實測連續(xù)寫入速度不計文件系統(tǒng)和磨損均衡大致在SPI時鐘20MHz單塊寫入大約500~700 KB/sSPI時鐘10MHz單塊寫入大約250~350 KB/s如果用的是FatFs 寫小文件 每寫一次就flush一次速度可能掉到幾十KB/s甚至更低。而你如果以較高的采樣率在采集數(shù)據(jù)比如每通道1kHz、雙通道、每個樣本2字節(jié)那一秒就是4KB看起來不多但如果你的采集任務(wù)里還有波形計算、浮點運算、顯示刷新、無線發(fā)送主控能留給SD卡的時間遠沒有你以為的那么多。我遇到過最典型的案例一個設(shè)備采集加速度計數(shù)據(jù)采樣率2kHz每個樣本6字節(jié)理論上每秒12KBSD卡完全扛得住。但程序用的是單緩沖主循環(huán)里先采集一批數(shù)據(jù)然后調(diào)用f_write寫入。f_write內(nèi)部要先查FatFs的狀態(tài)、移動文件指針、搬數(shù)據(jù)到內(nèi)部扇區(qū)緩沖實際耗時可能高達幾十毫秒。在寫入期間中斷照常觸發(fā)采集數(shù)據(jù)往哪放只能放同一個緩沖區(qū)。緩沖區(qū)被覆蓋數(shù)據(jù)丟了程序卻完全不知道。然后系統(tǒng)繼續(xù)運行表現(xiàn)就是日志文件有數(shù)據(jù)但數(shù)據(jù)中間缺了一大段看著像“記錄了一段時間就停了”。如果你用的緩沖區(qū)是固定數(shù)組沒有做“滿了就丟棄”或“滿了就覆蓋”的策略那緩沖區(qū)寫滿后唯一的下場就是寫指針覆蓋讀指針或者主循環(huán)阻塞等待緩沖區(qū)空出來——而緩沖區(qū)永遠不會空因為寫入線程也被卡住了。2.2 致命習慣只在緩沖區(qū)滿時才一次性寫入新手最常犯的錯誤是采集數(shù)據(jù)放到緩沖區(qū)等緩沖區(qū)滿了才一次性寫SD卡。這個設(shè)計貌似合理緩沖區(qū)大小設(shè)成512的整數(shù)倍一次寫入正好一個扇區(qū)效率最高。但問題在于緩沖區(qū)滿之前的那段時間如果采集線程一直在填寫入線程沒有機會介入。具體來說假設(shè)緩沖區(qū)是4KB采集速率是2KB/s那么2秒后緩沖區(qū)滿了。這時候觸發(fā)f_write寫入4KB數(shù)據(jù)。在寫入期間采集線程繼續(xù)運行又產(chǎn)生了新數(shù)據(jù)。新數(shù)據(jù)往哪放緩沖區(qū)還是那4KB已經(jīng)被寫入線程鎖住或者正在被讀取。要么丟數(shù)據(jù)要么阻塞采集線程要么雙線程共享緩沖區(qū)互相踩踏。解決思路只有兩個方向雙緩沖交替寫入采集線程寫B(tài)uffer A滿了之后切換Buffer B同時把Buffer A交給SD卡寫入。寫入期間采集線程繼續(xù)用Buffer B互不干擾。環(huán)形緩沖 丟舊保新或丟新保舊寫入線程持續(xù)工作緩沖區(qū)始終留有余量滿了就覆蓋最舊的數(shù)據(jù)。這兩個方案不只是實現(xiàn)差異背后的可靠性模型完全不同。雙緩沖在數(shù)據(jù)完整性上更好但對時序要求高你必須在緩沖區(qū)切換間隙完成所有寫入環(huán)形緩沖在實現(xiàn)上更簡單但必須有明確的數(shù)據(jù)丟失策略并且要在日志里標記丟了多少數(shù)據(jù)否則后續(xù)分析數(shù)據(jù)時會被誤導(dǎo)。2.3 寫入完成時間是個系統(tǒng)性殺手很多人只算平均寫入速度忘了算最壞寫入時間。SD卡的寫入速度不是恒定的實際測試中blocking write單次512字節(jié)的耗時可能從幾毫秒到幾百毫秒不等。特別是在跨FAT簇邊界時需要額外讀改寫FAT表文件系統(tǒng)碎片化之后寫入前需要查找空閑簇劣質(zhì)卡或老卡的擦除操作特別慢卡接近滿時閃存內(nèi)部GC垃圾回收占用大量時間。如果你在主循環(huán)里同步調(diào)用f_write那f_write最壞耗時幾百毫秒意味著你的系統(tǒng)每寫一筆數(shù)據(jù)就“卡頓”幾百毫秒。采樣線程如果在這段時間里被阻塞就會丟數(shù)據(jù)如果采樣靠中斷中斷服務(wù)程序里塞了采集邏輯那直接就是中斷丟失數(shù)據(jù)采不全。所以凡是采樣率超過1kHz、單次寫入數(shù)據(jù)量超過512字節(jié)的場景我強烈建議用RTOS 任務(wù)間隊列或者狀態(tài)機 環(huán)形緩沖來解耦采集和寫入。采集任務(wù)只負責往隊列里丟數(shù)據(jù)寫入任務(wù)在空閑時批量取數(shù)據(jù)、批量寫SD卡。這樣SD卡寫入再怎么慢都不影響采集時序。從實操上看我常用的一個判斷公式是緩沖區(qū)大小字節(jié) / 采樣速率字節(jié)每秒 最長可容忍寫入阻塞時間秒只要這個值小于SD卡最壞寫入耗時一定會出問題。你把這個值算出來很多“只記錄幾分鐘”的謎團當場就解開了。3. 電源與電氣問題SD卡在關(guān)鍵時刻“掉線”的真實原因3.1 電流尖峰與電壓跌落數(shù)據(jù)量越大越明顯SD卡寫入時電流不是恒定的峰值電流出現(xiàn)在擦除和編程階段特別是在寫滿一個塊、需要搬移數(shù)據(jù)的時候。瞬時電流可以達到100mA甚至更高如果供電鏈路阻抗大電壓會被瞬間拉低。MCU數(shù)據(jù)記錄設(shè)備大多是3.3V供電很多直接用AMS1117之類的LDO從5V或鋰電池降壓。LDO的響應(yīng)速度有限如果輸入電壓本身有波動或者輸出端沒有足夠的鉭電容/陶瓷電容做去耦SD卡寫入瞬間的電壓跌落很容易超過100mV。什么概念SD卡規(guī)范要求供電電壓范圍2.7V~3.6V。如果你的LDO輸出標稱3.3V紋波50mVSD卡寫入時再跌200mV就到了3.05V。看起來還在范圍內(nèi)但加上電源軌上的高頻噪聲就可能跌破2.7V的瞬間閾值卡就復(fù)位了。最要命的是這種復(fù)位不一定整個系統(tǒng)都復(fù)位。MCU可能在2.7V以上仍然正常工作但SD卡已經(jīng)悄悄重啟了。SD卡重啟后需要重新走一遍初始化流程而你的程序并不知道這件事繼續(xù)按原樣發(fā)寫命令SD卡不響應(yīng)程序超時、報錯、退出。退出后如果沒做好重試邏輯記錄就停了。外部表現(xiàn)就是日志寫了半小時然后沒了但LED還在閃MCU也沒死。3.2 線纜與卡座隱藏的串聯(lián)電阻我在實測試驗中發(fā)現(xiàn)很多“只在短時間記錄”的問題根源在卡座接觸電阻上。SD卡座的彈簧觸片用久了會氧化接觸電阻從幾十毫歐漲到幾百毫歐甚至1歐姆。如果設(shè)備還有振動接觸電阻還會間歇性跳變。當寫入電流達到100mA時1歐姆的接觸電阻會產(chǎn)生100mV壓降。再加上線纜、PCB走線的阻抗SD卡實際拿到的電壓比主控低得多。很多人習慣把SD卡電源直接接到主控3.3V網(wǎng)絡(luò)的末端中間經(jīng)過一段細長走線SD卡寫入時電壓被拉到閾值以下卡復(fù)位記錄中斷。用示波器一測寫得正歡的時候電源波形上有一個明顯的下陷問題當場現(xiàn)行。如果你的設(shè)備在野外跑還要考慮溫度。氧化層在低溫下阻抗更高冬天更容易出問題。所以我后來做記錄儀設(shè)計SD卡電源都會單獨走線并且在卡座旁邊加一個100uF的鉭電容 0.1uF陶瓷電容做局部去耦減小電源跌落幅度。3.3 SPI模式下的信號完整性與上拉電阻用SPI模式驅(qū)動SD卡信號完整性要求其實不低。SD卡SPI模式有四個信號SCK、MOSI、MISO、CS。每條線都需要上拉電阻通常10kΩ到100kΩ。但如果上拉電阻缺失或者走線太長信號上升沿變緩高速SPI下就會出現(xiàn)間歇性通信錯誤。特別典型的坑是CS信號沒拉好。如果CS懸空或上拉太弱SD卡可能進入未選中狀態(tài)然后所有寫命令都得不到響應(yīng)。有的程序在寫失敗后重試幾次然后又失敗最后直接關(guān)閉文件退出。這樣的問題在辦公室調(diào)試時可能一天都不出現(xiàn)到了現(xiàn)場因為線纜更長、環(huán)境電磁干擾更強就開始“記錄一段時間就斷”。我建議SPI時鐘先降到2MHz以下做穩(wěn)定性測試。如果在4MHz下不穩(wěn)定、2MHz下穩(wěn)定那基本就是信號完整性問題而不是SD卡本身的問題。通過降速來確定是不是電氣層面的問題非常直接有效。3.4 實測驗證方法示波器是唯一靠譜的裁判排查電源問題時不用跟我爭論理論直接測用示波器同時看SD卡VCC和GND用電流探頭或串聯(lián)采樣電阻看寫入電流讓設(shè)備按最高速率寫入數(shù)據(jù)持續(xù)跑幾分鐘到幾十分鐘觀察每次寫入時VCC的下陷幅度和恢復(fù)時間把數(shù)字萬用表設(shè)置成最小/最大記錄模式掛在VCC上跑一個完整的寫入循環(huán)。如果VCC下陷超過100mV或者觀測到低于2.7V的瞬態(tài)恭喜你找到根因了。如果波形很干凈電壓紋波小于30mV那再往邏輯層面查。有一類特殊情況SD卡和無線模塊共用電源。藍牙/WiFi/4G模塊發(fā)射瞬間電流可以達到幾百毫安甚至安培級直接把電壓拉下去。這種“記錄斷”往往和無線發(fā)送的時間點高度相關(guān)。你在日志里比對一下時間戳如果中斷時刻恰好和無線發(fā)送時間吻合那就不要懷疑SD卡了先給無線模塊單獨供電或者加電源時序控制。4. FAT文件系統(tǒng)邊界4GB/2GB的硬限制與簇表異常4.1 FAT16和FAT32的單文件大小上限很多人不知道SD卡日志記錄還有一個非常隱蔽的“定時炸彈”——FAT文件系統(tǒng)的單文件大小限制。FAT16格式的卷單文件最大2GBFAT32格式的卷單文件最大4GB超過之后f_write會返回錯誤大多數(shù)代碼庫不會正確處理這個錯誤程序就停了或者文件指針回繞覆蓋已有數(shù)據(jù)。如果設(shè)備采樣率不高2GB可能夠跑很久。但如果你做的是高采樣率記錄儀比如一個16位、4通道、10kHz的采集系統(tǒng)每秒就是80KB一小時約288MB。FAT32的4GB上限大約只能撐14個小時。FAT16的2GB上限更是撐不到7個小時。更麻煩的是很多SD卡出廠時格式化成FAT32但簇大小是64KB這種格式能支持大文件但小文件寫入性能極差跨簇邊界時頻繁更新FAT表寫入速度可能掉到幾KB/s。在長時間記錄場景下我的做法是不用單文件無限增長而是設(shè)計輪轉(zhuǎn)日志——數(shù)據(jù)寫到一定大小比如512MB就關(guān)閉文件新建一個帶序號的新文件在文件名里編碼時間戳比如LOG_20250101_120000.bin程序啟動時掃描已存在的日志文件按時間戳順序?qū)懕苊飧采w舊數(shù)據(jù)。這樣既繞開了單文件大小限制又方便事后按時間段快速定位數(shù)據(jù)。如果你非要單文件連續(xù)寫至少要先算清預(yù)計最大記錄時長并確保目標文件系統(tǒng)格式支持。4.2 簇表異常與斷電寫入FAT文件系統(tǒng)是日志型文件系統(tǒng)的反面教材它在寫數(shù)據(jù)時并不保證一致性。斷電時具體會發(fā)生什么程序請求寫4KB數(shù)據(jù)FatFs先更新FAT表標記數(shù)據(jù)要占用的簇。然后寫數(shù)據(jù)到簇對應(yīng)的扇區(qū)。最后更新目錄項更新文件大小。如果第2步寫完第3步?jīng)]執(zhí)行文件大小就沒更新數(shù)據(jù)雖然寫在卡里但文件系統(tǒng)不認。如果第2步剛寫完FAT表還沒來得及更新數(shù)據(jù)就是徹底的孤兒扇區(qū)目錄項還在舊狀態(tài)過一段時間會被新的寫入覆蓋掉。在電池供電的SD卡記錄設(shè)備里這是一個非常常見的“記錄了一段時間后文件損壞”的原因。解決辦法不是靠軟件能做到的只能從設(shè)計上減少暴露窗口增加一個掉電檢測電路比如比較器檢測電源電壓低于閾值時觸發(fā)MCU的緊急中斷把正在緩沖的數(shù)據(jù)和FAT表狀態(tài)刷到卡里然后安全關(guān)閉所有文件。如果沒有掉電檢測那就老老實實做輪轉(zhuǎn)文件并且控制每次寫入的數(shù)據(jù)量讓FAT表更新的頻率降低。注意不要頻繁調(diào)用f_sync或者f_flush。每寫一個扇區(qū)就flush一次雖然安全性提高但寫入速度和閃存壽命都會顯著下降。我一般設(shè)置一個合理的同步間隔比如每寫256KB同步一次或者每5秒同步一次根據(jù)數(shù)據(jù)的重要程度和系統(tǒng)掉電頻率來權(quán)衡。4.3 格式化工具與SD卡初始化細節(jié)標題下面帶了幾個熱搜詞“sd card formatter”“sd memory card formatter”。說明很多人在卡出問題后第一反應(yīng)是重新格式化。這方向沒錯但要注意格式化的參數(shù)。SD卡官方推薦的格式化工具會按照SD卡的實際能力設(shè)置默認格式和扇區(qū)大小比操作系統(tǒng)自帶的格式化更可靠。但有一點很重要不要在卡上建立多個分區(qū)SD卡日志記錄場景只用一個分區(qū)就夠了。多個分區(qū)在MCU環(huán)境下處理容易出問題部分FatFs移植代碼不支持多分區(qū)或者會默認訪問錯誤的分區(qū)號。另外有一個細節(jié)容易忽略SD卡可能存在“邏輯保護”卡上的撥桿Lock開關(guān)如果撥到鎖定位置寫入時卡會返回錯誤。這個開關(guān)是一個機械觸點在振動環(huán)境下可能松動導(dǎo)致寫一半突然變只讀。這種問題非常無厘頭但確實發(fā)生過。調(diào)試的時候先檢查這個開關(guān)別一上來就去改代碼。還有如果你用的是工業(yè)級或者三防SD卡它們有的支持“寫保護”命令即使沒有機械開關(guān)也可能拒絕寫入。這種卡拿回來先讀一下CSD寄存器的寫保護位省得后面排查半天。5. 完整排查鏈路從串口日志到波形測量的定位方法論很多朋友反映這些問題太難定位。其實不難按順序執(zhí)行下面這套排查鏈路大概率能在半天內(nèi)鎖定根因而不需要靠猜。5.1 第一步在日志里標記“最后存活時刻”先別急著改硬件先把你手頭的設(shè)備接上串口調(diào)試線打開所有調(diào)試輸出。在程序的關(guān)鍵節(jié)點加上日志輸出每次成功打開文件的時間每次f_write返回的字節(jié)數(shù)和耗時每次f_sync調(diào)用的返回碼初始化失敗、寫失敗、重試的次數(shù)。然后反復(fù)運行設(shè)備直到問題復(fù)現(xiàn)。注意看串口輸出的最后一條日志是什么。這一條日志會直接告訴你問題發(fā)生的上下文如果最后一條是“write timeout”說明卡沒響應(yīng)要查電源和信號如果最后一條是“file open failed”說明文件系統(tǒng)已經(jīng)掛了如果最后一條什么都不是串口也沒輸出那大概率是MCU死了或者進入異常狀態(tài)。這一步不需要任何額外工具但能砍掉一半的排查路徑。90%的人跳過了這一步直接去換卡純屬浪費生命。5.2 第二步跑一個寫入壓力測試隔離硬件問題如果串口日志看不出明確結(jié)論就做一次純粹的SD卡壓測。寫一段小程序不要采集任何傳感器數(shù)據(jù)直接全速往SD卡寫入固定大小的塊持續(xù)跑1小時同時用示波器監(jiān)視SD卡VCC和SCK線上的信號。如果壓測期間出問題說明問題在硬件鏈路或SD卡本身和你的業(yè)務(wù)邏輯無關(guān)。如果壓測穩(wěn)定說明問題出在數(shù)據(jù)采集/緩沖/任務(wù)調(diào)度層業(yè)務(wù)邏輯中的某個環(huán)節(jié)導(dǎo)致SD卡寫入被餓死或者被阻塞。注意壓測時寫入模式要對齊實際場景不要只測順序大塊寫。要復(fù)現(xiàn)你實際代碼里的寫入模式包括塊大小、寫入頻率、文件打開關(guān)閉頻率。不然壓測結(jié)果對定位沒有參考價值。5.3 第三步換卡交叉測試 格式化工具重建如果壓測期間出問題做交叉測試同一張卡換到另一臺設(shè)備上跑壓測另一張卡換到這臺設(shè)備上跑壓測新卡先跑官方SD卡格式化工具再跑壓測。注意SD卡速度和可靠性是兩個維度。當前市面上很多所謂“高速卡”在連續(xù)寫入中的表現(xiàn)反而不如一些老舊的工業(yè)卡。有些卡對SPI模式支持不好或者不支持部分CMD命令。買卡的時候別只盯著速度等級優(yōu)先選標注了“工業(yè)級”“寬溫”或者明確支持SPI模式的卡。如果你排查到這一步還沒結(jié)論可以從這幾個方向繼續(xù)檢查CSD寄存器確認SD卡容量和塊大小用邏輯分析儀抓SPI通信看命令響應(yīng)是否正常檢查文件系統(tǒng)格式化參數(shù)是否和代碼假設(shè)一致。5.4 第四步用示波器抓SD卡的“死亡瞬間”這是最后一步也是最硬核的一步。在問題復(fù)現(xiàn)時刻用示波器同時抓SD卡VCC對GND的波形SD卡MISO線上的波形主控IO口上一個標記信號比如寫失敗中斷時拉高。如果問題復(fù)現(xiàn)時VCC出現(xiàn)了明顯的跌落那電源問題實錘。如果VCC沒問題MISO線上出現(xiàn)異常電平或者長時間無響應(yīng)那就是SD卡狀態(tài)機跑飛了。如果一切波形都正常但寫程序就是失敗那就要檢查代碼中是不是有什么邏輯bug比如中斷優(yōu)先級搞亂了、看門狗誤觸發(fā)、緩沖區(qū)指針越界等。我個人的經(jīng)驗是80%的“只記錄短時間”問題能在第二步和第三步被鎖定。真正需要示波器出場的主要是電源紋波和信號完整性這兩類隱蔽問題。6. 架構(gòu)層面的根治方案讓記錄器穩(wěn)定運行數(shù)周的設(shè)計經(jīng)驗排查和修復(fù)不是終點。如果項目要長時間穩(wěn)定運行還是得在架構(gòu)上做減法。下面是我現(xiàn)在做SD卡記錄儀默認采用的方案按“抗故障優(yōu)先級”排列。6.1 雙緩沖 任務(wù)解耦讓采集和寫入徹底隔離無論你用不用RTOS都建議把采集和寫入拆成兩條執(zhí)行路徑。采集路徑只負責把數(shù)據(jù)丟進隊列寫入路徑只負責從隊列取數(shù)據(jù)、批量寫SD卡。兩條路徑之間用環(huán)形緩沖或消息隊列連接緩沖區(qū)大小按最壞情況計算。具體參數(shù)建議環(huán)形緩沖區(qū)大小至少是最大寫塊大小的4倍以上推薦8KB或更大寫入任務(wù)優(yōu)先級低于采集任務(wù)但高于普通計算任務(wù)隊列滿時策略是丟棄最舊的數(shù)據(jù)并在日志頭部做個標記比如丟了多少字節(jié)。這樣即使SD卡寫入卡頓幾百毫秒采集任務(wù)也不丟一點數(shù)據(jù)只是環(huán)形緩沖暫時積壓等寫入恢復(fù)后再慢慢消化。6.2 健康監(jiān)控與自動恢復(fù)不讓程序“死等”SD卡寫入失敗時代碼絕對不能死等重試。推薦實現(xiàn)一個“SD卡健康監(jiān)控”模塊檢測到連續(xù)N次寫入失敗比如10次就切換到“故障模式”在故障模式下停止寫入保留數(shù)據(jù)到RAM緩沖區(qū)或鐵電存儲器定期嘗試重新初始化SD卡發(fā)送CMD0、CMD1、CMD8、ACMD41等初始化命令成功后再恢復(fù)寫入如果重試超過閾值記錄錯誤狀態(tài)通過其他通道如無線上報故障。看門狗要獨立出來喂狗操作放在主循環(huán)里不要在SD卡寫入中斷里喂狗否則SD卡卡死時看門狗被錯誤喂掉系統(tǒng)永遠無法復(fù)位。6.3 輪轉(zhuǎn)日志 掉電檢測從根上繞開FAT表異常前面提過輪轉(zhuǎn)日志。這個設(shè)計不只是繞開單文件大小限制更大的價值在于故障隔離。一個文件壞了不影響其他文件。程序啟動時對所有文件做完整性檢查損壞的文件自動隔離新數(shù)據(jù)繼續(xù)寫入新文件。掉電檢測電路也很關(guān)鍵。簡單方案RESET引腳或ADC引腳接一個分壓電阻檢測主電源電壓。當電壓低于閾值時觸發(fā)中斷中斷里做兩件事關(guān)閉當前文件f_close會更新FAT表和目錄項把緩沖區(qū)里剩余的數(shù)據(jù)盡量寫出去如果時間允許。如果電壓跌得太快連寫操作都來不及完成那就只能靠輪轉(zhuǎn)日志兜底至少舊的日志不會受到太大破壞。6.4 選卡、格式化與使用習慣的終極大考最后給幾張卡的選擇和基本使用習慣做個建議項目建議理由卡的類型工業(yè)級、寬溫、MLC以上比消費級卡穩(wěn)定不容易在高溫/振動下掉速容量大小32GB以下大容量卡在FAT32格式化時簇大小文件寫入慢32GB以下兼容性最好文件系統(tǒng)FAT32簇大小16KB或32KB平衡寫入速度和空間浪費格式化工具官方SD Card Formatter能正確設(shè)置扇區(qū)和文件系統(tǒng)參數(shù)寫入模式批量順序?qū)懕苊忸l繁隨機小寫減少GC頻率降低寫入耗時的抖動數(shù)據(jù)備份定期導(dǎo)出日志格式化后重用卡長時間使用后GC碎片化速度會明顯下降個人實際測試中最穩(wěn)定的組合是32GB工業(yè)級microSD卡FAT32格式簇大小32KBSPI時鐘10MHz每次寫入2048字節(jié)或4096字節(jié)每寫入256KB做一次同步。這種配置下連續(xù)寫入速度大約300KB/s穩(wěn)定跑一個月沒出過問題。最后提醒一個常見誤區(qū)SD卡不是硬盤別拿它當隨機讀寫的存儲介質(zhì)用。它的強項是順序大塊寫。任何頻繁打開關(guān)閉文件、頻繁寫小數(shù)據(jù)量的設(shè)計都是在跟閃存管理邏輯對著干最終都會以“記錄中斷”或者“文件損壞”收場。