ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

工业级MRAM实战:PIC32MZ与MR25H40CDF的SPI驱动与可靠性设计

工业级MRAM实战:PIC32MZ与MR25H40CDF的SPI驱动与可靠性设计 1. 项目缘起与方案选型1.1 为什么要在工业场景里折腾 MRAM 这颗料做嵌入式这行十几年存储方案的选择一直是个绕不开的坎。早些年做工业数据采集器EEPROM 擦写寿命只有 100 万次铁电存储器容量又上不去NOR Flash 写之前还得先擦除整个扇区掉电瞬间数据就丢了。后来接触到 Everspin 的 MR25H40CDF 这颗磁阻存储器才算真正把高频写入 掉电不丢 长寿命这三个需求同时兜住。MR25H40CDF 是一颗 4Mbit512KB的 SPI 接口 MRAM采用 8 引脚 DFN 封装工作电压 2.7V 到 3.6V工业级温度范围覆盖 -40℃ 到 85℃。它最核心的价值在于写入不需要擦除、没有写延迟、擦写寿命理论上无限官方标称 10^14 次以上而且掉电后数据能保持 20 年以上。这几点放在工业现场那种随时可能断电、数据必须保住的环境里比什么都重要。我这次选的主控是 PIC32MZ2048EFH144Microchip 家的 32 位 MIPS 核心 MCU200MHz 主频2MB Flash512KB RAM自带 6 个 SPI 模块。选它一方面是因为手头项目对运算能力有要求另一方面是它的 SPI 外设配置灵活支持 8/16/32 位传输模式跟 MR25H40CDF 配合起来很顺手。1.2 方案对比MRAM 到底比 EEPROM 和 Flash 强在哪很多人问我既然都是 SPI 接口的存储芯片为什么不用便宜的 25 系列 SPI Flash 或者 24 系列 EEPROM我整理了一张对比表把实际项目里最关心的几个维度拉出来看对比维度MR25H40CDF (MRAM)25系列 SPI Flash24系列 EEPROM写入前是否需要擦除不需要需要按扇区擦除不需要字节写入时间无延迟直接写页编程约 0.7ms约 5ms擦写寿命10^14 次以上约 10 万次约 100 万次掉电数据保持20 年以上20 年40 年写入功耗极低较高擦除时中等单位容量成本高低中等接口速率最高 40MHz最高 104MHz最高 10MHz从表里能看出来MRAM 的短板就是贵同容量下价格可能是 SPI Flash 的十几倍甚至几十倍。但工业场景里如果每秒要记录几十次传感器数据而且设备可能随时被拉闸那 Flash 的擦除延迟和寿命限制就是致命伤。我之前有个项目用 SPI Flash 做数据记录现场跑了半年就出现扇区坏块返修成本远超芯片差价。所以这个方案的核心逻辑就是用成本换可靠性用简单换稳定。1.3 硬件连接的整体思路PIC32MZ2048EFH144 和 MR25H40CDF 之间就是标准的 SPI 四线制连接SCK、SDI、SDO、CS再加上电源和地。我选用的是 SPI2 模块映射到 RF4SCK、RF5SDI、RF3SDO、RB2CS。这里有个细节要注意MR25H40CDF 的 CS 引脚必须由主控的 GPIO 或者 SPI 外设的硬件片选来控制不能悬空否则芯片内部状态机会乱跳。电源部分我在 MR25H40CDF 的 VDD 引脚旁边放了 0.1uF 和 1uF 两颗电容一颗滤高频一颗滤低频。工业现场电磁环境复杂电源纹波大了会导致 SPI 通信误码这个钱不能省。另外 WP 引脚写保护我直接拉到 VDD因为项目里不需要硬件写保护功能软件层面控制就够了。2. SPI 通信底层细节与寄存器操作2.1 MR25H40CDF 的指令集拆解MR25H40CDF 的指令集不复杂但有几个地方容易踩坑。它支持标准 SPI 模式 0 和模式 3我选的是模式 0CPOL0CPHA0也就是时钟空闲低电平数据在上升沿采样。这个跟 PIC32MZ 的 SPI 配置要对应上不然读回来的数据全是 0xFF 或者 0x00。核心指令我列一下WREN (0x06)写使能任何写操作之前必须先发这条指令WRDI (0x04)写禁止写完可以发也可以不发芯片上电默认是禁止写READ (0x03)读数据后面跟 3 字节地址然后连续输出数据WRITE (0x02)写数据后面跟 3 字节地址然后连续写入数据RDSR (0x05)读状态寄存器WRSR (0x01)写状态寄存器这里有个关键点MR25H40CDF 的写操作没有页边界限制。SPI Flash 写的时候不能跨页一页 256 字节跨页就得重新发指令。MRAM 没这个毛病你可以从任意地址开始连续写写到芯片末尾都没问题。这个特性在记录不定长数据的时候特别爽不用去算页对齐。2.2 PIC32MZ 的 SPI 模块配置要点PIC32MZ 的 SPI 配置比 STM32 要繁琐一些因为它的寄存器分得更细。我一般用 Harmony 框架来初始化但底层寄存器也得心里有数。关键寄存器是 SPI2CON、SPI2BRG、SPI2STAT。SPI2CON 里要设置MODE32/16/8我选 8 位模式因为 MRAM 指令和地址都是按字节走的SMP采样相位模式 0 下设为 0在数据输出中间采样CKE时钟边沿模式 0 下设为 0空闲低上升沿变化MSTEN主模式使能SPIENSPI 使能SPI2BRG 决定时钟频率。PIC32MZ 的 SPI 时钟源是 PBCLK2我这边配置的是 100MHz。MR25H40CDF 最高支持 40MHz所以 BRG 值不能小于 100/40 - 1 1.5取整就是 2实际时钟 100/(21) 33.3MHz。这个速率在工业现场跑起来很稳再高的话 PCB 走线不好就容易出错。// SPI2 初始化代码片段寄存器级 SPI2CON 0; SPI2CONbits.MSTEN 1; // 主模式 SPI2CONbits.CKE 0; // 空闲低电平 SPI2CONbits.SMP 0; // 中间采样 SPI2CONbits.MODE8 1; // 8位模式 SPI2BRG 2; // 33.3MHz SPI2CONbits.ON 1; // 使能SPI2.3 片选信号的处理技巧SPI 通信里片选信号的处理最容易被忽视但恰恰是出问题最多的地方。PIC32MZ 的 SPI 模块支持硬件片选但我习惯用普通 GPIO 来控制 CS因为这样时序更可控。具体操作是每次传输前把 CS 拉低传输完再拉高。MR25H40CDF 要求 CS 下降沿到第一个时钟沿之间至少有 5ns 的建立时间CS 上升沿之前最后一个时钟沿之后至少有 5ns 的保持时间。33MHz 下时钟周期是 30ns所以只要代码里拉低 CS 之后不要立刻写 SPI 缓冲寄存器中间隔几条指令就足够了。注意如果你用硬件片选PIC32MZ 的 SPI 模块在传输结束后会自动拉高 CS但有时候最后一个字节还没完全移出去就拉高了导致数据截断。我实测下来软件片选更稳妥。3. 完整读写实操流程3.1 单字节写入的完整时序写一个字节到 MR25H40CDF 的指定地址流程分三步发 WREN、发 WRITE 指令加地址加数据、等待写入完成。虽然 MRAM 没有写延迟但状态寄存器的 WEL 位需要时间更新所以稳妥起见还是查一下状态。void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_LOW(); SPI2_ExchangeByte(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI2_ExchangeByte(0x02); // WRITE SPI2_ExchangeByte((addr 16) 0xFF); SPI2_ExchangeByte((addr 8) 0xFF); SPI2_ExchangeByte(addr 0xFF); SPI2_ExchangeByte(data); MRAM_CS_HIGH(); // 等待写入完成实际MRAM几乎无延迟但保险起见 while(MRAM_ReadStatus() 0x01); }这里 SPI2_ExchangeByte 函数就是往 SPI2BUF 写数据然后等 SPI2RXBUF 有数据回来再读走。PIC32MZ 的 SPI 是全双工的发一个字节的同时会收一个字节写操作时收到的字节直接丢弃就行。3.2 连续读取的优化写法读操作比写操作简单不需要 WREN。但连续读的时候有个效率问题如果每次读一个字节都重新发指令和地址那大部分时间都花在指令开销上了。MR25H40CDF 支持连续读模式发一次 READ 指令和起始地址之后可以一直读下去地址会自动递增。void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_CS_LOW(); SPI2_ExchangeByte(0x03); // READ SPI2_ExchangeByte((addr 16) 0xFF); SPI2_ExchangeByte((addr 8) 0xFF); SPI2_ExchangeByte(addr 0xFF); for(uint32_t i 0; i len; i) { buf[i] SPI2_ExchangeByte(0x00); } MRAM_CS_HIGH(); }这个写法在 33MHz 时钟下读 512 字节大概只需要 130 微秒左右。我实测过用逻辑分析仪抓波形SCK 连续不断数据一个接一个出来效率很高。3.3 数据记录场景的地址管理策略工业数据记录通常是个环形缓冲区的结构数据一直往后写写到末尾就绕回开头覆盖最老的数据。MR25H40CDF 有 512KB 空间我把它分成 512 个 1KB 的块每块存一条记录。记录头里放时间戳和校验和后面跟实际数据。地址管理我用两个指针写指针和读指针。写指针每次写完一条记录就往后移读指针由上位机查询时更新。两个指针都存在 MRAM 的固定地址里掉电也不会丢。这里有个细节指针更新和记录写入最好放在同一个写操作序列里避免掉电时指针和记录不一致。我的做法是先把记录写到新地址再更新指针这样即使掉电最坏情况就是丢最后一条记录不会破坏整个缓冲区结构。4. 常见问题排查与避坑经验4.1 读回来全是 0xFF 或者 0x00这是最典型的问题八成是 SPI 模式不对。MR25H40CDF 支持模式 0 和模式 3如果你主控配的是模式 1 或模式 2数据肯定错。检查方法很简单用示波器看 SCK 空闲时是高还是低再看数据在哪个边沿变化。模式 0 是空闲低、上升沿采样模式 3 是空闲高、下降沿采样。另一个可能是 CS 信号没接对。有些开发板把 CS 接到了 SPI 模块的硬件片选引脚上但代码里又用 GPIO 去控制结果两个信号打架。我建议统一用 GPIO 控制把硬件片选功能关掉。4.2 写入后读出来数据不对先确认 WREN 指令有没有发。MR25H40CDF 上电默认是写禁止状态不发 WREN 直接发 WRITE 指令数据写不进去。另外 WREN 之后如果发了其他指令比如读状态WEL 位会自动清零所以 WREN 和 WRITE 之间不能插入其他操作。还有一种情况是地址算错了。MR25H40CDF 是 4Mbit地址范围是 0x000000 到 0x07FFFF。如果你传了个 0x080000 进去地址会回绕到 0x000000数据就写到开头去了。这个在调试的时候很容易迷惑明明写的是末尾读出来却是开头的数据。4.3 高速通信下的误码问题33MHz 以上时钟频率时如果 PCB 走线太长或者没有做阻抗匹配SCK 和数据的边沿会出现振铃导致采样错误。我一般会在 SCK 和数据线上串 22 欧姆到 33 欧姆的电阻靠近主控端放。另外 SPI 走线尽量等长不要跨分割地平面。如果误码率还是高可以先把时钟降到 10MHz 试试。如果降速后正常那就是信号完整性问题不是芯片或代码的问题。工业现场我一般不会跑超过 25MHz留足余量比追求极限速度重要。4.4 常见问题速查表现象可能原因排查方法解决措施读回全 0xFFSPI 模式错误示波器看 SCK 空闲电平改为模式 0 或模式 3读回全 0x00MISO 没接或 CS 没拉低万用表测 MISO 通断检查焊接和 CS 控制写入无效没发 WREN读状态寄存器 WEL 位写前先发 0x06地址错乱地址超出范围核对地址计算限制地址在 0x00000-0x7FFFF高速误码信号完整性差降速测试串匹配电阻降低时钟掉电丢数据写入未完成就断电检查写时序写后读回校验5. 工业场景下的可靠性加固5.1 数据校验与恢复机制MRAM 虽然可靠但工业现场该做的校验一样不能少。我在每条记录后面加 2 字节 CRC16 校验读的时候先算 CRC 再比对。如果校验失败就把这条记录标记为无效跳过继续读下一条。这样即使某次写入过程中掉电导致数据不完整也不会影响其他记录。另外我在 MRAM 开头留了 1KB 做元数据区存记录总数、写指针、读指针、以及一个魔数比如 0x5A5A。上电初始化时先读魔数如果不对就说明 MRAM 是全新的或者数据格式变了这时候就重新格式化整个存储区。这个机制在固件升级后特别有用避免新旧数据格式混在一起。5.2 电源监控与掉电保护PIC32MZ2048EFH144 自带比较器我把它配置成电源电压监控当 VDD 降到 2.9V 以下时触发中断在中断里立刻停止所有写操作把关键指针存到 MRAM 里。因为 MRAM 写入不需要等待这个中断处理可以在几微秒内完成远快于电源电容的放电时间。硬件上我在电源输入端放了一颗 470uF 的电解电容保证掉电后 MCU 还能撑 10ms 以上。这个时间足够完成紧急数据保存。实测下来从检测到掉电到写完关键数据总共不到 1ms余量很足。5.3 温度漂移与长期稳定性MR25H40CDF 的工业级温度范围是 -40℃ 到 85℃但实际在极端温度下SPI 时序参数会有漂移。我在 -40℃ 低温箱里测试过33MHz 时钟下通信正常但 40MHz 时偶尔出错。所以如果产品要过宽温测试建议把时钟降到 25MHz 以下。长期稳定性方面MRAM 的数据保持能力跟温度有关温度越高保持时间越短。85℃ 下官方标称 10 年25℃ 下是 20 年以上。如果设备长期工作在高温环境建议每 5 年做一次数据刷新读出来重新写一遍相当于给数据充电。6. 性能实测与优化建议6.1 读写速度实测数据我用逻辑分析仪抓了实际波形统计了不同时钟频率下的读写速度时钟频率单字节写入耗时512字节连续读耗时512字节连续写耗时10MHz约 3.2us约 420us约 450us20MHz约 1.8us约 220us约 240us33MHz约 1.2us约 135us约 150us从数据看33MHz 下写 512 字节只要 150 微秒这个速度做高频数据记录完全够用。就算每秒记录 1000 次每次 512 字节总写入时间也才 150 毫秒CPU 占用率很低。6.2 DMA 传输的可行性分析PIC32MZ 的 SPI 模块支持 DMA 触发理论上可以用 DMA 来搬数据进一步降低 CPU 占用。但我实际试下来对于 512 字节以下的数据量DMA 的配置开销比直接轮询还大。DMA 适合那种一次传输几 KB 以上的场景比如把整个 MRAM 内容 dump 出来做备份。日常记录用轮询就够了代码简单出问题也好排查。6.3 磨损均衡的必要性讨论有人问 MRAM 要不要做磨损均衡。我的答案是不需要。MRAM 的擦写寿命是 10^14 次就算你每秒写同一个地址 1000 次也要 3000 多年才能用完。所以地址可以随便写不用像 Flash 那样搞复杂的均衡算法。这也是 MRAM 用起来最省心的地方——你不需要为存储寿命操任何心。7. 项目落地后的几点体会这个方案我在两个工业项目里实际用了一个是环境监测终端一个是电机振动记录仪。环境监测终端每秒记录一次温湿度和气压连续跑了 8 个月数据一条没丢。电机振动记录仪采样率更高每 10ms 记录一次三轴加速度MRAM 的写入速度完全跟得上。踩过的坑主要集中在前期的 SPI 配置上。第一次调试时读回来全是 0xFF查了半天发现是 SPI 模式配成了模式 1。还有一次是 CS 信号被硬件片选和 GPIO 同时驱动导致通信时好时坏。这些问题在逻辑分析仪面前都藏不住所以调试 SPI 设备逻辑分析仪是必备工具比万用表和示波器都好使。最后分享一个小技巧MRAM 的 WP 引脚如果不使用一定要拉到 VDD 或者 GND不能悬空。悬空时引脚电平不确定可能导致芯片内部状态机误动作出现随机写入失败。这个细节在数据手册里写得很清楚但很容易被忽略。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进