存儲方案:掉電不丟數(shù)據(jù)的高可靠SPI驅(qū)動實現(xiàn))
1. 項目緣起為什么要在工業(yè)場景里死磕 MRAM 這顆料做工業(yè)控制和嵌入式數(shù)據(jù)采集的朋友大概率都遇到過同一個讓人頭疼的問題設(shè)備在現(xiàn)場跑得好好的突然斷電再上電發(fā)現(xiàn)關(guān)鍵參數(shù)丟了或者日志文件最后幾條記錄變成亂碼。用 EEPROM 吧寫入速度慢得讓人著急擦寫壽命也就百萬次級別高頻采集場景下幾個月就寫廢了用 Flash 吧寫之前還得先擦一個扇區(qū)掉電瞬間正好在擦除整塊數(shù)據(jù)直接報廢用帶電池的 SRAM 吧電池在高溫高濕的工業(yè)環(huán)境里撐不了幾年還得考慮電池更換和環(huán)保問題。我這次要聊的MR25H40CDF就是沖著這個痛點來的。它是一顆 4Mbit 容量的MRAM磁性隨機存儲器SPI 接口DFN 封裝工作溫度覆蓋工業(yè)級甚至更寬的范圍。MRAM 的物理機制決定了它寫進去就是“非易失”的不需要擦除動作字節(jié)級寫入沒有寫延遲擦寫次數(shù)理論上近乎無限。而PIC18F87J11是 Microchip 家一顆很經(jīng)典的 8 位單片機自帶硬件 SPI 模塊3.3V 供電引腳多、外設(shè)全在工業(yè)儀表、電機控制、數(shù)據(jù)記錄儀里出鏡率極高。把這兩顆料湊在一起目標很明確在工業(yè)和嵌入式應(yīng)用里做一套掉電絕對不丟數(shù)據(jù)、寫入速度跟得上采集節(jié)奏、長期運行不用換電池的存儲方案。這篇文章我會從選型邏輯、硬件連接、SPI 時序配置、讀寫驅(qū)動實現(xiàn)、掉電保護策略、常見問題排查幾個維度把整個項目拆開揉碎講清楚。不管你是剛接觸 SPI 存儲的新手還是已經(jīng)在用 Flash/EEPROM 想升級方案的老手都能從里面找到可以直接抄作業(yè)的代碼和配置。我先把結(jié)論放前面MR25H40CDF 不是用來替代大容量 NAND 的它的定位是關(guān)鍵小數(shù)據(jù)的高頻、高可靠存儲。標定參數(shù)、設(shè)備序列號、運行日志、故障快照、累計計量值這些數(shù)據(jù)量不大但絕對不能丟的東西才是它的主戰(zhàn)場。PIC18F87J11 的硬件 SPI 配合 DMA 式的連續(xù)讀寫能把 MRAM 的寫入吞吐跑到 SPI 時鐘的極限這一點后面會詳細算。2. 核心器件拆解MR25H40CDF 與 PIC18F87J11 到底強在哪2.1 MR25H40CDF 的存儲原理與關(guān)鍵參數(shù)MR25H40CDF 的核心存儲單元是磁性隧道結(jié)MTJ。簡單類比一下它就像一個個微小的“磁鐵開關(guān)”通過改變自由層的磁化方向來存儲 0 和 1。這個狀態(tài)是物理性的斷電后磁化方向不會變所以數(shù)據(jù)天然非易失。跟 Flash 靠浮柵電荷存儲不一樣MRAM 不需要“擦除”這個步驟寫入就是直接改變磁化方向所以沒有擦除延遲也沒有擦寫壽命的硬限制。關(guān)鍵參數(shù)我列個表方便你選型時對照參數(shù)項數(shù)值說明容量4 Mbit512 KByte按字節(jié)尋址SPI 訪問接口SPIMode 0 和 Mode 3最高時鐘 40 MHz供電電壓2.7V ~ 3.6V典型 3.3V寫入方式字節(jié)級、頁級無需擦除擦寫壽命近乎無限10^14實際測試遠超 EEPROM數(shù)據(jù)保持20 年以上工業(yè)溫度范圍內(nèi)封裝8 引腳 DFN5mm x 6mm節(jié)省板面工作溫度-40°C ~ 105°C工業(yè)級部分批次更寬寫入電流典型 3mA比 Flash 擦除電流低很多待機電流典型 10μA適合電池備份場景這里有個細節(jié)值得展開MR25H40CDF 的 SPI 模式支持Mode 0CPOL0, CPHA0和Mode 3CPOL1, CPHA1。PIC18F87J11 的 MSSP 模塊兩種都支持但我在實際項目里更傾向用 Mode 0因為示波器抓波形時上升沿采樣看起來更直觀調(diào)試起來省事。后面配置寄存器時會具體說。2.2 PIC18F87J11 的 SPI 外設(shè)能力PIC18F87J11 是 PIC18F87J 系列里的中高配型號128KB Flash、4KB RAM、自帶MSSPMaster Synchronous Serial Port模塊可以配置成 SPI 主模式。它的 SPI 關(guān)鍵特性包括支持主/從模式主模式下時鐘由自身產(chǎn)生時鐘極性CKP和時鐘邊沿CKE可配對應(yīng)四種 SPI 模式支持 8 位數(shù)據(jù)寬度MSB 先行有獨立的 SSPBUF 發(fā)送/接收緩沖寄存器時鐘分頻可選 Fosc/4、Fosc/16、Fosc/64、TMR2 溢出我用的晶振是 8MHz經(jīng)過 PLL 倍頻到 32MHz 作為系統(tǒng)時鐘。SPI 時鐘選 Fosc/4 的話就是 8MHzMR25H40CDF 完全吃得下。如果你追求更高吞吐可以把系統(tǒng)時鐘拉到 40MHz 以上SPI 分頻后跑到 10MHz 甚至 20MHz但要注意 PCB 走線和線纜長度后面會講信號完整性的坑。2.3 為什么不是 EEPROM 或 Flash一次真實的對比我之前在一個電力監(jiān)測項目里用過 24LC512 EEPROMI2C 接口400kHz 時鐘寫一個字節(jié)要等 5ms 的寫周期。采集節(jié)點每 100ms 記錄一次三相電壓電流算下來每秒要寫 30 字節(jié)EEPROM 根本扛不住最后只能加 RAM 緩存再批量寫掉電還是丟數(shù)據(jù)。后來換 SPI FlashW25Q64 那種寫之前要擦 4KB 扇區(qū)擦除時間 50ms 起步掉電保護邏輯復雜得要命還得額外加超級電容。換成 MR25H40CDF 之后寫入就是直接發(fā) SPI 命令沒有等待周期寫完立刻可以讀回來驗證。我用邏輯分析儀抓過波形從 CS 拉低到數(shù)據(jù)寫入完成一個字節(jié)的完整事務(wù)不到 1μs8MHz 時鐘下。這個速度對于工業(yè)現(xiàn)場的高頻采集來說余量非常大。3. 硬件設(shè)計從原理圖到 PCB 的實操要點3.1 引腳連接與上拉電阻配置MR25H40CDF 是 8 引腳 DFN引腳定義如下引腳名稱功能連接要點1CS片選低有效接 PIC 的 RA5/SS 或任意 GPIO2SO數(shù)據(jù)輸出MISO接 PIC 的 SDI3WP寫保護低有效不用時上拉到 VCC4VSS地就近接地5SI數(shù)據(jù)輸入MOSI接 PIC 的 SDO6SCK時鐘接 PIC 的 SCK7HOLD保持低有效不用時上拉到 VCC8VCC電源加 0.1μF 去耦電容這里有兩個容易踩的坑。第一WP 和 HOLD 引腳絕對不能懸空。我見過有人圖省事不接結(jié)果 MRAM 偶爾寫不進去查了半天以為是時序問題其實是 HOLD 被干擾拉低SPI 事務(wù)被暫停了。第二CS 片選建議用 GPIO 而不是 MSSP 的 SS 引腳因為 PIC 的 SS 在主機模式下有時會被復用為其他功能用普通 IO 手動控制更靈活也方便后面做多設(shè)備共享 SPI 總線。上拉電阻我一般用 10kΩWP 和 HOLD 各一個CS 也建議加一個 10kΩ 上拉到 VCC防止單片機復位期間 CS 浮動導致誤觸發(fā)。去耦電容用 0.1μF 陶瓷電容盡量靠近 VCC 引腳放DFN 封裝底下如果有散熱焊盤記得接地。3.2 SPI 總線走線與信號完整性SPI 時鐘跑到 8MHz 以上時PCB 走線就不能隨便畫了。我的經(jīng)驗是SCK 走線盡量短最好不超過 5cm避免過孔SCK 和 MOSI 不要平行走太長容易串擾必要時中間加地線隔離MISO 上可以串一個 22Ω 電阻抑制反射特別是線纜連接時CS 走線遠離 SCK因為 CS 是異步信號容易被時鐘耦合干擾如果 MRAM 和單片機不在同一塊板上用排線連接時每根信號線旁邊配一根地線降低回路面積我在一個電機控制項目里MRAM 放在驅(qū)動板PIC 放在控制板中間用 20cm 排線連。一開始 SPI 跑 4MHz 都偶爾讀出錯數(shù)據(jù)后來把排線換成雙絞線信號地絞在一起SCK 串了 33Ω 電阻問題立刻消失。所以別小看這幾根線工業(yè)環(huán)境里電磁干擾比你想象的嚴重。3.3 電源設(shè)計與掉電檢測MR25H40CDF 的寫入電流典型 3mA峰值可能到 10mA電源要能瞬間供得上。我在 VCC 引腳旁邊放了 10μF 鉭電容 0.1μF 陶瓷電容的組合鉭電容負責儲能陶瓷電容負責高頻去耦。掉電檢測這塊PIC18F87J11 自帶HLVD高/低電壓檢測模塊可以設(shè)置一個閾值比如 2.9V當 VCC 掉到 2.9V 以下時觸發(fā)中斷。在中斷里立刻停止采集把關(guān)鍵數(shù)據(jù)寫進 MRAM然后進入休眠。因為 MRAM 寫入不需要等待整個保存過程在幾百微秒內(nèi)就能完成電源電容的儲能完全夠用。這個機制后面在軟件部分會詳細展開。4. SPI 驅(qū)動實現(xiàn)從寄存器配置到讀寫函數(shù)4.1 MSSP 模塊初始化代碼先上代碼這是 PIC18F87J11 用 XC8 編譯器配置 SPI 主模式的初始化函數(shù)#include xc.h // 假設(shè)系統(tǒng)時鐘 32MHzSPI 時鐘 Fosc/4 8MHz void SPI_Init(void) { // 配置引腳方向 TRISAbits.TRISA5 0; // CS 作為輸出 TRISCbits.TRISC3 0; // SCK 輸出 TRISCbits.TRISC4 1; // SDI 輸入 TRISCbits.TRISC5 0; // SDO 輸出 // 關(guān)閉模擬功能如果有 ANSELC 0x00; // MSSP 配置 SSP1STAT 0x00; // 采樣在中間時鐘上升沿發(fā)送 SSP1STATbits.CKE 1; // 時鐘從有效到空閑時發(fā)送數(shù)據(jù) SSP1CON1 0x00; SSP1CON1bits.SSPM 0b0000; // SPI 主模式時鐘 Fosc/4 SSP1CON1bits.CKP 0; // 空閑低電平對應(yīng) Mode 0 SSP1CON1bits.SSPEN 1; // 使能 MSSP CS 1; // 片選拉高空閑狀態(tài) }這里解釋幾個關(guān)鍵位。SSP1STATbits.CKE 1配合CKP 0對應(yīng) SPI Mode 0空閑時 SCK 為低數(shù)據(jù)在 SCK 上升沿被采樣下降沿輸出。MR25H40CDF 的數(shù)據(jù)手冊明確支持 Mode 0所以這個配置是安全的。SSPM 0b0000選擇 Fosc/432MHz 系統(tǒng)時鐘下就是 8MHz SPI 時鐘MRAM 最高支持 40MHz余量充足。4.2 字節(jié)收發(fā)函數(shù)與 CS 控制SPI 是全雙工的發(fā)一個字節(jié)的同時會收一個字節(jié)。我封裝了兩個函數(shù)一個用于發(fā)送一個用于接收// SPI 收發(fā)一個字節(jié) uint8_t SPI_Transfer(uint8_t data) { SSP1BUF data; // 寫入發(fā)送緩沖 while (!SSP1STATbits.BF); // 等待接收完成 return SSP1BUF; // 返回接收到的數(shù)據(jù) } // 片選控制 void MRAM_CS_Low(void) { CS 0; __delay_us(1); // 短暫延時確保 CS 建立 } void MRAM_CS_High(void) { __delay_us(1); CS 1; }__delay_us(1)這個延時看起來不起眼但在高速 SPI 下很重要。CS 拉低到第一個 SCK 邊沿之間需要滿足建立時間tCSSMR25H40CDF 要求最小 5ns1μs 遠遠夠用。但如果你用更快的單片機比如跑 100MHz 的 ARM這個延時就得按數(shù)據(jù)手冊重新算。4.3 MRAM 讀寫命令集與地址格式MR25H40CDF 的命令集很簡潔常用的就幾條命令十六進制功能地址字節(jié)數(shù)WREN0x06寫使能0WRDI0x04寫禁止0READ0x03讀數(shù)據(jù)3WRITE0x02寫數(shù)據(jù)3RDSR0x05讀狀態(tài)寄存器0WRSR0x01寫狀態(tài)寄存器0地址是 24 位的因為 4Mbit 512KByte需要 19 位地址但 MRAM 用 3 個字節(jié)傳輸高位補零。讀寫操作都是 MSB 先行跟 SPI 標準一致。寫數(shù)據(jù)之前必須先發(fā) WREN 命令這是很多新手容易忘的一步。我剛開始用的時候直接發(fā) WRITE 命令數(shù)據(jù)死活寫不進去讀回來全是 0xFF查了半天才發(fā)現(xiàn)漏了 WREN。MRAM 的寫使能是自動復位的每次寫操作前都要重新發(fā)一次。4.4 完整讀寫函數(shù)實現(xiàn)下面是寫字節(jié)和讀字節(jié)的完整實現(xiàn)// 寫一個字節(jié)到指定地址 void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_Low(); SPI_Transfer(0x06); // WREN MRAM_CS_High(); MRAM_CS_Low(); SPI_Transfer(0x02); // WRITE SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); SPI_Transfer(data); MRAM_CS_High(); // 等待寫入完成MRAM 幾乎不需要等待但保險起見 __delay_us(1); } // 從指定地址讀一個字節(jié) uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; MRAM_CS_Low(); SPI_Transfer(0x03); // READ SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); data SPI_Transfer(0x00); // 發(fā)送啞字節(jié)接收數(shù)據(jù) MRAM_CS_High(); return data; }讀函數(shù)里最后那個SPI_Transfer(0x00)是發(fā)送一個空字節(jié)來產(chǎn)生時鐘把 MRAM 的數(shù)據(jù)移進來。這是 SPI 讀操作的通用套路新手容易在這里卡住以為讀不到數(shù)據(jù)是硬件問題其實是忘了發(fā)時鐘。4.5 頁寫與連續(xù)讀優(yōu)化單字節(jié)讀寫雖然簡單但每次都要發(fā)命令3字節(jié)地址開銷不小。MR25H40CDF 支持頁寫一頁 256 字節(jié)連續(xù)寫的時候地址自動遞增不需要每字節(jié)都發(fā)命令。連續(xù)讀更簡單發(fā)一次 READ 命令和起始地址然后一直發(fā)啞字節(jié)就能連續(xù)讀出數(shù)據(jù)地址自動遞增到 512KByte 末尾才回繞。// 頁寫一次寫最多 256 字節(jié) void MRAM_WritePage(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t i; MRAM_CS_Low(); SPI_Transfer(0x06); // WREN MRAM_CS_High(); MRAM_CS_Low(); SPI_Transfer(0x02); // WRITE SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); for (i 0; i len; i) { SPI_Transfer(buf[i]); } MRAM_CS_High(); } // 連續(xù)讀 void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t i; MRAM_CS_Low(); SPI_Transfer(0x03); // READ SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); for (i 0; i len; i) { buf[i] SPI_Transfer(0x00); } MRAM_CS_High(); }頁寫的時候要注意不能跨頁。比如從地址 0x00FF 開始寫 10 個字節(jié)寫到 0x0100 時地址會回繞到頁首 0x0000而不是繼續(xù)到下一頁。這是 SPI 存儲器的通病MRAM 也一樣。我的做法是在上層封裝一個MRAM_WriteBuffer函數(shù)自動處理跨頁拆分調(diào)用者不用關(guān)心頁邊界。5. 掉電保護與數(shù)據(jù)完整性策略5.1 利用 HLVD 實現(xiàn)掉電中斷保存PIC18F87J11 的 HLVD 模塊可以監(jiān)測 VDD 電壓當電壓低于設(shè)定閾值時產(chǎn)生中斷。配置步驟如下void HLVD_Init(void) { HLVDCONbits.HLVDEN 0; // 先關(guān)閉 HLVDCONbits.HLVDL 0b0111; // 設(shè)置閾值約 2.9V HLVDCONbits.VDIRMAG 0; // 下降沿觸發(fā) PIR2bits.HLVDIF 0; // 清除標志 PIE2bits.HLVDIE 1; // 使能中斷 HLVDCONbits.HLVDEN 1; // 使能 HLVD } void __interrupt(high_priority) HighISR(void) { if (PIR2bits.HLVDIF) { PIR2bits.HLVDIF 0; // 緊急保存關(guān)鍵數(shù)據(jù) SaveCriticalData(); // 進入休眠或等待復位 Sleep(); } }SaveCriticalData()函數(shù)里把當前采集值、累計量、故障標志等寫入 MRAM 的固定地址。因為 MRAM 寫入不需要擦除也沒有寫延遲整個保存過程在 1ms 內(nèi)完成。電源端的 10μF 電容在 3.3V 下儲存的能量足夠支撐單片機在掉電后運行幾十毫秒余量非常大。5.2 數(shù)據(jù)校驗與冗余存儲工業(yè)現(xiàn)場干擾大SPI 通信偶爾會出錯。我在每個數(shù)據(jù)塊后面加一個CRC16 校驗讀回來先校驗再使用。如果校驗失敗就從備份區(qū)讀取。MRAM 容量 512KByte我一般把關(guān)鍵數(shù)據(jù)存兩份一份在 0x00000一份在 0x40000互為備份。typedef struct { uint32_t timestamp; float voltage; float current; uint16_t crc; } DataRecord; uint16_t CRC16_Calc(uint8_t *data, uint16_t len) { uint16_t crc 0xFFFF; uint16_t i, j; for (i 0; i len; i) { crc ^ data[i]; for (j 0; j 8; j) { if (crc 0x0001) { crc (crc 1) ^ 0xA001; } else { crc 1; } } } return crc; }寫入的時候先算 CRC把 CRC 一起寫進去。讀取的時候重新算一遍跟存儲的 CRC 對比。這個做法在 EEPROM 和 Flash 上也很常見但 MRAM 的好處是寫入快你可以每次采集都寫不用攢一批再寫數(shù)據(jù)新鮮度更高。5.3 寫入均衡與壽命考量雖然 MRAM 的擦寫壽命近乎無限但嚴格來說還是有物理極限的。不過對于工業(yè)應(yīng)用來說這個極限遠高于設(shè)備本身的設(shè)計壽命。我算過一筆賬假設(shè)每秒寫 1000 次一年 365 天不停一年也就 315 億次MRAM 的 10^14 次壽命夠?qū)?300 多年。所以正常使用根本不用考慮寫入均衡。但有一個細節(jié)要注意MRAM 的寫入功耗比讀取高。如果設(shè)備是電池供電頻繁寫入會縮短續(xù)航。我的做法是采集數(shù)據(jù)先放 RAM每 1 秒或每 10 秒批量寫一次 MRAM既保證數(shù)據(jù)不丟又控制功耗。掉電中斷里再補寫最后一次數(shù)據(jù)。6. 常見問題與排查技巧實錄6.1 SPI 讀回全 0xFF 或全 0x00這是最常遇到的問題原因通常有三個現(xiàn)象可能原因排查方法讀回全 0xFFMISO 沒接好或上拉檢查 MISO 連線示波器看波形讀回全 0x00CS 沒拉低或 SCK 沒輸出測 CS 和 SCK 引腳電平讀回隨機值時序不對或時鐘太快降低 SPI 時鐘到 1MHz 測試寫入后讀回不變漏發(fā) WREN 命令檢查寫函數(shù)是否先發(fā) 0x06我遇到過一次讀回全 0xFF查了半天發(fā)現(xiàn)是 MRAM 的 SO 引腳虛焊DFN 封裝引腳太密手工焊接容易出問題。后來改用熱風槍重新吹了一遍問題解決。所以如果你用 DFN 封裝焊接質(zhì)量一定要保證。6.2 高速 SPI 下的數(shù)據(jù)錯誤SPI 時鐘超過 10MHz 后如果 PCB 走線不好容易出現(xiàn)偶發(fā)性數(shù)據(jù)錯誤。排查思路先用示波器看 SCK 波形有沒有過沖、振鈴檢查 MISO 采樣點是否在數(shù)據(jù)穩(wěn)定區(qū)降低時鐘測試如果降速后正常就是信號完整性問題在 SCK 和 MOSI 上串 22Ω~33Ω 電阻縮短走線增加地線隔離我在一個項目里 SPI 跑 16MHz讀 1KB 數(shù)據(jù)偶爾錯幾個字節(jié)。后來把 SCK 串了 27Ω 電阻錯誤率從每天幾次降到幾乎為零。這個電阻的作用是匹配阻抗吸收反射成本幾分錢效果立竿見影。6.3 掉電保存失敗掉電保存失敗通常是電源儲能不夠或者 HLVD 閾值設(shè)得太低。排查步驟用示波器看掉電時 VCC 下降曲線算一下從閾值到單片機最低工作電壓的時間檢查電容容量10μF 不夠就加到 47μF 或 100μF確認 HLVD 中斷優(yōu)先級最高沒有被其他中斷阻塞保存函數(shù)盡量精簡不要調(diào)用浮點運算或復雜邏輯我一般會在保存函數(shù)里只寫最核心的數(shù)據(jù)比如累計量和故障標志其他非關(guān)鍵數(shù)據(jù)可以丟。這樣保存時間最短對電源要求最低。6.4 多設(shè)備共享 SPI 總線的片選沖突如果一個 SPI 總線上掛多個從設(shè)備CS 控制就很重要。常見問題是兩個設(shè)備的 CS 同時被拉低導致數(shù)據(jù)沖突。我的做法是每個設(shè)備的 CS 單獨用一個 GPIO初始化時所有 CS 都拉高切換設(shè)備時先拉高當前 CS再拉低目標 CS中間加 1μs 延時不要在中斷里切換 SPI 設(shè)備容易亂如果設(shè)備多可以用 74HC138 譯碼器擴展片選節(jié)省 IO 口。但譯碼器輸出要加上拉電阻防止上電瞬間誤選。7. 實際項目中的經(jīng)驗沉淀這個方案我在三個項目里落地過一個電力監(jiān)測終端一個電機參數(shù)記錄儀一個環(huán)境數(shù)據(jù)采集器。電力監(jiān)測那個項目要求每 20ms 記錄一次三相數(shù)據(jù)MRAM 連續(xù)寫了兩年多沒出過一次數(shù)據(jù)丟失。電機參數(shù)記錄儀工作在變頻器旁邊電磁干擾很強最初 SPI 跑 8MHz 偶爾出錯后來降到 4MHz 并加了屏蔽線穩(wěn)定運行至今。環(huán)境采集器是電池供電MRAM 的待機電流只有 10μA配合單片機的休眠模式兩節(jié) 18650 電池撐了一年半。有幾個細節(jié)是我踩過坑之后才明白的。第一MRAM 的 WP 引腳不要直接接地雖然接地是寫保護但有些批次接地后連狀態(tài)寄存器都寫不了最好通過 GPIO 控制需要寫的時候拉高。第二SPI 初始化之前先把 CS 拉高否則上電瞬間 CS 浮動可能導致 MRAM 進入錯誤狀態(tài)。第三讀操作不需要 WREN但寫操作每次都要發(fā)這個別搞混。如果你正在選型階段我的建議是數(shù)據(jù)量小于 1MB、寫入頻繁、掉電不能丟、環(huán)境溫度高這四條滿足兩條以上就值得上 MRAM。成本確實比 EEPROM 高但省掉的電池、超級電容、復雜掉電保護電路綜合算下來不一定虧。特別是工業(yè)設(shè)備現(xiàn)場維護一次的成本可能比芯片差價高幾十倍。最后分享一個調(diào)試小技巧用邏輯分析儀抓 SPI 波形時把 CS、SCK、MOSI、MISO 四根線都接上設(shè)置 CS 下降沿觸發(fā)這樣每次讀寫事務(wù)都能完整看到。MRAM 的時序很標準對照數(shù)據(jù)手冊的時間參數(shù)一看就知道問題在哪。我用的是一款國產(chǎn)的 8 通道邏輯分析儀一百多塊錢比示波器方便抓 SPI 協(xié)議還能自動解碼強烈建議入手一個。