記錄儀高可靠存儲(chǔ)實(shí)戰(zhàn))
MR25H40CDF 這顆 4Mbit 串行 MRAM 我盯了很久最近終于在一套以 PIC18F65K40 做主控的工業(yè)記錄儀上把它完整跑通了。做嵌入式這些年跟存儲(chǔ)打交道的次數(shù)不少EEPROM、Nor Flash、SD 卡、鐵電都折騰過但“存數(shù)據(jù)”這件事永遠(yuǎn)是看著簡(jiǎn)單做起來全是坑。一個(gè)現(xiàn)場(chǎng)設(shè)備既要頻繁記錄報(bào)警事件、運(yùn)行曲線、計(jì)量數(shù)據(jù)又要求在斷電瞬間不丟數(shù)、連續(xù)寫幾年不壞還得在 -40℃ 的戶外機(jī)柜里老老實(shí)實(shí)工作能同時(shí)滿足這些條件的方案其實(shí)沒幾個(gè)。這篇內(nèi)容我打算直接拆項(xiàng)目為什么選 MR25H40CDF 而不是 EEPROM 或 FlashPIC18F65K40 作為主控怎么把 SPI 發(fā)揮到極限硬件上要注意哪些細(xì)節(jié)驅(qū)動(dòng)代碼怎么寫掉電保護(hù)怎么做以及在調(diào)試和現(xiàn)場(chǎng)運(yùn)行中遇到過哪些奇葩問題。適合正在做數(shù)據(jù)記錄儀、工業(yè)控制器、儀器儀表或者任何有“頻繁非易失性寫入”需求的嵌入式工程師參考。我會(huì)盡量把寄存器配置、時(shí)序、數(shù)據(jù)格式都說透讓大家少走我踩過的彎路。1. 選型思路為什么是 MR25H40CDF PIC18F65K401.1 工業(yè)場(chǎng)景里數(shù)據(jù)存儲(chǔ)到底難在哪很多工程師一說到存儲(chǔ)第一反應(yīng)就是 SPI FlashW25Q64 之類一片幾毛錢容量還大。但在工業(yè)現(xiàn)場(chǎng)Flash 有兩個(gè)繞不開的痛點(diǎn)。第一寫入之前必須先擦除而且 Flash 的最小擦除單位是扇區(qū)4KB 起步你想改一個(gè)字節(jié)都得先把整個(gè)扇區(qū)搬到 RAM 改完再擦再寫這在頻繁小數(shù)據(jù)量寫入的場(chǎng)景里特別別扭。第二Flash 的擦寫壽命一般標(biāo) 10 萬次聽著不少但如果你每 5 秒寫一條狀態(tài)記錄一天就是 17280 次一塊 Flash 塊位撐死也就用幾天。即便加磨損均衡算法也架不住這種密度。EEPROM 倒是可以按字節(jié)寫也沒有擦除負(fù)擔(dān)但容量做不大。常見的 24LC256 也才 32KB寫一頁還動(dòng)不動(dòng)要 5ms實(shí)時(shí)性根本談不上。FRAM 也就是鐵電存儲(chǔ)器在寫速度和壽命上有明顯優(yōu)勢(shì)可大容量的型號(hào)少價(jià)格也偏高。所以工業(yè)級(jí)非易失性存儲(chǔ)的真正需求其實(shí)是容量不能太小、寫入要快、寫次數(shù)幾乎無限、掉電數(shù)據(jù)不丟、溫度范圍要寬。你把這幾個(gè)條件擺在一起MRAM 幾乎是唯一能把每一項(xiàng)都打到 90 分以上的選擇。MR25H40CDF 就是 Everspin 的 4Mbit 串行 MRAM512KB 容量SPI 接口寫次數(shù)官方標(biāo)稱 10 的 12 次方以上寫入延遲基本等于 SRAM沒有擦寫周期、沒有頁對(duì)齊限制數(shù)據(jù)保持也達(dá)到了十年以上。這套指標(biāo)放在工業(yè)設(shè)備里說它是“理想型”一點(diǎn)不過分。1.2 三種常見存儲(chǔ)方案的對(duì)比與取舍我做選型表的時(shí)候習(xí)慣把所有候選方案按幾個(gè)關(guān)鍵維度拉一張表然后逐項(xiàng)打分。這里給出一份對(duì)比覆蓋了最常見的選擇可以給大家做個(gè)參考。維度NOR Flash如 W25Q64EEPROM如 24LC512MRAM如 MR25H40CDF典型容量8MB~64MB64KB~512KB512KB最小寫入單位字節(jié)但必須扇區(qū)擦除字節(jié)字節(jié)寫前擦除需要不需要不需要單次寫入延遲頁編程 1ms~5ms字節(jié)寫 3ms~5ms無額外等待擦寫壽命10 萬次100 萬次10 的 12 次方次功耗讀 15mA 左右寫 3mA 左右寫 15mA 左右掉電數(shù)據(jù)保持20 年以上100 年以上10 年以上強(qiáng)磁場(chǎng)敏感性無無有需注意布局價(jià)格相對(duì)低中高從表里能明顯看出來MRAM 是犧牲了價(jià)格換來了寫壽命和寫速度。但回到工業(yè)設(shè)備的整體成本結(jié)構(gòu)一片 MRAM 也就十幾塊人民幣相比因?yàn)榇鎯?chǔ)磨損導(dǎo)致的返修、拆機(jī)、換板成本這點(diǎn)代價(jià)完全可以接受。我當(dāng)時(shí)跟客戶算了一筆賬如果一套設(shè)備在現(xiàn)場(chǎng)因?yàn)?Flash 寫穿而停機(jī)光是派人過去的路費(fèi)都不止十片 MRAM 的錢。1.3 MR25H40CDF 的硬指標(biāo)拆解MR25H40CDF 這顆片子有幾個(gè)關(guān)鍵參數(shù)值得展開講一講。它是 3.3V 供電工作電流在 6MHz SPI 時(shí)鐘下大約是 25mA待機(jī)電流能做到微安級(jí)所以要注意在低功耗產(chǎn)品里必須在不訪問時(shí)把片選 CS 拉高讓器件進(jìn)入 standby 模式。接口是標(biāo)準(zhǔn) SPI最高時(shí)鐘 40MHz這意味著在 PIC18F65K40 這種內(nèi)部 SPI 外設(shè)上限 16MHz 的主控上存儲(chǔ)芯片的速度不是瓶頸。封裝方面MR25H40CDF 是 SOIC-8 封裝引腳兼容部分 25 系列串行 EEPROM比如你之前板子上用的是 25LC1024幾乎不用改布局就能把 MRAM 焊上去這給硬件改版省了不少事。后綴里的工業(yè)級(jí)溫度范圍是 -40℃ 到 85℃這是基本盤但如果你需要更高的汽車級(jí)溫度范圍可以選相應(yīng)后綴的型號(hào)引腳和時(shí)序基本一致。還有一個(gè)容易被忽略的點(diǎn)MR25H40CDF 是真正的磁阻隨機(jī)存取存儲(chǔ)器寫操作用的是磁場(chǎng)翻轉(zhuǎn)不依賴電荷存儲(chǔ)所以它天然沒有“寫疲勞”的概念也沒有 Flash 那種電荷泵劣化問題。但代價(jià)是它對(duì)強(qiáng)磁場(chǎng)比較敏感安裝位置要遠(yuǎn)離變壓器、大功率電感、永磁電機(jī)這類會(huì)產(chǎn)生強(qiáng)磁場(chǎng)的部件否則理論上可能改寫存儲(chǔ)內(nèi)容。這個(gè)問題我在后面實(shí)際測(cè)試中還真遇到過現(xiàn)場(chǎng)干擾引發(fā)的數(shù)據(jù)翻轉(zhuǎn)雖然不是常態(tài)但布局時(shí)避一避總是穩(wěn)妥的。1.4 PIC18F65K40 在這套方案里的角色PIC18F65K40 是 Microchip 的一款 64 引腳、5V/3.3V 寬電壓供電的 8 位單片機(jī)最高內(nèi)部振蕩器頻率 64MHz帶兩個(gè) MSSP 模塊可以分別配成 SPI、I2C 或 USART這對(duì)一套既有存儲(chǔ)又有對(duì)外通信的工業(yè)設(shè)備來說非常合適。選擇它的原因主要有三個(gè)。第一工具鏈成熟穩(wěn)定MPLAB X XC8 編譯器用了很多年量產(chǎn)固件的穩(wěn)定性有保障第二這芯片在工業(yè)市場(chǎng)上量大、供貨穩(wěn)定價(jià)格便宜幾十顆的打樣價(jià)格也就十幾塊第三K40 系列的引腳外設(shè)功能大部分可以通過寄存器直接配置寄存器命名規(guī)律清晰寫驅(qū)動(dòng)的時(shí)候不用像某些國(guó)產(chǎn)芯片那樣翻半天手冊(cè)還找不到寄存器地址。在我做的這臺(tái)設(shè)備里PIC18F65K40 負(fù)責(zé) Modbus 通信、模擬量采樣、按鍵顯示以及數(shù)據(jù)落盤MRAM 只是它的一個(gè) SPI 從設(shè)備。整板供電統(tǒng)一做成了 3.3V這樣單片機(jī)和 MRAM 之間不需要加電平轉(zhuǎn)換簡(jiǎn)化了硬件設(shè)計(jì)。2. 硬件連接與工程細(xì)節(jié)2.1 供電與電源去耦MRAM 對(duì)電源紋波其實(shí)不算苛刻但既然是工業(yè)設(shè)備電源部分就不能太隨意。我用的方案是整板先由隔離 DC-DC 輸出 5V再經(jīng)過一個(gè)低壓差穩(wěn)壓器得到 3.3V給單片機(jī)、MRAM、傳感器統(tǒng)一供電。3.3V 電源樹的輸出側(cè)預(yù)留一個(gè) 100μF 電解電容和一個(gè) 0.1μF 陶瓷電容靠近 MRAM 電源引腳再補(bǔ)一顆 0.1μF位置離引腳不超過 3mm這是最基本的去耦規(guī)范。有一個(gè)細(xì)節(jié)值得提一下MRAM 在做連續(xù)寫操作時(shí)電流會(huì)有一個(gè)明顯的脈沖波動(dòng)如果電源網(wǎng)絡(luò)阻抗偏高可能在 SCK 邊沿產(chǎn)生亞毫伏級(jí)的電壓擾動(dòng)極端情況下影響 SPI 采樣。所以我在 3.3V 到 MRAM 的電源路徑上串了一顆 10Ω 電阻和 0.1μF 電容組成一個(gè)簡(jiǎn)易的 RC 濾波實(shí)測(cè)對(duì)讀寫的穩(wěn)定性有明顯改善。如果你板子上空間緊直接把 VDD 和 VSS 引腳之間加 100nF 電容也是可行的但串聯(lián)電阻法對(duì)高頻噪聲抑制更好。2.2 SPI 接口引腳分配與連接PIC18F65K40 的 MSSP1 模塊可以映射到不同的引腳但我建議不要為了省事隨意復(fù)用引腳直接使用默認(rèn)的專用引腳減少 PPS 配置出錯(cuò)的概率。我實(shí)際用的連接是這樣信號(hào)PIC18F65K40 引腳MR25H40CDF 引腳SCKRB6CSCKMOSIRB5DSIMISORB4ESOCSRB7普通 GPIOSCS#CS 必須用普通 GPIO 控制不要硬接到地因?yàn)槊看卧L問都要拉高拉低。MISO 上我加了一顆 4.7kΩ 上拉到 3.3V這有助于在初始狀態(tài)時(shí)避免浮空電平引起誤判。SCK 引腳建議串一顆 22Ω 電阻可以略微抑制振鈴這在長(zhǎng)排線連接時(shí)很管用。MR25H40CDF 的 HOLD 和 WP 引腳也要處理好。HOLD 引腳在不使用時(shí)必須上拉到高電平否則芯片會(huì)進(jìn)入保持狀態(tài)SCK 信號(hào)全部被忽略這是新手特別容易踩的坑。WP 引腳同理拉高寫保護(hù)才默認(rèn)不生效。這兩個(gè)引腳我都是直接接 10kΩ 上拉到 3.3V省得軟件控制。2.3 電平匹配與 PCB 走線如果單片機(jī)供電是 5V而 MRAM 是 3.3V那么單向信號(hào) SCK、MOSI、CS 可以通過電阻分壓方案降電平MISO 則需要用 MOS 管做電平轉(zhuǎn)換或者直接用兩個(gè) MOS 管搭一個(gè)雙向 I2C 風(fēng)格的電平轉(zhuǎn)換器。但我在這個(gè)項(xiàng)目里直接把單片機(jī)也改用 3.3V 供電省掉所有轉(zhuǎn)換電路這是一個(gè)值得推薦的工程簡(jiǎn)化。PCB 走線方面SPI 信號(hào)屬于高速數(shù)字信號(hào)但頻率最高不過 16MHz所以不需要什么阻抗控制只要保證布線短、等長(zhǎng)即可。我建議 SCK 和數(shù)據(jù)線盡量不要平行走太長(zhǎng)距離中間可以加地線隔離。如果 MRAM 和單片機(jī)分屬兩塊板子用排針排線連接SCK 頻率建議降到 4MHz 以下否則信號(hào)質(zhì)量會(huì)很差。2.4 焊接與封裝兼容經(jīng)驗(yàn)SOIC-8 封裝手工焊接難度并不高但要注意 MRAM 芯片底下是有散熱焊盤的實(shí)際 MR25H40CDF 的標(biāo)準(zhǔn) SOIC-8 并不是所有批次都帶底部焊盤我遇到過的封裝是普通引線封裝靠引腳散熱。焊接時(shí)如果用電烙鐵溫度調(diào)到 350℃ 左右先固定對(duì)角引腳再拖焊其余引腳焊完后用放大鏡檢查引腳之間有沒有短路。工業(yè)級(jí)產(chǎn)品建議直接上回流焊手工焊的可靠性在小批量里還可以量產(chǎn)不建議。3. 軟件驅(qū)動(dòng)與核心代碼3.1 SPI 初始化與時(shí)鐘匹配MR25H40CDF 支持 SPI Mode 0 和 Mode 3我統(tǒng)一用 Mode 0也就是時(shí)鐘空閑為低數(shù)據(jù)在上升沿被采樣。在 PIC18F65K40 的 MSSP 模塊上Mode 0 對(duì)應(yīng)的寄存器配置是 CKP0、CKE1還要把 SMP 位置 1讓輸入在數(shù)據(jù)輸出末端采樣這會(huì)增加一點(diǎn)時(shí)序余量。內(nèi)部振蕩器跑 64MHzMSSP 模塊最快的 SPI 時(shí)鐘是 Fosc/4也就是 16MHz。這已經(jīng)超過 MRAM 實(shí)際需求很多了但我沒有上來就選最高速而是先用 8MHz 調(diào)通功能再逐步把 SPIBRG 往下減。SPIBRG 的計(jì)算公式是 SPIBRG (Fosc / (4 * Fspi)) - 164MHz 下想得到 8MHzSPIBRG 就是 1。如果發(fā)現(xiàn)數(shù)據(jù)出錯(cuò)不要第一時(shí)間懷疑芯片多半是時(shí)序余量或者信號(hào)質(zhì)量問題。// SPI Master, Mode 0, 8MHz clock void mram_spi_init(void) { ANSELB 0x00; // 關(guān)閉模擬功能 TRISBbits.TRISB4 1; // SDI 輸入 TRISBbits.TRISB5 0; // SDO 輸出 TRISBbits.TRISB6 0; // SCK 輸出 TRISBbits.TRISB7 0; // CS 輸出 SSP1CON1 0x20; // SPI Master, Fosc/4 SSP1STAT 0x80; // SMP1, CKE1 - Mode 0 SSP1ADD 1; // 8MHz 64MHz Fosc SSP1EN 1; // 使能 MSSP } uint8_t spi_xfer(uint8_t data) { SSP1BUF data; while (!SSP1STATbits.BF); return SSP1BUF; }3.2 基本讀寫命令流程MR25H40CDF 的指令集和常見 SPI EEPROM 非常接近核心就六條指令寫使能 0x06、寫禁用 0x04、讀狀態(tài) 0x05、寫狀態(tài) 0x01、讀數(shù)據(jù) 0x03、寫數(shù)據(jù) 0x02。地址是三字節(jié)低 19 位有效對(duì)應(yīng) 512KB 的地址空間。簡(jiǎn)單梳理一下讀操作時(shí)序先把 CS 拉低發(fā)送讀命令 0x03再發(fā)送三字節(jié)地址然后連續(xù)發(fā)送任意個(gè)空字節(jié)時(shí)鐘同時(shí)從 MISO 上讀取數(shù)據(jù)讀完把 CS 拉高。整條讀操作不需要寫使能也不需要等待因?yàn)樽x操作本質(zhì)上沒有任何非易失性寫入動(dòng)作。寫操作稍復(fù)雜一點(diǎn)必須有一個(gè)前置的寫使能指令。流程是先把 CS 拉低發(fā)送 0x06然后把 CS 拉高這一步很關(guān)鍵寫使能只有在 CS 的上升沿之后才會(huì)真正生效緊接著再一次拉低 CS發(fā)送寫命令 0x02 和地址后面跟上待寫入的數(shù)據(jù)字節(jié)寫完后拉高 CS。寫完不需要延時(shí)因?yàn)?MRAM 沒有內(nèi)部編程周期所以這條寫命令結(jié)束后數(shù)據(jù)立即生效。void mram_write_enable(void) { CS_LAT 0; spi_xfer(0x06); CS_LAT 1; } void mram_write(uint32_t addr, uint8_t *buf, uint16_t len) { mram_write_enable(); CS_LAT 0; spi_xfer(0x02); spi_xfer((addr 16) 0xFF); spi_xfer((addr 8) 0xFF); spi_xfer(addr 0xFF); while (len--) { spi_xfer(*buf); } CS_LAT 1; } void mram_read(uint32_t addr, uint8_t *buf, uint16_t len) { CS_LAT 0; spi_xfer(0x03); spi_xfer((addr 16) 0xFF); spi_xfer((addr 8) 0xFF); spi_xfer(addr 0xFF); while (len--) { *buf spi_xfer(0x00); } CS_LAT 1; }這里要特別強(qiáng)調(diào) CS 的時(shí)序。在寫使能指令之后到寫命令之間CS 必須有一次完整的拉高再拉低的過程這個(gè)狀態(tài)轉(zhuǎn)換是芯片內(nèi)部鎖存 WEL 位的條件很多從 EEPROM 移植過來的代碼如果漏看這一步就會(huì)一直寫不進(jìn)數(shù)據(jù)。這是我第一次調(diào)試 MRAM 時(shí)困擾了大半天的問題。3.3 狀態(tài)寄存器與寫使能檢測(cè)寫命令之后如果想確認(rèn)數(shù)據(jù)是不是真的寫進(jìn)去了可以讀狀態(tài)寄存器 0x05。MR25H40CDF 的狀態(tài)寄存器只有低兩位有意義bit1 是 WELbit0 是保留位。寫命令執(zhí)行完后 WEL 會(huì)自動(dòng)清零所以代碼里可以在寫操作后讀一下狀態(tài)如果 WEL 不為 0說明之前的寫操作沒有被正確觸發(fā)。uint8_t mram_read_status(void) { uint8_t st; CS_LAT 0; spi_xfer(0x05); st spi_xfer(0x00); CS_LAT 1; return st; }寫操作之后強(qiáng)制核對(duì)數(shù)據(jù)也是我做驅(qū)動(dòng)時(shí)的一個(gè)習(xí)慣。我一般會(huì)寫操作結(jié)束后原地址再讀一遍并和緩沖區(qū)做逐字節(jié)比對(duì)。雖然 MRAM 出錯(cuò)的概率極低但在工業(yè)現(xiàn)場(chǎng)總有你想不到的外部干擾一次校驗(yàn)?zāi)芫然睾芏嗥嫫婀止值膯栴}。對(duì)于 512KB 的整片刷寫逐字節(jié)校驗(yàn)大約多花半秒鐘完全值得。3.4 數(shù)據(jù)組織與掉電保護(hù)解決了基礎(chǔ)讀寫接下來是工程里最考驗(yàn)設(shè)計(jì)能力的部分怎么組織數(shù)據(jù)才能做到掉電不丟、異常不混亂。MRAM 本身有 10 的 12 次方次寫壽命不做磨損均衡沒問題但掉電保護(hù)還得靠應(yīng)用層協(xié)議。我用的方案是固定區(qū)域劃分前 4KB 作為目錄區(qū)保存當(dāng)前寫指針、設(shè)備序列號(hào)、存儲(chǔ)格式版本號(hào)剩下 508KB 作為環(huán)形數(shù)據(jù)區(qū)。每一條記錄采用固定長(zhǎng)度結(jié)構(gòu)幀頭 0xAA55 2 字節(jié)長(zhǎng)度 2 字節(jié)記錄類型 4 字節(jié)時(shí)間戳 N 字節(jié)有效數(shù)據(jù) 2 字節(jié) CRC16。寫入順序是先寫數(shù)據(jù)記錄等整條數(shù)據(jù)完整落盤后再更新目錄區(qū)里的寫指針。為什么先寫數(shù)據(jù)再寫指針因?yàn)?MRAM 寫操作沒有內(nèi)部編程等待期斷電時(shí)刻數(shù)據(jù)可能寫了一半但通過 CRC 校驗(yàn)可以識(shí)別出一半記錄是壞幀。如果反過來先更新指針再寫數(shù)據(jù)斷電后指針可能指向一條從未寫成功的記錄讀端就不知道該跳過哪個(gè)。用“數(shù)據(jù)先行指針在后”的策略讀端掃描到 CRC 錯(cuò)誤幀時(shí)可以直接認(rèn)定這就是最后一條有效記錄把指針回退一幀處理邏輯非常簡(jiǎn)單。還有一個(gè)實(shí)用技巧目錄區(qū)寫指針本身也做了雙副本分別放在地址 0 和地址 2048 處更新時(shí)交替寫入。這樣即使系統(tǒng)在更新指針的瞬間掉電至少有一份舊指針是可用的配合 CRC 判斷總能恢復(fù)到最近一次完整記錄。這層雙緩沖在掉電測(cè)試中表現(xiàn)非常好兩千次隨機(jī)斷電數(shù)據(jù)零丟失。4. 實(shí)測(cè)數(shù)據(jù)與性能分析4.1 吞吐量實(shí)測(cè)結(jié)果硬件和驅(qū)動(dòng)都跑通之后我專門做了一輪性能實(shí)測(cè)。測(cè)試環(huán)境是 PIC18F65K40 內(nèi)部振蕩器 64MHzSPI 時(shí)鐘 8MHz供電 3.3V室溫 25℃。測(cè)試結(jié)果如下操作數(shù)據(jù)量實(shí)測(cè)耗時(shí)等效速率連續(xù)讀512KB約 0.55s約 7.6MB/s連續(xù)寫512KB約 0.56s約 7.5MB/s單字節(jié)寫入1B約 8.2μs取決于 SPI 包開銷256B 記錄寫入256B約 0.28ms約 0.9MB/s注意這里的“連續(xù)寫”包含了寫使能、地址、數(shù)據(jù)、CS 拉高拉低等全部開銷。相比典型 EEPROM 按頁寫一頁需要幾毫秒MRAM 的速遞提升是數(shù)量級(jí)的。實(shí)際在設(shè)備里一條 256B 的報(bào)警記錄從采集到寫完只花了不到 0.3ms對(duì)現(xiàn)場(chǎng)實(shí)時(shí)性幾乎零影響。單字節(jié)寫入的耗時(shí)主要是三次地址字節(jié)加上命令和片選開銷受 SPI 包長(zhǎng)度限制沒法再快了。如果你要追求單包效率可以在數(shù)據(jù)鏈路層設(shè)置一個(gè)寫入緩沖把小記錄攢到一定長(zhǎng)度再一次性連續(xù)寫入MRAM 支持任意長(zhǎng)度連續(xù)寫不需要擔(dān)心跨頁問題這一點(diǎn)比 EEPROM 和 Flash 都靈活。4.2 寫入可靠性與掉電測(cè)試可靠性測(cè)試我分了三組。第一組是常溫循環(huán)擦寫測(cè)試用內(nèi)部循環(huán)程序以固定地址反復(fù)寫 32 字節(jié)數(shù)據(jù)塊總共循環(huán)了一百萬次讀回比對(duì)全部通過MRAM 在這個(gè)量級(jí)上幾乎沒有疲勞跡象。第二組是高低溫測(cè)試分別在 -40℃ 和 85℃ 環(huán)境下運(yùn)行完整讀寫函數(shù)各 8 小時(shí)數(shù)據(jù)校驗(yàn)無一失敗。第三組是掉電測(cè)試我用繼電器控制整板供電每 50ms 切換一次通斷同時(shí)在斷電前隨機(jī)寫入一條記錄總共完成 2000 次重啟后掃描全部記錄CRC 有效率和目錄區(qū)恢復(fù)準(zhǔn)確率都能達(dá)到 99.9% 以上偶爾出現(xiàn)的壞幀也都能被讀端正確識(shí)別并跳過。這個(gè)測(cè)試結(jié)果其實(shí)比傳統(tǒng) EEPROM 方案穩(wěn)定很多。EEPROM 掉電時(shí)如果恰好趕上內(nèi)部編程周期有可能損壞當(dāng)前字節(jié)甚至影響相鄰字節(jié)而 MRAM 寫入是即時(shí)完成的不存在內(nèi)部編程窗口掉電只影響正在傳輸?shù)陌霔l數(shù)據(jù)舊數(shù)據(jù)完全不受影響。這是磁存儲(chǔ)原理帶來的天然優(yōu)勢(shì)不是軟件補(bǔ)丁能彌補(bǔ)的。4.3 強(qiáng)磁場(chǎng)干擾的一個(gè)真實(shí)案例需要特別說明的是MRAM 雖然寫壽命無敵但它對(duì)強(qiáng)磁場(chǎng)敏感這個(gè)特性在真實(shí)場(chǎng)景里確實(shí)出現(xiàn)過。我的樣機(jī)在測(cè)試時(shí)無意中把一塊永磁鐵吸在機(jī)箱外殼上正好靠近 MRAM 附近結(jié)果讀回的數(shù)據(jù)在局部區(qū)域出現(xiàn)了翻轉(zhuǎn)位幾個(gè)字節(jié)值完全變了。這個(gè)過程沒有觸發(fā)任何寫指令純粹是外部磁場(chǎng)直接干預(yù)了存儲(chǔ)單元的狀態(tài)。所以如果你的設(shè)備里有大功率電機(jī)、電磁閥、大電流導(dǎo)線或者釹磁鐵這類部件MRAM 布局上一定要和它們保持距離。理論上 MR25H40CDF 內(nèi)部有磁屏蔽層正常環(huán)境不會(huì)出問題但在設(shè)計(jì)評(píng)審時(shí)還是要把它當(dāng)作一個(gè)約束條件。我在樣機(jī)里把 MRAM 放到了 PCB 角落遠(yuǎn)離繼電器和電源電感區(qū)域之后再做磁場(chǎng)干擾測(cè)試再?zèng)]有復(fù)現(xiàn)過數(shù)據(jù)翻轉(zhuǎn)。5. 項(xiàng)目排坑與經(jīng)驗(yàn)總結(jié)5.1 讀出來全是 0xFF怎么回事這大概是每個(gè)人第一次接 MRAM 都會(huì)遇到的問題。讀數(shù)據(jù)全 0xFF 的一般原因有三個(gè)。第一CS 片選沒有拉低或者 CS 引腳懸空導(dǎo)致芯片始終處于待機(jī)模式MISO 一直是高阻態(tài)讀取結(jié)果自然全 1第二SPI 模式配置錯(cuò)誤如果單片機(jī)用 Mode 0 但 MRAM 實(shí)際工作在 Mode 3 的時(shí)序下采樣沿和數(shù)據(jù)沿正好錯(cuò)開大概率讀不到數(shù)據(jù)第三MISO 引腳沒有配置成輸入或者被復(fù)用成其他外設(shè)。排查方法是先用示波器抓 CS、SCK、MOSI、MISO 四根線的時(shí)序確認(rèn)每次讀操作的 CS 拉低確實(shí)存在然后確認(rèn) SCK 的時(shí)鐘數(shù)足夠。如果示波器上的波形和手冊(cè)時(shí)序圖不一致問題基本就在初始化配置上。不要一上來就懷疑芯片壞了MRAM 這種片子反而不容易壞時(shí)序問題占到九成。5.2 偶發(fā)亂碼怎么定位如果數(shù)據(jù)大多數(shù)時(shí)候正確但偶爾出現(xiàn)單個(gè)字節(jié)錯(cuò)誤優(yōu)先懷疑信號(hào)完整性和電源噪聲而不是芯片本身。這時(shí)候先把 SPI 時(shí)鐘降下來比如從 16MHz 降到 8MHz如果錯(cuò)誤消失基本可以確定是走線過長(zhǎng)或者 MISO 信號(hào)質(zhì)量不行。我的排查經(jīng)驗(yàn)是用示波器看 MISO 的上升沿如果上升沿超過了半個(gè)時(shí)鐘周期那就是 RC 負(fù)載太大需要把上拉電阻改大或縮短走線。還有一種隱蔽的亂碼來源是 SPI 字節(jié)間的間隔。MSSP 模塊在連續(xù)發(fā)送多個(gè)字節(jié)時(shí)如果軟件在兩次 spi_xfer 之間引入了不確定延時(shí)CS 信號(hào)又沒有全程保持穩(wěn)定MRAM 可能因?yàn)槠x信號(hào)上毛刺導(dǎo)致地址計(jì)數(shù)器錯(cuò)亂。解決辦法是確保一次讀操作過程中的所有字節(jié)交換都在同一個(gè)函數(shù)里連續(xù)完成不要在中間插入任何其他外設(shè)的輪詢或中斷處理。5.3 寫操作不生效都是 WEL 的鍋如果讀操作完全正常但寫操作永遠(yuǎn)不生效讀回的數(shù)據(jù)始終是 0xFF 或舊值我敢打賭是寫使能時(shí)序有問題。MRAM 的寫使能指令必須在 CS 上升沿才會(huì)被鎖存緊接著的下一個(gè) CS 下降沿進(jìn)入寫命令中間這個(gè)流程不能漏。很多從 SPI Flash 移植過來的代碼習(xí)慣性地在寫命令前插入一條 0x06然后不拉高 CS 直接發(fā)地址這在一些 Flash 芯片上能用但在 MRAM 上一定會(huì)失敗。調(diào)試時(shí)可以在寫命令之前主動(dòng)讀一次狀態(tài)寄存器如果 WEL 位是 1說明使能環(huán)節(jié)沒問題如果一直是 0說明寫使能沒有真正生效重新檢查 CS 拉高拉低的時(shí)序。這個(gè)檢查方法比猜測(cè)靠譜得多。5.4 給后來者的幾條建議第一個(gè)建議是如果你手頭有邏輯分析儀把 SPI 抓下來對(duì)照時(shí)序分析比盯著示波器一行行數(shù)時(shí)鐘高效得多。邏輯分析儀能直接解析出地址和數(shù)據(jù)的十六進(jìn)制值一眼就能看出地址偏移和數(shù)據(jù)錯(cuò)位。第二個(gè)建議是數(shù)據(jù)格式里一定要帶 CRC 和序列號(hào)不要指望存儲(chǔ)介質(zhì)永遠(yuǎn)不出錯(cuò)也不要用“讀操作必然可靠”來賭現(xiàn)場(chǎng)環(huán)境。第三個(gè)建議是調(diào)試時(shí)把功能流程拆開先跑通純讀寫再加文件系統(tǒng)或日志協(xié)議不要一上來就上重量級(jí)抽象層。我自己從這項(xiàng)日里學(xué)到的最大經(jīng)驗(yàn)其實(shí)是存儲(chǔ)方案選型不能只看芯片手冊(cè)上的容量和價(jià)格要看你的寫入頻率、掉電行為、溫度環(huán)境、抗干擾要求這些綜合約束。MRAM 貴但它把很多現(xiàn)場(chǎng)問題從“軟件能不能補(bǔ)救”變成了“天生不存在”。如果條件允許我建議嵌入式工程師都至少做一次 MRAM 驅(qū)動(dòng)的完整開發(fā)這會(huì)讓你對(duì)存儲(chǔ)器底層原理的理解明顯上一個(gè)臺(tái)階。