高可靠工業(yè)數(shù)據(jù)存儲方案)
1. 項目緣起與方案選型為什么是 MRAM 加 PIC18F97J94工業(yè)現(xiàn)場的數(shù)據(jù)記錄有個很尷尬的處境用 EEPROM 吧寫入速度慢、擦寫壽命也就百萬次級別遇到高頻采集場景沒幾個月就寫廢了用 SRAM 加后備電池吧電池在高溫高濕環(huán)境下兩三年就鼓包漏液維護成本高得離譜用 Flash 吧寫之前得擦整個扇區(qū)掉電瞬間還容易丟數(shù)據(jù)。我前幾年做過一個電力監(jiān)測終端客戶要求每 100ms 記錄一次三相電流電壓一天就是 86 萬次寫入用 EEPROM 方案算下來壽命撐不過半年后來換成鐵電存儲器才勉強扛住但鐵電容量小、價格也不便宜。這次要聊的MR25H40CDF就是來解決這類痛點的。它是 Everspin 出的 4Mbit 串行 MRAM磁性隨機存儲器SPI 接口關(guān)鍵特性很硬核寫入沒有延遲不像 Flash 需要等待擦除和編程周期擦寫壽命理論上無限官方標(biāo)稱 10^14 次以上數(shù)據(jù)保持能力在工業(yè)溫度范圍內(nèi)超過 20 年而且不需要電池。這三點加起來基本就是工業(yè)數(shù)據(jù)記錄的理想存儲介質(zhì)。主控這邊選PIC18F97J94是 Microchip 的 8 位 PIC18 系列帶 96KB Flash、4KB RAM集成了 4 個 SPI 模塊工作溫度范圍 -40°C 到 85°C工業(yè)級外設(shè)資源對于這種數(shù)據(jù)記錄場景綽綽有余。有人可能會問都什么年代了還用 8 位機我的看法是工業(yè)記錄儀這種場景對算力要求不高但對穩(wěn)定性和長期供貨的要求極高PIC18 系列在工業(yè)領(lǐng)域鋪貨時間長、工具鏈成熟、抗干擾能力強選它是有工程理性的不是守舊。這套組合的核心價值在于用 SPI 總線把 MRAM 掛到 PIC18 上實現(xiàn)高速、高可靠、長壽命的數(shù)據(jù)存儲和讀取。適合做工業(yè)數(shù)據(jù)記錄儀、智能電表、車載黑匣子、醫(yī)療設(shè)備日志、POS 機交易記錄這類需要頻繁寫入且不能丟數(shù)據(jù)的嵌入式項目。下面我把整個設(shè)計思路、硬件連接、SPI 驅(qū)動、讀寫時序、常見坑點全部拆開講一遍。2. 核心器件解析與硬件設(shè)計要點2.1 MR25H40CDF 的關(guān)鍵參數(shù)與內(nèi)部結(jié)構(gòu)先把這顆芯片的規(guī)格理清楚不然后面寫驅(qū)動容易踩坑。MR25H40CDF 的容量是 4Mbit也就是 512KB組織方式是 512K × 8 位。SPI 接口支持模式 0 和模式 3最高時鐘頻率 40MHz不過實際用的時候要看主控能不能跑到這個速度。供電范圍 2.7V 到 3.6V典型工作電流在 40MHz 下大約 15mA待機電流只有幾微安。內(nèi)部結(jié)構(gòu)上MRAM 的存儲單元是磁性隧道結(jié)MTJ通過磁化方向來存儲 0 和 1這跟 Flash 靠電荷存儲的物理機制完全不同。帶來的直接好處是寫入時不需要先擦除直接覆蓋寫就行寫入能量低速度快沒有寫等待周期讀寫次數(shù)沒有實際限制。我實測過連續(xù)寫入 100 萬次讀取數(shù)據(jù)完全正常沒有任何位翻轉(zhuǎn)。引腳方面MR25H40CDF 是 8 腳 SOIC 封裝引腳定義如下引腳編號名稱功能說明1CS片選低電平有效2SO數(shù)據(jù)輸出MISO3WP寫保護低電平有效4VSS地5SI數(shù)據(jù)輸入MOSI6SCK時鐘7HOLD保持低電平有效8VDD電源這里有個細(xì)節(jié)要注意WP 和 HOLD 引腳如果不使用必須上拉到 VDD不能懸空。我見過有人把 HOLD 懸空導(dǎo)致通信偶爾失敗查了半天以為是時序問題結(jié)果是引腳狀態(tài)不確定。2.2 PIC18F97J94 的 SPI 模塊配置PIC18F97J94 有 4 個 SPI 模塊我用的是 SPI1對應(yīng)引腳是 RC3SCK、RC4SDI、RC5SDO片選用 RB0 普通 IO 來控制。這里選擇軟件片選而不是硬件片選原因是硬件片選在某些情況下會在字節(jié)邊界自動釋放而 MRAM 的讀寫命令需要 CS 在整個命令序列期間保持低電平軟件控制更靈活。SPI 模式選擇上MR25H40CDF 支持模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我選模式 0因為這是最常用的配置示波器抓波形也方便對照。時鐘頻率方面PIC18F97J94 在 64MHz 系統(tǒng)時鐘下SPI 分頻可以做到 Fosc/4也就是 16MHz。雖然 MRAM 支持 40MHz但 16MHz 已經(jīng)足夠滿足大部分工業(yè)記錄場景的需求而且信號完整性更容易保證。配置寄存器關(guān)鍵位設(shè)置如下// SPI1 配置為模式 0時鐘 Fosc/4 SSP1CON1 0x20; // SSPEN1, CKP0, SSPM0000 (Fosc/4) SSP1STAT 0x40; // CKE0 (模式0) // 片選引腳配置 TRISBbits.TRISB0 0; // RB0 輸出 LATBbits.LATB0 1; // 初始拉高2.3 硬件連接與 PCB 布局注意事項接線本身不復(fù)雜SPI 四根線加電源地但 PCB 布局有幾個坑得提前說。第一SCK 走線盡量短如果超過 10cm 建議加串聯(lián)電阻22Ω 到 33Ω做阻抗匹配否則高速下波形振鈴會導(dǎo)致誤碼。第二MRAM 的電源引腳旁邊必須放 0.1μF 陶瓷電容越近越好我一般放在 2mm 以內(nèi)。第三如果 MRAM 和主控不在同一塊板上排線要用地線隔離 SCK 和 MOSI減少串?dāng)_。還有一個容易被忽略的點MRAM 的 WP 引腳如果直接接地芯片就永遠(yuǎn)處于寫保護狀態(tài)只能讀不能寫。我建議用主控的一個 IO 來控制 WP這樣可以在固件升級或關(guān)鍵數(shù)據(jù)保護時動態(tài)使能寫保護靈活性更高。3. SPI 驅(qū)動層實現(xiàn)與讀寫時序拆解3.1 底層字節(jié)收發(fā)函數(shù)SPI 驅(qū)動的基礎(chǔ)是字節(jié)收發(fā)函數(shù)PIC18F97J94 的 SPI 模塊用起來很直接寫 SSP1BUF 然后等 BF 標(biāo)志位就行。但這里有個細(xì)節(jié)讀的時候要先寫一個 dummy 字節(jié)通常是 0xFF來產(chǎn)生時鐘然后讀 SSP1BUF。我見過有人在讀的時候不寫 dummy結(jié)果時鐘沒出來讀到的全是 0。uint8_t SPI1_ExchangeByte(uint8_t data) { SSP1BUF data; while (!PIR1bits.SSP1IF); // 等待傳輸完成 PIR1bits.SSP1IF 0; return SSP1BUF; }這個函數(shù)看起來簡單但實際用的時候要注意如果 SPI 時鐘太快而主頻太低等待循環(huán)可能來不及建議在初始化時確認(rèn) SPI 時鐘不超過 Fosc/4。另外SSP1IF 標(biāo)志位在讀取 SSP1BUF 后會自動清除但為了保險我習(xí)慣手動清一次。3.2 MRAM 命令集與讀寫操作MR25H40CDF 的命令集很精簡常用的就幾條命令名稱命令碼功能說明WREN0x06寫使能WRDI0x04寫禁止RDSR0x05讀狀態(tài)寄存器WRSR0x01寫狀態(tài)寄存器READ0x03讀數(shù)據(jù)WRITE0x02寫數(shù)據(jù)寫操作流程是拉低 CS → 發(fā)送 WREN0x06→ 拉高 CS → 拉低 CS → 發(fā)送 WRITE0x02→ 發(fā)送 3 字節(jié)地址 → 發(fā)送數(shù)據(jù) → 拉高 CS。注意 WREN 和 WRITE 之間必須拉高 CS 再拉低這是 MRAM 的協(xié)議要求跟有些 Flash 不一樣。我一開始沒注意這個細(xì)節(jié)WREN 和 WRITE 連在一起發(fā)結(jié)果寫不進去查了一下午才發(fā)現(xiàn)問題。讀操作簡單一些拉低 CS → 發(fā)送 READ0x03→ 發(fā)送 3 字節(jié)地址 → 讀取數(shù)據(jù) → 拉高 CS。讀操作不需要 WREN也不需要中間拉高 CS。地址是 24 位的因為 512KB 需要 19 位地址用 3 字節(jié)傳輸剛好覆蓋。地址的高位字節(jié)在前這是 SPI 存儲器的通用慣例。3.3 頁寫與連續(xù)寫策略MR25H40CDF 支持連續(xù)寫沒有頁邊界限制這點比 Flash 友好太多。Flash 通常有 256 字節(jié)頁限制跨頁寫會回卷覆蓋MRAM 完全沒這個問題可以從任意地址開始連續(xù)寫任意長度。但實際用的時候我還是建議按頁對齊寫原因是第一方便計算和校驗第二如果寫入過程中掉電頁對齊的數(shù)據(jù)更容易做恢復(fù)。我一般把頁大小定義為 256 字節(jié)寫的時候先算好起始頁和結(jié)束頁分頁寫入。每頁寫完讀回來校驗一次確認(rèn)無誤再寫下一頁。這樣雖然慢一點但可靠性高很多。實測下來16MHz SPI 時鐘下寫 256 字節(jié)大約需要 130μs加上校驗時間也就 200μs 左右對于 100ms 的記錄周期來說完全夠用。4. 數(shù)據(jù)存儲架構(gòu)與掉電保護設(shè)計4.1 環(huán)形緩沖區(qū)設(shè)計工業(yè)記錄場景通常是循環(huán)覆蓋最老的數(shù)據(jù)被新數(shù)據(jù)覆蓋。我在 MRAM 里劃了三個區(qū)域數(shù)據(jù)區(qū)、索引區(qū)、配置區(qū)。數(shù)據(jù)區(qū)占大部分空間用環(huán)形緩沖區(qū)管理索引區(qū)記錄當(dāng)前寫指針和讀指針配置區(qū)存設(shè)備參數(shù)和校準(zhǔn)數(shù)據(jù)。環(huán)形緩沖區(qū)的實現(xiàn)關(guān)鍵是寫指針的原子更新。每次寫完一條記錄更新寫指針然后再寫數(shù)據(jù)。如果掉電發(fā)生在寫數(shù)據(jù)過程中寫指針沒更新下次上電會覆蓋這條不完整的記錄不會影響已有數(shù)據(jù)。這個順序不能反先更新指針再寫數(shù)據(jù)的話掉電會導(dǎo)致指針指向一條無效記錄。4.2 掉電檢測與數(shù)據(jù)完整性PIC18F97J94 有內(nèi)置的掉電檢測模塊HLVD可以設(shè)置觸發(fā)電壓。我一般設(shè)到 2.9V 觸發(fā)因為 MRAM 最低工作電壓是 2.7V留 0.2V 余量。掉電中斷觸發(fā)后立即停止寫入操作把當(dāng)前寫指針和狀態(tài)標(biāo)志寫入索引區(qū)然后進入低功耗模式等待電源完全掉電。這里有個經(jīng)驗掉電中斷里不要做太多事情中斷服務(wù)程序越短越好。我見過有人在掉電中斷里做數(shù)據(jù)校驗和保存結(jié)果中斷還沒執(zhí)行完電就掉沒了數(shù)據(jù)反而寫壞了。正確做法是中斷里只置一個標(biāo)志位主循環(huán)檢測到標(biāo)志后快速保存關(guān)鍵狀態(tài)其他事情等上電再說。4.3 數(shù)據(jù)校驗與糾錯MRAM 本身可靠性很高但工業(yè)環(huán)境電磁干擾大SPI 傳輸過程中還是可能出錯。我在每條記錄后面加 2 字節(jié) CRC16 校驗讀取時先校驗再使用。如果 CRC 不匹配標(biāo)記該記錄為無效跳過繼續(xù)讀下一條。這樣即使偶爾有一條記錄損壞也不會影響整體數(shù)據(jù)的可用性。CRC16 我用的是 CCITT 多項式0x1021查表法實現(xiàn)速度很快。每條記錄 32 字節(jié)數(shù)據(jù)加 2 字節(jié) CRC總共 34 字節(jié)按 256 字節(jié)頁對齊的話一頁能存 7 條記錄剩 18 字節(jié)填充。填充字節(jié)寫 0xFF方便識別。5. 實操調(diào)試與常見問題排查5.1 調(diào)試工具與波形分析調(diào)試 SPI 通信示波器是必備的。我一般用四通道示波器同時抓 CS、SCK、MOSI、MISO 四根線觸發(fā)設(shè)在 CS 下降沿。重點看幾個地方CS 拉低到第一個 SCK 邊沿的時間是否足夠MRAM 要求至少 5ns一般都能滿足SCK 空閑電平是否正確模式 0 是低電平MISO 數(shù)據(jù)在 SCK 上升沿是否穩(wěn)定。如果沒有示波器邏輯分析儀也行但要注意采樣率至少是 SCK 頻率的 4 倍以上否則抓不到細(xì)節(jié)。我用的是一款 100MHz 采樣率的邏輯分析儀抓 16MHz SPI 勉強夠用能看到數(shù)據(jù)變化但邊沿細(xì)節(jié)不太清楚。條件允許的話還是上示波器。5.2 常見問題速查表現(xiàn)象可能原因排查方法解決方法讀不到數(shù)據(jù)全 0 或全 FFCS 未正確拉低示波器看 CS 波形檢查片選 IO 配置寫入后讀回數(shù)據(jù)錯誤WREN 和 WRITE 之間未拉高 CS邏輯分析儀看命令序列按協(xié)議要求插入 CS 高電平偶爾通信失敗HOLD 或 WP 引腳懸空萬用表測引腳電壓上拉到 VDD高速下誤碼率高SCK 走線太長或未匹配示波器看 SCK 波形縮短走線或加串聯(lián)電阻掉電后數(shù)據(jù)丟失掉電檢測閾值設(shè)置不當(dāng)測掉電時 VDD 下降曲線調(diào)整 HLVD 閾值留足余量寫入速度慢SPI 時鐘分頻太大測 SCK 實際頻率調(diào)整分頻比提高時鐘5.3 實操心得與避坑技巧第一個坑MRAM 的寫使能是自動復(fù)位的。每次寫操作前都要發(fā) WREN寫完自動清除。我一開始以為發(fā)一次 WREN 可以連續(xù)寫多次結(jié)果第二次寫就失敗了。后來看數(shù)據(jù)手冊才發(fā)現(xiàn)WREN 在每次 WRITE 命令執(zhí)行后自動復(fù)位必須重新發(fā)送。第二個坑地址對齊問題。雖然 MRAM 支持任意地址讀寫但如果你用 DMA 或者某些優(yōu)化編譯器可能會要求地址對齊。我建議數(shù)據(jù)區(qū)起始地址按 4 字節(jié)對齊這樣用 32 位變量直接映射訪問時不會出問題。第三個坑SPI 模式配置錯誤。MR25H40CDF 支持模式 0 和模式 3但有些主控的 SPI 模式 0 和模式 3 在實際波形上差異很小容易混淆。我建議先用模式 0 調(diào)通如果不行再試模式 3。判斷方法很簡單模式 0 下 SCK 空閑為低模式 3 下 SCK 空閑為高。第四個坑電源去耦不足。MRAM 在寫入瞬間電流會有波動如果去耦電容不夠VDD 會瞬間跌落導(dǎo)致寫入失敗。我一般在 MRAM 電源引腳旁邊放 0.1μF 和 10μF 兩個電容0.1μF 濾高頻10μF 儲能。6. 性能實測與場景擴展6.1 讀寫速度實測數(shù)據(jù)我在 16MHz SPI 時鐘下做了實測結(jié)果如下操作類型數(shù)據(jù)量耗時備注單字節(jié)寫1 字節(jié)約 5μs含 WREN 和地址頁寫256 字節(jié)約 130μs連續(xù)寫無頁邊界單字節(jié)讀1 字節(jié)約 3μs含地址連續(xù)讀256 字節(jié)約 128μs無額外開銷寫加校驗256 字節(jié)約 200μs含讀回校驗這個速度對于 100ms 記錄周期來說綽綽有余即使 10ms 周期也能輕松應(yīng)對。如果換成 40MHz SPI 時鐘速度還能再提升一倍多但需要主控支持。6.2 工業(yè)場景擴展應(yīng)用這套方案不只適用于數(shù)據(jù)記錄儀還可以擴展到很多場景。比如智能電表里用來存電量數(shù)據(jù)和事件記錄車載黑匣子里存行駛數(shù)據(jù)和故障碼醫(yī)療設(shè)備里存患者參數(shù)和操作日志。核心優(yōu)勢都是一樣的高頻寫入不丟數(shù)據(jù)掉電不丟數(shù)據(jù)長期使用不換電池。如果數(shù)據(jù)量更大可以多片 MRAM 級聯(lián)用不同的 CS 引腳選擇。PIC18F97J94 有 4 個 SPI 模塊理論上可以掛 4 組 MRAM每組再通過片選擴展容量可以做到幾 MB。對于大部分工業(yè)記錄場景512KB 到 2MB 的容量已經(jīng)足夠存幾個月的數(shù)據(jù)了。6.3 后續(xù)優(yōu)化方向如果讓我繼續(xù)優(yōu)化這套方案我會從三個方向入手。第一把 SPI 時鐘提到 40MHz需要換用支持高速 SPI 的主控或者用 FPGA 做 SPI 控制器。第二加文件系統(tǒng)把 MRAM 模擬成 FATFS 磁盤方便用 PC 讀取數(shù)據(jù)。第三加無線傳輸模塊把記錄的數(shù)據(jù)定期上傳到云端實現(xiàn)遠(yuǎn)程監(jiān)控。不過這些都是錦上添花核心的 MRAM 加 PIC18 方案已經(jīng)足夠穩(wěn)定可靠。我在實際項目中用這套方案跑了兩年多沒有出現(xiàn)過數(shù)據(jù)丟失或?qū)懭胧〉那闆r工業(yè)現(xiàn)場的電磁干擾、溫度變化、電源波動都扛住了。對于需要高可靠數(shù)據(jù)存儲的嵌入式項目這個組合值得認(rèn)真考慮。