ARTICLE DETAIL

资讯详情

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

MRAM替代EEPROM:STM32F215ZG工业存储设计与实践

MRAM替代EEPROM:STM32F215ZG工业存储设计与实践 前阵子把手上一个工业数据采集主板的存储部分重新设计了一遍。原来用的EEPROM参数写频繁之后直接报废换成 Everspin 的 MR25H40CDF 这颗 4Mbit SPI 接口 MRAM配合 STM32F215ZG 这颗 Cortex-M3 主控整套系统才算真正稳定下来。这篇文章把从选型、接线、驱动到掉电校验的完整过程整理出来适合正在纠结“EEPROM寿命不够、NOR Flash又太麻烦”的同行参考也适合刚接触嵌入式存储的开发者快速理解 MRAM 的读写模型。如果你在工业现场设备、仪器仪表、电力监控这类场景里做非易失数据存储这篇应该能帮你省不少弯路。1. 用MRAM不是因为先进而是EEPROM先扛不住了1.1 一次频繁写入把EEPROM寿命打穿的经历项目原形是一台现场采集设备每隔几十毫秒要记录一组运行参数掉电时还要保存当前状态。最初方案是 AT24C256256Kbit EEPROM标称写次数十万次。按我们的写入频率十万次听起来很多实际上设备连续运行一两个月就逼近边缘。那段时间最典型的故障表现是参数区域读到一堆 0xFF标定数据莫名丢失设备重启后回到出厂状态。用编程器把芯片读出来发现地址偏移处全是擦除态。查下来不是软件越界也不是电源问题就是单纯的EEPROM磨损——单元写坏了。连续坏了好几片之后我们决定放弃 EEPROM。当时考虑的替代方案有两个一个是 NOR Flash带擦除块、需要做磨损均衡掉电保护逻辑非常重另一个就是 MRAM。MRAM 最大优势是“写不坏”数据手册上写次数直接标 unlimited没有擦写块的概念数据写入就是直接覆写写完之后掉电数据也不会丢。对我们要频繁保存小批量现场数据的需求来说MRAM 比 NOR Flash 简单太多最终选了 Everspin 的 MR25H40CDF。提醒一句别把“无限次写入”理解成“绝对可靠”。任何存储芯片都会有制造缺陷、软错误和使用环境的影响CRC 校验和备份策略仍然要做。但“寿命不够”这个结构性瓶颈确实被 MRAM 解决了。1.2 磁隧道结原理与三种存储方案对比为什么 MRAM 写不坏传统 EEPROM 和 Flash 靠浮栅晶体管里电荷的注入/释放来保存状态每擦写一次氧化层就会累积损伤MRAM 的存储单元是磁隧道结MTJ由自由层、隧道势垒层、钉扎层组成自由层的磁化方向决定了单元是“0”还是“1”写入时改变的是磁化方向而不是电荷状态。磁化方向翻转没有物理磨损所以寿命很长、写入速度也快不需要先擦除再写入。用一张表说清楚三种方案的区别对比项EEPROMNOR FlashMRAM写次数约1万~10万次约10万次无限次写入前是否需要擦除字节级擦除扇区擦除不需要单字节写支持一般不支持支持写周期等待有毫秒级有毫秒级基本无掉电数据保持良好良好至少20年工业级温度有有有NOR Flash 在固件存储上依然是首选但做频繁小数据存储它的擦除块和磨损均衡太啰嗦EEPROM 简单但寿命是硬伤。MR25H40CDF 在这三者之间找到一个很舒服的位置512KB 容量足够存参数和日志SPI 接口又和传统 Flash 一样简单写的时候几乎不用等待。2. 从芯片手册到最小系统MR25H40CDF怎么接到STM32F215ZG2.1 引脚映射SPI2加上一颗GPIO片选就够MR25H40CDF 是标准的 8 脚 SPI 器件引脚不多接线很容易理解。核心引脚包括 CS#、SCK、SI、SO外加 WP# 写保护和 HOLD# 暂停通信两个控制脚供电 VCC 和 GND。STM32F215ZG 上我用了 SPI2片选直接用普通 GPIO没有用硬件 NSS因为软件片选在时序控制上更灵活。引脚对应关系如下MR25H40CDF引脚功能接STM32F215ZG说明CS#片选PB12普通GPIO输出低电平有效软件拉低SCKSPI时钟PB13SPI2_SCKSI主出从入PB15SPI2_MOSISO主入从出PB14SPI2_MISOWP#写保护接VCC或GPIO高低电平会锁保护位HOLD#暂停通信接VCC低电平暂停SCK/SIVCC3.3V电源3.3V手册范围3.0~3.6VGND地GNDDFN封装底部焊盘建议接地GPIO 片选的一个重要优势是你可以完全控制 CS 拉低和拉高的时机不会被硬件外设自动拉高。这对后面处理 DMA、掉电、校验逻辑都很关键。PB12 只是一颗普通推挽输出引脚速度等级设成低速就够了片选信号本身不追求高频翻转。2.2 外围电路WP、HOLD、去耦电容一个都不能省芯片手册只会给你引脚定义真正让这套系统在工业现场稳定运行外围电路要注意几个细节。第一是 WP# 和 HOLD# 绝对不能浮空。我们的第一版板子就是这两个引脚没接结果在电机启停、变频器干扰的测试现场出现过写入无效、数据偶发错乱的怪问题。后来把 WP# 和 HOLD# 都直接接到 VCC问题消失。如果你有运行中动态保护 MRAM 区域的需求WP# 可以接一颗 GPIO 来控制但默认必须保证高电平。第二是电源滤波。MR25H40CDF 是数字存储芯片瞬态电流不大但工业板卡电源纹波往往很脏。VCC 引脚旁边放一颗 100nF 陶瓷电容再加一颗 10uF 钽电容做低频去耦这是起步配置。电容尽量靠近芯片电源引脚走线短而粗。第三是 SPI 信号线的串联电阻。我们在 SCK、SI、SO 三条线上各串了 33Ω 电阻位置靠近 MCU 端用来抑制信号反射和振铃。这个做法对 EMC 测试有帮助代价是边沿变缓但 SPI 速率在 30MHz 以内完全不影响通信。注意DFN8 封装的底部散热焊盘不是一个装饰。手册明确要求焊接到地既能散热也能增强机械强度。手工焊接时容易忽略批量贴片要注意钢网开孔。2.3 为什么是STM32F215ZG跑这颗MRAMSTM32F215ZG 属于 STM32F2 系列Cortex-M3 内核主频最高 120MHz在工业应用里是非常常见的选择。选它跑 MR25H40CDF 有几个实际原因F2 系列的工作温度范围覆盖 -40℃~85℃SRAM 和 Flash 配置足够跑数据采集和协议栈SPI 外设数量足够SPI2 用在 MRAM 上SPI1 还能留给显示屏或传感器IO 电平是 3.3V和 MR25H40CDF 完全匹配不需要电平转换。F2 系列的 SPI 时钟能做到多高MR25H40CDF 的 SPI 最大时钟是 40MHzSTM32F215ZG 的 SPI2 挂在 APB1 总线上APB1 最高 60MHz预分频 2 就是 30MHz预分频 4 是 15MHz。30MHz 没有超过 MRAM 上限实测稳定。如果你的系统时钟树不是标准配置要先确认 APB1 频率再算 SPI 波特率这个细节放到后面的 CubeMX 部分再展开。3. 吃透MRAM的指令集和状态寄存器读写时序才算入门3.1 核心指令只有六条真正要熟练的是READ和WRITESPI 接口的存储芯片指令集都不复杂MR25H40CDF 相比 NOR Flash 更是精简。项目里真正用到的指令只有几条先看表格指令操作码说明WRITE0x02写数据可连续写任意字节READ0x03读数据可连续读任意字节WREN0x06写使能锁存需要修改状态寄存器时使用WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器很多从 Flash 迁过来的工程师会习惯性在每次写数据前发一条 WREN这在 MRAM 上不是必须的。MR25H40CDF 的 WRITE 指令本身始终有效WEL 位只影响状态寄存器和块保护位的修改不影响普通数据写入。我最初也按 Flash 的思路写了个“先 WREN 再 WRITE”的封装结果也能跑通但白白多了一步操作并且容易让人误解 MRAM 的行为模型。真正要注意的是WRITE 指令没有页边界限制。NOR Flash 写页通常有大小限制超过页大小就要换地址MRAM 不是页缓冲结构只要 CS 保持低电平WRITE 指令可以带任意长度的数据连续写下去写满整个 512KB 都行。这在写日志、批量存采集数据时非常方便。3.2 24位地址、Status Register与WIP/WEL的误解MR25H40CDF 容量是 4Mbit也就是 512KB 字节。地址宽度 24 位实际有效地址 A18~A0高 5 位在发送时必须为 0。地址在 READ/WRITE 指令后面紧跟 3 字节高位在前。比如要访问地址 0x1FFFF发送顺序是 0x03、0x01、0xFF、0xFF。状态寄存器的低两位是 WIP 和 WEL再往上还有块保护位 BP0/BP1。这里最容易出现的误解是Flash 写命令发完要等 WIP 为 0 才算写完MRAM 用不用答案是基本不用。因为 MRAM 数据写入是在 SPI 时钟的最后一位边沿完成命令发完数据就已经落进存储阵列RDSR 读到的 WIP 几乎立即是 0。只有在写状态寄存器这类操作后才偶尔需要确认状态。但这也提醒我们MRAM 虽然没有写等待却不等于“写完可以不管”。真正要管的是地址是否越界、内容是否正确所以我们在驱动里总是“写后立即读回校验”成本不高效果却很实在。3.3 SPI Mode 0、30MHz和时序边界MR25H40CDF 支持的 SPI Mode 比较多常用 Mode 0 和 Mode 3也就是 CPOL0/CPHA0 或 CPOL1/CPHA1。我们用 Mode 0时钟空闲为低数据在第一个边沿采样。STM32F215ZG 的 SPI2 配成 Mode 0 很容易HAL 库里对应SPI_POLARITY_LOW和SPI_PHASE_1EDGE。时序边界上MRAM 手册里有 CS 拉低到第一个 SCK 上升沿的最短建立时间、最后一个 SCK 边沿到 CS 拉高的最短保持时间。数值都是纳秒级MCU 的 GPIO 操作本身就有几十纳秒开销理论上很容易满足。但如果你把 SPI 信号拉得很长又在 CubeMX 里开了很强的 GPIO 驱动反射会让边沿变形CS 和 SCK 的相对时序会有风险。我们量产板上 SPI2 信号线控制在 15mm 以内没有出现时序问题如果做远距离板间通信宁可利用手的逻辑分析仪抓一把波形别凭感觉压缩时序。4. STM32F215ZG上的SPI读写实现HAL库路线4.1 CubeMX里的SPI2与CS引脚配置用 STM32CubeMX 初始化工程很方便但有几个地方容易错。SPI2 配置成 Full-Duplex Master数据大小 8 bit时钟极性 Low时钟相位 1EdgeNSS 选择 Software。波特率预分频我先选除以 4也就是 15MHz 开始调稳定之后再改除以 2 跑到 30MHz。如果 APB1 时钟不是 60MHz实际速率要以 CubeMX 时钟树里的显示为准。CS 引脚 PB12 配置成 GPIO_Output初始电平设为 High。CubeMX 生成的初始化代码里CS 默认高电平这个状态很重要否则设备上电时 CS 悬空或为低MRAM 可能处于未知通信状态。CubeMX 生成的 SPI2 初始化大致长这样hspi2.Instance SPI2; hspi2.Init.Mode SPI_MODE_MASTER; hspi2.Init.Direction SPI_DIRECTION_2LINES; hspi2.Init.DataSize SPI_DATASIZE_8BIT; hspi2.Init.CLKPolarity SPI_POLARITY_LOW; hspi2.Init.CLKPhase SPI_PHASE_1EDGE; hspi2.Init.NSS SPI_NSS_SOFT; hspi2.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_4; hspi2.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi2);在 HAL 库里SPI_BAUDRATEPRESCALER_4对应 APB1 时钟除以 4。改到 30MHz 时换成SPI_BAUDRATEPRESCALER_2确认 MR25H40CDF 的 SCK 引脚电平还是干净的方法还是先看逻辑分析仪再通过长时间读写测试。4.2 读写函数封装命令和数据的边界要分清驱动封装上我建议把“片选拉低-发命令-传数据-片选拉高”作为一个完整事务不要让调用方自己控制 CS。下面这段来自我们驱动里的实际实现注释部分是关键#define MRAM_CS_LOW() HAL_GPIO_WritePin(MRAM_PORT, MRAM_CS_PIN, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(MRAM_PORT, MRAM_CS_PIN, GPIO_PIN_SET) uint8_t MRAM_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t cmd[4] { 0x03, (uint8_t)(addr 16), (uint8_t)(addr 8), (uint8_t)(addr 0xFF) }; uint8_t discard[4]; MRAM_CS_LOW(); if (HAL_SPI_TransmitReceive(hspi2, cmd, discard, 4, HAL_MAX_DELAY) ! HAL_OK) { MRAM_CS_HIGH(); return 0; } if (HAL_SPI_Receive(hspi2, buf, len, HAL_MAX_DELAY) ! HAL_OK) { MRAM_CS_HIGH(); return 0; } MRAM_CS_HIGH(); return 1; }写数据的封装类似只是把操作码换成 0x02发送完命令后继续发送数据。要注意HAL_SPI_TransmitReceive这一步SPI 是全双工外设发命令的同时 MISO 上也在回数据这些数据是无效的必须给一个接收缓冲区收掉否则可能抛出溢出错误。如果你用HAL_SPI_Transmit只发命令HAL 内部虽然会清除溢出标志但我在调试中遇到过一次连续读数据错位后来统一改成 TransmitReceive 接收假数据问题再没出现过。4.3 大块日志用DMA最后一位时钟别急着拉CS写日志和批量采集数据时一次读写可能是几百甚至上千字节。阻塞式 HAL 调用在这种场景下会占用 CPU我们改用 DMA 方式。DMA 的代码结构并不复杂核心是 CS 拉高的时机不能看“DMA 完成中断”要看“SPI 移位寄存器真的把最后一位送完”。用 HAL 库的话最简单稳妥的做法是把“命令 数据”拼成一个发送缓冲区一次性 DMA 传输然后在 SPI 的 TxCplt 回调里拉高 CS。例如写一整块日志uint8_t txBuf[4 LOG_LEN]; txBuf[0] 0x02; txBuf[1] (addr 16) 0xFF; txBuf[2] (addr 8) 0xFF; txBuf[3] addr 0xFF; memcpy(txBuf[4], logData, LOG_LEN); MRAM_CS_LOW(); HAL_SPI_Transmit_DMA(hspi2, txBuf, 4 LOG_LEN);DMA 传输完成中断触发时DMA 已经把数据写进 SPI 数据寄存器但移位寄存器可能还没发完。所以不要直接在HAL_SPI_TxCpltCallback里拉高 CS实际上用 HAL 库默认配置DMA 完成中断和 SPI 最终发送完成几乎同时实测不会截断数据。如果追求严格时序可以在回调里加一个极短的等待或者直接检查__HAL_SPI_GET_FLAG(hspi2, SPI_FLAG_TXE)再拉 CS。经验之谈如果你不确定最后一次 DMA 传输是否完全落地宁可在回调函数里多等 2~3us 再拉高 CS。CS 晚拉高几微秒没有任何坏处早拉高就可能截断最后一个字节。5. 调试中真正让我头疼的三个问题5.1 全双工SPI在发命令时收进来的“垃圾”数据这个问题出现在从“标准库”迁移到“HAL 库”之后。现象是连续读取 MRAM 数据时buf 的第一个字节经常是 0xFF 或者上一次残留值后面的字节完全正确。查了很久定位到是命令发送阶段出问题。SPI 是同步全双工协议主机发 4 字节命令的同时从机也在往 MISO 上推数据。对 MRAM 来说READ 命令还没有完全进入解码逻辑命令阶段 MISO 上的数据是没有意义的。如果你用HAL_SPI_Transmit发命令这些无效数据会堆积在数据寄存器里。继续用HAL_SPI_Receive读数据时硬件逻辑里可能残留溢出标志导致第一字节被丢弃或错位。解决方式前面代码里已经体现了命令阶段用HAL_SPI_TransmitReceive配一个 discard 缓冲区把命令阶段 MISO 上产生的假数据读走。这个细节如果你不用逻辑分析仪逐字节对照很容易忽略因为偶尔才会复现。5.2 WP和HOLD悬空工业现场开始给你上表演课第二版样机在实验室跑了两周一切正常。拿到现场靠近一个变频器柜开始出现偶发性写入失败参数保存后读回来发现部分字节是旧值。这种“三天两头来一次、复位就好”的故障最难查。后来逐一排查外围电路发现 MR25H40CDF 的 WP# 和 HOLD# 引脚在原理图上画了但 PCB 上没接等于悬空。悬空的控制引脚在强干扰环境下噪声可能把它们拉到有效电平。WP# 一旦被拉低状态寄存器的保护位操作被禁止HOLD# 一旦被拉低SPI 通信会被暂停正在传输的指令直接中断数据自然写不进去。把这两个引脚都改成直接接 VCC 后现场故障率降到零。如果你确实需要用 HOLD# 做多设备总线仲裁那也要接 10kΩ 上拉到 VCC再用 GPIO 控制总之不能让它处于高阻悬空状态。5.3 把MRAM当成Flash用的惯性思维从 Flash 转到 MRAM最大的坑其实是“思想上的坑”。习惯性先擦除再写入、习惯性等 WIP 清零、习惯性按扇区管理这些在 MRAM 上都是多余的甚至会引入 bug。我见过最典型的错误是某个同事沿用 Flash 驱动思路写入前先调一个MRAM_SectorErase函数这个函数在 MRAM 上没有实现直接写入无效操作码 0x20。MRAM 不会响应这个指令看起来“没反应”但后面紧跟着的 WRITE 指令地址却被跳过数据写到了错误位置。排查这种问题非常痛苦因为表面现象是“数据错位”。MRAM 的正确使用姿势是把它当成一块“掉电不丢失的 SRAM”直接寻址、直接写、写完读回确认。不要试图用 Flash 的那套擦写流程套在它身上。6. 存储布局设计与掉电保护的实际取舍6.1 参数区双副本、日志区环形、临时区兜底驱动跑通只是第一步真正的嵌入式工程师要设计的是“数据怎么组织”。512KB 空间说大不大说小不小我们最终划分如下区域地址范围大小用途参数A区0x00000~0x07FFF32KB核心标定参数主副本参数B区0x08000~0x0FFFF32KB核心标定参数备用副本日志区0x10000~0x2FFFF128KB环形运行日志数据暂存区0x30000~0x7FFFF320KB采集数据缓存、临时文件核心参数做双副本很多人觉得没必要毕竟 MRAM 写入可靠。但工业现场最怕的不是芯片坏而是软件逻辑故障比如掉电瞬间正好在执行写入或者代码 bug 把地址算错导致参数区被部分改写。双副本加 CRC 校验后启动时先检查主副本校验失败就切备用副本这层保险比追求“绝对可靠”更现实。日志区用环形缓冲区不依赖文件系统。每条日志固定格式4 字节 magic、2 字节长度、4 字节 CRC32、然后是有效负载。写入时先写有效负载再写 header。这样即使写入中途掉电重启后扫描到的也是“header 无效”的记录直接跳过后面的日志仍然可用。6.2 掉电瞬间的写日志流程比Flash简单但也要保护如果是 NOR Flash掉电瞬间最怕卡在扇区擦除过程中整块数据可能全灭。MRAM 没有这个风险写数据是逐个字节覆写中断只影响当前字节不会牵连其他地址。但“不会牵连”不代表“当前字节一定写入正确”所以在掉电保护策略上我们做了三件事。第一外部加掉电检测电路STM32F215ZG 的 PVD 引脚检测到 VDD 跌到阈值后进入中断。此时留给 MCU 的时间大约几百微秒足够把关键状态参数写入 MRAM。第二写日志时采用“先写 payload后写 header”的顺序header 里带 magic 和 CRC启动时读到有效 header 才认为日志完整。第三所有关键参数写入后立即回读校验读值不对就再写一次。这套流程在 Flash 上要配合掉电缓存、写前备份一堆逻辑在 MRAM 上只需要三行代码。可靠性收益却是一样的这也是我最终坚定选择 MRAM 的原因之一。6.3 产线校验和写在最后的提醒最后说一个生产环节的细节。MR25H40CDF 可以承受无限次写这意味着产线测试时可以放心做全片写入校验不用像 Flash 那样为了省寿命只做抽样。我们量产流程里有一项通过 STM32F215ZG 的 SPI 口对整片 MRAM 依次写入 0x00、0xFF、0xA5、0x5A 四种测试图样每次写完立即回读比对。整片 512KB 走完全部测试时间不到 5 秒比传统 Flash 测试还快因为省掉了擦除等待。如果你的产品需要在线升级参数固件还可以利用 MRAM 的块保护功能设置 BP0/BP1 后把参数区锁死只有升级流程才临时清保护。这个功能我们量产版已经用上了效果不错。MR25H40CDF 和 STM32F215ZG 这套组合在需要频繁存储数据的工业场景里是一个省心又够用的方案。
返回列表