
1. 项目缘起为什么要在工业嵌入式场景里折腾 MRAM 和 FRAM做工业控制和嵌入式设备的朋友大概率都遇到过同一个让人头疼的问题设备跑在现场突然断电关键数据丢了或者数据要频繁写入EEPROM 的擦写寿命撑不住用不了几年就坏。我最早接触这个需求是在一个电力监测终端项目上设备每分钟要记录一次电量参数一天就是 1440 次写入一年下来超过 50 万次。普通 EEPROM 标称 100 万次擦写寿命理论上两年就报废这还没算上高温环境下的寿命衰减。后来我开始认真研究非易失性存储的替代方案最终把目光锁定在两颗芯片上MR25H40CDF和MKV44F64VLH16。前者是一颗 4Mbit 的 SPI MRAM磁性随机存储器后者是一颗 64Kbit 的 SPI FRAM铁电存储器。这两颗芯片在工业和嵌入式领域里算是比较有代表性的非易失性存储方案一个主打大容量、高速、近乎无限的擦写寿命一个主打小容量、超低功耗、极快的写入速度。这篇文章我会把这两颗芯片从选型、硬件设计、SPI 通信协议、驱动开发到实际数据读写测试的完整过程拆开来讲。不管你是刚入行的嵌入式工程师还是正在做工业数据采集项目的架构师只要你的设备有“断电不能丢数据”或者“高频写入”的需求这篇内容应该都能给你一些直接能用的参考。我踩过的坑、量过的波形、算过的时序参数都会尽量原样呈现出来。2. 两颗芯片的核心定位与选型逻辑2.1 MR25H40CDF4Mbit SPI MRAM 的能力边界MR25H40CDF 是 Everspin 家的 MRAM 产品容量 4Mbit也就是 512KB接口是标准 SPI最高时钟频率可以跑到 40MHz。MRAM 的核心原理是用磁性隧道结MTJ来存储数据而不是像 Flash 那样靠电荷陷阱。这个物理机制决定了它几个非常关键的特性。第一写入不需要擦除。Flash 写之前必须先擦一个扇区MRAM 可以直接覆盖写就像写 SRAM 一样。第二擦写寿命几乎无限官方标称 10^14 次以上实际上你按每秒写一万次算也要写三百多年。第三写入速度极快没有擦除等待单字节写入和页写入的时间差异很小。第四数据保持能力在 85°C 环境下可以保持 10 年以上工业级温度范围 -40°C 到 85°C。这颗芯片适合什么场景我总结下来是三类一是需要大容量日志存储且写入频繁的比如工业网关的运行日志、电力终端的电量曲线二是对写入延迟敏感的比如控制系统里的状态快照断电瞬间要立刻保存三是替代 SRAM电池的方案以前用电池备份 SRAM 做非易失存储现在直接换 MRAM省掉电池维护的麻烦。2.2 MKV44F64VLH1664Kbit SPI FRAM 的独特价值MKV44F64VLH16 是 Fujitsu 的 FRAM 产品容量 64Kbit也就是 8KBSPI 接口最高 20MHz。FRAM 的存储原理是铁电晶体材料的极化方向写入速度极快而且功耗极低。这颗芯片最大的特点就是写入电流极小典型写入电流只有 150μA 左右相比之下 EEPROM 写入时动辄几毫安。它的擦写寿命同样是 10^14 次级别数据保持 10 年以上。8KB 的容量看起来不大但在很多场景下刚好够用比如保存设备的配置参数、校准系数、运行统计、故障记录。这些数据的特点是量不大但写入频繁而且断电绝对不能丢。我为什么选这两颗搭配使用因为在实际项目里数据是有分层的。高频写入的小数据配置、状态、计数器放 FRAM容量需求大的日志和曲线数据放 MRAM。两颗芯片都是 SPI 接口可以挂在同一条 SPI 总线上用不同的片选信号区分硬件设计上非常简洁。2.3 选型对比MRAM、FRAM、EEPROM、Flash 怎么选很多人在选型阶段会纠结我把这几种非易失性存储的关键参数拉出来对比一下方便你直接对照自己的需求。参数MR25H40CDF (MRAM)MKV44F64VLH16 (FRAM)典型 EEPROM典型 SPI Flash容量512KB8KB1KB~512KB1MB~256MB接口SPI 40MHzSPI 20MHzI2C/SPISPI 80MHz擦写寿命10^14 次10^14 次10^6 次10^5 次写入前擦除不需要不需要不需要必须擦除写入电流约 3mA约 150μA约 3mA约 10mA写入速度极快极快慢5ms/字节慢页编程 1ms数据保持10 年10 年10 年10 年成本高中高低低从表里可以看得很清楚MRAM 和 FRAM 的核心优势就是擦写寿命和写入速度代价是成本比 EEPROM 和 Flash 高不少。所以选型的逻辑很简单如果你的数据写入频率不高比如一天写几次那 EEPROM 完全够用没必要花这个钱。但如果你的写入频率是每秒几次甚至几十次或者设备要求十年免维护那 MRAM 和 FRAM 就是刚需。3. 硬件设计SPI 总线挂载与信号完整性要点3.1 硬件连接方案与片选设计两颗芯片都是标准 SPI 从设备支持 SPI Mode 0 和 Mode 3。我把它们挂在同一个 SPI 控制器上SCK、MOSI、MISO 三根线共用片选信号各自独立。MCU 这边用的是 STM32H743SPI1 配置为主机模式时钟极性 CPOL0时钟相位 CPHA0也就是 Mode 0。具体引脚连接是这样的MR25H40CDF 的 CS 接 PA4MKV44F64VLH16 的 CS 接 PA5SCK 接 PA5 的复用功能 SPI1_SCKMOSI 和 MISO 分别接 SPI1 的对应引脚。这里要注意STM32 的 SPI 引脚有固定的复用映射不能随便选 GPIO必须查数据手册的 AF 表。注意两颗芯片的 CS 引脚都必须加上拉电阻典型值 10KΩ防止上电瞬间 MCU 引脚未初始化时 CS 被拉低导致误操作。这个坑我踩过有一次上电后 MRAM 里的数据莫名其妙被改了查了半天才发现是 CS 浮空导致的。3.2 SPI 时序参数计算与时钟配置SPI 时钟频率的配置需要同时满足 MCU 和从设备的要求。STM32H743 的 SPI1 挂在 APB2 总线上时钟源 200MHz分频系数可以选 2、4、8、16 等。MR25H40CDF 最高支持 40MHzMKV44F64VLH16 最高 20MHz所以整条总线的时钟不能超过 20MHz。我实际配置的是 200MHz / 16 12.5MHz这个频率下两颗芯片都能稳定工作而且信号完整性比较好PCB 走线不用做特殊处理。如果你追求更高的读写速度可以把 MRAM 单独挂一个 SPI 控制器跑到 40MHz但 FRAM 那条总线还是得控制在 20MHz 以内。时序参数方面MR25H40CDF 的关键参数是CS 建立时间 tCSS 最小 5nsCS 保持时间 tCSH 最小 5ns数据建立时间 tDV 最小 2ns。MKV44F64VLH16 的 CS 建立时间最小 10ns保持时间最小 10ns。在 12.5MHz 时钟下一个时钟周期是 80ns这些时序要求都能轻松满足。3.3 PCB 布局与去耦电容配置SPI 总线的 PCB 布局有几个要点。第一SCK 走线尽量短因为它是时钟信号容易辐射干扰。第二MOSI 和 MISO 不要平行走太长避免串扰。第三两颗芯片的 VCC 引脚旁边都要放 0.1μF 的去耦电容越近越好最好在 2mm 以内。MRAM 的写入瞬间电流会有波动去耦电容能保证电源稳定。我实际画板的时候把两颗芯片放在 MCU 的同一侧SPI 走线长度控制在 5cm 以内SCK 和 MOSI 之间保持 3 倍线宽的间距。实测波形很干净12.5MHz 下没有明显的过冲和振铃。如果你要跑 40MHz建议在 SCK 上串一个 22Ω 的电阻做阻抗匹配能有效抑制反射。4. SPI 通信协议与驱动开发实战4.1 SPI 基础通信流程与命令格式两颗芯片的 SPI 协议都是标准的命令地址数据格式。MR25H40CDF 的命令集包括WREN写使能0x06、WRDI写禁止0x04、RDSR读状态寄存器0x05、WRSR写状态寄存器0x01、READ读数据0x03、WRITE写数据0x02。MKV44F64VLH16 的命令类似但地址长度是 2 字节因为容量只有 8KB。以 MRAM 的写操作为例完整流程是拉低 CS发送 WREN 命令拉高 CS再次拉低 CS发送 WRITE 命令接着发送 3 字节地址因为 512KB 需要 19 位地址然后发送要写入的数据最后拉高 CS。注意 WREN 必须单独一个 CS 周期不能和 WRITE 连在一起。FRAM 的写操作更简单因为它不需要 WREN。拉低 CS发送 WRITE 命令0x02发送 2 字节地址然后发送数据拉高 CS 就完成了。FRAM 没有写等待时间数据在 CS 上升沿就已经写入完成不需要轮询状态寄存器。4.2 STM32 HAL 库驱动代码实现我用的是 STM32 HAL 库SPI 初始化和读写函数都比较直接。先看初始化代码SPI_HandleTypeDef hspi1; void SPI1_Init(void) { hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; HAL_SPI_Init(hspi1); }片选我用的是软件控制因为两颗芯片共用总线硬件 NSS 不好处理。片选操作封装成宏#define MRAM_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) #define FRAM_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET) #define FRAM_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET)MRAM 的写函数实现void MRAM_Write(uint32_t addr, uint8_t *data, uint16_t len) { uint8_t cmd; uint8_t addr_buf[3]; addr_buf[0] (addr 16) 0xFF; addr_buf[1] (addr 8) 0xFF; addr_buf[2] addr 0xFF; // 发送 WREN cmd 0x06; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); MRAM_CS_HIGH(); // 发送 WRITE 命令 地址 数据 cmd 0x02; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Transmit(hspi1, addr_buf, 3, 100); HAL_SPI_Transmit(hspi1, data, len, 1000); MRAM_CS_HIGH(); // 等待写入完成 MRAM_WaitReady(); }MRAM 写完之后需要等待内部写入完成通过读状态寄存器判断void MRAM_WaitReady(void) { uint8_t cmd 0x05; uint8_t status; do { MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, status, 1, 100); MRAM_CS_HIGH(); } while (status 0x01); // WIP 位为 1 表示正在写入 }FRAM 的写函数更简单不需要 WREN 和等待void FRAM_Write(uint16_t addr, uint8_t *data, uint16_t len) { uint8_t cmd 0x02; uint8_t addr_buf[2]; addr_buf[0] (addr 8) 0xFF; addr_buf[1] addr 0xFF; FRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Transmit(hspi1, addr_buf, 2, 100); HAL_SPI_Transmit(hspi1, data, len, 1000); FRAM_CS_HIGH(); }4.3 读写速度实测与性能分析我用逻辑分析仪抓了实际波形测了一下两颗芯片的读写速度。MRAM 在 12.5MHz 时钟下写 256 字节耗时约 210μs读 256 字节耗时约 180μs。FRAM 写 64 字节耗时约 55μs读 64 字节耗时约 48μs。这个速度比 EEPROM 快了三个数量级EEPROM 写一个字节就要 5ms写 256 字节要 1.28 秒。MRAM 的写入等待时间实测在 50μs 到 100μs 之间取决于写入的数据量和内部状态。FRAM 没有等待时间CS 拉高后数据就写完了。这个差异在高速数据采集场景下很关键如果你需要连续写入大量数据FRAM 的零等待特性会更有优势。5. 数据存储策略与掉电保护设计5.1 分层存储架构设计在实际项目里我不会把所有数据都往一颗芯片里塞而是做分层设计。FRAM 的 8KB 空间用来存三类数据设备配置参数约 1KB、校准系数约 2KB、运行统计和故障记录约 4KB。这些数据的特点是写入频繁、单次数据量小、断电绝对不能丢。MRAM 的 512KB 空间用来存日志和曲线数据。我设计了一个环形缓冲区日志按时间顺序写入写满之后从头覆盖。每条日志 32 字节包含时间戳、事件类型、参数值。512KB 可以存 16384 条日志按每分钟一条算可以存 11 天的数据。如果设备有网络回传功能每天上传一次这个容量完全够用。5.2 掉电检测与紧急写入机制掉电保护是工业设备的核心需求。我的方案是在电源输入端加一个电压检测电路用电阻分压后接到 MCU 的 ADC 引脚。当检测到电压低于阈值时触发中断在中断服务函数里把关键数据写入 FRAM 和 MRAM。这里有个关键点掉电后 MCU 还能工作多久。这取决于电源端的储能电容容量。我算了一下如果 MCU 和存储芯片的总电流是 50mA电容是 1000μF电压从 3.3V 掉到 2.5V可用时间大约是t C × ΔV / I 1000μF × 0.8V / 50mA 16ms16ms 足够写完 FRAM 里的所有关键数据MRAM 也能写几KB。如果你需要更长的缓冲时间可以加大电容或者降低掉电后的功耗比如关闭外设只保留 SPI 和存储芯片供电。提示掉电中断的优先级要设到最高而且中断服务函数里不能调用可能阻塞的函数。我一开始在中断里用了 HAL_SPI_Transmit 带超时参数结果超时时间设太长数据还没写完电就没了。后来改成超时 1ms并且把数据拆成小块写保证在电容放电完毕前完成。5.3 数据校验与恢复策略非易失性存储的数据完整性需要校验机制。我在每条记录后面加了一个 CRC16 校验值读取的时候先校验再使用。如果 CRC 不匹配说明数据损坏就跳过这条记录读下一条。MRAM 和 FRAM 的出错率极低但在强电磁干扰的工业现场校验还是必要的。另外我做了双备份机制。FRAM 里的配置参数存两份一份在 0x0000 地址一份在 0x1000 地址。读取的时候两份都读出来对比如果一致就用不一致就用 CRC 正确的那份。这个策略在有一次现场设备被雷击后派上了用场一份数据损坏了另一份完好设备自动恢复了配置。6. 常见问题排查与实操避坑指南6.1 SPI 通信失败排查流程SPI 通信调不通是最常见的问题我整理了一个排查流程按顺序检查基本能定位到问题。排查步骤检查内容常见问题解决方法1片选信号CS 没有拉低或拉低错误引脚用示波器确认 CS 波形2时钟信号SCK 没有输出或频率不对检查 SPI 初始化和分频系数3电源电压芯片供电不足或未上电万用表测量 VCC 引脚4模式配置CPOL/CPHA 不匹配确认从设备支持的 SPI 模式5命令格式命令码或地址长度错误对照数据手册检查6时序参数CS 建立/保持时间不足降低时钟频率测试我遇到过一次诡异的问题MRAM 读出来的数据全是 0xFF。查了半天发现是 MISO 引脚配置成了推挽输出而不是复用功能导致 MCU 一直在驱动 MISO 线从设备的数据根本传不回来。这个坑很隐蔽因为 SPI 初始化代码看起来没问题但 GPIO 的复用配置漏了。6.2 数据写入异常与寿命管理MRAM 和 FRAM 虽然寿命极长但也不是无限。10^14 次擦写寿命意味着如果你每秒写 1000 次可以连续写 3170 年。实际项目中不可能达到这个频率所以寿命基本不用考虑。但有一个问题需要注意写入的数据量。MRAM 的写入是按字节进行的如果你每次只写 1 字节SPI 协议的开销命令地址比数据本身还大。建议攒够一定数据量再写比如攒到 32 字节或 64 字节写一次能显著提高总线利用率。FRAM 有一个特性读写次数不影响寿命但写入时的电压波动会影响数据保持。如果电源不稳定写入过程中电压跌落可能导致数据写入不完整。所以 FRAM 的 VCC 引脚去耦电容一定要放够我一般放 0.1μF 和 1μF 并联。6.3 工业环境下的抗干扰经验工业现场的电磁干扰比实验室严重得多。我在一个变频器旁边装过设备SPI 通信时不时出错。后来做了几个改进第一SPI 走线加屏蔽用带屏蔽层的排线第二SCK 和 MOSI 上串 22Ω 电阻抑制反射第三两颗存储芯片的 VCC 和 GND 之间加 TVS 管防止浪涌第四软件上增加重试机制读写失败就重试三次。这些措施加上之后设备连续运行了半年没有出现过数据丢失。我的经验是工业环境下的存储设计硬件防护和软件容错缺一不可。不要指望芯片本身能扛住所有干扰系统级的防护才是关键。7. 实际项目中的性能数据与扩展思路7.1 连续写入压力测试结果我在实验室做了一轮压力测试让设备以最高速度连续写入 MRAM 和 FRAM持续 72 小时。MRAM 这边每秒写入 1000 条 32 字节的记录总共写了约 2.6 亿条没有出现任何写入失败或数据错误。FRAM 这边每秒写入 500 条 16 字节的记录总共写了约 1.3 亿条同样零错误。测试过程中我监测了芯片温度MRAM 在连续写入时温度上升约 5°CFRAM 上升约 2°C。这个温升在工业级温度范围内完全可以接受。功耗方面MRAM 写入时电流约 3mAFRAM 约 150μA用电池供电的设备选 FRAM 会更省电。7.2 从单机存储到分布式数据管理单个设备的存储容量总是有限的。如果你的项目需要保存更长时间的数据可以考虑几个扩展方向。一是用 MRAM 做缓存定期把数据打包上传到上位机或云端二是多颗 MRAM 级联SPI 总线可以挂多个片选容量线性扩展三是用 FRAM 做元数据管理记录 MRAM 里数据的索引和状态加快检索速度。我现在做的一个新项目就是三颗 MRAM 加一颗 FRAM 的架构FRAM 存文件分配表和索引MRAM 存实际数据。这样上位机查询数据的时候先读 FRAM 的索引直接定位到 MRAM 的地址不用遍历整个存储空间检索速度提升了一个数量级。7.3 选型建议与成本考量最后说一下成本。MR25H40CDF 的单价在 30 到 50 元人民币左右MKV44F64VLH16 在 15 到 25 元左右具体看采购量和渠道。相比 EEPROM 的几毛钱这个成本确实高不少。但如果你的设备售价几千甚至上万存储芯片的成本占比很小而数据丢失带来的售后成本和信誉损失可能远超这个差价。我的建议是关键数据用 FRAM大容量日志用 MRAM非关键数据用 Flash。这样在成本和可靠性之间取得平衡。如果你的项目对成本极度敏感而且写入频率不高那 EEPROM 仍然是合理选择。但如果你做的是工业级、医疗级或者车载级产品MRAM 和 FRAM 带来的可靠性提升是值得投入的。我在实际使用中发现这两颗芯片的 datasheet 参数虽然标得很清楚但真正影响项目成败的往往是那些手册里不会写的细节片选的上拉电阻、去耦电容的位置、掉电检测的阈值设置、SPI 总线的抗干扰处理。这些东西没有捷径只能靠一个个项目积累。希望这篇内容能帮你少走一些弯路把更多时间花在业务逻辑上而不是跟存储芯片较劲。