资讯动态

MR25H40CDF与PIC32MZ搭配:打造不掉电的工业存储方案

发布时间:2026/10/4 1:59:26 来源:尧图企业网站定制
与不少朋友聊起工业项目的存储方案我经常有个感受真正让人夜不能寐的往往不是 CPU 跑到多少兆赫而是贴着数据手册精心设计的板子在断电的一瞬间把工控现场最看重的那几十个字节变成了乱码。股权早以前有过一套用电池保持 SRAM 保存参数的产品冬天低温一上来电池电压骤降整批设备开机之后全部要重新校准。后来我把存储换成 MRAM再没折腾过这一类问题。这篇文章要说的正是围绕 Everspin MR25H40CDF 这颗 4Mbit SPI MRAM 和 Microchip PIC32MZ1024EFF144 这颗 32 位 MCU如何在工业控制、数据采集这类嵌入式场景里把“存储和读取”这件事做得干净、稳定、抗掉电。1. 什么场景让我专门给 PIC32MZ1024EFF144 配一颗 MR25H40CDF1.1 从“电池掉电丢配置”的现场事故说起先聊一个真实的故障模型。某台工业控制器里需要保存一组频繁变动的运行参数旧设计是用锂电池给 SRAM 供电主电源断了照样能保持。听起来很合理但工业环境里“电池”是最不靠谱的东西之一低温下内阻上升主板漏电稍大一点电压就撑不住设备出厂两三年后电池没电维护单上只能写“不明原因丢参数”。最坑的是电池保持 SRAM 掉电后并不会报错系统重新上电读取到的是一堆随机值然后仪表用这些随机值去跑控制算法现场直接乱套。这也是我后来坚持把“频繁改写且断电不能丢”的数据移到 MRAM 上去的原因。MR25H40CDF 属于 Everspin 的磁阻随机存储器本质上存储单元是一颗磁隧道结写入靠电流改变磁化方向不需要擦除、不需要编程电压、没有块结构而且理论上写入耐久度远超 Flash 和 EEPROM。对它来说写一个字节和写一个扇区的行为模式完全一样——因为压根就没有扇区这个概念。1.2 工业非易失存储的四种典型方案对比没有对比就没有伤害。在工业嵌入式里常见的非易失存储绕不开这四种存储方案写入是否需要擦除写入寿命字节级操作掉电保持主要坑点NOR Flash需要先擦除再写页/扇区粒度约 10 万次/扇区支持整页/单字节读写通常按页是日志频繁写入会磨死扇区且写一个字节也要先擦一整块EEPROM不需要擦除但写周期长典型 100 万次/字节是是频繁写寿命仍然紧张写 3~5 ms 很常见Battery SRAM不需要擦除无限是依赖电池电池低温失效、维护麻烦MRAM不需要擦除数据手册普遍给到 10^13 次以上是是价格偏高单比特翻转概率虽低但不能完全忽略从这个表格能看出来MRAM 几乎是“SRAM 的速度 Flash 的保持特性 EEPROM 的单字节可写性”结合起来的东西。实际使用中我经常把它当作一个永远写不坏的、断电不丢的 RAM 来用而不是当作一块慢吞吞的“非易失存储设备”。1.3 这颗 MCU 与这颗 MRAM 的互补关系PIC32MZ1024EFF144 本身不是一款默默无闻的 MCU。它有 1MB Flash 用来放固件512KB RAM 可以当作临时缓冲主频能跑到 200MHz外设里集成多路 SPI、UART、CAN、USB、DMA 和 CRC 引擎。在这些外设里SPI 加 DMA 加 CRC 的组合正好和 MR25H40CDF 的 SPI 接口形成教科书式的配合。MR25H40CDF 的单片容量是 4Mbit即 512KB。这个容量不大但它不是用来装程序或固件镜像的它最适合装三类数据一是控制器标定参数和校准表二是现场运行日志和黑匣子记录三是频繁更新的掉电保持变量。512KB 对前两类场景非常充裕对第三类则绰绰有余。我会在后面把整个硬件连接、SPI 初始化、底层读写、掉电安全、循环日志实现和实测结果完整铺开。如果你已经在用 MPLAB Harmony本文的寄存器级示例也可以和 Harmony 的 PLIB 调用对照着看。2. 硬件连接把 MR25H40CDF 稳定挂到 PIC32MZ1024EFF144 的 SPI 总线上2.1 最小接线与引脚分配MR25H40CDF 是标准 SPI 接口典型封装带以下功能引脚片选 /CS、时钟 SCK、数据输入 SI对 MCU 来说是 SDO、数据输出 SO对 MCU 来说是 SDI、写保护 /WP、暂停 /HOLD、电源 VCC 和 GND。其中 /WP 和 /HOLD 是最容易被新手上板时忽略的。MR25H40CDF 的 /WP 引脚在低电平时会强行禁止写操作哪怕你发了 WREN 指令也没用/HOLD 引脚在低电平时会让芯片暂停响应时钟沿。因此正常工作时这两个引脚必须接高不能悬空。我习惯在 PCB 上给这两个引脚各加一颗 4.7kΩ 上拉电阻到 VCCMCU 不参与控制就保持默认允许写入的状态。PIC32MZ1024EFF144 的 SPI 引脚可以通过外设引脚选择功能映射到不同引脚这里给一个我实际调过的连接示例具体 pinout 以你的封装和板子为准信号MRAM 引脚PIC32MZ1024EFF144 端说明片选/CS任意 GPIO如 RD4空闲保持高命令开始拉低时钟SCKSCK1SPI 时钟输出数据输入SISDO1MCU 发送到 MRAM数据输出SOSDI1MRAM 返回给 MCU写保护/WP接 4.7kΩ 上拉不能悬空暂停/HOLD接 4.7kΩ 上拉不能悬空电源VCC3.3V按数据手册供电范围地GNDGND就近单点连接2.2 上电瞬间的 CS 电平问题这里有一个非常容易在实验室里“测不出来”的坑MCU 复位期间GPIO 方向寄存器还没完成初始化RD4 这个片选脚如果默认是输入且不带上下拉那它对外呈现的是高阻态。MRAM 的 /CS 在这个短暂窗口内处于悬空也许恰好在某个临界电平上下抖动。如果此时 SPI 总线上有噪声MRAM 可能误判 CS 有效进入一个不完整指令的等待状态。解决方式很简单给 /CS 加一颗 10kΩ 上拉到 3.3V。MCU 复位期间 CS 被电阻稳在高电平MRAM 不响应任何操作MCU 初始化完 GPIO 之后才把它拉低开始发指令。我一般不省这颗电阻工业现场的环境噪声远比开发台上恶劣。另外VCC 到 GND 之间必须在 MRAM 电源脚附近放一颗 100nF 陶瓷电容有条件再加 4.7uF 或 10uF 做低频退耦。MRAM 写操作的功耗脉冲极短但瞬时电流变化较快退耦不到位容易在 VCC 上砸出毛刺严重时会让芯片自己把自己打断。2.3 电平匹配与总线隔离PIC32MZ1024EFF144 是 3.3V 器件MR25H40CDF 的工作电压范围也覆盖 3.3V两者直接互联不需要电平转换。需要注意的反而是 MCU 其他外设如果板上还存在 5V 系统比如 5V 的 UART 收发器或 5V 的传感器必须保证这些信号不会倒灌到 SPI 引脚上。我见过因为 SPI 引脚旁边恰好有一颗 5V 上拉电阻导致 MRAM 的输入脚长期处于 4.7V 电平虽然当时没坏但热机后偶尔出现偶发读写错误。如果你要把 SPI 总线引到连接器或长走线建议把 SCK 和 SI 串入 33Ω 左右的电阻减少振铃SO 线允许稍微慢一点没关系但数据手册上对输入信号的上升下降时间有要求走线过长时要实测信号边沿。工业现场如果线缆超过 20cm别犹豫改用隔离 SPI 或把 MRAM 就近放在主控板上。3. 初始化 SPI 与指令集把最基础的读写路径跑通3.1 SPI 模式与时钟速率的选择MR25H40CDF 支持 SPI Mode 0CPOL0CPHA0和 Mode 3CPOL1CPHA1。我默认用 Mode 0因为对 Cortex-M 和 PIC32 这类 MCU 来说Mode 0 是默认值逻辑分析仪看起来也最直观。两块板子之间如果存在电平毛刺Mode 0 的空闲时钟为低电平比 Mode 3 更不容易产生误触发。时钟速率方面这颗 MRAM 的数据手册标称可以支持到 40MHz但我实际量产板上的 SPI 时钟用的是 10MHz 或 20MHz而不是顶格跑。原因有几个第一40MHz 下信号边沿更快PCB 布线稍差就容易在 MISO 上采到不稳定的电平第二PIC32MZ 的 PBCLK 经过 SPI 波特率发生器分频后未必能刚好凑出 40MHz 的整数配合而 20MHz 的分频误差小第三工业设备不像消费电子那样追求极限带宽稳定第一多花几十微秒根本无感。PIC32MZ1024EFF144 的 SPI1 波特率相关计算方式可以简化理解为F_SPI PBCLK / (2 * (SPI1BRG 1))。如果把 PBCLK 配置成 100MHzSPI1BRG 设为 4则得到 100 / (2 * 5) 10MHz非常方便。SPI1BRG 设为 1 则接近 25MHz我也不建议再往上。初始化时直接操作寄存器直观且便于排错void spi1_init(void) { SPI1CON 0; SPI1STAT 0; // 假设 PBCLK 100MHzBRG4 10MHz SPI1BRG 4; SPI1CONbits.MSTEN 1; // 主机模式 SPI1CONbits.CKP 0; // 空闲时钟为低 SPI1CONbits.CKE 0; // Mode 0 SPI1CONbits.SMP 0; // 中间采样 SPI1CONbits.MODE16 0; // 8 位数据 SPI1CONbits.ON 1; // 使能 SPI1 }如果你手头的板子是 MPLAB Harmony 生成的代码也可以直接用 Harmony 的SPI1_Transfer等封装函数但底层状态机基本一致。寄存器方式的价值在于万一通信异常你可以从 SPI1STAT 里的溢出位、忙位直接判断当前处于哪一步。3.2 状态寄存器、写使能指令和块保护MR25H40CDF 的指令集非常精简真正日常要用到的不超过六条WREN0x06写使能、WRDI0x04写禁止、RDSR0x05读状态寄存器、WRSR0x01写状态寄存器、READ0x03读数据、WRITE0x02写数据。状态寄存器里通常有写保护标志、写使能锁存位和忙标志。MRAM 的独特之处在于“忙”状态几乎可以忽略因为写入不需要像 Flash 那样等待编程时间命令执行完毕即完成。但块保护位仍是需要处理的如果状态寄存器里的 BP 位被设成非零即使 /WP 引脚接了高电平WRITE 指令也会被拒绝。出厂状态的保护位未必是 0所以上电后的标准动作应该是先读状态寄存器再决定要不要清保护位。读状态寄存器可以用下面这段uint8_t mram_rd_status(void) { uint8_t status; mram_cs_low(); spi_exchange(0x05); // RDSR status spi_exchange(0x00); // 读一个字节 mram_cs_high(); return status; }其中mram_cs_low/high是对 GPIO 的封装spi_exchange是 SPI 收发一体函数。PIC32MZ 的 SPI 在主机模式下一发一收所以即使我只需要发送也要把接收侧读掉否则接收缓冲会置溢出标志后面可能触发中断或错误状态。清除保护位时必须先发 WREN再发 WRSR 写入 0x00void mram_unlock(void) { if (mram_rd_status() 0xE0) { mram_cs_low(); spi_exchange(0x06); // WREN mram_cs_high(); mram_cs_low(); spi_exchange(0x01); // WRSR spi_exchange(0x00); // BP 位清零 mram_cs_high(); } }提示WREN 和 WRITE/WRSR 之间必须让 /CS 有一个完整的拉高再拉低过程。大部分 SPI Flash 都要求这样MRAM 也不例外。直接把 WREN 和 WRITE 连在一个 CS 低电平周期里是非法时序。3.3 基础读写函数的实现搞定状态寄存器之后读写就水到渠成。因为 MR25H40CDF 容量是 512KB地址需要 19 位也就是三个地址字节。读取函数如下int mram_read_bytes(uint32_t addr, uint8_t *buf, uint32_t len) { uint32_t i; if ((addr len) 0x80000UL) { return -1; } mram_cs_low(); spi_exchange(0x03); // READ spi_exchange((addr 16) 0xFF); // A18..A16 spi_exchange((addr 8) 0xFF); // A15..A8 spi_exchange(addr 0xFF); // A7..A0 for (i 0; i len; i) { buf[i] spi_exchange(0x00); } mram_cs_high(); return 0; }写函数类似只是命令码换成 0x02并且在写命令之前先来一次 WRENint mram_write_bytes(uint32_t addr, const uint8_t *buf, uint32_t len) { uint32_t i; if ((addr len) 0x80000UL) { return -1; } mram_cs_low(); spi_exchange(0x06); // WREN mram_cs_high(); mram_cs_low(); spi_exchange(0x02); // WRITE spi_exchange((addr 16) 0xFF); spi_exchange((addr 8) 0xFF); spi_exchange(addr 0xFF); for (i 0; i len; i) { spi_exchange(buf[i]); } mram_cs_high(); return 0; }不要认为底层读书写完就结束了。真正到了工业现场你还要考虑掉电时写到一半怎么办、数据损坏怎么发现、日志写多了会不会把地址边界写穿。这些才是这篇文章的重头戏。4. 工业现场最关心的两件事掉电安全与数据完整性设计4.1 MRAM 的掉电时序命令必须先走完CS 再抬起来很多人以为 MRAM 是非易失的所以断电瞬间随便断。实际上MRAM 的写入虽然是纳秒级翻转但整个 SPI 命令过程仍然需要电源维持到最后一个字节被接收并且要保证 CS 拉高时 VCC 还在有效工作电压之上。数据手册里往往能看到类似“在写入或读取期间 VCC 掉电可能造成不确定数据”的表述。现场经验是你不能在发送 WRITE 命令的中途断电但你在掉电检测中断里完成一整段小数据写入是完全可行的因为整段命令很短。我的做法通常分两层。第一层在电源输入端放一个足够大的保持电容让从“检测到掉电”到“MCU 彻底稳定”之间有 1~2ms 的缓冲。第二层在掉电中断里只做一件事把 RAM 里最新的日志块写进 MRAM写入完成后立刻停止一切外设活动。保持电容的容量可以按负载电流粗算。假设掉电后整个 MCU 板需要继续稳定工作 1ms电流约 50mA从 3.3V 掉到 2.7V 允许压降 0.6V那么最小电容是C I * t / dV 0.05 * 0.001 / 0.6 ≈ 83uF实际我会留 3 到 5 倍余量用 330uF 或 470uF 的电解电容。工业设备的启动时间通常不急这点电容的充电时间可以接受。在软件上PIC32MZ1024EFF144 这类 MCU 有内部掉电检测/比较器外设也可以接一个外部电压监视芯片把掉电信号直接映射到高优先级中断入口。中断里优先别干太多事就执行一次mram_write_bytes(LOG_HEAD_ADDR, pending, len)然后用 while 循环把 CPU 挂住等待复位。4.2 数据完整性MRAM 也不是绝对不坏CRC 必须有MRAM 的位翻转概率比 Flash 低很多但磁隧道结在宇宙射线、高温、强电磁干扰下同样存在单比特翻转的可能。工业设备里最怕的是它静默地坏一个字节然后带着错误参数继续跑。因此在关键数据上必须加校验。我习惯在每一条记录后面附带 CRC16。对于启动参数、校准表这类数据用双备份甚至三备份读入时逐个校验 CRC取校验通过的那一份作为有效值。只要两份里至少有一份是完整写入的系统就不会丢配置。以日志记录为例定义一个简单结构typedef struct { uint32_t magic; // 固定魔数比如 0x4D52414D uint32_t seq; // 序号 uint16_t len; // 数据长度 uint16_t crc; // CRC16 uint8_t data[256]; // 实际数据 } log_record_t;写入顺序有讲究先把除了magic之外的整条记录写好最后再把magic写入。这样如果写入过程中断电读到的magic就不是有效值程序可以判断该记录无效回退到上一条完整记录。这也叫“原子提交标志”。4.3 双槽位回滚断电发生在魔法字写入前怎么办单靠一个 magic 还有小概率踩坑假如断电正好发生在 magic 写入的后半段而磁存储单元幸运地保持到了一半那读出来的 magic 值可能碰巧不是预期值但也未必是明显的坏值。所以我更喜欢用双槽位设计槽位地址范围用途槽 A0x000000 ~ 0x000FFF参数副本 A槽 B0x001000 ~ 0x001FFF参数副本 B日志区0x002000 ~ 0x077FFF运行日志运行指针0x07F000 ~ 0x07FFFF当前日志写位置等上电后读取流程是先读槽 A校验 CRC失败就回退槽 B校验 CRC两份都失败才进入默认参数并报警。写入流程则是先写槽 A成功后再把槽 B 写成相同内容。每次设备关机时间极长、且参数不常改双槽位足够。4.4 掉电中断里的最小完整事务掉电中断里的代码要尽量短。我最多就做三件事把当前 RAM 缓冲区的日志块加上 CRC 算好写入日志区地址然后更新运行指针。MRAM 的写速度让这变得非常从容256 字节的块在 10MHz SPI 下大约 100us 写完300us 内全部收工。相比之下EEPROM 写一个 256 字节块的时间足够让 CPU 断电两次了。5. 用 DMA 提升批量读写做一个可追溯的循环日志记录器5.1 为什么日志场景要上 DMA 而不是 CPU 逐字节搬运数据采集系统里常见一个场景设备以 1kHz 采样每个采样点 16 字节每秒钟需要往存储里写 16KB。用 CPU 逐字节搬 SPI 数据每个字节都要占用一次循环和状态判断采样中断稍微一卡就被实时任务打断。PIC32MZ1024EFF144 有 DMA 控制器把 SPI 发送和接收交给 DMA 通道CPU 可以专心做滤波和控制。MRAM 不支持页编程、也不需要等待“写忙”这正好放大了 DMA 的优势DMA 把一整块 RAM 数据按照 SPI 时序连续送到 MRAM期间不需要像 Flash 那样在页边界处停顿也不需要反复查询状态寄存器。对记录器来说这是近乎完美的组合。5.2 SPI 与 DMA 配对时的关键原则第一个原则是PIC32MZ 的 SPI 外设在主机模式下只要使能发送每发一个字节必然接收一个字节。如果只配置了 DMA 发送通道接收侧不取走数据SPI 接收缓冲就会溢出最终导致通信状态异常。因此必须同时配置一个 DMA 接收通道把从 MRAM 返回的假数据写到一块“回收缓冲”里。第二个原则是DMA 传输完成后必须等最后一个字节彻底从移位寄存器送出才能把 /CS 拉高。过早拉高 CS等于把最后一个字节截断了MRAM 不认这条命令。PIC32MZ 有相应的 SPI 移位寄存器空标志可查或者在 DMA 中断里加一个小延时保证时序稳定。第三个原则是DMA 一次搬运不宜超过日志块大小。我一般把块大小控制在 64 到 256 字节。块太大掉电中断里保存不完块太小命令地址开销占比高吞吐率下降。16 字节采样点合 4 批正好是 64 字节非常顺手。5.3 循环日志区的整体结构和写指针管理日志区如果永远只往一个方向写用不了多久就会把 512KB 写满。因此要设计成循环缓冲。我在日志区头部维护一个运行指针结构包含写序号、当前写地址、记录个数、可读起点。初始化时把指针读到 RAM每次写入后更新。日志记录写入流程构造记录结构计算 CRC16。用 DMA 把记录写入当前位置。写序号加一写地址递增。如果写地址越过日志区末尾则回绕到日志区头部。将运行指针写回 MRAM 的指针区。为了定位方便我在每条日志头部加上 4 字节 magic 和 4 字节长度。读取端只要按序扫描 magic找到第一条最新记录就能顺着长度字段把整个循环区解析出来。PIC32MZ 的 DMA 初始化代码根据 MPLAB Harmony 版本不同有差异但思路是一致的申请两个 DMA 通道一个用于发送一个用于接收发送描述符指向记录缓冲接收描述符指向一个 8 字节的丢弃缓冲传输完成中断里做 CS 拉高和指针更新。实测 256 字节块写入时CPU 占用几乎可以忽略。6. 实测表现与踩坑记录这些细节数据手册上不会写6.1 在 10MHz SPI 下的实际读写吞吐我在自己的测试板上用 10MHz SPI 跑了一组数据。理论字节速率是 1.25MB/s但由于每个写命令都要额外携带 WREN、命令码和 3 字节地址实际吞吐会随块大小变化操作块大小理论时间估算实测时间估算实际吞吐连续读256 字节约 214us约 220us约 1.16MB/s写 16 字节16 字节约 17us约 19us约 0.84MB/s写 64 字节64 字节约 61us约 66us约 0.97MB/s写 256 字节256 字节约 217us约 232us约 1.10MB/s如果换用 20MHz SPI吞吐基本翻倍写 256 字节块的实际速度能超过 2MB/s。这个速度对绝大多数工业日志应用已经绰绰有余1kHz 采样、每样本 16 字节每秒 16KB 的写入压力只有 MRAM 吞吐能力的几十分之一。6.2 踩过的几个坑按影响程度排列第一个坑是 CS 上电悬空。我在第一版样品上没给 CS 加上拉助跑了几台机器后偶尔出现开机第一笔参数写成坏值。用示波器抓 MRAM 的 VCC 和 CS 时序发现 MCU 配置 GPIO 之前 CS 确实有一小段高阻。给 CS 加了 10kΩ 上拉之后这个问题彻底消失。第二个坑是 WREN 和 WRITE 连在一个 CS 低电平区间里。我在调试初期图省事以为 MRAM 的写使能可以像某些器件那样和写命令拼在一起发结果写进去的数据一直是 0xFF。后来仔细看数据手册的时序图才发现 WREN 命令结束后必须 CS 拉高芯片内部的写使能锁存器才会置位随后再拉低 CS 发 WRITE 命令才有效。第三个坑是 SPI 速率顶格跑导致偶发读回错误。40MHz 在开发板短走线上测试完全正常但整机装入金属机箱、线束靠近继电器之后开始出现偶发校验错误。我没有去改硬件布局直接把 SPI 车速降到 20MHz问题消失。对工业产品来说稳定比那点带宽重要得多。第四个坑是 DMA 发送时忘了处理接收侧溢出。最初只配置了 DMA 发送跑了几分钟后 SPI 中断一直被触发代码卡在错误处理里。加了 DMA 接收通道并定期清空 SPIROV 标志之后批量传输才真正稳定。6.3 最后一点经验先测极端温度再谈可靠性在工业项目里我最后基本都会把板子放进高低温箱从 -40°C 到 85°C 循环跑日志读写测试。MRAM 本身的工作温度范围通常可以覆盖工业级但你的 PCB 走线、上拉电阻、电源电容在不同的温度下表现会变。特别是 SPI 速率高温下信号边沿变缓、驱动能力下降低速档反而更省心。如果条件允许开跑之前先在每一块板子上做一次“写入后回读”测试全片写满 0x55、0xAA、随机数再逐字节读回比对。MRAM 不像 Flash 有坏块概念但这一步能帮你快速排除焊接不良、引脚虚焊、SPI 配置错误等低级问题也等于对线路板做了一次基础体检。对我个人而言MR25H40CDF 配 PIC32MZ1024EFF144 最大的价值不是性能数字而是让我在写“断电不掉数据”这类需求时终于不用再和电池、擦除磨损、等待时间这些东西斗智斗勇了。把硬件接稳、把写使能时序做对、再给数据加一层 CRC这套组合在产线上跑了几年一直很省心。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑