級(jí)SPI MRAM與PIC24FJ128GA310的嵌入式存儲(chǔ)實(shí)戰(zhàn))
1. 為什么工業(yè)現(xiàn)場(chǎng)還在用 SPI 接 MRAM而不是直接上 Flash如果你拆過(guò)工業(yè)控制器、電力監(jiān)測(cè)終端或者車載數(shù)據(jù)記錄儀大概率會(huì)在板子上看到一顆 8 腳的小芯片旁邊連著主控的 SPI 引腳。這顆芯片很多時(shí)候不是普通的 NOR Flash而是MRAM——磁性隨機(jī)存儲(chǔ)器。我這次要聊的組合就是MR25H40CDF這顆 512Kb 的 SPI MRAM搭配PIC24FJ128GA310這款 16 位單片機(jī)做工業(yè)與嵌入式場(chǎng)景下的數(shù)據(jù)存儲(chǔ)和讀取。先說(shuō)清楚這個(gè)組合解決什么問(wèn)題。工業(yè)現(xiàn)場(chǎng)的數(shù)據(jù)記錄有個(gè)很尷尬的特點(diǎn)寫入頻繁、單次數(shù)據(jù)量小、掉電隨時(shí)可能發(fā)生、環(huán)境溫度跨度大而且設(shè)備往往要求十年以上的生命周期。用 EEPROM 吧寫入速度慢、擦寫壽命有限用 NOR Flash 吧寫之前要擦除整個(gè)扇區(qū)掉電時(shí)正在擦除的扇區(qū)可能直接損壞用 FRAM 吧容量小、價(jià)格高。MRAM 恰好卡在中間像 RAM 一樣按字節(jié)隨機(jī)寫入不需要擦除掉電數(shù)據(jù)不丟讀寫壽命幾乎無(wú)限。MR25H40CDF 是 Everspin 的 512Kb64KBSPI MRAM40MHz SPI 接口2.7V 到 3.6V 供電工業(yè)級(jí)溫度范圍 -40 到 85 攝氏度。PIC24FJ128GA310 是 Microchip 的 16 位 MCU128KB Flash、8KB RAM帶多個(gè) SPI 模塊本身定位就是工業(yè)控制。兩者搭配一個(gè)負(fù)責(zé)永不丟失的快速暫存區(qū)一個(gè)負(fù)責(zé)邏輯控制和協(xié)議處理是很典型的工業(yè)數(shù)據(jù)記錄架構(gòu)。這篇文章適合誰(shuí)看如果你正在做工業(yè)數(shù)據(jù)采集、設(shè)備參數(shù)存儲(chǔ)、掉電保護(hù)記錄、黑匣子類的功能或者你手上有 PIC24 平臺(tái)但不確定怎么把 MRAM 用穩(wěn)那這篇內(nèi)容可以直接抄作業(yè)。我會(huì)從硬件連接、SPI 時(shí)序配置、讀寫驅(qū)動(dòng)、掉電保護(hù)策略、常見(jiàn)踩坑幾個(gè)角度完整講一遍中間會(huì)穿插我實(shí)際調(diào)試時(shí)遇到的問(wèn)題和解決辦法。提示MRAM 和 Flash 最大的區(qū)別是寫之前不用擦。這個(gè)特性決定了驅(qū)動(dòng)層的設(shè)計(jì)思路完全不同后面會(huì)重點(diǎn)展開(kāi)。2. MR25H40CDF 的引腳、時(shí)序與 PIC24FJ128GA310 的硬件對(duì)接2.1 先搞清楚 MR25H40CDF 的引腳定義和工作模式MR25H40CDF 是 8 腳 SOIC 封裝引腳定義很標(biāo)準(zhǔn)引腳名稱功能說(shuō)明1CS片選低電平有效2SO數(shù)據(jù)輸出MISO3WP寫保護(hù)低電平有效4VSS地5SI數(shù)據(jù)輸入MOSI6SCK時(shí)鐘7HOLD保持低電平有效8VDD電源 2.7V~3.6V這里有兩個(gè)容易被忽略的引腳WP 和 HOLD。WP 拉低時(shí)芯片進(jìn)入硬件寫保護(hù)所有寫操作被拒絕HOLD 拉低時(shí)當(dāng)前 SPI 傳輸暫停時(shí)鐘可以繼續(xù)跑但數(shù)據(jù)不采樣。很多人在原理圖階段直接把這兩個(gè)腳接地結(jié)果發(fā)現(xiàn)寫不進(jìn)去數(shù)據(jù)排查半天才發(fā)現(xiàn)是 WP 被拉低了。我的建議是WP 和 HOLD 都不要直接接地而是通過(guò) 10K 上拉到 VDD。如果確實(shí)需要硬件寫保護(hù)功能WP 可以接到 MCU 的一個(gè) GPIO 上正常工作時(shí)輸出高電平需要保護(hù)時(shí)拉低。HOLD 如果不使用上拉到 VDD 即可不要懸空。2.2 PIC24FJ128GA310 的 SPI 模塊配置要點(diǎn)PIC24FJ128GA310 有多個(gè) SPI 模塊我用的是 SPI1。配置 SPI 的核心寄存器是 SPI1CON1、SPI1CON2、SPI1STAT。工業(yè)場(chǎng)景下我推薦的配置是主模式MSTEN 1時(shí)鐘極性CKP 0空閑低電平時(shí)鐘邊沿CKE 1數(shù)據(jù)在時(shí)鐘上升沿采樣字長(zhǎng)MODE16 08 位模式預(yù)分頻主預(yù)分頻 4:1次預(yù)分頻 2:1MR25H40CDF 支持 SPI Mode 0 和 Mode 3。Mode 0 是 CKP0、CKE1也就是空閑低、上升沿采樣。這個(gè)模式和大多數(shù) SPI Flash 一致配置起來(lái)最省事。關(guān)于時(shí)鐘頻率MR25H40CDF 最高支持 40MHz但 PIC24FJ128GA310 在 32MHz 系統(tǒng)時(shí)鐘下SPI 最高能跑到 16MHzFCY/2。實(shí)際工業(yè)場(chǎng)景我不建議跑滿8MHz 左右是比較穩(wěn)的選擇。原因有兩個(gè)一是長(zhǎng)走線時(shí)高速 SPI 的信號(hào)完整性會(huì)變差二是 MRAM 的寫入時(shí)序在高溫下需要留裕量。計(jì)算一下如果 FCY 16 MIPS32MHz 晶振PLL 后主預(yù)分頻 4:1、次預(yù)分頻 2:1SPI 時(shí)鐘 16MHz / (4 × 2) 2MHz。這個(gè)速度對(duì) MRAM 來(lái)說(shuō)偏慢但勝在穩(wěn)定。如果要提速可以把主預(yù)分頻改成 2:1得到 4MHz再改成 1:1得到 8MHz。// PIC24FJ128GA310 SPI1 初始化示例 void SPI1_Init(void) { SPI1STATbits.SPIEN 0; // 先關(guān)閉 SPI SPI1CON1bits.MSTEN 1; // 主模式 SPI1CON1bits.CKP 0; // 空閑低電平 SPI1CON1bits.CKE 1; // 上升沿采樣 SPI1CON1bits.SMP 0; // 中間采樣 SPI1CON1bits.MODE16 0; // 8 位模式 SPI1CON1bits.PPRE 0b10; // 主預(yù)分頻 4:1 SPI1CON1bits.SPRE 0b110; // 次預(yù)分頻 2:1 SPI1STATbits.SPIROV 0; // 清除溢出標(biāo)志 SPI1STATbits.SPIEN 1; // 使能 SPI }2.3 硬件走線和去耦的實(shí)戰(zhàn)經(jīng)驗(yàn)SPI 雖然只有四根線但在工業(yè)板上走不好照樣出問(wèn)題。我踩過(guò)的坑包括SCK 和 MISO 平行走線太長(zhǎng)導(dǎo)致串?dāng)_、CS 沒(méi)有單獨(dú)走線而是和別的信號(hào)共用、去耦電容離芯片太遠(yuǎn)。正確的做法是MRAM 的 VDD 引腳旁邊放一個(gè) 0.1uF 的陶瓷電容距離不超過(guò) 2mm。如果板子上有多個(gè) SPI 從設(shè)備每個(gè)從設(shè)備的 CS 都要獨(dú)立走線不要用譯碼器共用。SCK 走線盡量短如果超過(guò) 5cm建議在源端串一個(gè) 22 到 33 歐姆的電阻做阻抗匹配。還有一點(diǎn)MR25H40CDF 的 WP 和 HOLD 上拉電阻不要省。我見(jiàn)過(guò)一個(gè)板子為了省兩個(gè)電阻把 WP 直接接 VDD、HOLD 懸空結(jié)果在濕度大的環(huán)境下 HOLD 引腳受干擾SPI 通信間歇性失敗。后來(lái)補(bǔ)上 10K 上拉就穩(wěn)定了。3. 驅(qū)動(dòng)層怎么寫從單字節(jié)讀寫到頁(yè)寫入的完整實(shí)現(xiàn)3.1 MR25H40CDF 的指令集和地址空間MR25H40CDF 的指令集很精簡(jiǎn)常用的就幾條指令操作碼功能WREN0x06寫使能WRDI0x04寫禁止RDSR0x05讀狀態(tài)寄存器WRSR0x01寫狀態(tài)寄存器READ0x03讀數(shù)據(jù)WRITE0x02寫數(shù)據(jù)地址空間是 64KB地址從 0x0000 到 0xFFFF用 16 位地址尋址。注意 MR25H40CDF 的 READ 和 WRITE 指令后面跟的是 16 位地址高位在前。狀態(tài)寄存器的 bit0 是 WEL寫使能鎖存bit1 是 BP0bit2 是 BP1bit7 是 SRWD。寫操作之前必須先發(fā) WREN 指令把 WEL 置 1寫完自動(dòng)清零。這個(gè)和 EEPROM、Flash 的邏輯一樣。3.2 單字節(jié)讀寫函數(shù)最基礎(chǔ)也最容易寫錯(cuò)的部分先看讀一個(gè)字節(jié)// 讀取 MR25H40CDF 指定地址的一個(gè)字節(jié) uint8_t MRAM_ReadByte(uint16_t addr) { uint8_t data; MRAM_CS_LOW(); // 片選拉低 SPI1_ExchangeByte(0x03); // 發(fā)送 READ 指令 SPI1_ExchangeByte(addr 8); // 地址高字節(jié) SPI1_ExchangeByte(addr 0xFF); // 地址低字節(jié) data SPI1_ExchangeByte(0xFF); // 讀數(shù)據(jù) MRAM_CS_HIGH(); // 片選拉高 return data; }寫一個(gè)字節(jié)稍微復(fù)雜因?yàn)橐劝l(fā) WREN// 寫入 MR25H40CDF 指定地址的一個(gè)字節(jié) void MRAM_WriteByte(uint16_t addr, uint8_t data) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); // WRITE SPI1_ExchangeByte(addr 8); SPI1_ExchangeByte(addr 0xFF); SPI1_ExchangeByte(data); MRAM_CS_HIGH(); // 等待寫入完成 while(MRAM_ReadStatus() 0x01); }這里有個(gè)關(guān)鍵點(diǎn)WREN 和 WRITE 之間必須拉高 CS。很多人習(xí)慣把 WREN 和 WRITE 放在同一個(gè) CS 低電平周期里這對(duì) MRAM 是不行的。MR25H40CDF 要求 WREN 指令在獨(dú)立的 CS 周期內(nèi)完成然后再開(kāi)一個(gè)新的 CS 周期發(fā) WRITE。3.3 頁(yè)寫入和連續(xù)讀取提升吞吐量的關(guān)鍵單字節(jié)寫雖然簡(jiǎn)單但每次都要發(fā) WREN、發(fā)地址、等寫入完成效率很低。MR25H40CDF 支持頁(yè)寫入一頁(yè)是 256 字節(jié)。連續(xù)寫的時(shí)候地址低 8 位會(huì)自動(dòng)遞增超過(guò) 255 會(huì)回繞到頁(yè)首所以跨頁(yè)寫入必須手動(dòng)分頁(yè)。// 頁(yè)寫入注意不能跨頁(yè) void MRAM_WritePage(uint16_t addr, uint8_t *buf, uint16_t len) { uint16_t page_end (addr 0xFF00) 0x100; if(addr len page_end) { len page_end - addr; // 截?cái)嗟巾?yè)邊界 } MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); SPI1_ExchangeByte(addr 8); SPI1_ExchangeByte(addr 0xFF); for(uint16_t i 0; i len; i) { SPI1_ExchangeByte(buf[i]); } MRAM_CS_HIGH(); while(MRAM_ReadStatus() 0x01); }連續(xù)讀取就簡(jiǎn)單多了發(fā)完 READ 指令和起始地址后可以一直讀地址自動(dòng)遞增直到 CS 拉高。// 連續(xù)讀取 void MRAM_ReadBuffer(uint16_t addr, uint8_t *buf, uint16_t len) { MRAM_CS_LOW(); SPI1_ExchangeByte(0x03); SPI1_ExchangeByte(addr 8); SPI1_ExchangeByte(addr 0xFF); for(uint16_t i 0; i len; i) { buf[i] SPI1_ExchangeByte(0xFF); } MRAM_CS_HIGH(); }3.4 SPI 字節(jié)交換函數(shù)的實(shí)現(xiàn)細(xì)節(jié)上面反復(fù)用到的SPI1_ExchangeByte是底層函數(shù)實(shí)現(xiàn)要注意等待標(biāo)志位uint8_t SPI1_ExchangeByte(uint8_t txData) { SPI1BUF txData; while(!SPI1STATbits.SPIRBF); // 等待接收完成 return SPI1BUF; }這里有個(gè)坑必須先寫 SPI1BUF 再等 SPIRBF。如果反過(guò)來(lái)先等標(biāo)志再寫第一次調(diào)用會(huì)死等。另外讀 SPI1BUF 會(huì)自動(dòng)清 SPIRBF 標(biāo)志所以不需要手動(dòng)清。注意PIC24 的 SPI 在高速時(shí)鐘下如果兩個(gè)字節(jié)之間間隔太短可能出現(xiàn) SPIROV 溢出。工業(yè)場(chǎng)景建議在連續(xù)讀寫時(shí)檢查 SPIROV如果置位要重新初始化 SPI。4. 掉電保護(hù)與數(shù)據(jù)可靠性MRAM 真正的價(jià)值所在4.1 為什么 MRAM 的掉電保護(hù)比 Flash 簡(jiǎn)單得多Flash 的掉電保護(hù)是個(gè)老大難問(wèn)題。寫 Flash 要先擦除扇區(qū)擦除過(guò)程中如果掉電整個(gè)扇區(qū)數(shù)據(jù)可能變成 0xFF 或者不確定狀態(tài)。所以用 Flash 做數(shù)據(jù)記錄通常要設(shè)計(jì)雙備份、日志結(jié)構(gòu)、磨損均衡代碼復(fù)雜度很高。MRAM 沒(méi)有這個(gè)問(wèn)題。MR25H40CDF 的寫入是直接覆蓋不需要擦除寫入過(guò)程中掉電要么是舊數(shù)據(jù)要么是新數(shù)據(jù)不會(huì)出現(xiàn)中間態(tài)。這個(gè)特性讓掉電保護(hù)邏輯大大簡(jiǎn)化。我實(shí)際項(xiàng)目里的做法是把 64KB 空間分成若干條記錄每條記錄帶一個(gè)序號(hào)和 CRC 校驗(yàn)。寫入時(shí)先寫數(shù)據(jù)區(qū)再寫序號(hào)和 CRC。讀取時(shí)掃描所有記錄找序號(hào)最大且 CRC 正確的作為最新數(shù)據(jù)。因?yàn)?MRAM 寫入快單字節(jié)寫入時(shí)間在微秒級(jí)掉電窗口極小。4.2 用狀態(tài)寄存器做寫保護(hù)的分級(jí)策略MR25H40CDF 的狀態(tài)寄存器有 BP0 和 BP1 兩位可以設(shè)置不同級(jí)別的寫保護(hù)BP1BP0保護(hù)范圍00無(wú)保護(hù)01保護(hù)高 1/4 地址空間10保護(hù)高 1/2 地址空間11保護(hù)整個(gè)地址空間工業(yè)場(chǎng)景下我通常把參數(shù)區(qū)放在低地址記錄區(qū)放在高地址。正常運(yùn)行時(shí)全片可寫當(dāng)設(shè)備進(jìn)入?yún)?shù)鎖定模式時(shí)把 BP10、BP01保護(hù)高 1/4 空間防止記錄區(qū)被誤寫。這個(gè)策略比軟件標(biāo)志位可靠因?yàn)樗怯布?jí)別的保護(hù)。4.3 寫入完成判斷輪詢狀態(tài)寄存器還是固定延時(shí)MRAM 的寫入速度很快但并不是發(fā)完就完。WRITE 指令的最后一個(gè)字節(jié)移入后芯片內(nèi)部還需要時(shí)間完成實(shí)際存儲(chǔ)。這個(gè)時(shí)間典型值是幾十納秒到幾百納秒但數(shù)據(jù)手冊(cè)給的最大值是 5ms實(shí)際上遠(yuǎn)小于這個(gè)值。我見(jiàn)過(guò)兩種做法一種是發(fā)完 WRITE 后直接延時(shí) 1ms 再操作另一種是輪詢狀態(tài)寄存器的 WIP 位。推薦輪詢方式因?yàn)檠訒r(shí)是拍腦袋的輪詢是確定性的。uint8_t MRAM_ReadStatus(void) { uint8_t status; MRAM_CS_LOW(); SPI1_ExchangeByte(0x05); status SPI1_ExchangeByte(0xFF); MRAM_CS_HIGH(); return status; }狀態(tài)寄存器的 bit0 是 WIPWrite In Progress寫入完成時(shí)該位為 0。輪詢這個(gè)位比固定延時(shí)靠譜得多。4.4 數(shù)據(jù)校驗(yàn)CRC16 在 PIC24 上的輕量實(shí)現(xiàn)工業(yè)數(shù)據(jù)不能只靠寫進(jìn)去就行必須有校驗(yàn)。我在 PIC24FJ128GA310 上用的是一個(gè)查表法的 CRC16-CCITT表放在 Flash 里運(yùn)行時(shí)查表計(jì)算。64KB 數(shù)據(jù)全量校驗(yàn)一次大概幾十毫秒對(duì)于周期性記錄場(chǎng)景完全夠用。// CRC16-CCITT 查表法 uint16_t CRC16_Calc(uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; while(len--) { crc (crc 8) ^ crc16_table[((crc 8) ^ *data) 0xFF]; } return crc; }每條記錄的結(jié)構(gòu)我設(shè)計(jì)成4 字節(jié)序號(hào) N 字節(jié)數(shù)據(jù) 2 字節(jié) CRC。讀取時(shí)先校驗(yàn) CRC再比較序號(hào)確保拿到的是完整且最新的數(shù)據(jù)。5. 調(diào)試過(guò)程中真實(shí)踩過(guò)的坑和排查思路5.1 讀出來(lái)全是 0xFF 或者 0x00這是最常見(jiàn)的現(xiàn)象。第一次調(diào) MRAM讀出來(lái)全是 0xFF第一反應(yīng)是芯片壞了。其實(shí)大概率是三個(gè)原因第一CS 沒(méi)有正確拉低。用邏輯分析儀抓一下 CS 引腳確認(rèn)讀操作期間 CS 確實(shí)是低電平。我遇到過(guò) CS 配置成開(kāi)漏輸出但沒(méi)加上拉導(dǎo)致 CS 一直是高。第二SPI 模式不對(duì)。MR25H40CDF 支持 Mode 0 和 Mode 3如果 MCU 配成 Mode 1 或 Mode 2數(shù)據(jù)會(huì)錯(cuò)位。用示波器看 SCK 空閑電平Mode 0 空閑低Mode 3 空閑高。第三WP 引腳被拉低。這個(gè)最隱蔽因?yàn)樽x操作不受 WP 影響但寫操作會(huì)被拒絕。如果你發(fā)現(xiàn)讀正常但寫不進(jìn)去先量一下 WP 引腳電壓。5.2 寫入后讀出來(lái)數(shù)據(jù)不對(duì)但偶爾又對(duì)這種間歇性錯(cuò)誤通常是時(shí)序問(wèn)題。我遇到過(guò)一次SPI 時(shí)鐘跑到 16MHz短走線時(shí)正常但板子裝進(jìn)金屬外殼后分布電容變化導(dǎo)致 SCK 邊沿變緩數(shù)據(jù)采樣出錯(cuò)。后來(lái)把時(shí)鐘降到 8MHz問(wèn)題消失。另一個(gè)可能是寫入后沒(méi)有等待完成就發(fā)起下一次操作。MRAM 雖然快但連續(xù)寫不同地址時(shí)如果不等 WIP 清零就發(fā)下一條 WRITE芯片可能忽略第二條指令。我的做法是每次寫操作后都輪詢 WIP雖然犧牲一點(diǎn)速度但穩(wěn)定性大幅提升。5.3 高溫環(huán)境下數(shù)據(jù)出錯(cuò)工業(yè)設(shè)備要做高低溫測(cè)試。我在 85 攝氏度環(huán)境下測(cè)試時(shí)發(fā)現(xiàn)偶爾有記錄丟失。排查后發(fā)現(xiàn)兩個(gè)問(wèn)題一是 SPI 時(shí)鐘在高溫下裕量不足二是去耦電容用的是 Y5V 材質(zhì)高溫下容量衰減嚴(yán)重。解決辦法SPI 時(shí)鐘從 8MHz 降到 4MHz去耦電容換成 X7R 材質(zhì)。改完之后高低溫循環(huán)測(cè)試 100 次沒(méi)有再出現(xiàn)數(shù)據(jù)錯(cuò)誤。提示工業(yè)級(jí) MRAM 雖然標(biāo)稱 -40 到 85 攝氏度但外圍電路的溫漂同樣要考慮。電容、電阻的材質(zhì)在極端溫度下表現(xiàn)差異很大。5.4 用邏輯分析儀抓 SPI 波形的正確姿勢(shì)調(diào)試 SPI 問(wèn)題邏輯分析儀比示波器好用。抓波形時(shí)注意幾點(diǎn)采樣率至少是 SCK 頻率的 10 倍觸發(fā)條件設(shè)成 CS 下降沿解碼協(xié)議選 SPI模式選對(duì)。我習(xí)慣同時(shí)抓 CS、SCK、MOSI、MISO 四根線這樣一眼就能看出是命令發(fā)錯(cuò)了還是數(shù)據(jù)錯(cuò)了。有一次我抓到的波形顯示W(wǎng)REN 指令發(fā)出后 CS 拉高但下一個(gè) CS 周期開(kāi)始時(shí) SCK 上有一個(gè)毛刺。這個(gè)毛刺導(dǎo)致 MRAM 誤判指令。后來(lái)在 SCK 線上串了一個(gè) 33 歐姆電阻毛刺消失。6. 把 MRAM 用出價(jià)值的幾個(gè)進(jìn)階思路6.1 用 MRAM 做參數(shù)存儲(chǔ)和日志記錄的分區(qū)設(shè)計(jì)64KB 空間說(shuō)大不大說(shuō)小不小。我的分區(qū)方案是低 16KB 做參數(shù)區(qū)存設(shè)備配置、校準(zhǔn)系數(shù)、序列號(hào)中間 32KB 做循環(huán)日志區(qū)每條記錄 64 字節(jié)可以存 512 條高 16KB 做備份區(qū)存關(guān)鍵參數(shù)的副本。參數(shù)區(qū)用固定地址訪問(wèn)讀寫簡(jiǎn)單。日志區(qū)用環(huán)形緩沖寫滿后覆蓋最舊記錄。備份區(qū)在參數(shù)更新時(shí)同步寫入主參數(shù)區(qū)損壞時(shí)可以恢復(fù)。6.2 和 Flash 配合使用的混合存儲(chǔ)架構(gòu)MRAM 容量小、價(jià)格高不適合存大量數(shù)據(jù)。我的做法是MRAM 做熱數(shù)據(jù)緩存Flash 做冷數(shù)據(jù)歸檔。實(shí)時(shí)采集的數(shù)據(jù)先寫 MRAM保證掉電不丟當(dāng) MRAM 里的記錄積累到一定數(shù)量再批量搬運(yùn)到 Flash。這樣既利用了 MRAM 的快速寫入和掉電安全又利用了 Flash 的大容量和低成本。搬運(yùn)過(guò)程要注意先寫 Flash校驗(yàn)成功后再清 MRAM 對(duì)應(yīng)區(qū)域避免搬運(yùn)過(guò)程中掉電導(dǎo)致數(shù)據(jù)丟失。6.3 低功耗場(chǎng)景下的 MRAM 使用技巧MRAM 的待機(jī)電流很低但 SPI 接口在空閑時(shí)如果 CS 一直拉低芯片會(huì)持續(xù)耗電。低功耗設(shè)計(jì)時(shí)每次操作完成后把 CS 拉高讓 MRAM 進(jìn)入待機(jī)模式。另外如果 MCU 支持可以把 SPI 引腳配置成普通 GPIO 輸出低電平進(jìn)一步降低漏電流。我做過(guò)一個(gè)電池供電的記錄儀MRAM 平時(shí)待機(jī)電流不到 10 微安每分鐘喚醒一次寫一條記錄整體功耗可以做到幾年不用換電池。6.4 從 MR25H40CDF 遷移到更大容量 MRAM 的注意事項(xiàng)如果 64KB 不夠用Everspin 有更大容量的 SPI MRAM比如 1Mb、4Mb 的型號(hào)。遷移時(shí)要注意幾點(diǎn)地址位數(shù)可能從 16 位變成 24 位指令集基本兼容但地址字節(jié)數(shù)不同頁(yè)大小可能變化寫入分頁(yè)邏輯要調(diào)整封裝可能不同PCB 要改版。我的建議是驅(qū)動(dòng)層做好抽象把地址寬度、頁(yè)大小做成宏定義遷移時(shí)只改配置不改邏輯。7. 寫在最后的一些個(gè)人體會(huì)這套 MR25H40CDF 加 PIC24FJ128GA310 的組合我在三個(gè)工業(yè)項(xiàng)目里用過(guò)最長(zhǎng)的已經(jīng)連續(xù)運(yùn)行四年多沒(méi)有出現(xiàn)過(guò)數(shù)據(jù)丟失。MRAM 這個(gè)器件最大的價(jià)值不是速度快而是它讓嵌入式工程師不用再為掉電時(shí)數(shù)據(jù)會(huì)不會(huì)丟這件事焦慮。用 Flash 做記錄你要設(shè)計(jì)日志、磨損均衡、掉電檢測(cè)、備份恢復(fù)代碼寫幾千行用 MRAM核心邏輯可能幾百行就搞定了。當(dāng)然 MRAM 也不是萬(wàn)能的。容量小、單價(jià)高這兩點(diǎn)決定了它只能用在關(guān)鍵數(shù)據(jù)的存儲(chǔ)上。我的經(jīng)驗(yàn)是凡是丟了會(huì)出大事的數(shù)據(jù)放 MRAM凡是丟了可以重采的數(shù)據(jù)放 Flash 或者外部存儲(chǔ)。這個(gè)原則幫我在多個(gè)項(xiàng)目里快速做了存儲(chǔ)架構(gòu)決策。最后分享一個(gè)小技巧MRAM 剛上電時(shí)建議先讀一遍狀態(tài)寄存器確認(rèn) BP0、BP1 都是 0WEL 是 0。如果狀態(tài)不對(duì)先寫狀態(tài)寄存器復(fù)位再做其他操作。這個(gè)習(xí)慣能避免很多莫名其妙寫不進(jìn)去的問(wèn)題。