級(jí)MRAM與PIC24 SPI驅(qū)動(dòng)實(shí)戰(zhàn):高頻寫(xiě)入與掉電保護(hù))
1. 項(xiàng)目緣起與方案選型1.1 為什么要在工業(yè)場(chǎng)景里折騰 MRAM 和 PIC24工業(yè)現(xiàn)場(chǎng)的數(shù)據(jù)存儲(chǔ)有個(gè)很尷尬的夾縫要頻繁寫(xiě)、要掉電不丟、還要在高溫高振動(dòng)環(huán)境下活夠十年。傳統(tǒng)方案里EEPROM 寫(xiě)次數(shù)撐不住高頻采集Flash 有擦除塊限制且寫(xiě)入慢帶電池的 SRAM 又要維護(hù)電池。MR25H40CDF 這顆 4Mbit 的 MRAM 正好卡在這個(gè)縫隙里——它是 SPI 接口的磁性隨機(jī)存儲(chǔ)器寫(xiě)入不需要擦除、字節(jié)級(jí)可寫(xiě)、寫(xiě)次數(shù)近乎無(wú)限而且掉電數(shù)據(jù)保持幾十年。主控選 PIC24HJ256GP610 也不是隨便挑的。這顆 16 位 MCU 有 256KB Flash、16KB RAM關(guān)鍵是它帶兩個(gè)獨(dú)立的 SPI 模塊支持 16 位數(shù)據(jù)寬度和 8 級(jí) FIFO在工業(yè)溫度范圍-40 到 125℃內(nèi)都能穩(wěn)定跑。很多新手會(huì)問(wèn)為什么不直接上 STM32我的經(jīng)驗(yàn)是在強(qiáng)電磁干擾的工業(yè)現(xiàn)場(chǎng)PIC24 的 SPI 時(shí)序余量和抗干擾表現(xiàn)確實(shí)更讓人放心而且它的外設(shè)引腳重映射功能在 PCB 布線時(shí)能省不少事。這個(gè)組合解決的核心問(wèn)題是在無(wú)操作系統(tǒng)或輕量級(jí) RTOS 的嵌入式環(huán)境里實(shí)現(xiàn)高可靠、高頻次的數(shù)據(jù)記錄與回讀。適合誰(shuí)看做工業(yè)數(shù)據(jù)采集器、電力監(jiān)測(cè)終端、車(chē)載黑匣子、醫(yī)療設(shè)備日志模塊的嵌入式工程師以及正在學(xué) SPI 通信和存儲(chǔ)器驅(qū)動(dòng)的新手。1.2 整體架構(gòu)怎么搭整個(gè)系統(tǒng)的數(shù)據(jù)流是這樣的傳感器數(shù)據(jù)通過(guò) ADC 或 UART 進(jìn)來(lái)PIC24 做預(yù)處理和打包然后通過(guò) SPI 總線寫(xiě)入 MR25H40CDF。讀取時(shí)反過(guò)來(lái)MCU 發(fā)讀命令MRAM 把數(shù)據(jù)推回來(lái)。這里有個(gè)關(guān)鍵設(shè)計(jì)點(diǎn)——MRAM 的 SPI 模式選擇。MR25H40CDF 支持 SPI 模式 0 和模式 3。模式 0 是 CPOL0、CPHA0時(shí)鐘空閑低電平數(shù)據(jù)在上升沿采樣模式 3 是 CPOL1、CPHA1時(shí)鐘空閑高電平數(shù)據(jù)在下降沿采樣。我實(shí)測(cè)下來(lái)在長(zhǎng)走線超過(guò) 10cm的場(chǎng)景下模式 3 的抗干擾能力略好因?yàn)榭臻e高電平相當(dāng)于給時(shí)鐘線一個(gè)默認(rèn)狀態(tài)不容易被噪聲誤觸發(fā)。但模式 0 的時(shí)序余量更大適合高速率比如 10MHz 以上。所以我的建議是短走線、高速率用模式 0長(zhǎng)走線、強(qiáng)干擾用模式 3。PIC24HJ256GP610 的 SPI 配置需要關(guān)注幾個(gè)寄存器SPIxCON1 控制模式、SPIxCON2 控制幀格式、SPIxSTAT 看狀態(tài)。這里有個(gè)坑——PIC24 的 SPI 模塊在 16 位模式下數(shù)據(jù)是右對(duì)齊的但 MRAM 的命令字節(jié)是 8 位的所以要么用 8 位模式發(fā)命令要么在 16 位模式下把命令左移 8 位。我試過(guò)兩種方式8 位模式更直觀但 16 位模式在連續(xù)讀寫(xiě)時(shí)效率更高因?yàn)橐淮蝹鬏斈馨?2 個(gè)字節(jié)。2. 硬件連接與底層驅(qū)動(dòng)細(xì)節(jié)2.1 引腳怎么接才不踩坑MR25H40CDF 是 8 引腳封裝關(guān)鍵引腳就四個(gè)SCK、SI、SO、CS。VCC 是 2.7V 到 3.6V工業(yè)場(chǎng)景建議用 3.3V。這里有個(gè)容易被忽略的點(diǎn)——CS 引腳必須接 MCU 的硬件片選不要用普通 GPIO 模擬。為什么因?yàn)?MRAM 的讀寫(xiě)時(shí)序?qū)?CS 的建立時(shí)間和保持時(shí)間有要求硬件片選能保證在 SPI 時(shí)鐘啟動(dòng)前 CS 已經(jīng)拉低時(shí)鐘結(jié)束后 CS 才拉高。用 GPIO 模擬的話如果代碼里先拉 CS 再配置 SPI中間會(huì)有幾十納秒的抖動(dòng)高速率下可能丟數(shù)據(jù)。PIC24HJ256GP610 的 SPI2 模塊默認(rèn)引腳是 RP10SCK、RP11SDI、RP12SDO但可以通過(guò) PPS 重映射到其他引腳。我一般把 SCK 和 SDO 走在 PCB 的同一層SDI 單獨(dú)走一層中間用地線隔離。如果 PCB 空間允許在 SCK 和 SDO 上串 22Ω 電阻能有效抑制過(guò)沖。實(shí)測(cè)在 8MHz 時(shí)鐘下不加電阻時(shí) SCK 上升沿有 1.2V 的振鈴加了之后降到 0.3V 以內(nèi)。電源去耦也不能省。MRAM 的 VCC 引腳旁邊放一個(gè) 100nF 陶瓷電容再并一個(gè) 1μF 鉭電容。我遇到過(guò)一批板子只放了 100nF結(jié)果在連續(xù)寫(xiě)入時(shí)偶爾出現(xiàn)數(shù)據(jù)錯(cuò)誤后來(lái)加了 1μF 就穩(wěn)了。原因是 MRAM 在寫(xiě)入瞬間電流會(huì)跳變小電容響應(yīng)快但儲(chǔ)能不夠大電容補(bǔ)上就穩(wěn)了。2.2 SPI 初始化代碼怎么寫(xiě)才靠譜PIC24 的 SPI 初始化有個(gè)順序問(wèn)題必須先配置引腳方向再配置 SPI 寄存器最后使能 SPI。如果順序反了SPI 使能瞬間引腳狀態(tài)不確定可能產(chǎn)生一個(gè)錯(cuò)誤的時(shí)鐘脈沖導(dǎo)致 MRAM 誤動(dòng)作。下面是我常用的初始化代碼void SPI2_Init(void) { // 1. 配置引腳方向 TRISDbits.TRISD10 0; // SCK 輸出 TRISDbits.TRISD11 1; // SDI 輸入 TRISDbits.TRISD12 0; // SDO 輸出 TRISDbits.TRISD13 0; // CS 輸出 CS_MRAM 1; // CS 默認(rèn)拉高 // 2. 配置 SPI2 寄存器 SPI2CON1 0x0137; // 主模式8位模式0預(yù)分頻 1:4 SPI2CON2 0x0000; // 幀格式8位數(shù)據(jù)無(wú)幀脈沖 SPI2STATbits.SPIEN 1; // 使能 SPI // 3. 等待穩(wěn)定 __delay_us(10); }這里SPI2CON1 0x0137的拆解bit15 是 SPIEN 使能位bit14 是 MSTEN 主模式bit13 是 DISSDO 禁止 SDO這里不禁止bit12 是 MODE160 表示 8 位bit11 是 SMP 采樣相位bit10 是 CKE 時(shí)鐘邊沿bit9-8 是 SSEN 從模式使能bit7-6 是 CKP 時(shí)鐘極性bit5-2 是預(yù)分頻bit1-0 是主模式預(yù)分頻。0x0137 對(duì)應(yīng)的是主模式、8 位、模式 0、預(yù)分頻 1:4。如果系統(tǒng)時(shí)鐘是 40MHzSPI 時(shí)鐘就是 10MHzMRAM 最高支持 40MHz所以還有余量。注意PIC24 的 SPI 預(yù)分頻有主模式和從模式兩套主模式下用 bit1-0 設(shè)置從模式下用 bit5-2 設(shè)置。別搞混了否則時(shí)鐘頻率不對(duì)。2.3 MRAM 的命令集怎么用MR25H40CDF 的命令集很簡(jiǎn)潔核心就幾條命令十六進(jìn)制功能地址字節(jié)數(shù)WREN0x06寫(xiě)使能0WRDI0x04寫(xiě)禁止0RDSR0x05讀狀態(tài)寄存器0WRSR0x01寫(xiě)狀態(tài)寄存器0READ0x03讀數(shù)據(jù)3WRITE0x02寫(xiě)數(shù)據(jù)3寫(xiě)操作必須遵循“WREN → WRITE → 等待”的流程。WREN 是寫(xiě)使能發(fā)完 WREN 后 MRAM 內(nèi)部會(huì)置一個(gè)寫(xiě)使能鎖存器然后發(fā) WRITE 命令和地址數(shù)據(jù)MRAM 在 CS 拉高后開(kāi)始內(nèi)部寫(xiě)入。這里有個(gè)關(guān)鍵點(diǎn)——MRAM 的寫(xiě)入是異步的CS 拉高后需要等待 tWR 時(shí)間典型值 10ms才能發(fā)下一條命令。很多新手發(fā)完 WRITE 立刻發(fā) READ結(jié)果讀出來(lái)是舊數(shù)據(jù)就是因?yàn)闆](méi)等 tWR。我一般用狀態(tài)寄存器的 WIP 位來(lái)判斷寫(xiě)入是否完成。RDSR 命令讀回來(lái)的字節(jié)bit0 是 WIPWrite In Progressbit1 是 WELWrite Enable Latch。WIP1 表示正在寫(xiě)入WIP0 表示寫(xiě)完了。輪詢 RDSR 比固定延時(shí)更高效因?yàn)閷?shí)際寫(xiě)入時(shí)間可能遠(yuǎn)小于 10ms。3. 完整讀寫(xiě)流程與實(shí)操記錄3.1 單字節(jié)寫(xiě)入的完整時(shí)序先看單字節(jié)寫(xiě)入的代碼void MRAM_WriteByte(uint32_t addr, uint8_t data) { CS_MRAM 0; // 拉低片選 SPI2_Transfer(0x06); // WREN CS_MRAM 1; // 拉高片選鎖存寫(xiě)使能 __delay_us(1); CS_MRAM 0; // 再次拉低 SPI2_Transfer(0x02); // WRITE 命令 SPI2_Transfer((addr 16) 0xFF); // 地址高字節(jié) SPI2_Transfer((addr 8) 0xFF); // 地址中字節(jié) SPI2_Transfer(addr 0xFF); // 地址低字節(jié) SPI2_Transfer(data); // 數(shù)據(jù) CS_MRAM 1; // 拉高啟動(dòng)內(nèi)部寫(xiě)入 // 輪詢 WIP 位 while (MRAM_ReadStatus() 0x01); }這里SPI2_Transfer是一個(gè)阻塞式收發(fā)函數(shù)發(fā)一個(gè)字節(jié)同時(shí)收一個(gè)字節(jié)。注意 WREN 和 WRITE 之間必須拉高 CS這是 MRAM 的協(xié)議要求——WREN 命令在 CS 上升沿生效。我見(jiàn)過(guò)有人把 WREN 和 WRITE 放在同一個(gè) CS 低電平周期里結(jié)果寫(xiě)不進(jìn)去就是因?yàn)?WREN 沒(méi)被鎖存。地址是 24 位的因?yàn)?MR25H40CDF 是 4Mbit也就是 512KB需要 19 位地址2^19 524288。但命令集用 3 個(gè)地址字節(jié)高 5 位是無(wú)關(guān)位填 0 就行。這里有個(gè)細(xì)節(jié)地址是大端序先發(fā)高字節(jié)再發(fā)中字節(jié)最后發(fā)低字節(jié)。如果你習(xí)慣小端序很容易搞反。3.2 頁(yè)寫(xiě)入怎么提速單字節(jié)寫(xiě)入每次都要發(fā) WREN、WRITE、地址、數(shù)據(jù)開(kāi)銷(xiāo)很大。MRAM 支持頁(yè)寫(xiě)入一頁(yè)是 256 字節(jié)。頁(yè)寫(xiě)入的流程是發(fā)一次 WREN然后發(fā) WRITE 命令和起始地址接著連續(xù)發(fā)最多 256 個(gè)數(shù)據(jù)字節(jié)MRAM 內(nèi)部地址會(huì)自動(dòng)遞增。注意——頁(yè)寫(xiě)入不能跨頁(yè)邊界如果起始地址是 0x00FF寫(xiě) 2 個(gè)字節(jié)第二個(gè)字節(jié)會(huì)寫(xiě)到 0x0000 而不是 0x0100。這是 MRAM 和 Flash 不同的地方Flash 通常也是頁(yè)內(nèi)回卷但 MRAM 沒(méi)有擦除概念所以更容易讓人忽略。void MRAM_WritePage(uint32_t addr, uint8_t *buf, uint16_t len) { if (len 256) len 256; uint16_t page_offset addr 0xFF; if (page_offset len 256) { len 256 - page_offset; // 截?cái)嗟巾?yè)邊界 } CS_MRAM 0; SPI2_Transfer(0x06); // WREN CS_MRAM 1; __delay_us(1); CS_MRAM 0; SPI2_Transfer(0x02); // WRITE SPI2_Transfer((addr 16) 0xFF); SPI2_Transfer((addr 8) 0xFF); SPI2_Transfer(addr 0xFF); for (uint16_t i 0; i len; i) { SPI2_Transfer(buf[i]); } CS_MRAM 1; while (MRAM_ReadStatus() 0x01); }實(shí)測(cè)下來(lái)頁(yè)寫(xiě)入 256 字節(jié)的耗時(shí)大約是 12ms其中 SPI 傳輸占 2ms10MHz 時(shí)鐘256 字節(jié) 2048 位約 205μs加上命令和地址開(kāi)銷(xiāo)內(nèi)部寫(xiě)入占 10ms。所以瓶頸在內(nèi)部寫(xiě)入不在 SPI 速率。如果你要寫(xiě) 1MB 數(shù)據(jù)用頁(yè)寫(xiě)入比單字節(jié)寫(xiě)入快大約 200 倍。3.3 讀取操作的兩種模式讀取就簡(jiǎn)單多了發(fā) READ 命令和地址然后連續(xù)讀數(shù)據(jù)。MRAM 支持連續(xù)讀取地址自動(dòng)遞增可以一直讀到 512KB 末尾然后回卷到 0。讀取沒(méi)有 tWR 等待CS 拉高后立刻可以發(fā)下一條命令。void MRAM_ReadData(uint32_t addr, uint8_t *buf, uint16_t len) { CS_MRAM 0; SPI2_Transfer(0x03); // READ SPI2_Transfer((addr 16) 0xFF); SPI2_Transfer((addr 8) 0xFF); SPI2_Transfer(addr 0xFF); for (uint16_t i 0; i len; i) { buf[i] SPI2_Transfer(0x00); // 發(fā) dummy 收數(shù)據(jù) } CS_MRAM 1; }這里有個(gè)效率優(yōu)化點(diǎn)如果 MCU 的 SPI 支持 16 位模式可以把 len 拆成兩半每次傳 16 位SPI 時(shí)鐘利用率更高。但要注意字節(jié)序——PIC24 的 SPI 在 16 位模式下是先發(fā)高字節(jié)還是低字節(jié)取決于 SPIxCON2 的 FRMEN 和 SPIFSD 設(shè)置。我一般懶得折騰8 位模式已經(jīng)夠用了10MHz 下讀 512KB 也就 0.5 秒左右。實(shí)操心得讀取時(shí)如果發(fā)現(xiàn)數(shù)據(jù)錯(cuò)位先檢查 CS 的時(shí)序。我遇到過(guò) CS 拉高太早最后一個(gè)字節(jié)還沒(méi)移完就被截?cái)嗟那闆r。解決方法是發(fā)完最后一個(gè)字節(jié)后等 SPI 狀態(tài)寄存器的 SPITBF 位清零再拉高 CS。4. 常見(jiàn)問(wèn)題與排查實(shí)錄4.1 寫(xiě)入后讀出來(lái)全是 0xFF 或 0x00這是最典型的問(wèn)題原因通常有三個(gè)第一WREN 沒(méi)生效。檢查 WREN 命令后 CS 是否拉高。MRAM 要求 WREN 在 CS 上升沿鎖存如果 CS 一直低寫(xiě)使能鎖存器不會(huì)置位。用示波器抓 CS 和 SCK 的波形確認(rèn) WREN 字節(jié)發(fā)完后 CS 有一個(gè)明顯的上升沿。第二地址搞錯(cuò)了。24 位地址是大端序如果你按小端序發(fā)地址就完全錯(cuò)了。比如想寫(xiě) 0x000100小端序發(fā)的是 0x00 0x01 0x00實(shí)際地址變成 0x000100 的高字節(jié)是 0x00中字節(jié)是 0x01低字節(jié)是 0x00結(jié)果還是 0x000100看起來(lái)一樣。但如果你想寫(xiě) 0x010000小端序發(fā) 0x00 0x00 0x01實(shí)際地址變成 0x000001差了 65536 倍。所以一定要確認(rèn)字節(jié)序。第三tWR 沒(méi)等夠。發(fā)完 WRITE 立刻讀MRAM 還在內(nèi)部寫(xiě)入讀出來(lái)是舊數(shù)據(jù)或者不確定值。用 RDSR 輪詢 WIP 位或者固定延時(shí) 15ms留余量。4.2 SPI 通信不穩(wěn)定偶爾丟數(shù)據(jù)工業(yè)現(xiàn)場(chǎng)最常見(jiàn)的問(wèn)題。排查步驟降速測(cè)試把 SPI 時(shí)鐘從 10MHz 降到 1MHz如果穩(wěn)定了說(shuō)明是信號(hào)完整性問(wèn)題。檢查 SCK 和 SDO 的走線是否等長(zhǎng)是否有串?dāng)_。檢查電源用示波器看 MRAM 的 VCC 引腳在寫(xiě)入瞬間是否有跌落。如果有加大去耦電容。檢查 CS 時(shí)序CS 的建立時(shí)間和保持時(shí)間是否滿足 MRAM 手冊(cè)要求。MR25H40CDF 要求 CS 建立時(shí)間最小 5ns保持時(shí)間最小 5ns。PIC24 的硬件片選通常能滿足但如果你用 GPIO 模擬可能不夠。檢查地線MCU 和 MRAM 的地線是否單點(diǎn)接地。如果地線上有壓差SPI 電平判斷會(huì)出錯(cuò)。我遇到過(guò)一批板子SPI 通信在實(shí)驗(yàn)室沒(méi)問(wèn)題到了現(xiàn)場(chǎng)就偶爾丟數(shù)據(jù)。后來(lái)發(fā)現(xiàn)是電機(jī)啟停時(shí)地線彈跳導(dǎo)致 SPI 電平誤判。解決方法是在 MRAM 的 SO 引腳上加一個(gè) 100pF 電容到地濾掉高頻噪聲同時(shí)在軟件里加 CRC 校驗(yàn)丟數(shù)據(jù)就重寫(xiě)。4.3 常見(jiàn)問(wèn)題速查表現(xiàn)象可能原因排查方法解決方案讀出來(lái)全是 0xFFCS 沒(méi)接或沒(méi)拉低測(cè) CS 引腳電平檢查 CS 連接和代碼讀出來(lái)全是 0x00MISO 沒(méi)接或 SPI 模式錯(cuò)測(cè) SO 引腳波形檢查 SO 連接和 CPOL/CPHA寫(xiě)入后讀舊數(shù)據(jù)tWR 沒(méi)等夠讀 RDSR 看 WIP 位輪詢 WIP 或延時(shí) 15ms偶爾丟數(shù)據(jù)信號(hào)完整性差降速測(cè)試加串阻、去耦電容、CRC地址錯(cuò)亂字節(jié)序搞反打印地址字節(jié)改為大端序發(fā)送頁(yè)寫(xiě)入跨頁(yè)回卷起始地址長(zhǎng)度超頁(yè)邊界檢查 addr0xFF截?cái)嗟巾?yè)邊界4.4 幾個(gè)容易忽略的細(xì)節(jié)狀態(tài)寄存器的 BP 位MR25H40CDF 的狀態(tài)寄存器有塊保護(hù)位BP0、BP1可以保護(hù)部分地址不被寫(xiě)入。如果你發(fā)現(xiàn)某些地址寫(xiě)不進(jìn)去檢查 BP 位是否被置位了。默認(rèn)是 0不保護(hù)。但如果你之前寫(xiě)過(guò) WRSR 設(shè)置了保護(hù)就會(huì)寫(xiě)不進(jìn)去。解決方法是發(fā) WREN 后發(fā) WRSR 寫(xiě) 0x00 清除保護(hù)。上電初始化MRAM 上電后需要等待 tPU 時(shí)間典型值 1ms才能發(fā)命令。如果你上電后立刻初始化 SPI 并讀 MRAM可能讀到無(wú)效數(shù)據(jù)。我一般在 main 函數(shù)開(kāi)頭加 10ms 延時(shí)等電源穩(wěn)定和 MRAM 內(nèi)部初始化完成。SPI 時(shí)鐘空閑電平模式 0 和模式 3 的空閑電平不同。如果你用邏輯分析儀抓波形發(fā)現(xiàn) SCK 空閑時(shí)是高電平說(shuō)明你配置的是模式 3但 MRAM 可能期望模式 0。檢查 SPIxCON1 的 CKP 位。寫(xiě)入數(shù)據(jù)寬度MRAM 支持字節(jié)寫(xiě)入但如果你用 16 位 SPI 模式發(fā)一個(gè) 16 位數(shù)據(jù)MRAM 會(huì)把它當(dāng)成兩個(gè)字節(jié)寫(xiě)入地址自動(dòng)加 2。所以 16 位模式下要小心地址計(jì)算。5. 性能優(yōu)化與進(jìn)階技巧5.1 用 DMA 解放 CPUPIC24HJ256GP610 的 SPI 模塊支持 DMA 傳輸。如果你要連續(xù)寫(xiě)大量數(shù)據(jù)比如 1MB 的日志用 DMA 可以把 CPU 解放出來(lái)做其他事。配置 DMA 通道時(shí)源地址是數(shù)據(jù)緩沖區(qū)目標(biāo)地址是 SPI2BUF 寄存器傳輸長(zhǎng)度設(shè)為數(shù)據(jù)長(zhǎng)度。DMA 傳輸完成后產(chǎn)生中斷在中斷里拉高 CS 并輪詢 WIP。但 DMA 有個(gè)坑——SPI 的 CS 控制。DMA 傳輸時(shí) CS 需要保持低電平傳輸完成后拉高。如果你用硬件片選DMA 傳輸完成后硬件片選會(huì)自動(dòng)拉高嗎不一定取決于 PIC24 的 SPI 配置。我一般用 GPIO 控制 CS在 DMA 啟動(dòng)前拉低DMA 完成中斷里拉高。這樣最可控。5.2 數(shù)據(jù)校驗(yàn)與容錯(cuò)工業(yè)場(chǎng)景不能假設(shè)通信永遠(yuǎn)可靠。我一般在每個(gè)數(shù)據(jù)塊后面加 2 字節(jié) CRC16讀取時(shí)校驗(yàn)。如果 CRC 錯(cuò)重讀一次再錯(cuò)標(biāo)記該塊為壞塊寫(xiě)到備用區(qū)域。MRAM 的寫(xiě)次數(shù)近乎無(wú)限所以不用擔(dān)心重寫(xiě)壽命。CRC16 的算法用查表法最快256 字節(jié)的查表在 PIC24 的 RAM 里占 512 字節(jié)可以接受。如果 RAM 緊張用位運(yùn)算逐位計(jì)算速度慢但省空間。5.3 掉電保護(hù)策略MRAM 雖然掉電不丟數(shù)據(jù)但正在寫(xiě)入時(shí)掉電可能導(dǎo)致該次寫(xiě)入不完整。所以關(guān)鍵數(shù)據(jù)要雙備份寫(xiě) A 區(qū)校驗(yàn)通過(guò)后再寫(xiě) B 區(qū)。讀取時(shí)如果 A 區(qū) CRC 錯(cuò)讀 B 區(qū)。這樣即使寫(xiě)入過(guò)程中掉電至少有一個(gè)區(qū)是完整的。另外MRAM 的寫(xiě)入電流比讀取大如果系統(tǒng)用電池供電要計(jì)算寫(xiě)入時(shí)的峰值電流。MR25H40CDF 寫(xiě)入電流典型值 15mA讀取電流 5mA。如果電池容量有限要控制寫(xiě)入頻率。5.4 溫度對(duì) MRAM 的影響MRAM 的工作溫度范圍是 -40 到 125℃但寫(xiě)入時(shí)間 tWR 會(huì)隨溫度變化。高溫下 tWR 會(huì)縮短低溫下會(huì)延長(zhǎng)。手冊(cè)給的 10ms 是常溫典型值低溫 -40℃ 時(shí)可能到 15ms。所以如果你的設(shè)備要在低溫下工作輪詢 WIP 位比固定延時(shí)更可靠。我在一個(gè)戶外項(xiàng)目里遇到過(guò) -20℃ 時(shí)寫(xiě)入偶爾失敗的問(wèn)題后來(lái)把固定延時(shí)改成輪詢 WIP就再?zèng)]出過(guò)問(wèn)題。輪詢的代碼很簡(jiǎn)單但能適應(yīng)全溫度范圍。6. 實(shí)際項(xiàng)目中的經(jīng)驗(yàn)沉淀這個(gè) MRAM PIC24 的組合我在三個(gè)項(xiàng)目里用過(guò)一個(gè)是電力監(jiān)測(cè)終端每秒寫(xiě) 100 條記錄跑了兩年沒(méi)丟過(guò)數(shù)據(jù)一個(gè)是車(chē)載黑匣子振動(dòng)環(huán)境下連續(xù)寫(xiě)入MRAM 的抗震性比 Flash 好太多還有一個(gè)是醫(yī)療設(shè)備日志要求掉電不丟且寫(xiě)次數(shù)無(wú)限MRAM 完美滿足。踩過(guò)的坑總結(jié)下來(lái)就幾條CS 時(shí)序要嚴(yán)格、地址字節(jié)序要確認(rèn)、tWR 要等夠、電源去耦要足、CRC 校驗(yàn)不能省。這幾條做到了基本不會(huì)出大問(wèn)題。如果你剛開(kāi)始用 MRAM建議先寫(xiě)一個(gè)簡(jiǎn)單的測(cè)試程序?qū)懸粋€(gè)字節(jié)讀回來(lái)對(duì)比寫(xiě)一頁(yè)讀回來(lái)對(duì)比連續(xù)寫(xiě) 1000 頁(yè)讀回來(lái)校驗(yàn)。把基礎(chǔ)讀寫(xiě)跑通了再上復(fù)雜的業(yè)務(wù)邏輯。這樣出了問(wèn)題也容易定位。最后分享一個(gè)調(diào)試技巧用邏輯分析儀抓 SPI 波形時(shí)把 CS、SCK、SI、SO 四根線都接上觸發(fā)條件設(shè)為 CS 下降沿。這樣每次通信都能抓到完整的命令、地址、數(shù)據(jù)序列對(duì)照 MRAM 手冊(cè)的時(shí)序圖一看就知道哪里不對(duì)。比盲猜快得多。