動(dòng)實(shí)戰(zhàn):工業(yè)存儲(chǔ)掉電保存無需等待)
1. 為什么工業(yè)存儲(chǔ)翻牌到MRAM從一次掉電丟數(shù)說起去年做一臺(tái)變頻器配套的參數(shù)記憶模塊時(shí)我被一塊普通EEPROM整得夠嗆。產(chǎn)線上電的一瞬間要求設(shè)備在母線電壓跌到閾值后搶在幾十毫秒內(nèi)把當(dāng)前運(yùn)行參數(shù)、故障碼、累計(jì)運(yùn)行時(shí)間寫進(jìn)非易失存儲(chǔ)器。原方案用24C256按頁(yè)寫一頁(yè)寫滿要5毫秒還得先等內(nèi)部擦除周期更怕的是連續(xù)多次掉電后正好踩在寫入周期上數(shù)據(jù)就是不落盤。后來?yè)Q成MR25H40CDF這顆SPI接口的MRAM配合STM32F412RE整個(gè)保存流程從搶時(shí)間變成了隨手一寫徹底不用等。這篇文章就圍繞這套組合展開MR25H40CDF是什么、為什么適合工業(yè)場(chǎng)景、怎么在STM32F412RE上把SPI驅(qū)動(dòng)跑通、以及我在實(shí)際項(xiàng)目中踩過的幾個(gè)坑。適合正在做數(shù)據(jù)采集終端、伺服驅(qū)動(dòng)器、電力監(jiān)控、工業(yè)控制器想換掉Flash/EEPROM方案又怕選錯(cuò)的工程師參考。1.1 一次掉電保存的完整時(shí)間線傳統(tǒng)方案的時(shí)間線大概是檢測(cè)到掉電 → MCU把數(shù)據(jù)搬到RAM緩沖 → 啟動(dòng)EEPROM頁(yè)擦除通常3~5ms→ 頁(yè)寫再花3~5ms→ 校驗(yàn)。運(yùn)氣不好主控中斷響應(yīng)慢了一點(diǎn)整條鏈路10ms就會(huì)超時(shí)。而且EEPROM有10萬次寫入壽命掉電頻繁的場(chǎng)合一年就能磨到額頭。MRAM是完全不同的思路。它的存儲(chǔ)單元是磁隧道結(jié)MTJ寫入靠改變磁化方向而不是電荷遷移物理上沒有擦除概念每一位都能隨時(shí)從0改成1、從1改成0。寫操作就是總線級(jí)別的流動(dòng)SPI時(shí)鐘進(jìn)去多少數(shù)據(jù)就落盤多少不額外占用時(shí)間。提示MR25H40CDF標(biāo)稱寫耐久達(dá)到10的16次方量級(jí)數(shù)據(jù)保持20年以上工作溫度覆蓋工業(yè)級(jí)-40℃到85℃這幾點(diǎn)正是它在工業(yè)場(chǎng)景里比Flash/EEPROM值錢的根本原因。1.2 四類非易失存儲(chǔ)放在一張表里看給還在糾結(jié)選型的同學(xué)一張對(duì)比表參數(shù)以典型值為準(zhǔn)維度MRAM MR25H40NOR FlashEEPROMFRAM寫方式直接覆寫先擦除后編程直接覆寫直接覆寫擦除需求無有塊/扇區(qū)級(jí)字節(jié)級(jí)免擦無單字節(jié)寫時(shí)間隨SPI時(shí)鐘即時(shí)編程約數(shù)十μs擦除數(shù)百ms3~5ms隨總線即時(shí)寫耐久約10^16次約10^5次約10^6次約10^15次隨機(jī)訪問任意字節(jié)按頁(yè)/扇區(qū)任意字節(jié)任意字節(jié)抗輻射能力強(qiáng)一般強(qiáng)一般典型單價(jià)偏高低較低中等看到?jīng)]MRAM和FRAM都免等待但MRAM在密度、抗輻射和溫度范圍上更有優(yōu)勢(shì)FRAM適合小容量數(shù)據(jù)記錄。工業(yè)產(chǎn)品要存幾百KB以內(nèi)的配置和運(yùn)行記錄MR25H40這個(gè)級(jí)別的芯片基本是甜點(diǎn)位置。2. MR25H40CDF特性速覽與STM32F412RE接線準(zhǔn)備2.1 先從型號(hào)和引腳說起MR25H40CDF是Everspin的SPI接口MRAM容量4Mbit也就是512KB8引腳封裝。供電范圍3.0~3.6V正好卡在3.3V系統(tǒng)的標(biāo)準(zhǔn)頭上讀時(shí)鐘最高可到40MHz級(jí)別。引腳功能非常直觀引腳號(hào)名稱說明1CS#片選低有效2SCKSPI時(shí)鐘3SIMOSI主發(fā)從收4SOMISO主收從發(fā)5HOLD#保持輸入拉低暫停通信6WP#寫保護(hù)輸入7GND地8VCC3.3V電源和普通NOR Flash比這顆芯片脾氣簡(jiǎn)單很多沒有ID讀取之外的復(fù)雜狀態(tài)機(jī)真正要關(guān)心的寄存器只有一個(gè)寫使能鎖存位。但別小看這個(gè)鎖存位后面我會(huì)專門用一節(jié)講清楚。2.2 接線圖與器件選型細(xì)節(jié)STM32F412RE這邊我用了SPI1三個(gè)引腳分別是PA5SCK、PA6MISO、PA7MOSI片選用普通GPIO控制不要用硬件NSS因?yàn)镸RAM的CS#要求完全由軟件掌控方便在一條指令中間做電平操作。我的標(biāo)準(zhǔn)接法MR25H40CDF引腳STM32F412RE引腳備注VCC3.3V靠近芯片放100nF陶瓷電容再并一個(gè)2.2μFGNDGND單點(diǎn)接地SPI回路盡量短CS#PB0推挽輸出空閑拉高SCKPA5SPI1_SCK復(fù)用功能SIPA7SPI1_MOSISOPA6SPI1_MISOHOLD#3.3V必須上拉不能懸空WP#3.3V上拉或接GPIO配合WPEN位使用兩個(gè)必須上拉的引腳很多人會(huì)漏。HOLD#懸空時(shí)如果PCB走線附近有繼電器、電機(jī)驅(qū)動(dòng)這類干擾源噪聲脈沖可能讓芯片誤入保持狀態(tài)表現(xiàn)就是通信偶發(fā)丟字節(jié)、校驗(yàn)失敗。WP#如果懸空同樣可能出現(xiàn)莫名其妙的寫保護(hù)行為。我在量產(chǎn)板上直接把這兩個(gè)引腳用10kΩ電阻上拉到3.3V省心。2.3 為什么選STM32F412RE當(dāng)主控有人可能問隨便一顆M0不就行了還真不是。工業(yè)記錄場(chǎng)景需要做掉電快速保存、CRC校驗(yàn)、掉電前把多塊數(shù)據(jù)重新組織并寫入有時(shí)候還要開著實(shí)時(shí)時(shí)鐘和通信接口。STM32F412RE是100MHz的Cortex-M4F512KB Flash、256KB SRAM接口資源充足還能用內(nèi)置PVD做掉電檢測(cè)一顆片子把主控和電源監(jiān)控都干了。關(guān)鍵是它的SPI1掛在APB2總線上總線本身能跑到100MHzMR25H40CDF的40MHz上限不會(huì)被總線頻率拖后腿。3. 指令集與時(shí)序?qū)懯鼓苕i存是繞不開的門檻3.1 操作指令一覽MR25H40CDF的指令集比Flash簡(jiǎn)單得多日常用到的主要是這幾條指令名稱操作碼WREN寫使能0x06WRDI寫禁止0x04RDSR讀狀態(tài)寄存器0x05WRSR寫狀態(tài)寄存器0x01READ讀數(shù)據(jù)0x03WRITE寫數(shù)據(jù)0x02FAST_READ快速讀0x0BSLEEP睡眠0xB9WAKEUP喚醒0xAB讀數(shù)據(jù)命令是3字節(jié)地址加數(shù)據(jù)直出沒有Flash那種先讀ID再讀數(shù)據(jù)的繁瑣流程快速讀多一個(gè)dummy字節(jié)適合高頻連續(xù)讀。睡眠模式值得單獨(dú)說一句工業(yè)設(shè)備待機(jī)時(shí)能用0xB9讓芯片進(jìn)入超低功耗狀態(tài)電流能從毫安級(jí)掉到微安級(jí)喚醒用0xAB即可代價(jià)是喚醒后要等一小段時(shí)間才能正常通信。3.2 WEL鎖存為什么每次寫都要先發(fā)WREN這是MRAM和EEPROM最大的操作習(xí)慣差異。EEPROM通常發(fā)完寫命令就自己慢慢寫前提條件少M(fèi)RAM寫入太快了一個(gè)字節(jié)跟著SPI時(shí)鐘就落盤芯片廠商害怕總線上一個(gè)毛刺導(dǎo)致誤寫所以強(qiáng)制所有寫操作必須前置WREN把狀態(tài)寄存器里的WEL位置1之后才允許WRITE指令生效。正確的寫指令序列是這樣CS#拉低 → 發(fā)0x06 → CS#拉高 // WREN必須在CS#上升沿才生效 CS#拉低 → 發(fā)0x02 → 3字節(jié)地址 → 數(shù)據(jù)字節(jié) → CS#拉高如果你漏了WRENWRITE指令會(huì)被芯片靜默忽略讀回來還是舊數(shù)據(jù)而且不會(huì)報(bào)錯(cuò)。這種無聲失敗最容易坑人。我的習(xí)慣是把WREN封裝在每次寫函數(shù)里而不是靠調(diào)用者自覺。3.3 和Flash/EEPROM完全不同的三個(gè)時(shí)序認(rèn)知第一寫操作沒有等待時(shí)間。Flash寫一頁(yè)要等內(nèi)部狀態(tài)機(jī)跑完EEPROM也要等內(nèi)部寫周期MRAM寫完CS#一拉高馬上就能讀回新數(shù)據(jù)中間不需要任何延時(shí)。這對(duì)寫日志類應(yīng)用是質(zhì)變。第二沒有擦除操作。Flash想改一個(gè)字節(jié)如果這個(gè)字節(jié)現(xiàn)在不是0xFF就得先擦整個(gè)扇區(qū)再寫MRAM每一位都能任意翻向0x5A改0xA5就是一次總線寫不需要中間態(tài)。第三地址上限要自己管。4Mbit也就是512KB3字節(jié)地址字段里前面5個(gè)bit是冗余的讀寫超出0x7FFFF的地址屬于未定義行為。連續(xù)多字節(jié)讀寫時(shí)地址自動(dòng)遞增但到存儲(chǔ)尾部怎么回卷要以手冊(cè)為準(zhǔn)穩(wěn)妥做法是驅(qū)動(dòng)里做長(zhǎng)度邊界判斷防止把數(shù)據(jù)寫到天邊去。4. STM32F412RE SPI驅(qū)動(dòng)實(shí)現(xiàn)初始化、讀寫與自測(cè)4.1 初始化SPI1代碼與參數(shù)解釋我用的是標(biāo)準(zhǔn)外設(shè)庫(kù)風(fēng)格寄存器邏輯一目了然移植到HAL也只是換個(gè)殼。#include stm32f4xx.h #include stm32f4xx_spi.h #include stm32f4xx_gpio.h #include stm32f4xx_rcc.h #define MR25_CS_PORT GPIOB #define MR25_CS_PIN GPIO_Pin_0 #define MR25_CS_LOW() GPIO_ResetBits(MR25_CS_PORT, MR25_CS_PIN) #define MR25_CS_HIGH() GPIO_SetBits(MR25_CS_PORT, MR25_CS_PIN) void MR25_SPI_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); // CS: 推挽輸出空閑高電平 GPIO_InitStructure.GPIO_Pin MR25_CS_PIN; GPIO_InitStructure.GPIO_Mode GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOB, GPIO_InitStructure); MR25_CS_HIGH(); // SPI1: PA5 SCK, PA6 MISO, PA7 MOSI GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); 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; // 25MHz SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }幾個(gè)關(guān)鍵點(diǎn)的理由SPI模式選0CPOL0CPHA0手冊(cè)支持也是最標(biāo)準(zhǔn)的接法波特率預(yù)分頻選4SPI1在APB2上跑到100MHz除以4就是25MHz離MR25H40CDF的40MHz上限留了足夠余量NSS選軟件管理是因?yàn)镃S#要精確控制指令邊界。初次調(diào)試驗(yàn)證時(shí)我建議跑12.5MHz等波形確認(rèn)沒問題再上25MHz。4.2 單字節(jié)與批量讀寫函數(shù)底層收發(fā)函數(shù)先寫一個(gè)字節(jié)級(jí)的邏輯清晰static uint8_t MR25_SPI_Byte(uint8_t byte) { while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(SPI1, byte); while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) RESET); return SPI_I2S_ReceiveData(SPI1); }寫單字節(jié)時(shí)一定記得先WREN再WRITEvoid MR25_WriteByte(uint32_t addr, uint8_t data) { MR25_CS_LOW(); MR25_SPI_Byte(0x06); // WREN MR25_CS_HIGH(); MR25_CS_LOW(); MR25_SPI_Byte(0x02); // WRITE MR25_SPI_Byte((addr 16) 0xFF); MR25_SPI_Byte((addr 8) 0xFF); MR25_SPI_Byte(addr 0xFF); MR25_SPI_Byte(data); MR25_CS_HIGH(); } uint8_t MR25_ReadByte(uint32_t addr) { uint8_t val; MR25_CS_LOW(); MR25_SPI_Byte(0x03); // READ MR25_SPI_Byte((addr 16) 0xFF); MR25_SPI_Byte((addr 8) 0xFF); MR25_SPI_Byte(addr 0xFF); val MR25_SPI_Byte(0x00); // 讀時(shí)鐘MOSI發(fā)0 MR25_CS_HIGH(); return val; }批量讀寫同樣套路只是數(shù)據(jù)段循環(huán)。注意三個(gè)地址字節(jié)的順序是高位先出和SPI默認(rèn)的MSB first對(duì)齊。4.3 上電自測(cè)寫一讀一與擦除邊界驗(yàn)證驅(qū)動(dòng)寫好后別急著接業(yè)務(wù)邏輯先跑一個(gè)裸板自測(cè)把芯片幾個(gè)關(guān)鍵地址都掃一遍void MR25_SelfTest(void) { const uint32_t testAddr[4] {0x00000, 0x0FFFF, 0x3FFFF, 0x7FFFF}; uint8_t wr 0xA5, rd, i; for (i 0; i 4; i) { MR25_WriteByte(testAddr[i], wr); rd MR25_ReadByte(testAddr[i]); if (rd ! wr) { // 錯(cuò)誤處理打印地址、期望值、實(shí)際值 } } // 邊界翻轉(zhuǎn)0x00 ~ 0xFF 來回寫 for (i 0; i 8; i) { MR25_WriteByte(0x7FFFF, 0xFF); MR25_WriteByte(0x7FFFF, 0x00); } }自測(cè)要覆蓋到最高地址0x7FFFF因?yàn)楹芏喑跏蓟瘑栴}體現(xiàn)在高位地址線上拉線沒焊好、地址字節(jié)順序錯(cuò)了通常在小地址區(qū)間測(cè)不出來。8次翻轉(zhuǎn)寫是為了驗(yàn)證無擦除邏輯下0?1切換是否干凈MRAM對(duì)這種模式毫無壓力。4.4 如果你用的是HAL庫(kù)HAL寫法初始化思路一致句柄配置里把SPI參數(shù)填相同值片選GPIO自己操作。讀寫時(shí)用HAL_SPI_TransmitReceive()注意HAL在傳輸時(shí)會(huì)等硬件事件性能瓶頸在等待循環(huán)上如果對(duì)實(shí)時(shí)性要求高可以用HAL_SPI_TransmitReceive_DMA()。但我個(gè)人建議關(guān)鍵路徑上用寄存器直操或標(biāo)準(zhǔn)庫(kù)避免HAL的HAL_SPI_IRQHandler和業(yè)務(wù)中斷互相干擾。工業(yè)項(xiàng)目里穩(wěn)定可預(yù)期比代碼優(yōu)雅重要。4.5 時(shí)間開銷估算25MHz SPI下512KB整片讀約需要164ms如果只讀一個(gè)字節(jié)命令地址數(shù)據(jù)約40位耗時(shí)約1.6μs。寫同樣量級(jí)。這意味著掉電保存緊要時(shí)刻保存幾十字節(jié)加CRC的時(shí)間不到5μs加上中斷響應(yīng)和電源判決時(shí)間整個(gè)鏈路富余量非常大。對(duì)比EEPROM按字節(jié)寫要3~5ms這快了三到四個(gè)數(shù)量級(jí)。5. 工業(yè)現(xiàn)場(chǎng)可靠性設(shè)計(jì)掉電保護(hù)、冗余存儲(chǔ)與校驗(yàn)5.1 掉電瞬間怎么寫最穩(wěn)妥MRAM寫入快但有一個(gè)特性必須正視芯片內(nèi)部沒有Flash那樣的編程狀態(tài)機(jī)寫入依靠供電電流直接翻轉(zhuǎn)磁隧道結(jié)。如果VCC在CS#為低期間掉到最低工作電壓以下最后這一筆數(shù)據(jù)可能處于不確定狀態(tài)。所以掉電保存不能只靠MCU來得及還要靠電源監(jiān)測(cè)把寫操作限定在安全電壓區(qū)間內(nèi)。我推薦兩層保護(hù)。第一層用STM32F412RE內(nèi)置的PVD電源電壓檢測(cè)把閾值設(shè)在2.9V左右電壓從正常值跌到閾值時(shí)觸發(fā)PVD中斷在這個(gè)中斷里立刻把關(guān)鍵數(shù)據(jù)寫入MRAM。第二層在硬件上保證3.3V電容儲(chǔ)能至少能撐5ms給保存流程留出上限余量。實(shí)測(cè)下來從PVD觸發(fā)到寫完32字節(jié)加CRC我這邊不到1ms相當(dāng)從容。注意不要在掉電中斷里做完整日志落盤。掉電時(shí)間窗口再寬也是有限資源應(yīng)該只保存真正需要掉電保留的狀態(tài)參數(shù)普通運(yùn)行日志平時(shí)就寫不占用掉電時(shí)間。5.2 磨損均衡MRAM到底需不需要Flash做磨損均衡是因?yàn)椴翆憠勖挥?0萬次一個(gè)LOGO區(qū)域?qū)懸粌赡昃蛷U。MRAM耐久量級(jí)在10^16次什么概念你就算以1kHz頻率持續(xù)寫同一個(gè)字節(jié)一年也就3.15×10^10次離10^16還有五個(gè)數(shù)量級(jí)。所以常規(guī)工業(yè)應(yīng)用完全不需要磨損均衡算法隨便寫。唯一需要注意的是別把MRAM當(dāng)SRAM用在死循環(huán)里瘋狂刷同一塊數(shù)據(jù)。雖然耐久足夠但省電模式下頻繁喚醒寫數(shù)據(jù)會(huì)白白增加功耗。寫日志類應(yīng)用按固定周期批量寫依然沒有任何磨損壓力。5.3 關(guān)鍵數(shù)據(jù)的CRC與雙備份可靠性不能只靠芯片本身。工業(yè)現(xiàn)場(chǎng)電磁環(huán)境惡劣SPI線上的毛刺可能讓某次讀寫出錯(cuò)。我在關(guān)鍵參數(shù)區(qū)用了雙備份CRC的經(jīng)典組合參數(shù)區(qū)占前16KB分為A/B兩份每份頭部寫16位CRC和寫入序號(hào)。上電讀取時(shí)先讀ACRC不正確且序號(hào)小于B則回退到B兩個(gè)都錯(cuò)就走默認(rèn)參數(shù)并報(bào)警。每次更新參數(shù)時(shí)當(dāng)前版本號(hào)加1寫到序號(hào)較大的那份。這個(gè)方案成本低、邏輯簡(jiǎn)單能擋住絕大多數(shù)電磁干擾造成的靜默損壞。MRAM的字節(jié)隨意寫特性讓雙備份實(shí)現(xiàn)非常順手這在Flash上是不可想象的——每次更新都要考慮當(dāng)前扇區(qū)能不能直接寫麻煩得多。6. 實(shí)測(cè)數(shù)據(jù)與踩坑記錄6.1 實(shí)測(cè)性能與功耗我手頭這套組合在25MHz SPI下實(shí)測(cè)連續(xù)寫512字節(jié)耗時(shí)約210μs含指令開銷連續(xù)讀相同長(zhǎng)度約200μs和理論值基本吻合。整機(jī)3.3V母線電流增量在持續(xù)讀時(shí)約18mA左右寫操作因?yàn)榭偩€時(shí)間短平均電流反而低很多。進(jìn)入睡眠模式后MR25H40CDF的靜態(tài)電流掉到微安級(jí)別對(duì)電池供電的便攜設(shè)備很友好。溫度方面MRAM的特性很穩(wěn)定我在恒溫箱里跑過-20℃到70℃的讀寫循環(huán)沒出現(xiàn)溫度相關(guān)的數(shù)據(jù)錯(cuò)位。這和Flash在低溫擦寫變慢有明顯的體驗(yàn)差異。6.2 坑一WREN沒生效寫入被靜默丟棄第一次點(diǎn)亮?xí)r我寫一個(gè)字節(jié)讀回來是0xFF排查了半天以為是地址錯(cuò)了。后來才意識(shí)到WREN必須通過CS#上升沿提交如果WREN和WRITE之間CS#沒有拉高過WEL位根本不會(huì)置1。還有一次是初始化代碼里CS#默認(rèn)沒拉高全程通信都是亂的。處理辦法CS#空閑必須保持高電平WREN之后至少要有一個(gè)高脈沖再拉低發(fā)WRITE。6.3 坑二HOLD#懸空導(dǎo)致偶發(fā)性丟字節(jié)最初的手工樣板沒上拉HOLD#整機(jī)測(cè)試時(shí)發(fā)現(xiàn)日志偶爾會(huì)跳幀CRC錯(cuò)誤率雖然不高但足以觸發(fā)報(bào)警邏輯。用示波器抓HOLD#引腳能看到繼電器動(dòng)作時(shí)上面出現(xiàn)幾十納秒的毛刺芯片被短暫保持住正好吞掉半個(gè)字節(jié)。量產(chǎn)板加上10kΩ上拉后這個(gè)問題再?zèng)]出現(xiàn)過。如果你板子空間緊張至少也要在HOLD#和電源之間加一個(gè)電容濾波。6.4 坑三SPI速率飆得太高手冊(cè)說支持40MHz我就想把SPI1預(yù)分頻調(diào)到2跑50MHz——超了點(diǎn)不礙事吧結(jié)果長(zhǎng)線測(cè)試時(shí)偶發(fā)讀錯(cuò)位尤其是溫度一高更明顯。后來把頻率降到25MHz同時(shí)在SCK、SI上各串33Ω電阻波形邊沿變緩后誤碼率歸零。MRAM本身快不代表你的PCB走線能支撐極限速率SPI信號(hào)完整性照樣要認(rèn)真對(duì)待。6.5 坑四掉電判決和省電模式打架我早期設(shè)計(jì)里待機(jī)時(shí)讓MRAM進(jìn)入睡眠模式PVD掉電中斷來時(shí)直接寫數(shù)據(jù)。結(jié)果發(fā)現(xiàn)從WAKEUP命令發(fā)出到芯片完全恢復(fù)工作有一段穩(wěn)定時(shí)間掉電窗口短的時(shí)候就會(huì)寫失敗。后來改成平時(shí)不讓MRAM睡覺待機(jī)功耗多幾毫安但換來掉電保存的確定性。如果你的產(chǎn)品對(duì)待機(jī)功耗極其敏感一定要實(shí)測(cè)睡眠喚醒時(shí)間并把它計(jì)入掉電保存的預(yù)算里別拍腦袋留余量。另外一個(gè)小經(jīng)驗(yàn)量產(chǎn)測(cè)試時(shí)可以在產(chǎn)線流程里加一段全地址掃描寫0x55、讀回、寫0xAA、讀回成本只要幾秒鐘能篩掉絕大多數(shù)焊接虛焊、地址線短路的板子。這個(gè)測(cè)試腳本留到現(xiàn)在是我所有帶MRAM項(xiàng)目出廠的標(biāo)配。MR25H40CDF和STM32F412RE這套組合我后面做數(shù)據(jù)記錄儀時(shí)又復(fù)用了一次只是把掉電保存邏輯從PVD中斷挪到了外部電源監(jiān)控芯片上核心驅(qū)動(dòng)幾乎沒改。整體感受是換掉等擦除、等寫入的老思維之后工業(yè)存儲(chǔ)從摳時(shí)間窗口變成了隨手一寫心里踏實(shí)多了。如果你手里有類似的高頻記錄或掉電保存需求值得花半天時(shí)間把這套驅(qū)動(dòng)跑起來試試。