不掉電存儲(chǔ)的SPI讀寫方案)
在做工業(yè)嵌入式設(shè)備的這些年間數(shù)據(jù)存儲(chǔ)始終是最讓我頭疼的環(huán)節(jié)之一?,F(xiàn)場(chǎng)設(shè)備要掉電保存參數(shù)、要記錄運(yùn)行日志、要頻繁更新配置MR25H40CDF 這顆 SPI 接口的 MRAM 芯片配上 PIC18LF46K80 這顆帶 CAN 控制器的 8 位單片機(jī)把“掉電丟數(shù)據(jù)”和“Flash 寫穿”兩個(gè)老大難問題一次解決了。這篇就把選型依據(jù)、硬件接線、C 語言讀寫實(shí)現(xiàn)、現(xiàn)場(chǎng)調(diào)試踩過的坑完整記錄下來給做工業(yè)控制、儀器儀表和車載電子的同行一個(gè)可直接抄作業(yè)的參考方案。我最早接觸到這對(duì)組合是在一個(gè)需要連續(xù)記錄設(shè)備運(yùn)行狀態(tài)的項(xiàng)目里。原來的方案用 SPI NOR Flash 存日志結(jié)果現(xiàn)場(chǎng)反饋回來一堆壞塊和丟數(shù)據(jù)的問題查到最后發(fā)現(xiàn)是寫入頻率太高Flash 的擦寫壽命被提前耗盡了。換 EEPROM 容量不夠換帶電池的 SRAM 又怕電池漏液和定期維護(hù)最后目光落到了 MRAM 上。MR25H40CDF 是 Everspin 的 4Mbit 串行 MRAM磁阻隨機(jī)存儲(chǔ)掉電不丟寫前不用擦除寫入壽命比 Flash 高了幾個(gè)數(shù)量級(jí)。PIC18LF46K80 則是 Microchip 的增強(qiáng)型 8 位 MCU自帶 CAN 2.0B 控制器、12 位 ADC、硬件 SPI 和多個(gè)串口配上 XC8 編譯器用 C 語言開發(fā)整體方案既工業(yè)又順手。1. 為什么工業(yè)現(xiàn)場(chǎng)我選了“MRAM 8位機(jī)”這對(duì)組合1.1 工業(yè)數(shù)據(jù)存儲(chǔ)的三個(gè)老大難問題先聊聊工業(yè)設(shè)備里數(shù)據(jù)存儲(chǔ)的特殊性。消費(fèi)電子產(chǎn)品里Flash 存固件、EEPROM 存配置一年也寫不了幾次所以壽命壓力不大。但工業(yè)現(xiàn)場(chǎng)完全不是這個(gè)玩法。設(shè)備可能每隔幾秒就記一條運(yùn)行數(shù)據(jù)一天下來就是上萬次寫入操作工可能一天改十幾次工藝參數(shù)每次都要保存到非易失存儲(chǔ)里。普通 NOR Flash 的寫入壽命通常在 10 萬次到 100 萬次之間看著不少但按照這個(gè)頻率算幾個(gè)月到一兩年就寫穿了。寫穿之后要么數(shù)據(jù)錯(cuò)亂要么整個(gè)扇區(qū)變成只讀現(xiàn)場(chǎng)設(shè)備只能停機(jī)返廠損失遠(yuǎn)超一片芯片的價(jià)格。第二個(gè)問題是寫入速度。NOR Flash 寫數(shù)據(jù)之前要先擦除一個(gè)扇區(qū)擦除動(dòng)輒幾十到幾百毫秒。如果設(shè)備突然斷電系統(tǒng)需要在極短時(shí)間內(nèi)把關(guān)鍵現(xiàn)場(chǎng)數(shù)據(jù)保存下來備電電容只能撐幾十毫秒Flash 根本來不及完成擦除再寫入的操作。EEPROM 倒是可以按字節(jié)寫但單次寫的等待時(shí)間也要 3 到 5 毫秒而且大容量 EEPROM 價(jià)格偏高容量還往往不夠用。第三個(gè)問題是環(huán)境溫度。很多工業(yè)控制器裝在戶外或高溫機(jī)柜里環(huán)境溫度經(jīng)常超過 70 攝氏度部分軍工和車載場(chǎng)景要求 -40 到 125 攝氏度。民用級(jí) Flash 芯片在高溫下數(shù)據(jù)保持時(shí)間會(huì)大幅縮短最后演變成“寫進(jìn)去當(dāng)時(shí)是好的過幾天就丟數(shù)據(jù)”這種最難查的故障。MRAM 是磁阻存儲(chǔ)靠磁性狀態(tài)保存數(shù)據(jù)天然不受溫度導(dǎo)致電荷泄漏的影響高溫下的數(shù)據(jù)保持特性比 Flash 和 EEPROM 靠譜得多。1.2 為什么選 PIC18LF46K80 而不是隨便一顆 ARM先說結(jié)論這個(gè)項(xiàng)目不是跑不了 ARM而是沒有必要。工業(yè)控制器講究的是穩(wěn)定、供貨周期長(zhǎng)、外設(shè)匹配。PIC18LF46K80 這顆片子有 64KB 程序閃存、3.7KB 數(shù)據(jù) RAM、1KB EEPROM集成了 CAN 2.0B 控制器、12 位 ADC、兩個(gè) EUSART、兩個(gè) MSSP 硬件 SPI/I2C 模塊、多個(gè)定時(shí)器和 CCP/ECCP 模塊。對(duì)于大多數(shù)工業(yè)數(shù)據(jù)采集和控制場(chǎng)景這個(gè)外設(shè)規(guī)模完全夠用主頻 64MHz處理 PID 算法、Modbus 協(xié)議、CAN 報(bào)文收發(fā)綽綽有余。LF 后綴代表低電壓版本工作電壓 1.8V 到 3.6V這樣可以直接使用 3.3V 供電和 MR25H40CDF 的電源軌完全一致不需要額外的電平轉(zhuǎn)換芯片。這一點(diǎn)在實(shí)際硬件設(shè)計(jì)里能省掉很多麻煩。如果選 5V 供電的 PIC18F46K80MRAM 是 3.3V 器件SPI 信號(hào)電平匹配就得額外做處理要么加轉(zhuǎn)換芯片要么所有 IO 配置成開漏加上拉徒增復(fù)雜度。另外要夸一下 Microchip 的供貨策略。8 位 PIC 系列生命周期極長(zhǎng)很多型號(hào)官方承諾 15 年以上供應(yīng)周期。這對(duì)工業(yè)產(chǎn)品是剛需設(shè)備要賣十年芯片不能五年后停產(chǎn)。ARM 內(nèi)核的 MCU 雖然性能強(qiáng)但選型時(shí)要花大量精力評(píng)估供貨穩(wěn)定性、停產(chǎn)風(fēng)險(xiǎn)、開發(fā)工具鏈。PIC 配合 MPLAB X IDE 和免費(fèi)的 XC8 編譯器一個(gè)人從零開始寫工程半天就能點(diǎn)亮。對(duì)老工程師來說是老朋友對(duì)新手來說學(xué)習(xí)曲線也很平緩。還有一個(gè)容易被忽視的理由PIC18LF46K80 自帶 CAN 控制器。工業(yè)現(xiàn)場(chǎng)設(shè)備互通CAN 總線是絕對(duì)主力尤其是車輛、工程機(jī)械、分布式控制系統(tǒng)。這顆芯片把 CAN 控制器集成在內(nèi)部外圍只需要加一個(gè) CAN 收發(fā)器芯片就能組網(wǎng)成本低抗干擾能力也比普通串口好。數(shù)據(jù)采集、存儲(chǔ)、上報(bào)、組網(wǎng)一次搞定整個(gè)方案的物料清單非常簡(jiǎn)潔。2. 關(guān)鍵硬件設(shè)計(jì)與電路連接細(xì)節(jié)2.1 MR25H40CDF 芯片特性與引腳說明MR25H40CDF 是一顆 4Mbit 的串行 MRAM也就是 512KB 存儲(chǔ)空間供電電壓 3.3VSPI 接口最高串行時(shí)鐘可達(dá) 40MHz 級(jí)別具體以數(shù)據(jù)手冊(cè)為準(zhǔn)支持 SPI Mode 0 和 Mode 3。它的指令集設(shè)計(jì)成和傳統(tǒng) SPI NOR Flash 高度兼容所以大多數(shù)現(xiàn)成的 SPI Flash 驅(qū)動(dòng)代碼稍作修改就能跑起來這對(duì)快速移植非常友好。芯片一共 8 個(gè)引腳常見 DFN8 封裝或者 SOP8 封裝引腳定義很清楚。CS# 是片選低電平有效SCK 是串行時(shí)鐘SI 是串行輸入MOSISO 是串行輸出MISOWP# 是寫保護(hù)低電平有效HOLD# 是暫停輸入低電平有效VDD 和 VSS 是電源和地。這兩個(gè)控制引腳在普通讀寫場(chǎng)景下一定要處理妥當(dāng)很多新手栽跟頭就栽在這里。引腳名稱功能說明連接建議CS#片選低電平選中接 MCU GPIO加 10k 上拉到 VDDSCKSPI 時(shí)鐘接 MCU SCK 引腳SI串行數(shù)據(jù)輸入接 MCU SDOMOSISO串行數(shù)據(jù)輸出接 MCU SDIMISOWP#寫保護(hù)直接接 VDD禁用寫保護(hù)HOLD#暫停通信直接接 VDD確保不掛起VDD電源 3.3V加 0.1uF 和 10uF 去耦電容VSS地接系統(tǒng)地HOLD# 引腳如果懸空在受到干擾時(shí)可能會(huì)意外拉低導(dǎo)致芯片進(jìn)入暫停狀態(tài)主機(jī)發(fā)數(shù)據(jù)毫無反應(yīng)表現(xiàn)出來就是 SPI 通信超時(shí)。WP# 懸空同樣有風(fēng)險(xiǎn)一旦被拉低狀態(tài)寄存器里的 WPEN 位會(huì)被激活寫操作直接無效。我的習(xí)慣是這兩個(gè)引腳都通過 10k 電阻上拉到 VDD上電默認(rèn)鎖定在高電平從根上排除隱患。2.2 PIC18LF46K80 側(cè)的電路要素PIC18LF46K80 的電源就近放置 0.1uF 和 10uF 的去耦電容這一點(diǎn)在工業(yè)環(huán)境里格外重要?,F(xiàn)場(chǎng)電機(jī)啟停、繼電器吸合都會(huì)在電源線上制造巨大的毛刺去耦不良的板子會(huì)在高速 SPI 通信時(shí)隨機(jī)出 bit 錯(cuò)誤。所有數(shù)字 IO 的串入電阻可以按需加 33 到 100 歐姆有效抑制邊沿振鈴。SPI 接線方面MR25H40CDF 的數(shù)據(jù)輸入 SI 接 PIC 的 SDO 引腳數(shù)據(jù)輸出 SO 接 PIC 的 SDI 引腳時(shí)鐘 SCK 對(duì) SCK片選可以接到任意普通 GPIO。這里我建議不要把 CS# 直接接地雖然這顆芯片只掛在一個(gè) SPI 總線上時(shí)接地也能工作但后期如果多掛一顆 Flash 或者傳感器CS# 就必須由主機(jī)控制。用 GPIO 控制 CS# 還有一個(gè)額外好處即使 SPI 模塊配置出了問題軟件也能通過手動(dòng)拉低 CS# 來模擬時(shí)序調(diào)試階段極其有用。上電時(shí)序這個(gè)問題容易忽略。MR25H40CDF 內(nèi)部有上電復(fù)位邏輯VDD 上升到穩(wěn)定電平后芯片需要一個(gè)短暫的時(shí)間來完成內(nèi)部初始化典型時(shí)間在幾百微秒到幾毫秒量級(jí)。如果 MCU 復(fù)位完成后立刻發(fā)起 SPI 操作可能碰到芯片還沒準(zhǔn)備好的窗口第一條指令就丟失了。穩(wěn)妥做法是在系統(tǒng)初始化代碼里對(duì) MRAM 執(zhí)行片選拉低再拉高的空操作并延時(shí) 1 毫秒以上再開始正式通信。后來的經(jīng)驗(yàn)告訴我這 1 毫秒的投資非常值。3. 讀寫程序?qū)崿F(xiàn)從 SPI 協(xié)議到可跑的 C 代碼3.1 SPI 模式配置與初始化MR25H40CDF 支持 SPI Mode 0 和 Mode 3兩者在時(shí)鐘極性和相位定義上不同。PIC 的 MSSP 模塊通過設(shè)置時(shí)鐘極性位和時(shí)鐘邊沿位來匹配。我的建議是優(yōu)先使用 Mode 0即空閑時(shí)時(shí)鐘為低、數(shù)據(jù)在上升沿采樣。理由主要有兩個(gè)一是大部分 MCU 的硬件 SPI 默認(rèn)模式就是 Mode 0配置最簡(jiǎn)單二是 Mode 0 和 Mode 3 在實(shí)際通信中都能正常工作選擇 Mode 0 意味著將來換用其他 MCU 平臺(tái)時(shí)代碼移植改動(dòng)最小。下面這段是基于 MPLAB X XC8 的 SPI 初始化代碼以 PIC18LF46K80 的 MSSP 模塊為例。需要注意的是不同型號(hào)的 MSSP 實(shí)例編號(hào)可能不同使用前請(qǐng)查對(duì)你手上芯片數(shù)據(jù)手冊(cè)中 SPI 模塊的具體寄存器名。void spi_init(void) { // 設(shè)置 SPI 引腳SCK、SDO 為輸出SDI 為輸入 TRISCbits.TRISC3 0; // SCK 輸出 TRISCbits.TRISC5 0; // SDO 輸出 TRISCbits.TRISC4 1; // SDI 輸入 // CS 引腳配置為普通 GPIO 輸出初始為高電平未選中 CS_MRAM_TRIS 0; CS_MRAM 1; // MSSP 配置為 SPI Master 模式Mode 0時(shí)鐘 FOSC / 16 SSPCON1 0x00; SSPCON1bits.SSPM 0b0010; // SPI Master mode, clock FOSC/64 SSPCON1bits.SSPEN 1; // 使能 MSSP SSPSTATbits.SMP 1; // 輸入采樣在數(shù)據(jù)輸出末尾 }關(guān)于分頻系數(shù)FOSC/64 是把 64MHz 系統(tǒng)頻率分到 1MHz 的 SPI 時(shí)鐘。這個(gè)速度對(duì) MRAM 來說非常輕松而且低速能顯著提高抗干擾能力。調(diào)試階段從低速開始跑通了以后再逐步提高分頻這是一個(gè)靠譜的開發(fā)節(jié)奏。3.2 寫使能每次寫入前不能少的操作MR25H40CDF 的指令集里寫操作前置條件是先發(fā)送寫使能指令 0x06。這個(gè)設(shè)計(jì)是沿襲了標(biāo)準(zhǔn) SPI NOR Flash 的習(xí)慣目的是防止誤寫。在實(shí)際程序里必須保證 0x06 指令發(fā)出后緊接著發(fā)起后續(xù)的寫狀態(tài)寄存器或?qū)憯?shù)據(jù)指令中間不要夾雜其他 SPI 操作。寫使能后可以通過讀狀態(tài)寄存器 0x05 來確認(rèn) WEL 位是否置 1。狀態(tài)寄存器的 bit 1 就是 WELWrite Enable Latch讀出來的值為 0x02 時(shí)代表寫使能已經(jīng)生效。這個(gè)確認(rèn)步驟在調(diào)試階段很有用可以把問題快速定位到是外部原因還是軟件原因。void mram_write_enable(void) { CS_MRAM 0; spi_write_byte(0x06); // WRITE ENABLE 指令 CS_MRAM 1; } unsigned char mram_read_status(void) { unsigned char status; CS_MRAM 0; spi_write_byte(0x05); // READ STATUS 指令 status spi_read_byte(); CS_MRAM 1; return status; }寫完使能、讀狀態(tài)、確認(rèn) WEL再到正式寫數(shù)據(jù)這段代碼看起來多做了幾步但實(shí)際價(jià)值非常大。我見過太多現(xiàn)場(chǎng)故障報(bào)告說“數(shù)據(jù)寫不進(jìn)去”最后發(fā)現(xiàn)就是代碼里漏了寫使能或者寫使能和寫數(shù)據(jù)之間又插了別的 SPI 占用操作導(dǎo)致 WEL 被清零。3.3 數(shù)據(jù)讀取與寫入的完整函數(shù)讀數(shù)據(jù)用 0x03 指令操作最簡(jiǎn)單。MCU 先拉低 CS#發(fā)送讀指令和高 16 位地址然后連續(xù)產(chǎn)生時(shí)鐘芯片會(huì)把指定地址開始的數(shù)據(jù)依次從 SO 引腳送出來。MRAM 支持連續(xù)讀不需要像 Flash 那樣按頁翻讀多長(zhǎng)都行只要主機(jī)持續(xù)給時(shí)鐘。void mram_read_data(unsigned long addr, unsigned char *buf, unsigned int len) { unsigned int i; CS_MRAM 0; spi_write_byte(0x03); // READ DATA spi_write_byte((addr 16) 0xFF); // 地址高字節(jié) spi_write_byte((addr 8) 0xFF); // 地址中字節(jié) spi_write_byte(addr 0xFF); // 地址低字節(jié) for (i 0; i len; i) { buf[i] spi_read_byte(); // 連續(xù)讀回?cái)?shù)據(jù) } CS_MRAM 1; }寫數(shù)據(jù)使用 0x02 指令邏輯上比 Flash 寫簡(jiǎn)單得多關(guān)鍵區(qū)別在于不需要發(fā)擦除命令不需要等待內(nèi)部擦寫完成。每條指令如下拉低 CS#發(fā)送 0x02 指令、3 字節(jié)地址然后連續(xù)發(fā)送要寫的數(shù)據(jù)最后拉高 CS# 事務(wù)結(jié)束。MRAM 在內(nèi)部會(huì)自動(dòng)完成寫入典型值在幾十納秒級(jí)別從主機(jī)視角看寫完立刻就可以讀甚至不需要查詢忙狀態(tài)。void mram_write_data(unsigned long addr, unsigned char *buf, unsigned int len) { unsigned int i; mram_write_enable(); // 寫操作前必須發(fā)寫使能 CS_MRAM 0; spi_write_byte(0x02); // WRITE DATA spi_write_byte((addr 16) 0xFF); spi_write_byte((addr 8) 0xFF); spi_write_byte(addr 0xFF); for (i 0; i len; i) { spi_write_byte(buf[i]); // 連續(xù)寫入數(shù)據(jù) } CS_MRAM 1; }如果跨 64KB 邊界理論上需要把地址拆成兩次操作。不過實(shí)際工程中我會(huì)在應(yīng)用層約定好存儲(chǔ)結(jié)構(gòu)把參數(shù)區(qū)和日志區(qū)分別規(guī)劃在固定地址段每次寫入不超過 256 字節(jié)這樣既簡(jiǎn)化了驅(qū)動(dòng)代碼也方便將來做數(shù)據(jù)管理。這個(gè)習(xí)慣從 Flash 時(shí)代保留下來放 MRAM 上依然合理。這里再補(bǔ)充一個(gè)使用習(xí)慣上的細(xì)節(jié)MRAM 的寫操作不需要擦除因?yàn)樗拇鎯?chǔ)單元不是靠電荷而是靠磁阻狀態(tài)表示數(shù)據(jù)寫入時(shí)直接覆蓋舊值。所以完全不需要像 Flash 驅(qū)動(dòng)那樣做“讀-改-寫”的操作更不需要維護(hù)塊映射表。這也是它能替代“SRAM電池”方案的核心優(yōu)勢(shì)。4. 實(shí)測(cè)性能表現(xiàn)與功耗評(píng)估4.1 寫入速度和壽命對(duì)比以前用 NOR Flash 時(shí)最難受的就是擦除操作。往 Flash 里寫一頁 256 字節(jié)要先擦除整個(gè)扇區(qū)等扇區(qū)擦除完成再逐頁編程整個(gè)過程幾百毫秒。而 MR25H40CDF 的寫入是完全即時(shí)性的數(shù)據(jù)字節(jié)被時(shí)鐘打入后內(nèi)部立刻完成存儲(chǔ)狀態(tài)翻轉(zhuǎn)無需等待。實(shí)測(cè)在 1MHz SPI 時(shí)鐘下傳輸 256 字節(jié)數(shù)據(jù)全程耗時(shí)大約 2 毫秒出頭其中絕大多數(shù)時(shí)間花在 SPI 字節(jié)傳輸上芯片內(nèi)部寫入時(shí)間幾乎可以忽略。把 SPI 時(shí)鐘提高到 5MHz 以上256 字節(jié)寫入時(shí)間可以壓到 0.5 毫秒以下。這個(gè)速度在掉電保存場(chǎng)景非常關(guān)鍵。控制器的備電電容如果只夠維持 3 毫秒Flash 方案可能連扇區(qū)擦除都完不成而 MRAM 方案已經(jīng)可以把幾百字節(jié)的現(xiàn)場(chǎng)數(shù)據(jù)完整寫進(jìn)去了。壽命方面是 MRAM 的絕對(duì)主場(chǎng)。常規(guī) EEPROM 寫入壽命標(biāo)稱 100 萬次SPI NOR Flash 普遍在 10 萬次到 100 萬次。MR25H40CDF 的標(biāo)稱寫入耐久性在 10^14 次以上這是什么概念就算每微秒寫一次也要寫三年多才夠到壽命上限。實(shí)際工業(yè)設(shè)備每秒寫 100 次連續(xù)跑十年也才消耗了壽命的極小一部分。這意味著在設(shè)計(jì)數(shù)據(jù)存儲(chǔ)策略時(shí)基本不需要寫均衡算法不需要磨損均衡邏輯徹底省心。4.2 功耗與數(shù)據(jù)保持工業(yè)設(shè)備經(jīng)常要求低功耗尤其是電池供電的采集終端。MR25H40CDF 在待機(jī)狀態(tài)下的功耗極低典型待機(jī)電流在微安級(jí)別。相比帶電池的 SRAM 方案MRAM 方案省掉了電池供電電路和掉電檢測(cè)電路整個(gè)系統(tǒng)的靜態(tài)功耗反而更低還消除了電池漏液、更換維護(hù)、高溫鼓包等一串隱患。數(shù)據(jù)保持方面MRAM 的優(yōu)勢(shì)是天然免疫于溫度和輻射導(dǎo)致的電荷衰減。Flash 靠浮柵電荷存儲(chǔ)數(shù)據(jù)電荷會(huì)隨時(shí)間緩慢泄漏溫度越高泄漏越快高溫環(huán)境下的數(shù)據(jù)保持時(shí)間可能縮短到幾年甚至幾個(gè)月。MRAM 依靠磁性隧道結(jié)的磁化方向存儲(chǔ)數(shù)據(jù)不存在電荷泄漏問題數(shù)據(jù)保持時(shí)間按年和系統(tǒng)壽命來算。磁存儲(chǔ)還有一個(gè)特性是抗輻射gamma 射線、重離子對(duì)磁化狀態(tài)的影響遠(yuǎn)小于對(duì)電荷存儲(chǔ)的影響所以在航天、軍工和核工業(yè)場(chǎng)景MRAM 是少數(shù)能用的非易失存儲(chǔ)方案。不過要提醒一句MRAM 雖然很多指標(biāo)優(yōu)秀但它和所有電子器件一樣會(huì)被強(qiáng)磁場(chǎng)影響。系統(tǒng)設(shè)計(jì)時(shí)不要把 MRAM 芯片靠近大功率電機(jī)、電磁鐵或強(qiáng)磁體布置 PCB 時(shí)保持至少 1 厘米以上的距離否則強(qiáng)磁場(chǎng)可能翻轉(zhuǎn)存儲(chǔ)單元的磁化方向?qū)е聰?shù)據(jù)被改寫。這一點(diǎn)在電機(jī)控制板設(shè)計(jì)時(shí)尤其要注意。5. 常見問題與現(xiàn)場(chǎng)排查實(shí)錄5.1 讀出來全是 0xFF 的排查順序這是我在工程中最常遇到的第一個(gè)問題幾乎每個(gè)第一次用 MRAM 的開發(fā)者都會(huì)碰到。出現(xiàn)讀回?cái)?shù)據(jù)全是 0xFF按照這個(gè)順序查一般幾分鐘內(nèi)能定位先查供電。MRAM 的 VDD 是不是 3.3V用萬用表實(shí)測(cè)芯片引腳上的電壓別只看電源指示燈亮不亮。很多開發(fā)板上電瞬間電壓下降或者在長(zhǎng)走線末端電壓跌落芯片等于沒工作。再查 CS# 引腳確認(rèn)片選信號(hào)確實(shí)被拉低有些代碼里 GPIO 初始化順序不對(duì)上電瞬間 CS# 懸空之后雖然代碼拉低了但第一次事務(wù)可能已經(jīng)被破壞。接下來查 HOLD# 和 WP#這兩個(gè)引腳如果懸空在帶電插拔、靜電放電時(shí)可能出現(xiàn)低電平芯片被掛起或?qū)懕Wo(hù)表現(xiàn)為要么不響應(yīng)要么只能讀不能寫。如果硬件檢查都正常那就是 SPI 模式不匹配。MRAM 支持 Mode 0 和 Mode 3而 MCU 側(cè)默認(rèn)可能是 Mode 0 也可能是 Mode 1比如某些 STM32 的 SPI 默認(rèn) CPOL 和 CPHA 配置就和 Mode 0 不一樣。檢查你的 SPI 模塊配置確保時(shí)鐘極性和采樣沿與芯片一致。用示波器看 SCK 空閑電平最直觀Mode 0 空閑為低Mode 3 空閑為高。5.2 數(shù)據(jù)寫不進(jìn)或者寫完讀不對(duì)寫不進(jìn)的問題九成出在寫使能環(huán)節(jié)。程序里發(fā)送 0x06 寫使能后必須在下一條寫指令之前保持 WEL 位有效。如果中間插入了讀狀態(tài)寄存器以外的操作或者 CS# 被拉高又拉低WEL 可能被清掉。正確的做法是寫使能和寫數(shù)據(jù)盡量連續(xù)公共驅(qū)動(dòng)函數(shù)內(nèi)部完成這兩個(gè)操作對(duì)外只暴露“寫某地址某數(shù)據(jù)”的 API不讓調(diào)用者有機(jī)會(huì)在中間插入其他操作。還有一種現(xiàn)象是寫入后立刻讀數(shù)據(jù)是錯(cuò)的但再讀一次又對(duì)了。這種大概率是 SPI 時(shí)序邊沿問題。MCU 采樣和數(shù)據(jù)翻轉(zhuǎn)的相位與芯片實(shí)際輸出不匹配導(dǎo)致第一位或最后一位丟失。把 SPI 時(shí)鐘頻率降低一半再試如果問題消失就是高速模式下的時(shí)序裕量不足。解決辦法是調(diào)整 SMP 位和時(shí)鐘分頻找到最穩(wěn)定的配置。5.3 上電瞬間的數(shù)據(jù)被篡改這個(gè)故障很隱蔽我一度排查了很久?,F(xiàn)象是設(shè)備斷電再上電后MRAM 里原本保存的參數(shù)在某個(gè)固定地址上出現(xiàn)隨機(jī)變化。查到最后發(fā)現(xiàn)是 MCU 復(fù)位期間 GPIO 處于高阻態(tài)如果 CS# 引腳連接的線路有較長(zhǎng)的寄生電容和上拉電阻復(fù)位瞬間 CS# 可能短暫處于低電平MRAM 檢測(cè)到片選有效同時(shí) SCK 引腳又受到電源爬坡時(shí)的干擾跳動(dòng)了幾拍結(jié)果就發(fā)生了一次意外的寫操作。解決方案很直接給 CS# 加一個(gè) 10k 到 100k 的下拉電阻確保 MCU 復(fù)位期間 CS# 維持在高電平強(qiáng)制芯片不被選中。同時(shí)把 WP# 和 HOLD# 接 VDD即使出現(xiàn)異常片選芯片也處于寫保護(hù)和正常通信狀態(tài)被誤寫的概率大幅下降。對(duì)于一些對(duì)可靠性要求極高的設(shè)備還可以在上電初始化時(shí)先向關(guān)鍵參數(shù)區(qū)寫入一串固定的校驗(yàn)字節(jié)程序啟動(dòng)時(shí)校驗(yàn)失敗就判定為存儲(chǔ)區(qū)數(shù)據(jù)異常自動(dòng)恢復(fù)默認(rèn)參數(shù)并重新初始化。5.4 工業(yè)現(xiàn)場(chǎng)的電磁干擾和布線策略MRAM 本身抗干擾能力不弱但 SPI 總線的數(shù)據(jù)線在長(zhǎng)距離傳輸時(shí)仍然會(huì)被現(xiàn)場(chǎng)電磁噪聲干擾。我遇到的典型場(chǎng)景是把存儲(chǔ)板通過排線連接到主控板排線長(zhǎng)度超過 20 厘米結(jié)果在伺服電機(jī)啟停時(shí)出現(xiàn)偶發(fā)的讀寫數(shù)據(jù)錯(cuò)位。排查時(shí)用示波器觀察 SCK 和 SO 線上的波形可以看到明顯的振鈴噪聲。針對(duì)這類問題我的處理方案是SPI 時(shí)鐘頻率從 2MHz 降到 500kHz信號(hào)完整性大幅改善SCK、SI、SO 三根線并聯(lián) 33pF 到 50pF 對(duì)地電容濾除高頻噪聲所有 SPI 信號(hào)線在 PCB 上用地線包絡(luò)減少環(huán)路面積如果排線較長(zhǎng)改用帶屏蔽層的 FFC 排線屏蔽層單端接地。這些措施全部實(shí)施后問題徹底消失。順便說一句把 SPI 通信的 CS# 控制和數(shù)據(jù)校驗(yàn)都搬進(jìn)中斷里而不是靠主循環(huán)輪詢能夠顯著減少因?yàn)槿蝿?wù)調(diào)度延遲導(dǎo)致的時(shí)序抖動(dòng)。6. 這套方案還能怎么用一點(diǎn)個(gè)人體會(huì)項(xiàng)目做完之后我把這套“SPI 接口 MRAM 低電壓 8 位 MCU”的組合方案沉淀成了模塊在后面好幾個(gè)產(chǎn)品里直接復(fù)用。一個(gè)典型的適用場(chǎng)景是設(shè)備運(yùn)行日志系統(tǒng)PIC18LF46K80 通過 CAN 總線收集各節(jié)點(diǎn)數(shù)據(jù)按時(shí)間戳寫入 MRAM掉電不丟上電后通過 Modbus RTU 或 USB 轉(zhuǎn)串口導(dǎo)出上位機(jī)解析生成 CSV 文件。整個(gè)過程不需要文件系統(tǒng)不需要掉電檢測(cè)不需要備用電池硬件成本壓得很低可靠性卻很高。具體到代碼路徑上我再分享一個(gè)實(shí)際經(jīng)驗(yàn)的排序先把 SPI 裸讀寫跑通用固定數(shù)據(jù)模式反復(fù)讀寫校驗(yàn)再把狀態(tài)寄存器檢查加上確保每次寫入前 WEL 總是 1然后在應(yīng)用層加 CRC16 校驗(yàn)字段讓每條記錄獨(dú)立可驗(yàn)證最后再做掉電測(cè)試特意讓 MCU 在寫入過程中斷電再上電去檢查數(shù)據(jù)區(qū)完整性。這一套流程下來基本上現(xiàn)場(chǎng)出現(xiàn)的問題都能被提前攔截。我記得有次去客戶現(xiàn)場(chǎng)處理故障設(shè)備在高溫高濕的車間里連續(xù)運(yùn)行了半年其他板卡上的 Flash 存儲(chǔ)芯片已經(jīng)出現(xiàn)扇區(qū)錯(cuò)誤而這套 MRAM 方案的設(shè)備一直穩(wěn)定運(yùn)行數(shù)據(jù)一條沒丟。那一刻我就覺得選型時(shí)候較真的那些參數(shù)最終都在長(zhǎng)期可靠性上還回來了。如果讀者朋友也在做工業(yè)數(shù)據(jù)采集和存儲(chǔ)相關(guān)的工作手里有 PIC 平臺(tái)或者類似 SPI 主控強(qiáng)烈建議試試 MR25H40CDF 這顆芯片驅(qū)動(dòng)代碼半小時(shí)就能移植完卻能把你從 Flash 壽命和掉電保存的泥潭里徹底解放出來。