存儲(chǔ)方案:從原理到掉電保存實(shí)戰(zhàn))
開(kāi)頭先聊點(diǎn)實(shí)際的我在一個(gè)工業(yè)數(shù)據(jù)采集項(xiàng)目里需要在高噪聲、頻繁掉電的環(huán)境下保存關(guān)鍵運(yùn)行參數(shù)最初用的是Flash結(jié)果寫(xiě)壞過(guò)扇區(qū)、掉電丟數(shù)據(jù)后來(lái)?yè)Q成MR25H40CDF這顆MRAM芯片搭配STM32F042K6主控整套存儲(chǔ)方案才算真正穩(wěn)下來(lái)。這篇博文就圍繞這個(gè)組合講清楚為什么選MRAM、怎么接線、怎么把讀寫(xiě)代碼跑起來(lái)、以及工業(yè)場(chǎng)景里那些容易踩的坑。如果你正在評(píng)估嵌入式存儲(chǔ)方案或者準(zhǔn)備在STM32上接MRAM這篇內(nèi)容可以直接作為參考。1. MRAM是什么選型前的核心認(rèn)知1.1 一句話看懂MRAM工作原理MRAMMagnetoresistive Random Access Memory磁性隨機(jī)存儲(chǔ)器的核心存儲(chǔ)單元是一個(gè)磁性隧道結(jié)它由兩層鐵磁材料和中間一層極薄的絕緣勢(shì)壘組成。兩層鐵磁材料的磁化方向平行時(shí)隧道結(jié)電阻低代表數(shù)據(jù)“0”反平行時(shí)電阻高代表數(shù)據(jù)“1”。讀取數(shù)據(jù)時(shí)就是檢測(cè)這個(gè)電阻狀態(tài)寫(xiě)數(shù)據(jù)時(shí)通過(guò)改變自由層的磁化方向來(lái)實(shí)現(xiàn)。這個(gè)原理帶來(lái)的直接好處是數(shù)據(jù)存儲(chǔ)依賴磁化方向而不是電荷所以斷電后數(shù)據(jù)不會(huì)丟而且它不涉及電子捕獲或隧穿導(dǎo)致的電荷泄漏也就沒(méi)有Flash那樣的擦寫(xiě)損耗和寫(xiě)前擦除操作。MRAM的寫(xiě)入本質(zhì)是改變磁化狀態(tài)不是注入電荷理論上寫(xiě)壽命幾乎無(wú)限MR25H40CDF標(biāo)稱超過(guò)10^14次。這一點(diǎn)在工業(yè)設(shè)備里非常重要因?yàn)樵O(shè)備參數(shù)、計(jì)數(shù)器、運(yùn)行日志這些數(shù)據(jù)可能會(huì)頻繁寫(xiě)入如果還按Flash的“先擦后寫(xiě)”思路很容易把扇區(qū)擦壞。1.2 同場(chǎng)競(jìng)技MRAM與FRAM、Flash、BBSRAM的真實(shí)差距工業(yè)嵌入式存儲(chǔ)里常被放在一起比較的方案是Flash、FRAM、電池供電的BBSRAM以及MRAM。我做過(guò)一個(gè)對(duì)比表方便你直接看差異特性MRAMMR25H40CDFFRAM如FM25V10NOR Flash如W25Q64BBSRAM寫(xiě)壽命10^14次10^12次10^4~10^5次/扇區(qū)接近無(wú)限寫(xiě)前擦除無(wú)無(wú)必須按扇區(qū)擦除無(wú)單字節(jié)寫(xiě)入支持支持不支持最小擦除單位是扇區(qū)支持寫(xiě)入速度最快40MHz SPI單命令寫(xiě)類似SRAM總線速度擦除慢寫(xiě)入也受頁(yè)限制并行總線快數(shù)據(jù)保持10年105°C約10年10~20年靠電池需維護(hù)抗輻射/抗干擾強(qiáng)磁存儲(chǔ)不怕電離輻射較強(qiáng)一般一般從表格能看出來(lái)MRAM最大的優(yōu)勢(shì)就是“無(wú)限寫(xiě)壽命 無(wú)需擦除 掉電不丟”。FRAM寫(xiě)壽命也很高但容量偏低、成本偏高Flash雖然便宜但在頻繁寫(xiě)入場(chǎng)景下要么加均衡算法要么就得定期搬運(yùn)扇區(qū)復(fù)雜度上去了還不一定扛得住BBSRAM速度快但要外接電池、還要擔(dān)心電池壽命和更換周期。MRAM幾乎是這幾項(xiàng)屬性的平衡點(diǎn)。1.3 MR25H40CDF關(guān)鍵參數(shù)解讀MR25H40CDF是Everspin現(xiàn)算恒憶/Infineon旗下的4Mbit SPI接口MRAM工作電壓2.7V到3.6V正好匹配STM32F042K6的3.3V供電。具體參數(shù)容量4Mbit即512KB按頁(yè)256字節(jié)組織共2048頁(yè)SPI時(shí)鐘最高40MHz比大部分MRAM芯片的25MHz要高寫(xiě)保護(hù)支持WP引腳硬件寫(xiě)保護(hù)以及狀態(tài)寄存器里的軟件寫(xiě)保護(hù)位休眠模式支持Sleep命令待機(jī)電流可以降到微安級(jí)別數(shù)據(jù)保持105°C環(huán)境下大于10年工業(yè)溫度范圍-40°C到105°C封裝DFN8/DFN8L等體積小適合緊湊型設(shè)計(jì)這里有個(gè)細(xì)節(jié)要提醒MR25H40CDF是SPI接口的MRAM所以它與MCU的接線本質(zhì)上就是和一個(gè)SPI NOR Flash的接線方式類似差別在不需要“擦除”這個(gè)環(huán)節(jié)代碼上簡(jiǎn)化了很多。2. 硬件接線與SPI模式選擇STM32F042K6怎么連MR25H40CDF2.1 引腳對(duì)接全解STM32F042K6是Cortex-M0內(nèi)核、主頻48MHz內(nèi)置1路SPI/I2SPA5、PA6、PA7正好可以復(fù)用為SPI1的SCK、MISO、MOSI配合PB0做片選。我把MR25H40CDF和STM32的引腳對(duì)應(yīng)關(guān)系整理成表MR25H40CDF引腳功能連接目標(biāo)CS#片選低有效STM32 GPIO如PB0推挽輸出SCKSPI時(shí)鐘STM32 SPI1_SCKPA5SI串行輸入STM32 SPI1_MOSIPA7SO串行輸出STM32 SPI1_MISOPA6WP#寫(xiě)保護(hù)低有效可接PB1或直接上拉到VccHOLD#暫停通信低有效可接PB2或直接上拉到VccVcc電源3.3V并接0.1uF去耦電容Vss地系統(tǒng)地我實(shí)際項(xiàng)目的做法是WP#和HOLD#都不直接固定而是各用一個(gè)GPIO控制。雖然它們都有內(nèi)部上拉傾向但工業(yè)環(huán)境里電平不穩(wěn)與其賭芯片默認(rèn)狀態(tài)不如讓程序上電時(shí)主動(dòng)拉高。只有CS#、SCK、SI、SO是真正必須掛在SPI上的信號(hào)WP和HOLD做普通IO控。2.2 SPI模式為什么必須是SPI Mode 0MR25H40CDF的數(shù)據(jù)手冊(cè)里寫(xiě)明它支持SPI Mode 0CPOL0CPHA0。意思是SCK空閑時(shí)為低電平數(shù)據(jù)在SCK上升沿被采樣。STM32的SPI外設(shè)可以配置成Mode 0或Mode 3但必須明確選擇Mode 0。我把這個(gè)配置寫(xiě)進(jìn)SPI初始化代碼里SPI_InitTypeDef SPI_InitStructure; SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA SPI_CPHA_1Edge; SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_4; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE);分頻系數(shù)用4意味著48MHz主頻下SPI時(shí)鐘是12MHz。這個(gè)速度在40MHz上限內(nèi)同時(shí)在工業(yè)PCB布線上也比較容易保證信號(hào)完整性。如果后續(xù)要提速可以改分頻到224MHz但要注意走線長(zhǎng)度和地平面質(zhì)量。2.3 布局與電源工業(yè)現(xiàn)場(chǎng)的隱藏細(xì)節(jié)MRAM本身不挑電源但對(duì)紋波敏感——這和所有數(shù)字存儲(chǔ)芯片都一樣我卻見(jiàn)過(guò)不止一次因?yàn)殡娫醇y波過(guò)大導(dǎo)致讀取數(shù)據(jù)偶爾出錯(cuò)的情況。布線時(shí)注意三點(diǎn)Vcc引腳旁放0.1uF陶瓷電容盡量靠近芯片引腳如果PCB空間允許再加一個(gè)1uF鉭電容做低頻去耦CS#、SCK、SI的走線要遠(yuǎn)離電機(jī)驅(qū)動(dòng)、繼電器控制線至少保持5mm以上間距實(shí)在不行就加地線隔離如果系統(tǒng)里同時(shí)有DC-DC電源模塊MRAM和MCU的3.3V最好從同一路穩(wěn)壓輸出取避免兩個(gè)電源域之間出現(xiàn)壓差工業(yè)現(xiàn)場(chǎng)的干擾往往是共模噪聲SPI信號(hào)線短且粗、走線在同一層、參考地完整一般都能扛住。我建議在PCB上把這幾個(gè)信號(hào)線都加上串聯(lián)電阻比如22歐姆既能降低振鈴也能在異常插拔時(shí)保護(hù)引腳。3. 讀寫(xiě)操作實(shí)現(xiàn)從指令集到完整代碼3.1 指令集8條指令解決所有問(wèn)題MR25H40CDF的指令集非常簡(jiǎn)潔通過(guò)SPI發(fā)送一條8位命令即可完成對(duì)應(yīng)操作指令名操作碼說(shuō)明WREN0x06設(shè)置寫(xiě)使能鎖存位WEL1寫(xiě)/寫(xiě)狀態(tài)寄存器前必須執(zhí)行WRDI0x04清除寫(xiě)使能鎖存位WEL0RDID0x9F讀取廠商/設(shè)備IDRDSR0x05讀取狀態(tài)寄存器WRSR0x01寫(xiě)狀態(tài)寄存器READ0x03從指定地址開(kāi)始順序讀WRITE0x02從指定地址開(kāi)始寫(xiě)入1~N字節(jié)SLEEP0xB9進(jìn)入深度休眠WAKE0xAB喚醒休眠中任何非WAKE指令都會(huì)被忽略其實(shí)嚴(yán)格說(shuō)是9條SLEEP和WAKE是成對(duì)出現(xiàn)的。這個(gè)指令集和普通SPI Flash很像所以如果你之前寫(xiě)過(guò)W25Q系列的驅(qū)動(dòng)搬到MRAM上會(huì)很順——唯一區(qū)別是WRITE指令后不再需要等待“擦寫(xiě)時(shí)間”寫(xiě)完就是寫(xiě)完。3.2 狀態(tài)寄存器與寫(xiě)使能鎖存狀態(tài)寄存器一共8位其中真正用到的是bit1WEL和bit6WPEN。WEL是寫(xiě)使能鎖存位。MRAM要求每一次WRITE或WRSR命令之前必須先發(fā)WREN0x06指令把WEL置1。這和SPI Flash的邏輯一致。RDSR讀出來(lái)的bit1如果是1說(shuō)明寫(xiě)使能已經(jīng)打開(kāi)如果是0寫(xiě)命令會(huì)被忽略。WPEN是軟件寫(xiě)保護(hù)使能位它和硬件引腳WP#配合工作。當(dāng)WPEN1且WP#引腳為低時(shí)整個(gè)芯片被保護(hù)WRITE和WRSR都被忽略WPEN0時(shí)WP#引腳的電平不影響寫(xiě)操作。我一般把WPEN保持為0用GPIO控制WP#引腳這樣既有硬件保護(hù)手段又不至于鎖死無(wú)法恢復(fù)。3.3 讀操作與讀模式分析READ命令格式是先拉低CS#發(fā)送0x03然后發(fā)送3字節(jié)地址高位在前之后每一個(gè)SCK周期從SO輸出一個(gè)字節(jié)數(shù)據(jù)。地址是24位對(duì)4Mbit來(lái)說(shuō)真正有效的只有低19位0x000000~0x07FFFF高5位應(yīng)為0。我一般封裝一個(gè)讀函數(shù)uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CS# low SPI_SendByte(0x03); // READ command SPI_SendByte((addr 16) 0xFF); // addr[23:16] SPI_SendByte((addr 8) 0xFF); // addr[15:8] SPI_SendByte(addr 0xFF); // addr[7:0] data SPI_SendByte(0x00); // read one byte GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS# high return data; }SPI_SendByte這個(gè)底層函數(shù)在發(fā)送一個(gè)字節(jié)的同時(shí)會(huì)從MISO收到一個(gè)字節(jié)所以讀數(shù)據(jù)時(shí)發(fā)送0x00作為虛擬時(shí)鐘。地址會(huì)自動(dòng)遞增連續(xù)讀時(shí)不需要重新發(fā)送命令。有個(gè)細(xì)節(jié)值得注意MRAM讀操作支持整個(gè)地址空間的連續(xù)尋址地址從0x07FFFF翻轉(zhuǎn)到0x000000時(shí)不會(huì)出錯(cuò)。這個(gè)特性在讀取大型日志記錄時(shí)很有用因?yàn)椴恍枰馞lash那樣按頁(yè)處理。3.4 寫(xiě)操作完整時(shí)序?qū)懖僮鞯臅r(shí)序和讀類似只是把命令換成0x02后續(xù)跟著地址和數(shù)據(jù)字節(jié)。數(shù)據(jù)可以是一個(gè)字節(jié)也可以連續(xù)多個(gè)字節(jié)CS#在整個(gè)過(guò)程中保持低電平。我封裝的寫(xiě)字節(jié)函數(shù)void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_WriteEnable(); // send WREN command GPIO_ResetBits(GPIOB, GPIO_Pin_0); // CS# low SPI_SendByte(0x02); // WRITE command SPI_SendByte((addr 16) 0xFF); // addr[23:16] SPI_SendByte((addr 8) 0xFF); // addr[15:8] SPI_SendByte(addr 0xFF); // addr[7:0] SPI_SendByte(data); // data byte GPIO_SetBits(GPIOB, GPIO_Pin_0); // CS# high }關(guān)鍵點(diǎn)在于CS#必須在發(fā)完數(shù)據(jù)之后才拉高。如果CS#提前拉高這次寫(xiě)操作就會(huì)被中斷寫(xiě)入的數(shù)據(jù)不保證正確。另外一次WRITE命令可以寫(xiě)任意長(zhǎng)度的字節(jié)只要CS#不放下來(lái)地址會(huì)自動(dòng)遞增——這個(gè)特性在寫(xiě)日志緩沖區(qū)時(shí)很有用。寫(xiě)操作本身是即時(shí)完成的不需要查狀態(tài)位等“忙標(biāo)志”。這是MRAM和Flash最大的體驗(yàn)差異沒(méi)有擦除等待、沒(méi)有編程等待寫(xiě)完就是寫(xiě)完了。3.5 寫(xiě)保護(hù)與休眠模式工業(yè)設(shè)備上電和掉電瞬間GPIO狀態(tài)不穩(wěn)定如果此時(shí)MRAM正好被錯(cuò)誤地寫(xiě)操作命中可能會(huì)寫(xiě)入臟數(shù)據(jù)。為了避免這種情況硬件上我會(huì)把WP#引腳連到一個(gè)默認(rèn)拉低的GPIO系統(tǒng)初始化完成后再拉高。這樣在MCU復(fù)位、時(shí)鐘未建立、GPIO未配置的窗口期MRAM保持硬件寫(xiě)保護(hù)狀態(tài)。睡眠模式在低功耗設(shè)備中很重要。發(fā)送SLEEP命令后芯片進(jìn)入深度休眠電流降到微安級(jí)任何命令都可以當(dāng)“喚醒信號(hào)”但手冊(cè)建議用WAKE指令。我實(shí)測(cè)過(guò)發(fā)送WAKE后大概需要等30到50微秒才能執(zhí)行下一條正常命令所以代碼里喚醒后要加個(gè)小延時(shí)否則第一筆讀操作可能讀到無(wú)效數(shù)據(jù)。4. 工業(yè)場(chǎng)景的可靠性設(shè)計(jì)與數(shù)據(jù)安全4.1 掉電瞬間如何保住最后一條數(shù)據(jù)工業(yè)設(shè)備最怕的不只是掉電而是“半掉電”——電壓在閾值附近抖動(dòng)MCU處于復(fù)位邊緣程序跑到一半被掐死。此時(shí)如果正在寫(xiě)MRAM會(huì)不會(huì)把數(shù)據(jù)寫(xiě)壞MRAM的物理寫(xiě)入過(guò)程是磁化方向翻轉(zhuǎn)由芯片內(nèi)部時(shí)序控制。只要CS#和數(shù)據(jù)線在掉電瞬間沒(méi)有異常擾動(dòng)寫(xiě)入操作要么完整完成要么不執(zhí)行。這和Flash的“寫(xiě)一半然后整片扇區(qū)損壞”完全不同。但要在工程上真正可靠不能只靠芯片特性還需要配合掉電檢測(cè)電路。我項(xiàng)目里的方案是用一顆外部電壓檢測(cè)芯片比如TCM809監(jiān)控3.3V電壓當(dāng)電壓跌到2.93V以下時(shí)立即觸發(fā)STM32的EXTI中斷在中斷里把WP#拉低、關(guān)閉MRAM寫(xiě)使能。這樣即便MCU主循環(huán)還在亂跑硬件寫(xiě)保護(hù)也能把最后的寫(xiě)入動(dòng)作屏蔽掉。4.2 ARM Cortex-M0中斷與臨界區(qū)保護(hù)STM32F042K6的Cortex-M0沒(méi)有硬件優(yōu)先級(jí)分組寄存器沒(méi)有NVIC的PRIMASK那種細(xì)粒度控制想實(shí)現(xiàn)臨界區(qū)保護(hù)常用的方法是關(guān)中斷__disable_irq(); MrDrv_WaitReady(); // for MRAM, virtually no wait MRAM_WriteStatusRegister(0x00); __enable_irq();要說(shuō)明的是MRAM寫(xiě)操作本身就很快不像寫(xiě)EEPROM要等幾個(gè)毫秒。所以“臨界區(qū)”其實(shí)非常短通常也就是幾十個(gè)時(shí)鐘周期。真正要防的不是寫(xiě)操作本身被中斷而是中斷服務(wù)程序里碰了SPI外設(shè)導(dǎo)致總線狀態(tài)錯(cuò)亂。因此我推薦MRAM讀寫(xiě)相關(guān)的SPI收發(fā)都放在同一個(gè)任務(wù)或同一個(gè)鎖內(nèi)中斷服務(wù)程序里只置標(biāo)志位不要在中斷里直接調(diào)MRAM驅(qū)動(dòng)。另外STM32F042K6的SPI1有一個(gè)FIFO發(fā)送和接收是解耦的。我在驅(qū)動(dòng)里會(huì)確保每筆SPI傳輸都完整執(zhí)行完再允許上下文切換。方法很簡(jiǎn)單所有MRAM驅(qū)動(dòng)函數(shù)不帶阻塞等待、不主動(dòng)讓出CPU天然就是原子化的。4.3 數(shù)據(jù)完整性CRC校驗(yàn)與壞塊管理MRAM雖然寫(xiě)壽命長(zhǎng)、不會(huì)出現(xiàn)Flash那種“壞塊”但工業(yè)現(xiàn)場(chǎng)的電磁干擾、MCU程序跑飛、SPI時(shí)序被擾亂依然可能導(dǎo)致個(gè)別字節(jié)出錯(cuò)。所以我在設(shè)計(jì)存儲(chǔ)協(xié)議時(shí)還是加了常見(jiàn)的數(shù)據(jù)完整性手段每個(gè)記錄塊頭部存4字節(jié)魔數(shù) 2字節(jié)長(zhǎng)度記錄塊里保存32位CRC校驗(yàn)值讀數(shù)據(jù)時(shí)先校驗(yàn)?zāi)?shù)和CRC不對(duì)就丟棄并重新從備份區(qū)讀取我通常把數(shù)據(jù)存兩份主區(qū)和備份區(qū)交替寫(xiě)入。讀的時(shí)候先讀主區(qū)CRC校驗(yàn)失敗就切備份區(qū)。這個(gè)策略在MRAM上實(shí)現(xiàn)成本很低因?yàn)椴恍枰馞lash那樣為了磨損均衡維護(hù)復(fù)雜的地址映射——MRAM寫(xiě)不壞你可以用最樸素的“兩個(gè)固定地址塊”方案簡(jiǎn)單可靠。5. 實(shí)測(cè)踩坑記錄與問(wèn)題排查速查表5.1 坑一片選懸空導(dǎo)致寫(xiě)錯(cuò)地址項(xiàng)目第一次打樣MRAM偶爾會(huì)在運(yùn)行時(shí)寫(xiě)入錯(cuò)誤地址的數(shù)據(jù)。排查到最后發(fā)現(xiàn)CS#引腳在沒(méi)有GPIO控制時(shí)處于高阻態(tài)而MRAM的CS#內(nèi)部并沒(méi)有強(qiáng)上拉稍微一點(diǎn)噪聲就會(huì)讓它在高低電平之間抖動(dòng)。此時(shí)SCK和SI上只要有數(shù)據(jù)信號(hào)芯片就可能把它當(dāng)成一次有效的命令序列。解決方式CS#直接接MCU GPIO推挽輸出并確保初始化GPIO時(shí)先把它拉高再初始化SPI外設(shè)。所有配置順序里片選必須是最早被穩(wěn)定住的那個(gè)信號(hào)。5.2 坑二狀態(tài)寄存器讀到0x00現(xiàn)象是發(fā)RDSR后讀到的值始終是0x00但芯片是好的寫(xiě)操作也能成功。后來(lái)發(fā)現(xiàn)是SPI模式配錯(cuò)了我的初始化代碼里CPHA設(shè)置成了2Edge即Mode 1MRAM在Mode 1下雖然能輸出部分?jǐn)?shù)據(jù)但MISO采樣點(diǎn)不對(duì)導(dǎo)致讀回全是0。改成CPHA1Edge后正常。排查這類問(wèn)題有個(gè)小技巧先用邏輯分析儀抓取CS#、SCK、MOSI、MISO四根線的波形和手冊(cè)時(shí)序圖對(duì)比。肉眼就能看出采樣點(diǎn)錯(cuò)位的問(wèn)題。5.3 坑三電機(jī)干擾下的總線錯(cuò)誤這個(gè)項(xiàng)目在設(shè)備旁有一臺(tái)三相電機(jī)啟動(dòng)瞬間電流沖擊很大導(dǎo)致MRAM讀取偶爾返回錯(cuò)誤數(shù)據(jù)。示波器看波形發(fā)現(xiàn)SPI的MISO線上疊加了大量毛刺。處理策略分三步一是把SPI時(shí)鐘從12MHz降到6MHz分頻8讓信號(hào)抗干擾能力更強(qiáng)二是MISO線上加一個(gè)10k歐姆上拉電阻到3.3V提高信號(hào)電平的驅(qū)動(dòng)強(qiáng)度三是在靠近電機(jī)的位置加一個(gè)磁珠減少傳導(dǎo)干擾。改完后連續(xù)跑了72小時(shí)一次錯(cuò)誤都沒(méi)有出現(xiàn)。5.4 更多問(wèn)題排查速查表現(xiàn)象可能原因處理建議寫(xiě)入后讀回?cái)?shù)據(jù)全為0xFFWREN指令沒(méi)發(fā)WEL位為0確保每次WRITE前執(zhí)行WREN寫(xiě)入后讀回?cái)?shù)據(jù)全為0x00CS#被拉低后提前拉高寫(xiě)中斷檢查CS#時(shí)序?qū)懭胱詈笠蛔止?jié)后再拉高數(shù)據(jù)偶發(fā)出錯(cuò)電源紋波過(guò)大加強(qiáng)Vcc去耦必要時(shí)加LDO喚醒后第一筆讀數(shù)據(jù)失敗WAKE后延時(shí)不夠加50us以上延時(shí)再發(fā)READ寫(xiě)操作無(wú)效且WPEN讀回1軟件寫(xiě)保護(hù)鎖死WRSR清WPEN前提是WP#引腳為高多個(gè)設(shè)備共用SPI總線MRAM被誤寫(xiě)片選競(jìng)爭(zhēng)每個(gè)設(shè)備的CS#用獨(dú)立GPIO切換前先拉高所有CS#最后再分享一個(gè)我自己用慣了的小技巧新板子回來(lái)后先用RDSR讀狀態(tài)寄存器驗(yàn)證SPI通信鏈路能讀到0x02說(shuō)明芯片正常工作讀不到就先查硬件不要急著跑讀寫(xiě)測(cè)試。這個(gè)動(dòng)作我每次都會(huì)做省掉過(guò)很多無(wú)謂的排錯(cuò)時(shí)間。MR25H40CDF STM32F042K6這套組合只要硬件上把CS#、WP#、HOLD#三個(gè)控制腳處理干凈軟件上記得每次寫(xiě)前發(fā)WREN讀寫(xiě)的穩(wěn)定性會(huì)讓你省心很多在頻繁記錄、掉電保存、抗干擾要求高的工業(yè)場(chǎng)景里MRAM真的是那種能給你兜底的選擇。