资讯详情

MRAM替代EEPROM:基于STM32 SPI接口的工业频繁写存储方案

📅 2026/10/4 1:38:04 | 华诺云谱 👁 阅读
MRAM替代EEPROM:基于STM32 SPI接口的工业频繁写存储方案
最近在搞一台老设备的控制系统里面的工业EEPROM快被写穿了。设备每班都要记录几百条产量和报警信息加上调试时反复改参数原来那片EEPROM的寿命撑不过一年。后来我把存储方案换成了Everspin的MR25H40CDF配合STM32F215RE主控通过SPI口直接读写。MR25H40CDF是4Mbit的串口MRAM容量不大但胜在写入随便写、掉电不丢。STM32F215RE是一颗Cortex-M3核跑120MHz外设资源丰富拿来驱动它再合适不过。这篇文章我把选型思路、硬件接线、驱动代码和调试过程完整过一遍给还在EEPROM、Flash和MRAM之间犹豫的朋友做个参考。1. 为什么用MRAM替代EEPROM和Flash1.1 MR25H40CDF的核心特性MR25H40CDF这颗片子表面上看就是个普通SPI存储芯片8个引脚512KB容量按字节寻址接上SCK、MOSI、MISO、CS就能通信。但它和人的本质区别在于存储介质EEPROM和Flash靠电荷保存数据而MRAM靠磁隧道结的磁阻状态保存数据。所以它没有擦除周期写字节之前不需要把整个扇区擦一遍指定地址直接写就行。一颗典型的MR25H40CDF支持40MHz的SPI时钟数据传输率在同容量非易失芯片里算很拔尖的。写入寿命这块MRAM标称 endurance 可以达到10^14次以上工业上基本可以当成“无限写”。断电数据保持时间一般也能达到20年。这些数值对于需要频繁记录参数的设备来说非常诱人。我拿它和手头常用的存储芯片列了个对比项目MR25H40CDF外置EEPROM 24C256SPI Flash W25Q64容量4Mbit256Kbit64Mbit写入单位字节无需擦除字节无需擦除先擦扇区再写页写寿命10^14次约100万次一般10万次SPI时钟最高40MHz最高1MHz左右最高80MHz读掉电数据保持20年以上100年以上20年以上写入前操作只需写使能只需写使能必须执行块擦除这表一眼就能看出问题EEPROM寿命虽然能接受但速度太慢容量偏小Flash容量大但频繁写日志要处理擦除和磨损均衡逻辑复杂一旦掉电时正好在擦除数据就悬了。MR25H40CDF更像一个“掉电不丢的SRAM”所以放在工业记录场景里非常顺手。1.2 STM32F215RE在这套方案里的定位STM32F215RE之所以合适首先是因为它有一堆可用的SPI外设。以我实际用的SPI1为例它挂在APB2上APB2最高60MHzSPI1通过预分频可以跑到30MHz甚至更高基本能接近MR25H40CDF的最大时钟。再把PA4作为软件CS脚PA5、PA6、PA7分别接SCK、MISO、MOSI一套硬件就成型了。其次是STM32F215RE自带DMA批量读写MRAM时不需要CPU一个字节一个字节去喂SPIDMA跑起来之后CPU还能去干别的算法任务。而STM32F2系列的电源管理里还有PVD可编程电压检测器可以在掉电瞬间检测到电压跌落进中断把关键数据紧急写入MRAM。这套组合天然适合做掉电保护。第三个原因是芯片本身的市场存量很大工业控制产品用了这么多年芯片资料、库函数、硬件设计参考都非常成熟哪怕中途换人接手项目也容易找到熟悉STM32F2系列的开发者。STM32F215RE虽然有512KB Flash和128KB RAM但我在这个方案中主要拿它当控制器MRAM负责外部非易失数据存储两级存储角色非常清晰。1.3 工业频繁写场景为什么最后选了MRAM我这次的设备需要保存的信息有三类生产计数、故障报警履历、掉电前的运行状态。生产计数每次加工零件都要加一锻压设备一分钟可能完成几十个零件一天的写入次数就上万。报警履历更麻烦现场一次故障可能短时间内连续上报几十条传统EEPROM在这种压力下寿命很快见底。Flash的情况也没好到哪里去。虽然单字节写入前要先擦除整个扇区如果设备突然断电数据可能停留在半擦除状态。而且为了保证Flash寿命通常还得做磨损均衡、空闲块管理工作量和代码复杂度直接上一个台阶。MRAM就不存在这些问题。它不需要擦除写入地址可以任意指定写寿命又高SPI时钟上来之后一次写入只有零点几微秒的时间掉电窗口被压得非常小。所以我认为对于“频繁写、掉电不丢、数据量不超过512KB”的工业场景MR25H40CDF几乎是贴着需求来的。2. 硬件电路设计从引脚到PCB2.1 引脚连接与最小系统MR25H40CDF的引脚逻辑和传统SPI Flash很像但有几个细节特别容易踩坑。核心引脚是CS#、SCK、SI、SO。CS#就是片选低电平有效SCK是时钟SI接主控MOSISO接主控MISO。我用的STM32F215RE这边是PA4做CS#PA5做SCKPA7做MOSIPA6做MISO连接关系如下MR25H40CDF引脚功能连接到STM32F215RECS#片选PA4GPIO输出SCK时钟PA5SPI1_SCKSI数据输入PA7SPI1_MOSISO数据输出PA6SPI1_MISOWP#写保护接GPIO或VCCHOLD#暂停通信接VCCVCC电源3.3VGND地GNDWP#这个引脚平时很多人直接拉高这样软件写保护没打开读写都自由。如果想在系统层面防误写可以单独用一个GPIO控制正常拉高需要锁存时拉低。HOLD#必须处理不能悬空。HOLD#拉低时SPI总线被冻结SO输出高阻看起来就像芯片“死掉”了。我在样板调试时遇到过这个情况后面会细说。2.2 电源去耦与HOLD#、WP#的详细处理MRAM本质上是个数字存储芯片对电源噪声不算特别苛刻但工业现场的电源环境千奇百怪电机的启停、变频器的斩波都可能把干扰引到3.3V上。我在VCC和GND之间放了1个10uF钽电容和1个100nF陶瓷电容两者尽量贴近芯片引脚。SCK和CS这样的敏感信号线上没有串电阻但走线都控制在10mm内避免形成天线。HOLD#和WP#如果有条件不要只接一个死的跳线而是在芯片附近用10k电阻上拉到VCC。这样做的好处是主控复位瞬间IO电平不确定时MRAM不会因为HOLD#被意外拉低而进入暂停状态。对于一些使用电池供电的便携式设备还可以把WP#接到一个高边开关上电池电量低时硬件自动锁定防止写入不完整的数据。PCB布局上我建议把MR25H40CDF放在MCU同一面、靠近SPI引脚的位置中间不要穿过连接器。如果是两层板尽量保证SPI走线下方有完整的地平面避免形成大的回流环路。实际测试中走线超过30mm后SCK边沿会出现明显过冲SPI时钟降到10MHz才好一些。所以布局紧凑对高速通信有益。2.3 与SPI Flash的兼容性坑MR25H40CDF的封装和引脚定义与不少SPI Flash接近有人会想着直接把它替换到原来Flash的PCB上。但这里要特别小心因为两者的指令集和状态寄存器定义并不完全一致而且WP#和HOLD#的位置在不同厂家的封装里可能有变化。我在一块测试板上试过直接换片原板子按W25Q64编写驱动换上MR25H40后发送读ID指令返回值完全对不上读写操作也没有响应。原因很简单MR25H40的读ID指令和数据格式与Winbond不同而且有些引脚的功能在两种芯片上是颠倒的。所以不要指望直接替代硬件上核对每一个引脚软件上按Everspin的数据手册重写驱动。另外还要注意MRAM的容量与地址范围。MR25H40CDF是512KB地址范围0x00000到0x7FFFF超过上界的地址操作不会得到反馈读出来的数据是所有地址线为高时的无效数据。驱动里对传入地址做严格限制尤其在使用不同批次芯片时更要小心。3. 底层驱动实现把MRAM当内存用3.1 SPI外设初始化我使用STM32 HAL库把SPI1配成主机模式8位数据MSB先行时钟极性低、第一边沿采样也就是SPI Mode 0。MR25H40CDF也支持Mode 3但Mode 0最省事和大多数ARM单片机的SPI默认配置一致。SPI_HandleTypeDef hspi1; void MRAM_SPI_Init(void) { __HAL_RCC_SPI1_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitTypeDef GPIO_Init {0}; GPIO_Init.Pin GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; GPIO_Init.Mode GPIO_MODE_AF_PP; GPIO_Init.Pull GPIO_PULLUP; GPIO_Init.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_Init.Alternate GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, GPIO_Init); // PA4作为片选脚必须是推挽输出 GPIO_Init.Pin GPIO_PIN_4; GPIO_Init.Mode GPIO_MODE_OUTPUT_PP; GPIO_Init.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, GPIO_Init); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); 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_2; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); }预分频我选SPI_BAUDRATEPRESCALER_2STM32F215RE的SPI1在APB2 60MHz下可以得到30MHz的SCK。如果MCU工作在120MHz之外的其他频率记得核算实际SCK不要超过40MHz留一点裕量更稳妥。3.2 指令集与状态寄存器MR25H40CDF的指令集不复杂常用的就那么几条指令操作码说明WREN0x06写使能任何写操作前必须发送WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读取数据支持连续读WRITE0x02写入数据支持连续写RDID0x9F读ID用于验证芯片状态寄存器里最常用的是bit0 WEL和bit1 WIP。WEL是写使能锁存发送WREN后置1芯片上电或发送WRDI后清零。WIP是写进行中标志对于MRAM来说写入几乎是瞬时完成的但严谨起见可以在批量写后轮询一下WIP。写操作前强制发一次WREN不然芯片会把WRITE指令忽略掉。这一点和EEPROM很像很多第一次用MRAM的人只发送WRITE指令而忘记WREN结果数据写不进去回读全是旧值。3.3 单字节与多字节读写代码单字节读、单字节写是最底层的原语。我的实现里把CS拉低后发送指令和地址最后一个字节数据接收或发送完再把CS拉高。CS拉高是MRAM执行内部写操作的时刻所以整个SPI数据帧必须保持CS低中间不能断开。void MRAM_CS_Low(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); } void MRAM_CS_High(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } uint8_t MRAM_ReadStatus(void) { uint8_t cmd 0x05; uint8_t status 0; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); HAL_SPI_Receive(hspi1, status, 1, 100); MRAM_CS_High(); return status; } void MRAM_WriteEnable(void) { uint8_t cmd 0x06; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 1, 100); MRAM_CS_High(); } uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t cmd[4] {0x03, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF}; uint8_t data 0; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Receive(hspi1, data, 1, 100); MRAM_CS_High(); return data; } void MRAM_WriteByte(uint32_t addr, uint8_t data) { uint8_t cmd[4] {0x02, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF}; MRAM_WriteEnable(); MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Transmit(hspi1, data, 1, 100); MRAM_CS_High(); }多字节读写不需要额外改地址。MRAM支持连续地址自动递增一次CS低电平保持期间可以发任意长度的数据。这点和Flash非常不同Flash一页通常256字节跨页要自己拆包而MRAM不存在页的概念。void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4] {0x03, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF}; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Receive(hspi1, buf, len, 100); MRAM_CS_High(); } void MRAM_WriteBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4] {0x02, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF}; MRAM_WriteEnable(); MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 4, 100); HAL_SPI_Transmit(hspi1, buf, len, 100); MRAM_CS_High(); }多字节操作时务必把长度控制在地址不会越过0x7FFFF。一旦地址到了末尾还继续发数据内部地址会回绕到0可能把你不想覆盖的地方覆盖掉。3.4 用DMA做批量数据传输工业设备记录数据时经常要一次性把几十甚至上百字节的工况数据写入MRAM。如果都用阻塞方式传SPICPU会被占住很久。STM32F215RE的SPI1支持DMA配合MRAM连续读写可以做到很高效。用DMA之前先把对应DMA流配置好我习惯让SPI1_TX走DMA2_Stream5SPI1_RX走DMA2_Stream2。读写命令的4字节地址头和DMA缓冲区分离避免地址头变化影响数据。关键点是CS信号必须由DMA传输完成回调拉高不能在启动DMA后立刻拉高。void MRAM_WriteBuffer_DMA(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4] {0x02, (addr 16) 0xFF, (addr 8) 0xFF, addr 0xFF}; MRAM_WriteEnable(); MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 4, HAL_MAX_DELAY); HAL_SPI_Transmit_DMA(hspi1, buf, len); } void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { MRAM_CS_High(); } }这里有个细节DMA回调里拉高CS要确保MRAM的连续写操作已经把最后一个数据锁存。SPI时钟在最后一个数据位之后还有一小段保持时间DMA发送完成中断被响应时SCK已经转完最后一拍所以CS拉高的时机是安全的。实测用30MHz时钟DMA搬运1KB数据耗时不到340微秒比CPU逐字节发送快了不少。4. 掉电保存与工业可靠性设计4.1 MRAM为什么不怕掉电Flash和EEPROM写入时要先建立电荷这个过程需要时间也存在中间状态。一旦中途掉电写入的比特可能处于“半写”状态读出来非0非1。MRAM的原理决定了它没有这个问题磁隧道结在写入过程中是按位翻转写入操作实际上就是把当前位设置为所需状态不存在擦除等待所以掉电对MRAM本身不会造成数据损坏。更妙的是MRAM在读操作过程中也不会破坏数据读和写通路是分开的。这意味着你在掉电瞬间读到什么状态之后上电还是那个状态。这点在工业设备上特别重要很多设备上电后要判断“上次是正常关机还是断电停机”有了MRAM这个标志位可以放心地靠掉电保存。不过硬件上仍然要注意CS引脚在上电过程中的电平。MCU的IO口在复位期间是高阻如果CS浮空外界干扰可能把CS拉低让MRAM误进入SPI通信状态。我用的解决方法是CS引脚外接10k上拉电阻到VCC保证主控还没初始化时CS是确定的无效电平。4.2 用PVD触发紧急数据保存MRAM本身不怕掉电真正需要处理的是“掉电那一刻MCU有没有把关键数据成功写进去”。最常用的方案是STM32F2系列的PVD电压检测器设定一个阈值当VDD跌到阈值以下时触发中断在中断里紧急保存现场数据。配置PVD其实很简单。STM32F215RE的PVD阈值可调我选的是2.9V左右供电3.3V正常工作时不会触发电压跌到2.9V说明外部电源已经断了。PVD中断优先级设到最高因为这是最后的机会等电压跌到2.4V以下时芯片内部逻辑可能已经无法保证SPI通信。void PVD_IRQHandler(void) { if (PWR_GetFlagStatus(PWR_FLAG_PVDO) ! RESET) { // 紧急保存运行状态 MRAM_WriteBuffer(SAVE_ADDR, (uint8_t*)DevStatus, sizeof(DevStatus)); // 清标志 PWR_ClearFlag(PWR_FLAG_PVDO); } }由于MRAM写入速度快这个中断里能做的操作很多。实际项目中我在一个12ms掉电测量窗口内连续写完了4K字节的运行参数如果换成SPI Flash根本不可能完成这么大体积的擦写。配合外部一个470uF电解电容整套方案在掉电后甚至能维持20ms以上绰绰有余。4.3 数据完整性校验与双区策略MRAM解决了“介质不会坏”的问题但SPI链路在工业现场仍然可能被电磁干扰打乱。比如一次雷击浪涌或变频器干扰SCK上多出一个毛刺传输的数据可能就错了。所以不能因为用了MRAM就放弃校验。我的做法很简单每条记录在头部放4字节CRC32写入时对整块数据算CRC读取时重新算一遍。CRC不对就判定该条记录无效。如果需要更高的可靠性可以在MRAM里划两个区交替写入写A区时如果掉电下次上电发现A区校验失败就自动回退到B区。这个思路和Flash的双备份是一样的但MRAM不需要先擦除所以实现起来更简单速度也更快。还要注意一点MRAM虽然支持任意字节写但并不是说连续写入一个长度超过64字节的数据也能保证原子性。MRAM内部没有页缓冲数据是一字节一字节落进去的如果中间掉电前面写进去的字节已经生效后面没写进去的保持旧值。这种“部分更新”在逻辑上是存在的所以关键数据最好用校验和与双区配合而不是单纯依赖介质本身的可靠性。4.4 硬件写保护与现场抗干扰MR25H40CDF的WP#引脚可以配合状态寄存器的保护位使用。如果在初始化时通过WRSR设置了保护再拉低WP#整个芯片或者某段地址区域就变成只读。这在产品出厂后防误写很有用但调试阶段建议不要开不然每次烧写都要先解锁麻烦。现场抗干扰方面我遇到过SPI总线被变频器干扰导致读到的状态寄存器异常。后来在SCK和CS上各串了一个22欧姆电阻靠近芯片端再对地接一个小电容效果改善非常明显。不过加电容会影响上升沿SCK频率高时容易劣化时序需要权衡一般100MHz以下的控制信号使用10到100pF的电容问题不大。另一个容易被忽略的是HOLD#。HOLD#在工业环境下如果被毛刺拉低芯片会暂停SPI通信主控发完数据后收不到响应程序卡在等待看起来像芯片死锁了。把HOLD#用10k电阻上拉到VCC后这个现象再没出现过。所以我在所有涉及MRAM的图纸上都会特别标注HOLD#必须上拉。5. 调试实录常见问题与排查方法5.1 读回全0xFF或0x00这是SPI设备最常见的故障。如果读到全0xFF先怀疑芯片根本没被选中或者SO线没接通。用万用表量CS脚在通信时是否有低电平脉冲量SO与PA6之间是否导通。如果读到全0x00重点检查WP#是否被拉低同时检查是否误发了WRDI指令。再有一个隐蔽原因就是HOLD#悬空。HOLD#被干扰拉低后SO输出高阻读数据时MISO线上多次采样到同一个电平表现出固定读值。我把HOLD#上拉后问题彻底消失。5.2 写入正常但掉电后数据丢失如果在线调试时能正常写入、正常读出但断电几分钟后数据没了那基本可以排除MRAM本身的问题。第一检查点是我前面说的CS上拉电阻MCU复位期间CS电平不定有可能对整个存储区执行了奇怪的写入指令。第二检查点看电源是否在MRAM最低工作电压以下时主控还在跑这会导致SPI写入失败但MCU无感知。我在一块样板上遇到过一次原因是3.3V电源掉电时跌落到2V附近MRAM不再保证写操作但MCU还在PVD中断里执行写函数。后来我把掉电检测阈值调高并且在一进入掉电流程后就关闭SPI的时钟输出不让无效的SCK继续翻转数据就稳定了。5.3 偶发读写错误与SPI时序问题偶发错误排查起来比固定错误头疼。我先用逻辑分析仪抓了整个SPI帧发现CS下降沿之前SCK线上有几次毛刺。这些毛刺来自主控GPIO初始化时序在硬件复位时PA5上没有默认上拉导致SPI时钟在配置前浮动。解决方法是把SCK配置为带上拉的复用功能并且在初始化MRAM驱动之前不要操作SPI外设的时钟。如果还有偶发错误可以把SPI预分频调大比如从30MHz降到15MHz看问题是否复现。MRAM支持40MHz但实际设备里布线、插座、经过连接器都会降性能。工业产品不必追求极限速度稳定更重要。实测30MHz在短走线上很稳一旦穿过一个2.54mm排针连接器信号质量明显下降降到15MHz后所有异常消失。5.4 用RDID验证连通性调试初期我建议先写一个读ID函数芯片能正确返回ID才能保证后续所有读写操作有效。发送0x9F操作码CS拉低连续读3到4个字节。Everspin的SPI MRAM返回的数据格式和传统SPI Flash不同具体字节内容以数据手册为准不要照搬Winbond的ID表。我自己的排查习惯是先读ID再读状态寄存器然后写一个已知数据的地址紧接着读回比对。这三步过了再去跑连续读写压力测试。压力测试可以每秒钟写1000次同一个地址然后累计读回校验跑几个小时不出现一次错误这套驱动才敢交给产线。最后再说一个经验MRAM和传统存储芯片的驱动代码不要随便套用。网上很多现成的SPI Flash驱动拿过来改个厂家ID就以为能用结果指令集、状态寄存器和写保护逻辑全都要重新核对。宁可花20分钟把MR25H40CDF的数据手册认真读一遍也不要等设备批量交付后因为存储故障被客户喊回现场。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑