尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

STM32L152ZD 与 MR25H40CDF MRAM:工业非易失存储实施方案

STM32L152ZD 与 MR25H40CDF MRAM:工业非易失存储实施方案 做了几年工业控制器我最怕的就是设备一断电标定系数、运行状态跟着没。以前用 SPI NOR Flash 存参数每次写之前要擦除、要等待掉电伺候不好还会丢数据后来在电能表项目里换上 Everspin 的 MR25H40CDF 这颗 4Mbit 串行 MRAM配合 STM32L152ZD 主控才把“存储”这件事彻底做踏实。这套组合很适合工业嵌入式场景MR25H40CDF 是非易失 MRAM读写对称、没有擦除概念、写入次数无限STM32L152ZD 是带 LCD 段码驱动的超低功耗 Cortex-M3拿来做表计、PLC、数据采集终端非常合适。MRAM 负责掉电保存关键参数、运行日志和校准数据MCU 负责采集、运算、显示和通信。这篇东西把我实际踩过的坑和最终跑通的完整读写方案写出来硬件、软件、可靠性加固、问题排查都覆盖到给想做工业存储的朋友一个可以直接照抄的参考。1. 方案拆解MR25H40CDF 与 STM32L152ZD 为什么是工业存储的稳妥组合1.1 MR25H40CDF 到底是什么东西MR25H40CDF 是 Everspin 的串行 SPI MRAM容量 4Mbit也就是 512KB组织成 524288 × 8bit。MRAM 的全称是 Magnetoresistive Random Access Memory利用磁隧道结MTJ的自由层磁化方向来保存数据自由层与参考层平行表现为低电阻代表 0反平行表现为高电阻代表 1。读数据时只需要测量这个电阻值写数据时用双向电流产生磁场翻转自由层磁化方向。正是这种物理机制带来了三个让工业开发者非常舒服的特性非易失断电不丢数据保持时间超过 20 年不需要备份电池。无限次写入没有 Flash/EEPROM 的擦写寿命问题写日志、写计数器随便写。读写对称读快写也快不需要先擦除再写入写入后也不需要等待毫秒级编程时间。对比一下传统方案就清楚了SPI NOR Flash 写一页要先把整块擦掉擦除时间动不动几十毫秒而且有 10 万次左右的擦写寿命限制EEPROM 按字节写没问题但是容量普遍几百 KB 封顶写一个字节也要 5ms 级等待FRAM 写入快但容量通常到不了 4Mbit价格也高。MR25H40CDF 用 SPI 接口最高 40MHz 时钟待机电流典型 180µA进入 Sleep 后可以压到 1.8µA 左右工作温度范围 -40℃ 到 85℃完全满足工业现场需求。型号后缀值得解释一下C 代表 3.3V 供电版本D 代表工业温度等级F 是封装类型8 引脚 DFN 小封装。所以 MR25H40CDF 就是 3.3V 供电压控、工业级、DFN 封装的 4Mbit SPI MRAM。市面上也有 5V 版本选型时千万别搞混。1.2 STM32L152ZD 为什么是合适的搭档STM32L152ZD 属于 STM32L1 系列超低功耗产品线Cortex-M3 内核最高 32MHz内置 384KB Flash 和 48KB SRAM。它最特别的是带段码 LCD 控制器可以直接驱动玻璃 LCD 屏这在做电表、水表、气表这类设备时能省掉一颗专门的 LCD 驱动芯片。选择这颗 MCU 和 MR25H40CDF 搭配逻辑很简单超低功耗架构STOP 模式下电流能做到 0.4µA 级别和 MRAM 的 Sleep 模式配合正好满足电池供电或表计设备对整机静态功耗的要求。SPI 外设数量足够SPI1 最高能跑到 16Mbit/s虽然芯片支持 40MHz但 MCU 的 SPI 时钟已经足够满足大多数工业数据记录场景512KB 全片读写只需要零点几秒。内置 PVD 可编程电压检测器可以在掉电瞬间产生中断利用 MRAM 的写入速度把关键数据抢存下来。384KB Flash 对应用代码来说非常宽裕大量日志缓存和通信协议栈随便放。这个组合的本质是“超低功耗控制核心 高可靠非易失存储”。一个管计算和通信一个管数据保底各干各擅长的分工非常明确。1.3 这套组合解决的三个实际问题工业现场对存储的诉求总结起来就三种断电保存、频繁记录、长期可靠。MR25H40CDF STM32L152ZD 正好一一对上。第一个场景是掉电保存配置参数。电能表、变频器、机器人控制器都有标定系数、通信地址、运行模式这些参数用户不希望一断电就回出厂值。MRAM 非易失而且写入快PVD 检测到掉电后几百字节参数在几十微秒内就能写完完全不用等。第二个场景是高频日志记录。PLC 里要记录故障事件、报警记录、操作历史采集终端要存每分钟的冻结数据。这些记录频繁但每次数据量不大用 Flash 会反复擦写很快报废用 MRAM 就完全是常规操作写就完了百万亿次都不怕。第三个场景是长期无人值守的可靠性。无论是地下管网的监测节点还是配电柜里的保护装置换一批电池可能几年都不去管它。MRAM 不需要维护、不用擦除、不怕断电数据保持 20 年以上比电池方案省心太多。2. 硬件接线与 PCB 布局把 MRAM 正确挂在 STM32L152ZD 上2.1 引脚定义与接线表MR25H40CDF 是 8 引脚封装引脚不算多但每个引脚的处理方式直接决定了系统稳不稳。先说引脚定义再说我实际项目里的接法。MR25H40CDF 引脚引脚名功能说明STM32L152ZD 连接1/CS片选低有效PA4任意 GPIO推挽输出2DOMISO数据输出PA6SPI1_MISO3/WP写保护低有效10kΩ 上拉到 VDD4VSS地GND5SIMOSI数据输入PA7SPI1_MOSI6SCKSPI 时钟PA5SPI1_SCK7/HOLD暂停传输低有效10kΩ 上拉到 VDD8VDD电源3.3V靠近引脚放 100nF 去耦电容/WP 和 /HOLD 是最容易被忽视的两个引脚很多人直接悬空结果现场偶发写失败、通信中途卡死。实测下来这两个引脚必须稳定拉高。为什么/WP 低电平时状态寄存器里的块保护位会被硬件锁住即使软件发 WREN WRSR 也没办法解除如果某个时刻不小心把 BP 位置位了整个阵列就只读不写了。/HOLD 是挂在 SPI 总线上的暂停信号悬空时受干扰一旦被拉低正在传输的字节就会悬在半空芯片内部状态错乱后面所有指令都白发。所以我在所有设计里都把 /WP 和 /HOLD 用 10kΩ 电阻接到 VDD上电瞬间就固定在高电平。如果想在系统运行中实时控制写保护可以把 /WP 单独接一个 GPIO但一定要先拉高再操作避免上电时序混乱。2.2 SPI 模式、电平匹配和信号完整性MR25H40CDF 支持 SPI Mode 0CPOL0CPHA0和 Mode 3CPOL1CPHA1STM32L152ZD 的 SPI 外设两种都能配置我这里统一用 Mode 0也就是空闲时钟为低、第一个边沿采样。数据格式 8bitMSB first这些参数在 CubeMX 里一眼就能确认。电平方面STM32L152ZD 是 3.3V 供电MR25H40CDF 的 C 版本也是 3.3V直接相连不需要电平转换这点比 5V 版本省事。接 5V 版本的 MRAM 就必须加电平转换电路总线电压不匹配很容易导致读回数据偶尔出错还不好查。PCB 布局上我踩过几个值得说的坑SPI 走线要短特别是 SCK 和 MOSI尽量控制在 3cm 以内不要穿过大电流区域否则 8MHz 以上时钟容易眼图闭合。如果实在绕不开降频到 4MHz 保平安。MR25H40CDF 的 VDD 旁边放一个 100nF 陶瓷电容位置越近越好如果系统供电波动大再并一个 4.7µF 也是可以的。多个 SPI 设备共用总线时每一颗芯片的 /CS 都必须用独立 GPIO不要在总线上直接拉低多个片选。如果做的是插拔式板卡建议在 CS 上串联一个 22Ω 电阻减小热插拔时的过冲。2.3 掉电保持电路的工程计算既然要谈工业掉电存储就不能只说“用 MRAM 不怕掉电”还要保证掉电那一瞬间系统有足够能量完成最后的数据写入。我的做法是用 STM32L152ZD 的 PVD 检测电压跌落触发中断后立刻把关键数据写入 MRAM而整个 MCU MRAM 的供电由一个大电容临时撑着。以 470µF 电容为例系统正常 3.3V 工作PVD 阈值设在 3.0V允许电压从 3.0V 掉到 2.7V可用的能量是E 0.5 × C × (U1² - U2²) 0.5 × 470 × 10⁻⁶ × (3.0² - 2.7²) ≈ 0.4mJ如果掉电瞬间系统总电流是 20mA有效工作电压约 3V功耗是 60mW那么电容能支撑的持续时间大约t E / P 0.4mJ / 60mW ≈ 6.7ms而写 512 字节数据到 MRAM在 8MHz SPI 时钟下只需要 512 × 8 / 8MHz 0.512ms算上中断响应和命令开销6.7ms 绰绰有余。即使系统负载更大、电流达到 50mA也还有约 2.7ms足够完成写入。所以 470µF 是我做表计项目时的起步值如果系统还挂了 LCD、通信模块建议加大到 1000µF或者把不重要的外设掉电后立刻关掉只给 MCU 和 MRAM 供电。3. 软件驱动实现SPI 初始化、指令与 MRAM 读写代码3.1 SPI 外设初始化我用 STM32CubeMX 生成工程SPI1 配置在主模式引脚是 PA5/PA6/PA7CS 用普通 GPIO PA4。关键初始化参数如下static void MX_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_2; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; HAL_SPI_Init(hspi1); }注意这里有两个地方特别容易出错。一是 NSS 必须配成 SPI_NSS_SOFT因为硬件 NSS 引脚我们没有接如果不用软件管理片选SPI 外设会自动改变 NSS 状态导致收发错乱。二是 BaudRatePrescaler 为 2 时SPI 时钟是 APB2 的一半工程默认 APB2 是 32MHz所以实际 SPI 时钟就是 16MHz正好在 MR25H40CDF 支持范围内。如果 PCB 走线较长或现场干扰大就先降一档到分频 4也就是 8MHz 验证稳定后再说。3.2 MR25H40CDF 指令集速查MR25H40CDF 的指令集比 SPI NOR Flash 简单得多去掉了擦除、页编程这些命令核心就是读写。我整理了一份常用指令表。指令操作码功能说明WREN0x06设置状态寄存器中的写使能锁存位WRDI0x04清除写使能锁存位RDSR0x05读取状态寄存器WRSR0x01写状态寄存器需要先 WRENREAD0x03读阵列数据地址 3 字节WRITE0x02写阵列数据地址 3 字节SLEEP0xB9进入深度睡眠模式WAKE0xAB从深度睡眠唤醒和 EEPROM 不一样的是MR25H40CDF 的阵列写操作在硬件上不需要等待编程时间写入是即时的所以读改写不用专门做等待轮询。不过我在驱动里还是会在每次写数据前先发一次 WREN这个操作没有副作用但能保证状态寄存器中的 WEL 位始终为 1防止某些严格实现条件下写操作被忽略。3.3 核心读写代码SPI 通信的关键是 CS 低电平有效整个命令帧期间 CS 必须保持低电平命令帧结束后拉高 CS芯片才会认为一次操作完成。我的驱动函数如下#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 MRAM_CMD_WREN 0x06 #define MRAM_CMD_RDSR 0x05 #define MRAM_CMD_READ 0x03 #define MRAM_CMD_WRITE 0x02 #define MRAM_CMD_SLEEP 0xB9 #define MRAM_CMD_WAKE 0xAB static void MRAM_WriteEnable(void) { uint8_t cmd MRAM_CMD_WREN; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); MRAM_CS_HIGH(); } static void MRAM_WriteBytes(uint32_t addr, const uint8_t *buf, uint32_t len) { uint8_t hdr[4]; hdr[0] MRAM_CMD_WRITE; hdr[1] (addr 16) 0xFF; hdr[2] (addr 8) 0xFF; hdr[3] addr 0xFF; MRAM_WriteEnable(); MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, hdr, 4, HAL_MAX_DELAY); HAL_SPI_Transmit(hspi1, (uint8_t *)buf, len, HAL_MAX_DELAY); MRAM_CS_HIGH(); } static void MRAM_ReadBytes(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t hdr[4]; hdr[0] MRAM_CMD_READ; hdr[1] (addr 16) 0xFF; hdr[2] (addr 8) 0xFF; hdr[3] addr 0xFF; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, hdr, 4, HAL_MAX_DELAY); HAL_SPI_Receive(hspi1, buf, len, HAL_MAX_DELAY); MRAM_CS_HIGH(); }这里解释几个关键点。地址为什么要发 3 字节SPI 接口规范固定了 24 位地址格式虽然 MR25H40CDF 只有 512KB 容量实际只需要 19 位地址但协议上仍然必须发送完整的 A23~A0高 5 位忽略掉芯片内部会自动屏蔽。为什么读的时候可以不等状态寄存器MRAM 没有 Flash 那种“硬件忙”状态读操作拉低 CS 后立刻就能读到数据。HAL_SPI_Receive 在 Master 模式下会自动产生时钟并接收数据不需要额外发 dummy 字节。为什么写一个字节也要先 WREN这个问题我在 3.2 提过这里再说透一点。数据手册里 WRITE 指令本身不依赖 WEL 位但 WRSR 写状态寄存器必须依赖 WEL我在写数据前排一个 WREN 只是统一了操作习惯没有副作用也不会拖慢性能。如果项目对响应速度要求高可以不用 HAL_SPI_Transmit 一步一步发改成 DMA 模式三个区域分别送命令头、数据缓冲或者直接把命令头和寄存器一次性拼好。但对于大多数工业设备HAL 阻塞式收发在 8MHz 时钟下已经很快了不需要刻意优化。3.4 连续读写性能实测我在实际板上用示波器抓过时序SPI 8MHz 时钟下读 512 字节从 CS 拉低到拉高总耗时约 0.52ms写 512 字节也差不多。换到 16MHz单次 512 字节读写大概 0.28ms 左右。作为对比一颗常见的 SPI NOR Flash 写入 512 字节光页编程就要 0.7ms 到 1ms前面还得加扇区擦除时间运气不好碰上擦除刚好涉及旧数据整体要 50ms 以上。实际项目里这个性能带来了非常明显的好处日志记录可以做到毫秒级完成掉电保存关键参数时即使系统供电已经很不稳定也能在电容维持窗口内干净利落地写完一整页数据不需要像 Flash 那样还要等擦除。4. 工业可靠性加固掉电保存、数据校验与低功耗联动4.1 数据校验与双备份策略MRAM 写入不会损坏但不代表软件层面不需要做数据管理。工业设备长期运行最怕的是写入过程中断、数据被意外篡改或者代码 Bug 把旧数据当成新数据用。我的惯例是两条CRC 校验 双区备份。512KB 空间我习惯分成三个区域区域地址范围用途配置区 A0x00000 ~ 0x001FF当前生效参数512B配置区 B0x00200 ~ 0x003FF备份参数512B日志区0x00400 ~ 0x7FFFF环形事件日志每个配置区的末尾 4 字节存 CRC32 校验值写入流程是先在 B 区写入新参数和 CRC再把 A 区顶部的一个“有效标志位”清除然后写入 A 区新参数和 CRC最后重新置上有效标志。上电后先读 A 区校验失败就自动回退到 B 区整个启动流程只有几毫秒。有人会觉得双区浪费空间但 512KB 对配置参数来说完全够用。关键是 MRAM 无限次写入这套备份切换机制可以无限制执行不会像 Flash 时代那样备份区和主区互相磨损。4.2 写保护与状态寄存器MR25H40CDF 的状态寄存器里有一组块保护位BP0、BP1、WPEN 等默认值是 0表示整个阵列未保护可以自由读写。但如果某些异常代码序列或者调试过程把 BP 位改成了非 0 值就会出现“写不进、读正常”的怪现象。排查这个问题的方法是读状态寄存器static uint8_t MRAM_ReadStatus(void) { uint8_t txbuf[2] {MRAM_CMD_RDSR, 0x00}; uint8_t rxbuf[2] {0x00, 0x00}; MRAM_CS_LOW(); HAL_SPI_TransmitReceive(hspi1, txbuf, rxbuf, 2, HAL_MAX_DELAY); MRAM_CS_HIGH(); return rxbuf[1]; }如果读回来不是 0x00先检查 /WP 引脚是不是被意外拉低了然后执行一次 WRSR 把状态寄存器全部清零static void MRAM_ResetStatus(void) { uint8_t cmd[2]; MRAM_WriteEnable(); cmd[0] 0x01; // WRSR cmd[1] 0x00; // 清除 BP 位和 WPEN MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 2, HAL_MAX_DELAY); MRAM_CS_HIGH(); }这里有个硬件前提如果 /WP 引脚被拉低WRSR 指令会被芯片直接忽略所以我上面提到所有设计里 /WP 必须默认拉高否则软件层面完全没办法解除保护。4.3 低功耗模式与系统睡眠联动STM32L152ZD 本身是超低功耗芯片要把整机功耗真正压下来MRAM 也不能一直满速待机。MR25H40CDF 有两种低功耗状态常规待机电流大约 180µA进入深度睡眠后电流降到 1.8µA 左右。MCU 进 STOP 模式之前我建议给 MRAM 发一条 SLEEP 指令MCU 唤醒后第一条 MRAM 操作前先发 WAKE 指令。逻辑非常简单static void MRAM_Sleep(void) { uint8_t cmd MRAM_CMD_SLEEP; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); MRAM_CS_HIGH(); } static void MRAM_Wakeup(void) { uint8_t cmd MRAM_CMD_WAKE; MRAM_CS_LOW(); HAL_SPI_Transmit(hspi1, cmd, 1, HAL_MAX_DELAY); MRAM_CS_HIGH(); }注意SLEEP 指令后 CS 拉高芯片正式进入深度睡眠这时候再次拉低 CS 访问芯片不会响应 READ/WRITE必须先唤醒。唤醒后不要立刻做高强度读写留 30~50µs 让内部电路稳定实测下来这个延时非常必要否则睡眠唤醒后第一次读操作偶发返回错误数据。这个细节在多数教程里没人提但在表计设备低功耗轮询场景里很容易踩到。5. 调试实录与常见问题排查这些坑我替你先踩了5.1 读回全是 0xFF 或 0x00这是刚上板时最常见的现象基本可以断定 SPI 根本没通。读回 0xFF优先检查 CS 和时钟。CS 用普通 GPIO 时GPIO 模式必须配成推挽输出初始电平是高SCK 引脚要确认复用到了 SPI1不是普通 GPIO。还要检查 /WP、/HOLD 是不是悬空如果悬空被干扰拉低芯片可能根本不进入正常工作。读回 0x00 则更多是 SPI 模式配置问题。MR25H40CDF 支持 Mode 0 和 Mode 3但 STM32 的 SPI 外设如果 CPOL、CPHA 和芯片不匹配MISO 上采到的数据全是 0。我建议先用逻辑分析仪抓一下 MOSI 波形确认第一个字节是 0x03READ 指令再检查后三个地址字节波形对上了再怀疑芯片。5.2 写入数据后读回错位数据错位高发原因是地址发送顺序。MR25H40CDF 要求先发高地址字节再发中间字节最后发低地址字节也就是 hdr[1] 是地址的 bit23~bit16hdr[3] 是 bit7~bit0。如果把地址按小端顺序发了读出来的数据就是全片偏移几个字节。另一个原因是 CS 时序。SPI 指令是以 CS 拉高为边界的如果连续两次操作之间 CS 拉高时间过短芯片可能把上一次指令和下一次指令连起来解析导致多读或少读一个字节。我的经验是 CS 拉高后至少留一个 SPI 字节的时钟宽度再拉低工程里加个 1µs 延时最省心。5.3 偶发写失败HOLD、WP 和 SPI 频率的坑偶发写失败比完全读不出来难查多了因为它不是必现的。我遇到过一次现场复位后参数丢失查到最后是 /HOLD 悬空电机启动瞬间电磁干扰把 /HOLD 拉低了正在执行的写操作被暂停。从那以后 /HOLD 和 /WP 一律 10kΩ 上拉再没犯过。还有一个隐藏问题HAL 库的阻塞式收发在中断频繁时可能超时特别是中断里同时操作 MRAM 和别的外设容易把 CS 拉高时机搞乱。解决办法是 MRAM 的读写要么全部放主循环要么全部放中断里不要两边交叉调用否则 CS 一错位指令就废了。现场干扰重的环境SPI 频率不要顶满。8MHz 和 16MHz 在近距离走线时都没有问题但如果经过了接插件、排线或者板内长走线先降到 4MHz 验证稳定后再逐步提频。工业设备不是跑分设备稳定压倒一切。5.4 常见问题速查表现象可能原因排查与处理读回全 0xFFCS/SCK 引脚配置错误/WP 或 /HOLD 悬空检查 GPIO 复用测量引脚电平上拉 /WP、/HOLD读回全 0x00SPI Mode 不匹配MISO 没配成复用输入确认 CPOL/CPHA检查 PA6 复用设置数据错位地址字节顺序反了或 CS 高电平时间太短确认高地址在先CS 拉高后加 1µs 延时偶发写不进去BP 位被置为保护/WP 被干扰拉低CS 毛刺读状态寄存器并清零上拉 /WPCS 加 RC 滤波睡眠唤醒后首读失败唤醒后未发 WAKE 或稳定时间不足每次唤醒后发 WAKE延时 30~50µs 再操作写入数据掉电后丢失掉电瞬间写入期间供电崩溃加大储能电容启用 PVD 中断优先写 512B 内的关键区最后再分享一个实际使用习惯我把 MRAM 驱动做成了一个独立模块对外只暴露 Read、Write、Sleep、Wakeup 四个接口上层业务完全不感知底层的 SPI 细节。这样无论是换 MCU 还是换另一颗 MRAM驱动层改动都很小。另外每次代码发布前固定跑一轮“写全片读全片对比”的自检灯亮了再进下一阶段省得后期被数据问题折磨到怀疑人生。
返回列表