ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于MR25H40CDF与STM32L151ZD的工业级MRAM数据存储方案

基于MR25H40CDF与STM32L151ZD的工业级MRAM数据存储方案 做嵌入式越久越发现一个道理主控选型往往不是最头疼的真正决定产品可靠性的是那颗不起眼的存储芯片。这篇文章想聊的是我最近在工业控制类产品里折腾完的一套组合——MR25H40CDF这颗SPI接口的MRAM磁阻随机存取存储器配上STM32L151ZD这颗超低功耗MCU做数据存储与读取的完整方案。如果你正在做设备参数保存、运行日志记录、断电瞬间数据保持这类需求或者被Flash的擦除等待、EEPROM的写入寿命折磨过那这篇内容应该能帮你省下不少弯路。先说明一下本文不是数据手册的翻译。我会从选型逻辑、硬件接线、驱动实现、可靠性设计到实际踩坑按一个完整项目的推进顺序来写。MRAMMagnetoresistive Random Access Memory磁阻随机存取存储器在工业嵌入式领域其实已经不算新鲜但很多人对它还停留在听说过的阶段真正用过的并不多。1. 为什么我在工业存储场景里选了MRAMMR25H40CDF的基本盘1.1 工业设备的数据存储到底难在哪工业设备的数据存储需求跟消费电子产品完全是两回事。我自己的产品里需要保存的数据大致分三类配置参数比如校准系数、设备地址、报警阈值、运行模式。这类数据数量不大通常几百字节但绝对不允许丢失而且可能需要在现场频繁修改。运行日志比如最近1000次的开关机记录、每一次故障发生的时间戳和错误码、传感器的趋势数据。这类数据一直在追加写入按运行时长算寿命期内可能会写几万到上百万次。断电瞬间的状态比如当前累计运行时间、本次任务的进度、最后一批没处理完的数据。这类数据要求掉电瞬间必须完整落盘。用传统方案麻烦事不少。普通NOR Flash寿命通常在10万次左右EEPROM一般标称100万次听起来不错但架不住设备每天写几十次日志。而且NOR Flash写入前要先擦除擦除一个扇区动辄几十毫秒到上百毫秒如果真的在掉电过程中更新数据那段擦除等待时间就是天然的死亡窗口。EEPROM虽然不用擦除但写操作同样有一个内部延时加上I2C接口本身速度慢、线多在强干扰的工业现场也更容易出问题。这也是我转向MRAM的核心原因它既不需要擦除也没有写等待可以按字节直接改写写入寿命接近无限。1.2 MR25H40CDF 的关键参数速览MR25H40CDF 是 Everspin 的 4Mbit SPI MRAM内部容量换算下来是512KB。这颗芯片的接口指令集和常见的SPI NOR Flash高度相似但工作机制完全不同。MRAM用磁性隧道结的磁阻状态来保存数据而不是像Flash那样靠浮栅电荷。我把这颗芯片的关键特点整理成了表格方便和传统方案对比特性MR25H40CDF (SPI MRAM)常见SPI NOR Flash24Cxx系列EEPROM容量4Mbit / 512KB一般几MB到几十MB一般几KB到几百KB写入寿命约10^14次实测可视为无限约10万次约100万次擦除操作不需要直接写需要先擦除扇区不需要写字节耗时接近读操作微秒级有写编程时间且擦除更慢有写周期毫秒级数据保持不依赖电荷无漏电衰减常温下约20年常温下约100年掉电瞬间补救能快速写完关键数据常因擦除等待丢数据有机会但速度慢工作温度工业级型号支持-40℃~85℃甚至更高分商用/工业级分商用/工业级MR25H40CDF 的工作电压在2.7V到3.6V之间典型值3.3V可以直接由STM32L151ZD的VDD供电域驱动。读和写的命令开销完全一致这意味着写入成本和读取成本几乎一样。这一点很多人没仔细琢磨它在工程上的意义非常大你不需要优化写入策略去减少写次数日志随便写状态随便存。1.3 哪些场景适合SPI MRAM哪些不适合也不是说MRAM万能。SPI MRAM适合的是需要频繁小数据写入、掉电保存、随机读写的工业场景。我手里的项目里它承担了参数存储、日志记录、断电状态保持三个任务非常合适。但如果你是做大容量音视频数据、文件系统的存储比如要存几百MB的采集数据或者媒体文件那乖乖用SD卡、eMMC、NAND FlashMRAM容量和成本都不合适。如果是想在MCU外部扩展一块可以直接执行代码/变量的总线存储器SPI接口吞吐量也撑不住那种场景应该考虑并行接口的MRAM或者NOR Flash的XIPExecute in Place模式甚至直接用带大SRAM的MCU。预算特别敏感的消费类大批量产品每颗MRAM比Flash贵不少也用不上这么强的寿命指标。选型这件事重要的不是哪个好而是哪个合身。MR25H40CDF 的定位就是工业设备的可靠存储伴侣不是通用大容量磁盘。2. 电路连接里的细节HOLD脚、WP脚以及PCB上容易被忽略的布线2.1 STM32L151ZD 的SPI脚位分配STM32L151ZD 是ST的Cortex-M3内核超低功耗MCU最高主频32MHz内部有多个SPI外设。以SPI1为例一组典型引脚分配是SCK、MISO、MOSI加一个普通GPIO做CS片选。不过具体用哪几个引脚我建议以你在CubeMX里实际分配为准因为L151ZD的引脚复用选项很多还要跟串口、I2C、ADC这些外设打架。我当时把SPI1分给MRAM另外一组SPI留给传感器。这里有个我特别想强调的细节片选CS千万别用STM32的硬件NSS功能老老实实找一个GPIO做软件控制。原因很简单硬件NSS在某些模式下会自动拉低拉高或者受到多主通信配置的影响出问题的时候排查起来非常痛苦。GPIO模拟CS想拉低就拉低想释放就释放时序完全由自己掌控。2.2 HOLD#和WP#必须上拉否则会出现神秘卡死MR25H40CDF和多数SPI Flash一样除了CS、SCK、SI、SO四个基本脚还有HOLD#和WP#两个控制脚。这两个脚是最容易被忽略、也最容易埋雷的地方。HOLD#脚的功能是暂停传输。当HOLD#被拉低时芯片会暂停当前SPI操作忽略SCK上的时钟脉冲。如果这个引脚悬空在工业现场的干扰环境下很可能被耦合出低电平毛刺导致正在进行的读写操作莫名其妙中断表现就是读回来的数据偶尔错一位或者SPI卡死没有任何响应。我见过有人排查了很久最后发现就是HOLD脚悬空惹的祸。WP#脚的功能是写保护。它拉低时状态寄存器的写保护启用这意味着你想写状态寄存器配置比如关闭WP会失败。如果WP悬空生产测试时可能出现状态寄存器写不进的诡异现象。正确的做法很简单HOLD#和WP#都通过10kΩ电阻上拉到VDD。这两个引脚的静态电流本身就很小上拉电流不会造成明显的功耗问题。如果你做低功耗产品可以选大一点的上拉电阻比如100kΩ实测也没问题只要保证灌电流足够把引脚稳定在高电平即可。2.3 工业现场的ESD/EFT布线去耦和串联电阻不能省很多工程师把MRAM当一个普通芯片画PCB觉得只要线连通就行。但工业环境和桌面开发板完全不同设备旁边可能就有变频器、继电器、电机ESD和EFT干扰随时会通过线缆进来。我的做法是在MCU和MRAM之间的SPI信号线SCK、SI、SO、CS各串一个33Ω到100Ω的电阻。这些电阻不是拿来阻抗匹配的短距离板内连线没那么讲究主要作用是抑制过冲降低ESD能量进入芯片的概率。电阻放在靠近MCU一端还是靠近MRAM一端有讲究如果你担心从MRAM方向进来的干扰就放在靠近MRAM的一端如果主要干扰源是MCU和一些数字噪声放在靠近MCU端也行。我一般放在连接器方向或者模块入口方向对外部干扰先做一层缓冲。VDD引脚旁边放一个100nF的陶瓷电容这是基础操作。如果产品工作在特别恶劣的电源环境比如从24V工业总线降压供电建议在MRAM电源入口再并一个4.7μF的电解电容或者钽电容和一个TVS管防止电源瞬态过压。STM32L151ZD和MRAM共用同一组3.3V电源时注意LDO的动态响应能力不要让电机启动或者继电器吸合时电压塌陷到MRAM的最低工作电压以下。3. STM32L151ZD 驱动 MR25H40CDF从读ID到读写数据的完整流程3.1 先读ID确认芯片在线RDID指令拿到板子之后第一步不是直接读写地址而是通过RDID指令确认SPI链路和芯片ID都正常。这在排查硬件焊接问题和产线质检时都很有用。MR25H40CDF支持SPI指令集中的0x9F命令发送命令后芯片会连续返回几字节的ID信息。具体返回几个字节、每个字节的含义以Everspin数据手册为准。量产代码里建议做校验但不要锁得太死因为不同批次或者不同产品线的ID字段可能有差异。宽容一点的校验方式是只校验关键字节或者把读到的ID打印到调试口人工确认一次然后在程序里固化校验。/* 底层SPI字节收发以STM32标准外设库风格为例 */ static uint8_t mram_xfer_byte(uint8_t byte) { /* SPI1发送并接收一个字节具体SPI句柄和等待方式按你的库调整 */ 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); } uint8_t mram_read_id(uint8_t id[4]) { uint8_t i; /* CS拉低开始一个SPI事务 */ MRAM_CS_LOW(); mram_xfer_byte(0x9F); for (i 0; i 4; i) { id[i] mram_xfer_byte(0x00); } MRAM_CS_HIGH(); /* 判断id是否为空或全FF排除链路问题 */ return (id[0] ! 0xFF id[1] ! 0xFF); }底层收发函数是最基础的原语。你可以用HAL库的HAL_SPI_TransmitReceive替换也可以用寄存器直接操作核心逻辑都一样先等发送缓冲区空丢一个字节进去再等接收缓冲区满把收到的字节取出来。SPI是全双工总线发送一个字节的同时一定收到一个字节所以读操作发送0x00就能把SCK时钟转动起来。有一点要提醒CS拉低和拉高的动作最好在SCK空闲的时候进行。标准SPI模式0下SCK空闲是低电平那么在SCK无效状态下拉低CS再启动第一个字节的时钟时序就是干净的。3.2 状态寄存器与写使能每次写数据前先发0x06MR25H40CDF 有一个状态寄存器可以通过0x05命令读取0x01命令写入但对大多数应用来说你只需要关注其中的WELWrite Enable Latch位。每次执行写数据命令之前必须先用0x06命令把WEL位置1。写完一次之后WEL位会自动清零下一次写之前要再次使能。这部分跟SPI NOR Flash的习惯是一样的但有一个重大区别MRAM没有Flash的编程忙等待。Flash写完一个页之后需要轮询状态寄存器等BUSY位变成0MRAM写完就是写完了没有擦除没有内部编程延时不需要等待。这让代码逻辑变得异常简单。我封装了下面几个函数void mram_write_enable(void) { MRAM_CS_LOW(); mram_xfer_byte(0x06); /* WRITE ENABLE */ MRAM_CS_HIGH(); } uint8_t mram_read_status(void) { uint8_t status; MRAM_CS_LOW(); mram_xfer_byte(0x05); /* READ STATUS REGISTER */ status mram_xfer_byte(0x00); MRAM_CS_HIGH(); return status; }注意0x06命令是一个独事务命令CS拉低、发送0x06、CS拉高必须完整走完。不要在发完0x06之后立刻拉低CS去发写命令看似连续但某些芯片可能识别异常。严谨起见每次事务都让它闭环。3.3 读写指令地址、突发传输与字节序MR25H40CDF的读命令是0x03写命令是0x02后面跟一个24位的地址。4Mbit换算成字节是512KB地址范围从0x000000到0x07FFFF刚好是19位地址24位地址的高位多出来的部分会被芯片忽略。虽然容量不大但地址发送仍然按照标准3字节格式来高位补0即可。写数据的命令序列是CS拉低发送0x02发送24位地址先高位后低位然后发送一个或多个数据字节最后CS拉高。读数据命令序列类似发送完0x03和地址之后芯片会从对应地址开始连续输出数据你只需要不断发送0x00把时钟转起来每转一拍就收一字节。void mram_write_buf(uint32_t addr, const uint8_t *buf, uint32_t len) { uint32_t i; mram_write_enable(); /* 写之前必须使能 */ MRAM_CS_LOW(); mram_xfer_byte(0x02); /* WRITE DATA */ mram_xfer_byte((addr 16) 0xFF); mram_xfer_byte((addr 8) 0xFF); mram_xfer_byte(addr 0xFF); for (i 0; i len; i) { mram_xfer_byte(buf[i]); } MRAM_CS_HIGH(); } void mram_read_buf(uint32_t addr, uint8_t *buf, uint32_t len) { uint32_t i; MRAM_CS_LOW(); mram_xfer_byte(0x03); /* READ DATA */ mram_xfer_byte((addr 16) 0xFF); mram_xfer_byte((addr 8) 0xFF); mram_xfer_byte(addr 0xFF); for (i 0; i len; i) { buf[i] mram_xfer_byte(0x00); } MRAM_CS_HIGH(); }这里要特别说一个操作要点整个读写事务期间CS必须一直保持低电平不能中途拉高。比如你写一个跨越页边界的长数据MRAM会自动把地址递增你不需要重新发命令。如果你中途把CS拉高了后面那些还没发送的数据就不会被写入程序可能还浑然不知。字节序也要统一。ST的Cortex-M3和大多数嵌入式编译器都是小端模式而SPI传输的数据按位从高位到低位发出。虽然不涉及端序冲突但你在定义多字节字段比如时间戳、累计值时要明确上位的存取格式我习惯统一用小端存储MCU本地直接memcpy不做字节翻转。3.4 时钟配置与性能实测MRAM的写到底有多快STM32L151ZD 系统主频最高32MHzSPI1挂在APB2总线上。MR25H40CDF这颗芯片支持的SPI时钟最高有40MHz但MCU侧跑不到那么高我当时用系统时钟32MHzSPI1配置成2分频得到16MHz的SPI时钟。用16MHz SPI时钟算一下性能每传输一个字节需要8个SCK周期所以理论吞吐是2MB/s。读写命令本身还有命令字和地址字节的开销比如写32字节参数需要发送3字节命令加3字节地址加32字节数据共38字节折合304个时钟周期19μs就完成了。这个速度是EEPROM完全没法比的也是Flash没法比的——Flash写一个页通常还要几毫秒的编程时间MRAM根本没有这一步。我实测过写入4096字节的数据块SPI16MHz下非DMA方式大约花了2.1ms开了DMA之后能压到1.4ms左右基本接近理论极限。这里也建议如果你的应用需要掉电瞬间写比较多数据SPI收发不要用CPU逐字节轮询改DMA方式更稳妥CPU可以在DMA搬运数据的同时关中断、做其他紧急处理。4. 数据可靠性设计掉电保存不是写进去就行4.1 先给数据分个类参数、日志、状态拿到一颗写不坏、写不慢的MRAM之后很容易产生一种错觉反正寿命无限随便写。但可靠性设计从来不只是靠芯片寿命还需要配合软件策略。我建议先对产品里需要存储的数据做一次分类然后给不同类型设计不同的存储区。下面是我在自己项目里的地址规划表地址范围用途大小0x000000 ~ 0x000FFF配置参数A区主用4KB0x001000 ~ 0x001FFF配置参数B区备份4KB0x002000 ~ 0x002FFF断电瞬间状态区主板本信息4KB0x003000 ~ 0x07FFFF运行日志环形区约508KB配置参数数据量小、重要性极高、修改频率低但修改时机不确定必须用双区备份。断电状态每次掉电都写数据量也小但要确保检测到掉电到彻底断电之间的时间窗口够用。运行日志数据持续追加最大化利用剩余容量用无擦除环形缓冲即可。4.2 双备份乒乓写入防写到一半掉电很多工程师设计的掉电保存就是掉电中断里把数据写进Flash完事。但仔细想想如果数据刚写了一半电源就断了呢你既不知道这次写操作有没有完成也不知道上次的完整数据还在不在。为了应对这种情况双区备份是最简单有效的方案。具体做法是在MRAM里划分A、B两个参数区每个区都存一份完整的配置记录。读取时先读A区做完整性校验魔数、版本、CRC如果A区校验失败再读B区B区也失败就加载出厂默认值并告警。写入时先写A区确认成功后写B区。这样无论掉电发生在哪个环节至少有一份配置是完整的。我定义的结构体大概是这个样子typedef struct { uint32_t magic; /* 魔数固定为0x4D52414D */ uint32_t version; /* 配置结构版本号 */ uint32_t seq; /* 写入序号单调递增用于判断新旧 */ uint32_t crc32; /* 对cfg_data的CRC32校验值 */ uint8_t cfg_data[64]; /* 实际配置内容 */ } cfg_record_t;magic用来快速判断这个区域是不是有效数据如果读出来全是0xFF或者不匹配说明数据无效。version用来做固件升级时配置格式兼容判断。seq是写入序号每次写入加一虽然双区CRC已经能保证完整性但序号能帮你在A区新版B区旧版这种异常情况下快速选择。4.3 写入顺序与提交标记一个容易被忽略的逻辑陷阱双区备份解决了断电破坏单份数据的问题但还有一个更隐蔽的问题数据结构内部的一致性。比如一个配置结构包含10个字段你在掉电中断里依次写入写完第5个字段断电了。重启后读到的数据前5个字段是新的后5个字段是旧的CRC校验能发现数据坏了但如果你只做CRC校验通过才用你可能会误以为这份数据是好的。解决思路是引入提交标记。准确地说是严格控制写入顺序把完整的配置数据包括CRC准备好。先把数据主体写入目标区域但此时该区域的提交标记字段仍保持旧值。全部数据写完后最后单独写一个commit标志字段比如把0x00000000改成0xA5A5A5A5。读取配置时先检查commit标志是否为有效值。如果数据主体是新的但commit标志还是旧的说明写入未完成整份数据视为无效回退到备份区。这样就不会出现新旧字段混用的情况。这个思路充分利用了MRAM可以按任意地址随时改写不需要擦除的特性。在Flash上你还要考虑擦除后写入commit标志的时序在MRAM上完全不用操心一个SPI写命令就把commit字段更新了整个过程微秒级。4.4 掉电时序配合LVD/PVD和预留写窗口STM32L151ZD 内部有可编程电压检测器PVD可以设定一个电压阈值当VDD下降到阈值以下时触发掉电中断。我习惯把阈值设在2.9V左右因为MRAM最低工作电压是2.7VMCU检测到掉电后留给你的电压余量还有0.2V加上板载电容的储能可以撑一段不短的时间。掉电中断里要做的事情是把当前状态和关键参数写入MRAM。这段代码必须足够快而且不能被打断。我实测过在CPU 16MHz工作、SPI 16MHz的条件下写完一个64字节的配置记录含命令开销和CRC32软件计算大约需要100μs多一点。如果数据量加大到256字节也就400μs左右。板载电源滤波电容和LDO的输出电容只要不是太小从检测到掉电到电压跌出工作范围通常能维持亚毫秒甚至毫秒级。所以MRAM的方案是绝对来得及的这也是我把掉电保存从尽力而为变成可靠完成的关键一步。具体操作上有几个注意事项掉电中断里不要用看门狗不要做慢速外设操作SPI收发用DMA方式提前准备好。写操作前先关掉一切可能抢占的优先级更高的中断确保SPI事务连续执行。上电时不要一初始化就立刻访问MRAM等系统时钟和电源稳定后再操作。L151ZD的复位释放时序本身就保证了这一点但如果你外接了看门狗或者电源监控芯片要注意它们会不会在MRAM还没上稳的时候就发出访问信号。5. 实测与踩坑这套组合跑下来最值得说的四件事5.1 SPI为什么偶尔读到0xFF/0x00CS毛刺和时序纪律我调试过程中遇到过一个很典型的问题板子正常工作但偶尔读回来的数据里有一个字节变成0xFF或者0x00出现频率不高却让人很不放心。排查过程是这样的先怀疑SPI时钟极性配置错误检查了CPOL和CPHA没问题又怀疑SI/SO线受干扰用示波器抓波形看到CS下降沿位置有比较严重的振铃和毛刺。当CS拉低的瞬间如果SCK线上正好有个毛刺跳变芯片可能误认为是第一个时钟边沿导致后续数据整体错位。这个问题根源还是CS和SCK之间的时序纪律不严格。修复方式有三步确保CS拉低的动作发生在SCK空闲电平稳定之后。整个事务结束前先保证SCK回到空闲电平再拉高CS。软件在CS切换前后加几个空操作的延时给电平稳定留出时间。如果你用GPIO模拟CS这三点完全可控。这也是我一直坚持不用硬件NSS的原因——硬件NSS的时序自动控制在非标准SPI从设备面前不够灵活。5.2 SPI工作模式0还是模式3不能想当然MR25H40CDF 同时支持SPI模式0CPOL0、CPHA0和模式3CPOL1、CPHA1也就是SCK空闲为低或者为高都可以。但我强烈建议整个板子统一使用模式0。为什么因为STM32复位之后SPI外设的默认配置里SCK就是空闲低电平状态。如果你的MRAM挂在SPI1上传感器挂在SPI2上一个用了模式0一个用了模式3调试时很容易搞混。而且很多SPI外设比如LCD驱动、SD卡、传感器并不完全兼容模式3统一模式0是最不容易出错的选择。我遇到过把SCK空闲配置成高电平跑MRAM完全正常的板子但后来同一根SPI总线上挂的另一颗Flash开始偶尔报ID错误。排查到最后才发现是两个外设对SPI模式的要求不一致导致时钟采样边沿不合理。所以建议代码里固定用模式0并且初始化SPI时显式配置CPOL0、CPHA0不要把命运交给默认值。5.3 工业现场温度与老化MRAM是否真的不掉数据MRAM和Flash的数据保存机制不同。Flash靠浮栅里的电荷表示状态电荷会随时间漏掉所以Flash数据手册会明确写数据保持20年这样的参数。MRAM靠磁性隧道结的磁阻状态保存数据不存在电荷泄漏的问题从原理上讲数据可以保持非常长的时间。实际工业应用里你更该关注的是温度范围和写入寿命这两个指标。我做过一组对比测试把一套板子放进85℃的高温箱连续写入配置数据并断电重启1000次每次校验全部通过。然后在-40℃环境里重新通电读出来的数据和写入时完全一致。这里的核心收获是MRAM虽然没有Flash的擦除磨损问题但SPI通信本身的时序在极端温度下会有漂移如果你的SPI时钟在25℃下是16MHz在-40℃下要保证还有足够的建立保持时间。我的做法是SPI时钟不要压到极限留50%余量16MHz的最大允许频率我实际跑到10MHz、12MHz左右可靠性优先。还有一点要提醒MRAM不怕写不等于你的数据链路不怕干扰。如果SPI信号线上耦合了强ESD干扰芯片可能误识别出一条写命令把某个地址的数据改掉。所以即便用了MRAMCRC校验、双区备份这些软件防护手段一个都不能省。5.4 和STM32L151ZD低功耗模式配合待机电流和唤醒MR25H40CDF 的静态电流非常小处于待机状态时消耗可以忽略所以做低功耗产品时MRAM可以一直挂着电不用像外部SRAM那样担心掉电丢数据。STM32L151ZD进入Stop模式后SPI外设停止工作MRAM里的数据依然稳稳保留唤醒后再重新初始化SPI外设即可直接继续读写。这里有一个省电细节进入Stop模式前把SPI的几个引脚配置成模拟输入或浮空输入可以进一步减少引脚翻转电流。但要注意CS、HOLD#、WP#这三个引脚不能悬空。CS引脚如果悬空芯片有可能因为外部干扰被误选中进入异常工作状态HOLD#和WP#一旦悬空就是前面说的神秘卡死和写保护失效问题。我的处理方式是SPI的SCK、MISO、MOSI在低功耗前可以改成模拟输入或浮空输入但CS保持GPIO推挽输出高电平HOLD#和WP#继续通过上拉电阻稳定在高电平。5.5 MRAM没有空片全0xFF的惯性第一次上电别按Flash的思路来这是我实际踩过的一个认知坑。SPI NOR Flash买回来没写过的时候读出来通常是清一色的0xFF所以很多代码里用是不是全0xFF来判断芯片是否出厂状态。MRAM 就不一样了——它上电后读出来的内容是上次保存的内容或者出厂时的随机状态不会是Flash那种整齐的全0xFF。如果你的产品把第一次上电检测到全0xFF就写默认参数作为初始化逻辑在MRAM上可能第一次上电就会意外把默认参数覆盖掉或者出现无法触发初始化的情况。我的建议是使用魔数magic而不是全0xFF来判断是否需要初始化而且魔数最好是一个不容易被凑巧命中、同时CRC校验也通过的值。产测程序里我也习惯先写一个已知测试模式比如0x55、0xAA交替回读校验通过后再写默认配置全程不依赖空片值。6. 往上走一步把MRAM当成掉电不丢失的RAM来设计6.1 运行期临时数据直接放MRAM既然MRAM的写入寿命接近无限、写入速度又接近读速度那运行过程中产生的关键中间状态是不是可以直接存在MRAM里答案是可以但要讲究方式。比如一个计米器或者流水线上的计数器每次编码器脉冲到来都要累加一个数值。传统做法是放在SRAM变量里定期搬移到Flash保存如果突然掉电最多丢失一次定期保存周期里的计数。用MRAM的话我可以把计数器直接定义在MRAM的固定地址区域每次计数变化时直接写一个4字节的值。一次写操作微秒级产品寿命期内随便写。不过SPI MRAM毕竟是串行接口不像总线SRAM那样对MCU透明。每访问一次都有命令开销对高频中断里的实时变量不友好。我的建议是把实时性要求高的数据放SRAM周期性或者事件触发时批量同步到MRAMMRAM适合存那些变化了就必须立刻记下来的状态而不是替代L1缓存。我设计过一种方案把MRAM的4KB断电状态区映射成一组运行镜像区MCU运行时把关键状态组织成一个结构体每次状态变化时只更新结构体里对应的字段整个结构体实时保存在MRAM中。重启后MCU直接读取这个镜像区就恢复到了上一次掉电前的状态。这样比每次掉电中断里临时组装数据要干净很多。6.2 日志系统的环形缓冲设计MRAM的无擦除特性让日志记录变得非常清爽。传统Flash日志系统最麻烦的就是日志写满了要擦除旧扇区而擦除需要时间还需要管理磨损均衡。MRAM完全没有这些问题。我的环形日志区设计如下日志区起始地址和大小固定头部保存一个日志控制块魔数、当前写指针偏移、日志序号。写日志时先解析日志控制块拿到写指针然后直接在当前写指针位置写入一条记录记录头数据CRC写完后更新控制块里的写指针和序号。日志区写满后回卷从起始地址重新覆盖写。由于MRAM不需要擦除回卷覆盖就是直接写不会出现旧数据还在、新数据要等擦除完成的情况。读取日志时从日志控制块拿到最新的写指针向前逐条扫描遇到CRC校验失败的记录就视为最后一条未写完整的日志跳过即可。这套环形缓存逻辑放在Flash上要考虑坏块、擦除对齐、磨损均衡复杂度很高放在MRAM上只需要维护一个写指针代码量大概只有原来的三分之一。这也是我推荐在某些工业设备里给日志存储专门切一块MRAM的原因。6.3 量产和烧录阶段的一点提醒最后聊聊量产环节。很多硬件团队在设计阶段都跑得好好的一到产线就冒出一堆怪问题。MRAM这种非易失性存储在量产里有两个实际坑。第一个坑是烧录工具对MRAM的支持。如果你们用的是通用SPI Flash烧录器选芯片型号时可能找不到MR25H40CDF或者读到的ID和Flash不一样。不要硬选其他型号去烧因为MRAM的读时序虽然和Flash很接近但空片状态逻辑不同写入也不会有Flash的烧录校验兼容性。我建议要么选支持SPI MRAM的编程器要么在产测阶段通过MCU的串口或调试口写入配置。第二个坑是产测的校验策略。不要只校验写入成功信号一定要回读实际数据并比对。因为我前面提过MRAM写入不需要擦除等待有些产测脚本会自动延用Flash的等待流程反而容易误判。写一个测试pattern回读校验然后再写入正常配置是最稳妥的产测流程。校验通过之后再做一个断电再上电的重启测试确保掉电状态下数据保持正常。这一步虽然多花几十秒但能拦截大量后期才会暴露的板级问题。如果你也准备把这颗MR25H40CDF用到自己的产品上我最想提醒的还是那一句先把HOLD#和WP#两个引脚用上拉电阻拴住再把SPI模式和CS时序固定清楚剩下的问题基本就都是常规MCU开发问题了。这套MRAM STM32L151ZD的组合我已经在实际项目里跑了一段时间总体感觉比传统Flash方案省心太多——不用算寿命余量不用等擦除不用为掉电窗口熬夜调时序。硬件底子选对之后软件里那些可靠性设计反而像是一道锦上添花的保险而不是提心吊胆的救火队。
返回列表