嵌入式存儲(chǔ)方案:MR25H40CDF與PIC18LF47K42驅(qū)動(dòng)實(shí)戰(zhàn))
1. 為什么 MRAM 在工業(yè)嵌入式場(chǎng)景里越來越香搞過工業(yè)設(shè)備數(shù)據(jù)采集的朋友應(yīng)該都有過這種體驗(yàn)設(shè)備跑在現(xiàn)場(chǎng)環(huán)境溫度動(dòng)輒七八十度偶爾還要斷電重啟結(jié)果回頭一看關(guān)鍵參數(shù)丟了或者存儲(chǔ)芯片的某個(gè)扇區(qū)寫壞了。傳統(tǒng)的 EEPROM 擦寫壽命有限SPI NOR Flash 雖然容量大但寫入速度慢、擦除塊大鐵電存儲(chǔ)器 FRAM 容量又太小。這幾年我一直在找一種能兼顧非易失、高速寫入、近乎無限擦寫、寬溫工作的存儲(chǔ)介質(zhì)直到把 Everspin 的 MR25H40CDF 這顆 MRAM 用進(jìn)項(xiàng)目里才算真正把這個(gè)問題按住了。MR25H40CDF 是一顆 4Mbit512KB的串行 MRAM走標(biāo)準(zhǔn) SPI 接口最高時(shí)鐘能跑到 40MHz。它最吸引我的地方在于寫入不需要擦除、字節(jié)級(jí)隨機(jī)寫入、擦寫壽命號(hào)稱 10^14 次以上、數(shù)據(jù)保持 20 年以上而且工業(yè)級(jí)溫度范圍覆蓋 -40℃ 到 85℃部分批次到 105℃。這些特性放在工業(yè)現(xiàn)場(chǎng)儀表、電力監(jiān)測(cè)終端、醫(yī)療設(shè)備日志記錄、汽車電子標(biāo)定參數(shù)存儲(chǔ)這些場(chǎng)景里幾乎是量身定做的。而 PIC18LF47K42 是 Microchip 的一款 8 位單片機(jī)帶增強(qiáng)型 SPI 外設(shè)低功耗版本工作電壓 1.8V 到 5.5V內(nèi)置 128KB Flash、8KB RAM外設(shè)資源豐富特別適合做小型工業(yè)控制器的主控。把這兩顆芯片搭在一起就是一套低功耗主控 高可靠非易失存儲(chǔ)的經(jīng)典組合。這篇文章我會(huì)從方案選型、硬件連接、SPI 時(shí)序配置、讀寫驅(qū)動(dòng)實(shí)現(xiàn)、掉電保護(hù)策略、常見問題排查幾個(gè)維度把整套方案完整拆一遍。不管你是剛接觸 SPI 存儲(chǔ)的新手還是已經(jīng)在用 Flash/EEPROM 想換 MRAM 的老手都能從里面找到可以直接抄作業(yè)的東西。2. 方案整體設(shè)計(jì)與選型思路拆解2.1 為什么是 MRAM 而不是 EEPROM 或 NOR Flash先把這個(gè)為什么講透因?yàn)檫x型選錯(cuò)了后面代碼寫得再漂亮也是白搭。我做過一個(gè)對(duì)比把三種常見非易失存儲(chǔ)在工業(yè)場(chǎng)景下的關(guān)鍵指標(biāo)拉出來指標(biāo)EEPROM如 24LC512SPI NOR Flash如 W25Q64MRAMMR25H40CDF寫入方式字節(jié)寫需等待按扇區(qū)擦除后寫字節(jié)隨機(jī)寫無需擦除擦寫壽命約 100 萬次約 10 萬次10^14 次以上寫入速度慢ms 級(jí)擦除慢幾十 ms快無擦除延遲容量小KB 級(jí)大MB 級(jí)中512KB寬溫表現(xiàn)一般一般優(yōu)秀掉電風(fēng)險(xiǎn)寫入窗口長擦除窗口極長寫入窗口極短從表里能看出來MRAM 的核心優(yōu)勢(shì)就是寫入快 壽命長 無需擦除。工業(yè)設(shè)備里最怕的就是寫一半掉電Flash 擦除一個(gè)扇區(qū)要幾十毫秒這期間斷電數(shù)據(jù)就廢了MRAM 寫一個(gè)字節(jié)是納秒級(jí)完成掉電窗口極小配合合理的寫入策略基本不會(huì)丟數(shù)據(jù)。當(dāng)然 MRAM 也有短板容量比 NOR Flash 小單位成本高。所以我的選型原則是——關(guān)鍵參數(shù)、頻繁寫入的日志、掉電必須保住的數(shù)據(jù)放 MRAM大塊固件、圖片、字庫這類只讀或極少寫的數(shù)據(jù)放 Flash。兩者分工各取所長。2.2 PIC18LF47K42 的 SPI 外設(shè)能力評(píng)估PIC18LF47K42 內(nèi)部集成了獨(dú)立的 SPI 模塊MSSP支持主機(jī)/從機(jī)模式時(shí)鐘極性CKP和時(shí)鐘邊沿CKE可配置能覆蓋 SPI Mode 0~3 全部四種模式。這一點(diǎn)很關(guān)鍵因?yàn)?MR25H40CDF 支持 Mode 0 和 Mode 3主控必須能匹配。這顆片子我選它的另一個(gè)原因是低功耗。LF 版本工作電壓低至 1.8V配合 MRAM 的 2.7V~3.6V 供電整體系統(tǒng)在電池供電的現(xiàn)場(chǎng)儀表里能撐很久。它的 SPI 時(shí)鐘源來自系統(tǒng)時(shí)鐘分頻Fosc 用 64MHz 內(nèi)部振蕩器時(shí)SPI 可以分到 16MHz 甚至更高跑 MR25H40CDF 的 40MHz 上限綽綽有余實(shí)際我一般跑 10~20MHz留時(shí)序余量。還有一點(diǎn)PIC18LF47K42 的引腳復(fù)用靈活SPI 的 SCK/SDI/SDO 可以映射到多個(gè)引腳組PCB 布線時(shí)不用被引腳位置卡死。這個(gè)細(xì)節(jié)在緊湊型工業(yè)模塊設(shè)計(jì)里非常實(shí)用。2.3 整體架構(gòu)與數(shù)據(jù)分區(qū)規(guī)劃整套方案的架構(gòu)其實(shí)很清晰PIC18LF47K42 作為 SPI 主機(jī)MR25H40CDF 作為從機(jī)通過四線 SPISCK、SDI、SDO、CS通信。MRAM 的 512KB 地址空間我一般這樣分區(qū)0x00000 ~ 0x000FF設(shè)備信息區(qū)存序列號(hào)、固件版本、出廠標(biāo)定參數(shù)0x00100 ~ 0x00FFF運(yùn)行參數(shù)區(qū)存用戶配置、閾值、校準(zhǔn)系數(shù)0x01000 ~ 0x7FFFF日志/數(shù)據(jù)記錄區(qū)循環(huán)寫入配合磨損均衡雖然 MRAM 不太需要但邏輯上做環(huán)形緩沖更清晰分區(qū)的好處是不同區(qū)域?qū)懭腩l率不同出問題時(shí)定位快固件升級(jí)或參數(shù)復(fù)位時(shí)可以只擦除MRAM 其實(shí)是覆蓋寫某個(gè)區(qū)域不影響其他數(shù)據(jù)。提示MRAM 雖然沒有擦除概念但覆蓋寫依然要注意寫入順序。對(duì)于多字節(jié)的結(jié)構(gòu)體建議先寫數(shù)據(jù)體最后寫一個(gè)有效標(biāo)志字節(jié)這樣掉電時(shí)可以通過標(biāo)志判斷數(shù)據(jù)是否完整。3. 硬件連接與 SPI 時(shí)序配置細(xì)節(jié)3.1 硬件連線與去耦要點(diǎn)MR25H40CDF 是 8 腳 SOIC 封裝引腳定義很標(biāo)準(zhǔn)引腳 1CS片選低有效引腳 2SO數(shù)據(jù)輸出接主控 SDI引腳 3WP寫保護(hù)低有效不用時(shí)接高引腳 4VSS地引腳 5SI數(shù)據(jù)輸入接主控 SDO引腳 6SCK時(shí)鐘引腳 7HOLD保持低有效不用時(shí)接高引腳 8VDD電源2.7V~3.6V連線本身不復(fù)雜但有幾個(gè)坑我踩過第一CS 一定要用硬件片選。我早期圖省事用普通 GPIO 軟件拉片選結(jié)果在高速時(shí)鐘下 CS 建立/保持時(shí)間不夠偶爾讀到錯(cuò)數(shù)據(jù)。后來改用 MSSP 的硬件 CS 功能或者至少保證 CS 翻轉(zhuǎn)和 SCK 之間有足夠延時(shí)問題就沒了。軟件片選不是不能用但時(shí)序要自己算清楚。第二去耦電容必須靠近芯片電源腳。MRAM 在寫入瞬間電流會(huì)有波動(dòng)我一般放一個(gè) 0.1uF 陶瓷電容緊貼 VDD-VSS再并一個(gè) 1uF 鉭電容做儲(chǔ)能。工業(yè)現(xiàn)場(chǎng)電源紋波大這一步能顯著降低誤寫概率。第三WP 和 HOLD 不要懸空。懸空引腳在電磁干擾強(qiáng)的現(xiàn)場(chǎng)會(huì)亂跳導(dǎo)致意外寫保護(hù)或通信中斷。不用就老老實(shí)實(shí)上拉到 VDD。3.2 SPI 模式選擇與時(shí)序參數(shù)計(jì)算MR25H40CDF 支持 SPI Mode 0CPOL0, CPHA0和 Mode 3CPOL1, CPHA1。我一般選 Mode 0因?yàn)榇蠖鄶?shù)主控默認(rèn)就是這個(gè)模式調(diào)試起來直觀。在 PIC18LF47K42 上配置 Mode 0 的關(guān)鍵寄存器位CKP時(shí)鐘極性 0空閑時(shí) SCK 為低CKE時(shí)鐘邊沿 1數(shù)據(jù)在 SCK 上升沿采樣下降沿變化SMP采樣相位 0在數(shù)據(jù)輸出末尾采樣時(shí)鐘頻率的計(jì)算假設(shè)系統(tǒng)時(shí)鐘 Fosc 64MHzSPI 分頻系數(shù) SSPxADD 決定時(shí)鐘。SPI 時(shí)鐘 Fosc / (4 * (SSPxADD 1))。要得到 16MHzSSPxADD 0要得到 8MHzSSPxADD 1。我實(shí)測(cè) 16MHz 下 MRAM 讀寫穩(wěn)定但如果你 PCB 走線較長超過 10cm建議降到 8MHz 甚至 4MHz給信號(hào)完整性留余量。注意SPI 時(shí)鐘不是越高越好。MRAM 標(biāo)稱 40MHz但那是理想條件下的芯片能力。實(shí)際系統(tǒng)里受 PCB 寄生電容、走線阻抗、主控驅(qū)動(dòng)能力影響盲目追高只會(huì)帶來偶發(fā)錯(cuò)誤。我一般先用低速調(diào)通再逐步提速找穩(wěn)定上限。3.3 上電初始化順序上電順序也有講究。我的做法是主控上電配置系統(tǒng)時(shí)鐘等待穩(wěn)定配置 SPI 引腳為數(shù)字模式PIC18 的模擬/數(shù)字引腳復(fù)用要關(guān)掉模擬功能初始化 MSSP 模塊設(shè)置 Mode 0、時(shí)鐘分頻拉高 CS給 MRAM 至少 1ms 上電穩(wěn)定時(shí)間發(fā)送 WREN寫使能前先讀一次狀態(tài)寄存器確認(rèn)通信正常第 5 步很多人會(huì)跳過但這是驗(yàn)證 SPI 鏈路是否通的最快方法。讀狀態(tài)寄存器返回 0x00 或 0xFF 通常說明通信有問題比如 CS 沒接對(duì)、時(shí)鐘模式錯(cuò)了。4. 讀寫驅(qū)動(dòng)實(shí)現(xiàn)與核心代碼拆解4.1 MR25H40CDF 指令集梳理這顆 MRAM 的指令集和標(biāo)準(zhǔn) SPI Flash 很像但少了擦除指令多了些細(xì)節(jié)。常用指令如下指令名稱指令碼功能說明WREN0x06寫使能寫操作前必須發(fā)WRDI0x04寫禁止RDSR0x05讀狀態(tài)寄存器WRSR0x01寫狀態(tài)寄存器READ0x03讀數(shù)據(jù)WRITE0x02寫數(shù)據(jù)RDID0x9F讀器件 ID關(guān)鍵點(diǎn)每次寫操作前都要發(fā) WREN寫完自動(dòng)清除寫使能。這個(gè)和 EEPROM 類似但比 Flash 簡單Flash 還要發(fā)擦除指令、等忙。RDID 返回的器件 ID 可以用來確認(rèn)芯片型號(hào)和通信是否正常。MR25H40CDF 的 ID 是 0x18廠商碼 Everspin 是 0x18 開頭讀出來對(duì)不上就說明鏈路有問題。4.2 底層字節(jié)讀寫函數(shù)實(shí)現(xiàn)先看最底層的兩個(gè)函數(shù)這是所有上層操作的基礎(chǔ)。我用的是 MCC 生成的 SPI 庫但為了講清楚原理這里給出寄存器級(jí)的實(shí)現(xiàn)思路。// 寫一個(gè)字節(jié)到 SPI 總線返回同時(shí)讀到的字節(jié) uint8_t SPI_ExchangeByte(uint8_t data) { SSP1BUF data; // 寫入發(fā)送緩沖 while (!SSP1STATbits.BF); // 等待接收完成 return SSP1BUF; // 返回接收到的數(shù)據(jù) } // 讀 MRAM 指定地址的一個(gè)字節(jié) uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t result; CS_LOW(); // 片選拉低 SPI_ExchangeByte(0x03); // 發(fā)送 READ 指令 SPI_ExchangeByte((addr 16) 0xFF); // 地址高字節(jié) SPI_ExchangeByte((addr 8) 0xFF); // 地址中字節(jié) SPI_ExchangeByte(addr 0xFF); // 地址低字節(jié) result SPI_ExchangeByte(0xFF); // 讀數(shù)據(jù)發(fā) dummy CS_HIGH(); // 片選拉高 return result; } // 寫 MRAM 指定地址的一個(gè)字節(jié) void MRAM_WriteByte(uint32_t addr, uint8_t data) { CS_LOW(); SPI_ExchangeByte(0x06); // WREN 寫使能 CS_HIGH(); CS_LOW(); SPI_ExchangeByte(0x02); // WRITE 指令 SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); SPI_ExchangeByte(data); // 寫入數(shù)據(jù) CS_HIGH(); // MRAM 無需等待忙寫入即時(shí)完成 }這里有個(gè)細(xì)節(jié)值得說WREN 和 WRITE 之間必須拉一次 CS。因?yàn)?WREN 是一個(gè)獨(dú)立指令它需要在 CS 下降沿開始、上升沿結(jié)束。如果 WREN 和 WRITE 在同一個(gè) CS 低電平周期里連著發(fā)芯片不會(huì)識(shí)別。這個(gè)坑我第一次用的時(shí)候踩了寫進(jìn)去全是 0xFF查了半天才發(fā)現(xiàn)是 CS 沒分開。4.3 頁寫與連續(xù)讀的實(shí)現(xiàn)單字節(jié)讀寫效率太低實(shí)際項(xiàng)目里都是批量操作。MR25H40CDF 支持連續(xù)讀地址自動(dòng)遞增跨頁也不回卷和頁寫每頁 256 字節(jié)寫跨頁會(huì)回卷到頁首這點(diǎn)和 Flash 一樣要注意。連續(xù)讀很簡單READ 指令后一直發(fā) dummy 時(shí)鐘數(shù)據(jù)就源源不斷出來地址自動(dòng)加一直到 CS 拉高。這個(gè)特性用來讀日志區(qū)特別方便一次 CS 周期能讀幾百字節(jié)。頁寫要小心如果寫入的起始地址不是頁對(duì)齊且寫入長度超過頁邊界多出來的數(shù)據(jù)會(huì)回卷到本頁開頭覆蓋前面的數(shù)據(jù)。我的做法是寫之前先算好頁邊界跨頁就拆成多次寫。// 跨頁安全的批量寫 void MRAM_WriteBuffer(uint32_t addr, uint8_t *buf, uint16_t len) { while (len 0) { uint16_t page_remain 256 - (addr 0xFF); // 本頁剩余空間 uint16_t write_len (len page_remain) ? len : page_remain; CS_LOW(); SPI_ExchangeByte(0x06); // WREN CS_HIGH(); CS_LOW(); SPI_ExchangeByte(0x02); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i write_len; i) { SPI_ExchangeByte(buf[i]); } CS_HIGH(); addr write_len; buf write_len; len - write_len; } }這段代碼的核心就是那個(gè)page_remain計(jì)算保證每次寫入不跨頁。雖然 MRAM 寫入快但跨頁回卷這個(gè)行為是硬件決定的代碼里必須防。4.4 狀態(tài)寄存器與寫保護(hù)配置MR25H40CDF 的狀態(tài)寄存器里有一位 BP0/BP1塊保護(hù)可以設(shè)置某些地址區(qū)域?yàn)橹蛔x。工業(yè)設(shè)備里我一般把設(shè)備信息區(qū)設(shè)為寫保護(hù)防止程序跑飛誤改標(biāo)定參數(shù)。配置方法發(fā) WRSR 指令0x01后面跟狀態(tài)字節(jié)。比如要保護(hù)高 1/4 區(qū)域就設(shè)置對(duì)應(yīng)的 BP 位。具體位定義要查數(shù)據(jù)手冊(cè)不同批次可能略有差異我一般直接讀出來改位再寫回避免硬編碼出錯(cuò)。提示寫狀態(tài)寄存器前也要發(fā) WREN。而且狀態(tài)寄存器寫入后建議讀回來確認(rèn)一次工業(yè)現(xiàn)場(chǎng)干擾大寫配置這種低頻操作多校驗(yàn)一次不虧。5. 掉電保護(hù)與數(shù)據(jù)完整性策略5.1 為什么 MRAM 也需要掉電保護(hù)有人會(huì)問MRAM 寫入是納秒級(jí)完成的還需要掉電保護(hù)嗎答案是——芯片層面不需要但系統(tǒng)層面需要。問題出在多字節(jié)結(jié)構(gòu)體上。假設(shè)你要存一個(gè) 20 字節(jié)的配置結(jié)構(gòu)體MRAM 寫每個(gè)字節(jié)都很快但 20 個(gè)字節(jié)是分 20 次 SPI 傳輸完成的整個(gè)過程可能持續(xù)幾十微秒。如果在這期間掉電就會(huì)寫進(jìn)去一半新數(shù)據(jù)、一半舊數(shù)據(jù)結(jié)構(gòu)體就損壞了。所以掉電保護(hù)的核心不是保護(hù)單個(gè)字節(jié)而是保證一組相關(guān)數(shù)據(jù)的原子性。5.2 雙備份 校驗(yàn)和的實(shí)現(xiàn)我的做法是雙備份 校驗(yàn)和 有效標(biāo)志三件套同一份數(shù)據(jù)存兩個(gè)副本A 區(qū)和 B 區(qū)每個(gè)副本帶一個(gè) CRC16 校驗(yàn)和每個(gè)副本帶一個(gè)遞增的序列號(hào)寫入時(shí)先寫副本 A數(shù)據(jù) CRC 序列號(hào)再寫副本 B。讀取時(shí)比較 A 和 B 的序列號(hào)取序列號(hào)大的那個(gè)如果某個(gè)副本 CRC 校驗(yàn)失敗就用另一個(gè)。這樣即使寫入過程中掉電最多只有一個(gè)副本損壞另一個(gè)還是好的。序列號(hào)保證能識(shí)別出哪個(gè)是最新數(shù)據(jù)。typedef struct { uint16_t seq; // 序列號(hào) uint8_t data[16]; // 實(shí)際數(shù)據(jù) uint16_t crc; // 校驗(yàn)和 } DataRecord; void SaveConfig(uint8_t *cfg) { static uint16_t seq 0; DataRecord rec; rec.seq seq; memcpy(rec.data, cfg, 16); rec.crc CRC16(rec.data, 16); // 交替寫入 A/B 區(qū) uint32_t addr (seq 1) ? ADDR_A : ADDR_B; MRAM_WriteBuffer(addr, (uint8_t*)rec, sizeof(rec)); }這個(gè)策略我在電力監(jiān)測(cè)終端上跑了兩年多現(xiàn)場(chǎng)斷電無數(shù)次配置數(shù)據(jù)一次沒丟過。5.3 環(huán)形日志緩沖的設(shè)計(jì)日志記錄是另一個(gè)高頻寫入場(chǎng)景。我用環(huán)形緩沖日志區(qū)從 0x01000 開始每條記錄固定長度比如 32 字節(jié)寫滿一圈就從頭覆蓋。關(guān)鍵是要有一個(gè)寫指針存在固定位置每次寫日志前先更新指針。指針本身也要做雙備份否則指針丟了整個(gè)日志區(qū)就亂了。環(huán)形緩沖的好處是寫入地址連續(xù)SPI 傳輸效率高壞處是覆蓋舊數(shù)據(jù)所以要根據(jù)日志產(chǎn)生速率和存儲(chǔ)容量算好能存多久。512KB 減去前面的參數(shù)區(qū)大概有 480KB 給日志每條 32 字節(jié)能存 15000 條左右按每分鐘一條算能存 10 天夠大多數(shù)現(xiàn)場(chǎng)設(shè)備用了。6. 常見問題排查與實(shí)操避坑指南6.1 通信失敗類問題速查現(xiàn)象可能原因排查方法讀回全 0xFFCS 未拉低 / 時(shí)鐘模式錯(cuò)示波器看 CS 和 SCK 波形讀回全 0x00MISO 未接 / 引腳配置錯(cuò)檢查 SDI 引腳方向和數(shù)字模式偶發(fā)數(shù)據(jù)錯(cuò)誤時(shí)鐘太快 / 走線太長降速到 4MHz 測(cè)試寫入無效WREN 和 WRITE 未分開 CS檢查 CS 翻轉(zhuǎn)邏輯器件 ID 讀不對(duì)上電時(shí)間不足增加上電延時(shí)到 5ms這張表是我這幾年攢下來的基本覆蓋了 90% 的 SPI 存儲(chǔ)通信問題。遇到問題先按表排查比盲目改代碼快得多。6.2 時(shí)序問題的示波器診斷法SPI 問題最終都要落到波形上。我的診斷順序是先看 CS確認(rèn)每次操作 CS 有完整的下降沿和上升沿且低電平期間覆蓋整個(gè)指令數(shù)據(jù)再看 SCK確認(rèn)空閑電平符合所選模式Mode 0 空閑低時(shí)鐘數(shù)量對(duì)得上最后看數(shù)據(jù)線確認(rèn)數(shù)據(jù)在正確的時(shí)鐘邊沿穩(wěn)定我遇到過最詭異的一次是 CS 和 SCK 之間有幾十納秒的競(jìng)爭(zhēng)導(dǎo)致第一個(gè)時(shí)鐘沿采到錯(cuò)誤數(shù)據(jù)。后來在 CS 拉低后加了一個(gè)NOP延時(shí)才解決。這種問題不看波形根本找不到。6.3 工業(yè)現(xiàn)場(chǎng)的電磁干擾應(yīng)對(duì)工業(yè)現(xiàn)場(chǎng)變頻器、繼電器、電機(jī)一大堆SPI 這種高速信號(hào)很容易被干擾。我的應(yīng)對(duì)措施SPI 走線盡量短最好在同一層避免過孔SCK 和 CS 之間不要平行走長線減少串?dāng)_必要時(shí)在 SCK 上串一個(gè) 22~33 歐姆的電阻抑制振鈴MRAM 電源腳的去耦電容一個(gè)都不能省還有一點(diǎn)軟件上加重試機(jī)制。讀數(shù)據(jù)時(shí)如果 CRC 校驗(yàn)失敗自動(dòng)重讀一次寫數(shù)據(jù)后回讀校驗(yàn)不對(duì)就重寫。工業(yè)設(shè)備不能假設(shè)通信永遠(yuǎn)可靠重試是最便宜的容錯(cuò)手段。6.4 實(shí)操心得與經(jīng)驗(yàn)總結(jié)最后分享幾個(gè)文檔里不會(huì)寫、但實(shí)際很管用的經(jīng)驗(yàn)第一先用低速調(diào)通再提速。我見過太多人一上來就配 20MHz結(jié)果通信不通然后花幾天查硬件。其實(shí)先用 1MHz 調(diào)通確認(rèn)邏輯沒問題再逐步提速效率高得多。第二寫一個(gè)簡單的讀寫測(cè)試程序。上電后往 MRAM 某個(gè)測(cè)試地址寫一個(gè)遞增數(shù)讀回來比對(duì)循環(huán)跑。這個(gè)測(cè)試能快速暴露時(shí)序和硬件問題比直接上業(yè)務(wù)代碼調(diào)試快。第三MRAM 的無需擦除不代表可以亂寫。雖然壽命長但頻繁寫同一地址在邏輯上還是要注意尤其是日志區(qū)用環(huán)形緩沖分散寫入地址既清晰又安全。第四保留一個(gè)出廠恢復(fù)區(qū)域。把默認(rèn)配置存在一個(gè)只讀區(qū)域設(shè)備參數(shù)亂了可以一鍵恢復(fù)。這個(gè)功能在現(xiàn)場(chǎng)維護(hù)時(shí)能省大量時(shí)間。這套 MR25H40CDF PIC18LF47K42 的方案我從最初的選型驗(yàn)證到批量部署前后迭代了三個(gè)硬件版本。現(xiàn)在回頭看最值得投入時(shí)間的就是前期把 SPI 時(shí)序和掉電保護(hù)策略做扎實(shí)后面現(xiàn)場(chǎng)基本不用操心存儲(chǔ)問題。工業(yè)嵌入式這行存儲(chǔ)可靠性是底線選對(duì)介質(zhì)、寫對(duì)代碼設(shè)備才能在現(xiàn)場(chǎng)安安穩(wěn)穩(wěn)跑下去。