ARTICLE DETAIL

资讯详情

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

MRAM与PIC24实现工业高频采集掉电保护方案

MRAM与PIC24实现工业高频采集掉电保护方案 1. 项目缘起与方案选型为什么是 MRAM 加 PIC241.1 一个真实的需求场景前阵子接了个工业数据采集终端的活儿需求说起来不复杂设备装在配电柜里每隔几十毫秒采一次电流电压把关键数据存下来断电不能丢上电要能立刻读出来。听起来像是随便一颗 EEPROM 或者 Flash 就能搞定的事但真正上手才发现坑不少。问题出在写入频率和掉电时机上。EEPROM 的擦写寿命通常在百万次量级按 50ms 写一次算一天就是 172 万次几天就把一颗芯片写废了。Flash 更麻烦写之前得擦整个扇区擦写寿命也就十万次左右而且写入过程中如果突然断电整个扇区可能直接变砖。铁电存储器 FRAM 倒是不错写入快、寿命高但容量普遍偏小、价格偏高而且很多型号供货周期不稳定。后来把目光转向了 MRAM也就是磁性随机存储器。选型的落点是Everspin 的 MR25H40CDF4Mbit 容量SPI 接口最关键的是它号称无限次读写、写入无需擦除、掉电数据保持二十年以上。主控这边选了Microchip 的 PIC24EP512GU81416 位 MCU512KB Flash、48KB RAM带多个 SPI 模块工业温度范围供货也还算稳。这套组合在工业嵌入式里算是比较典型的“高可靠小数据存储”方案。1.2 MR25H40CDF 到底是个什么东西先把这颗存储芯片讲清楚。MR25H40CDF 是一颗 4Mbit512KB的 SPI 接口 MRAM工作电压 2.7V 到 3.6V最高 SPI 时钟可以跑到 40MHz。它的存储单元用的是磁性隧道结MTJ靠电子自旋方向来存储 0 和 1而不是像 Flash 那样靠电荷。这个物理原理上的差异带来几个直接好处写入不需要擦除Flash 写之前必须擦MRAM 直接覆盖写省掉了擦除这一步也就没有了“擦写寿命”这个概念上的限制。写入速度极快没有擦除等待单次写入的时间和读取差不多都是纳秒到几十纳秒级别。掉电数据不丢磁性状态是非易失的断电后数据能保持很久官方标称 20 年。抗辐射、抗磁场干扰能力较强工业环境里电磁干扰大这一点比 Flash 有优势。不过要注意MR25H40CDF 的“无限次读写”是相对概念实际工程中我们还是会关注总写入量但相比 EEPROM 和 Flash它的寿命优势是碾压级的。另外它的容量只有 512KB适合存配置参数、运行日志、关键状态量不适合当大容量数据盘用。1.3 PIC24EP512GU814 的角色定位PIC24EP512GU814 是 Microchip PIC24EP 系列里资源比较丰富的一款。512KB Flash 存程序48KB RAM 跑缓冲主频可以到 70 MIPS带 4 个 SPI 模块、多个 UART、I2C、USB 和 CAN。选它主要看中几点SPI 模块多可以一个接 MRAM一个接显示屏一个接传感器互不打架。工业级温度范围-40°C 到 125°C配电柜里夏天能到 70°C 以上这个范围够用。DMA 支持SPI 配合 DMA 可以做到大数据块搬运不占 CPU对实时采集很关键。生态成熟Microchip 的 XC16 编译器、Harmony 框架、参考手册都比较全调试起来省心。这套组合的核心思路就是用 MRAM 解决“高频写入 掉电不丢”的存储难题用 PIC24 的 SPI 和 DMA 解决“快速搬运 低 CPU 占用”的控制难题。下面我把整个设计、实操和踩过的坑完整拆一遍。2. 硬件设计与 SPI 接口细节2.1 硬件连接与引脚规划MR25H40CDF 是标准的 8 脚 SOIC 封装引脚定义很清晰VCC、GND、SCK、SI、SO、CS、WP、HOLD。实际接线时我把它挂在 PIC24 的 SPI2 模块上具体分配如下MR25H40CDF 引脚PIC24EP512GU814 引脚说明VCC3.3V供电加 0.1uF 去耦电容GNDGND地SCKRPGA1 / SPI2CLKSPI 时钟SIRPGA2 / SPI2DI主出从入SORPGA3 / SPI2DO主入从出CSRB0软件片选普通 GPIOWP3.3V写保护不用时拉高HOLD3.3V保持不用时拉高这里有个细节值得说CS 我用的是软件片选而不是 SPI 模块的硬件片选。原因在于 MRAM 的时序要求比较严格硬件片选在某些 PIC24 型号上和 SPI 时钟的配合会有半个周期的偏差导致第一个字节采样不稳。软件片选虽然多占一个 GPIO但时序完全可控调试起来也直观。这个取舍在后面“常见问题”里还会展开。2.2 SPI 模式与时序参数MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我选的是模式 0因为 PIC24 的 SPI 模块在模式 0 下配置最直接时钟空闲低电平数据在上升沿采样。时序上要注意几个关键参数SCK 最高频率 40MHz但实际跑的时候我没敢直接拉满。PIC24 在 70 MIPS 下SPI 分频后能到 17.5MHz 或 35MHz我最终用的是 17.5MHz。原因是 PCB 走线不长但也没做阻抗匹配35MHz 下眼图已经有点糊了17.5MHz 下非常干净。CS 建立时间CS 拉低到第一个 SCK 上升沿之间至少要留 5ns。软件片选下我习惯在拉低 CS 后插一个NOP再发数据。CS 保持时间最后一个 SCK 下降沿到 CS 拉高之间至少 5ns。同样插一个NOP。写入周期时间MRAM 没有擦除等待但单次写入后到下一次操作之间官方建议留 0ns实际我留了 100ns 的余量稳。2.3 PCB 布局的几个经验点SPI 总线在工业环境里跑PCB 布局不能太随意。我总结了几条SCK 走线尽量短并且远离模拟信号和电源开关节点。SCK 是持续翻转的辐射比数据线大。CS 走线单独走不要和 SCK 平行长距离并排否则 CS 上容易耦合出毛刺导致误触发。去耦电容紧贴 VCC 引脚0.1uF 的陶瓷电容加一个 1uF 的钽电容两个并联。MRAM 在写入瞬间电流会有小尖峰去耦不到位会导致写入失败。地平面完整不要在地平面上开槽。SPI 的返回电流走地平面地不完整会导致信号回流路径变长时序裕量变小。这些点听起来是老生常谈但我在第一版板子上就吃了亏去耦电容离芯片远了 8mm结果高温下偶发写入失败换了布局之后问题消失。3. 软件架构与驱动实现3.1 整体软件分层软件这边我分了三层底层 SPI 驱动、MRAM 读写驱动、上层数据管理。这样分层的好处是换主控或者换存储芯片时只需要改底层上层逻辑不动。底层 SPI 驱动封装 SPI2 的初始化、字节收发、片选控制。MRAM 驱动封装 MRAM 的读、写、状态寄存器操作提供按地址读写字节、页、块的接口。数据管理层定义数据帧格式、写入策略、掉电保护逻辑、上电恢复逻辑。3.2 SPI 底层初始化代码PIC24 的 SPI 初始化涉及几个寄存器SPI2CON1、SPI2CON2、SPI2STAT、SPI2BRG。下面是我实际用的初始化代码注释里写了每个参数的计算依据。void SPI2_Init(void) { // 关闭 SPI2 再配置 SPI2CON1bits.SPIEN 0; // 模式0CKP0(空闲低), CKE1(上升沿采样) // 8位数据主模式 SPI2CON1bits.CKP 0; SPI2CON1bits.CKE 1; SPI2CON1bits.MODE16 0; // 8位模式 SPI2CON1bits.MSTEN 1; // 主模式 SPI2CON1bits.SMP 0; // 中间采样 // 波特率Fcy 70MHz目标 17.5MHz // BRG Fcy / (2 * Fsck) - 1 70M / (2 * 17.5M) - 1 1 SPI2BRG 1; // 配置 SCK、SI、SO 引脚为 SPI 功能 // 具体寄存器名根据具体封装和引脚复用确定 // 这里省略引脚映射代码实际项目中用 PPS 配置 // 使能 SPI2 SPI2CON1bits.SPIEN 1; }这里SPI2BRG 1的计算过程值得说清楚PIC24 的 SPI 波特率公式是Fsck Fcy / (2 * (BRG 1))。Fcy 是外设时钟我这边配的是 70MHz。要得到 17.5MHz反推BRG 1 70M / (2 * 17.5M) 2所以BRG 1。如果直接写SPI2BRG 0得到的是 35MHz虽然 MRAM 标称支持 40MHz但实际板子上 35MHz 下误码率明显上升所以退到 17.5MHz。3.3 MRAM 读写驱动实现MR25H40CDF 的指令集很简洁常用的就几条指令操作码说明READ0x03读数据后跟 3 字节地址WRITE0x02写数据后跟 3 字节地址WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器写操作之前必须先发 WREN这是很多新手容易漏的一步。下面是我封装的写字节函数void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_LOW(); SPI2_Transfer(0x06); // WREN MRAM_CS_HIGH(); __asm__ volatile (nop); // CS 保持时间 MRAM_CS_LOW(); SPI2_Transfer(0x02); // WRITE SPI2_Transfer((addr 16) 0xFF); SPI2_Transfer((addr 8) 0xFF); SPI2_Transfer(addr 0xFF); SPI2_Transfer(data); MRAM_CS_HIGH(); }读操作更简单不需要 WRENuint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; MRAM_CS_LOW(); SPI2_Transfer(0x03); // READ SPI2_Transfer((addr 16) 0xFF); SPI2_Transfer((addr 8) 0xFF); SPI2_Transfer(addr 0xFF); data SPI2_Transfer(0x00); // 发送哑字节读回数据 MRAM_CS_HIGH(); return data; }3.4 批量读写与 DMA 优化单字节读写够用但工业采集场景下数据量大单字节循环效率太低。MRAM 支持连续读写地址会自动递增所以可以一次 CS 周期内连续搬运多个字节。我进一步用 DMA 把 SPI 收发和内存搬运解耦CPU 只负责启动 DMA 和等完成标志。void MRAM_WriteBlock_DMA(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_CS_LOW(); SPI2_Transfer(0x06); // WREN MRAM_CS_HIGH(); __asm__ volatile (nop); MRAM_CS_LOW(); SPI2_Transfer(0x02); // WRITE SPI2_Transfer((addr 16) 0xFF); SPI2_Transfer((addr 8) 0xFF); SPI2_Transfer(addr 0xFF); // 启动 DMA 发送 buf 中的 len 个字节 DMA_Setup_TX(buf, len); DMA_Start(); while (!DMA_IsDone()); MRAM_CS_HIGH(); }用 DMA 之后写 512 字节的时间从原来的约 300us 降到约 30usCPU 占用从 100% 降到几乎为 0。这个提升在 50ms 采集周期下非常关键CPU 腾出来可以做滤波和通信。4. 数据存储策略与掉电保护4.1 数据分区设计512KB 的 MRAM 不能乱用我把它分成几个区区域地址范围用途写入频率配置区0x00000 - 0x00FFF设备参数、校准系数极低日志区0x01000 - 0x3FFFF运行日志、事件记录中实时数据区0x40000 - 0x7FFFF采集数据环形缓冲高配置区用双备份加 CRC 校验写的时候先写备份区再写主区读的时候两个区都读CRC 对不上的那个丢弃。日志区用追加写写满后从头覆盖。实时数据区用环形缓冲写指针和读指针都存在 MRAM 固定位置掉电后能恢复。4.2 掉电保护的关键设计掉电保护是这个项目的核心难点。设备断电是随机的可能正好发生在写 MRAM 的过程中。虽然 MRAM 单次写入是原子的但一个数据帧可能跨多个字节写到一半断电就会产生半截数据。我的做法是双缓冲加序号每个数据帧写两份分别放在缓冲 A 和缓冲 B每份带一个递增的序号和 CRC。上电恢复时读两份比较序号取序号大的且 CRC 正确的那份。这样即使写到一半断电至少有一份是完整的。typedef struct { uint32_t seq; uint16_t len; uint8_t data[64]; uint16_t crc; } DataFrame_t; void SaveDataFrame(DataFrame_t *frame) { static uint32_t seq 0; frame-seq seq; frame-crc CRC16(frame-data, frame-len); // 交替写入 A/B 缓冲 if (seq % 2 1) { MRAM_WriteBlock_DMA(ADDR_BUF_A, (uint8_t*)frame, sizeof(DataFrame_t)); } else { MRAM_WriteBlock_DMA(ADDR_BUF_B, (uint8_t*)frame, sizeof(DataFrame_t)); } }这个策略的代价是存储空间翻倍但换来了掉电安全。在工业场景里这个代价完全值得。4.3 上电恢复流程上电后第一件事不是初始化外设而是先读 MRAM 恢复状态。流程是这样的读缓冲 A 和缓冲 B 的帧头。分别校验 CRC。取 CRC 正确且序号大的那份作为有效数据。如果两份都错说明首次上电或数据损坏用默认值初始化。恢复写指针和读指针继续环形缓冲。这个流程我实测过很多次包括在写入过程中直接拔电上电后都能正确恢复到最近一个完整帧。5. 常见问题与排查实录5.1 写入失败但读状态正常这是我在调试初期遇到的最诡异的问题读 MRAM 一切正常写进去再读出来却是旧数据。排查了半天最后发现是WREN 指令后 CS 拉高的时间太短。MRAM 在收到 WREN 后需要一点时间把写使能锁存器置位如果 CS 拉高后立刻又拉低发 WRITE锁存器可能还没置位WRITE 就被忽略了。解决办法很简单WREN 之后 CS 拉高插一个短延时我用了 1us再拉低发 WRITE。这个延时在数据手册里没有明确写是我用逻辑分析仪抓波形对比出来的。5.2 高温下偶发误码板子装到配电柜里跑了一周发现偶尔有数据 CRC 校验失败。常温下复现不了加热到 70°C 后能复现。最后定位到两个原因一是去耦电容离芯片太远高温下电容等效串联电阻变大去耦效果下降二是 SPI 时钟在高温下裕量变小17.5MHz 下边沿变缓。解决方法是重新布局去耦电容紧贴引脚同时把 SPI 时钟降到 8.75MHz。降速后误码消失写入时间从 30us 增加到 60us对 50ms 周期来说完全可接受。5.3 常见问题速查表现象可能原因排查方法解决读正常写无效WREN 后延时不足逻辑分析仪抓 CS 和 SCKWREN 后加 1us 延时高温误码去耦不良或时钟过快加热复现测电源纹波改布局降 SPI 时钟上电数据错乱掉电时写到一半检查双缓冲序号逻辑确保 A/B 交替写CS 毛刺误触发CS 与 SCK 平行走线示波器看 CS 波形分开走线加下拉写入后读回旧值地址计算错误打印地址和返回值检查 24 位地址拼接5.4 几个实操心得逻辑分析仪是必备工具。SPI 的问题光看代码看不出来抓波形一目了然。我用的是一款 8 通道 100MHz 的够用。先跑通单字节再上 DMA。DMA 配置错了很难查先用单字节验证硬件和时序再换 DMA。MRAM 的 WP 和 HOLD 引脚不要悬空。悬空时电平不确定可能导致随机写保护或保持一定要拉高。写测试数据用 0x55 和 0xAA 交替。这两个值的位模式互补能暴露很多走线和时序问题。6. 性能实测与方案对比6.1 实测性能数据在 17.5MHz SPI 时钟下我实测了几组数据操作数据量耗时备注单字节写1B约 2us含 WREN 和 CS 开销单字节读1B约 1.5us无 WREN块写512B约 30usDMA块读512B约 28usDMA连续写1MB约 60ms含地址递增按 50ms 采集周期算每次写 512B 只占 30usCPU 占用几乎为零完全满足实时性要求。6.2 与 EEPROM、Flash、FRAM 的对比特性MR25H40CDF (MRAM)EEPROMNOR FlashFRAM写入前擦除不需要不需要需要不需要擦写寿命近乎无限100万次10万次10^14次写入速度极快慢慢快掉电保持20年40年20年10年容量512KB小大小价格中高低低高工业适用性优中中优从表里能看出来MRAM 在写入寿命和速度上是碾压 EEPROM 和 Flash 的代价是价格偏高、容量偏小。对于“高频写、小数据、高可靠”的场景MRAM 是最优解。6.3 这套方案的适用边界不是什么场景都适合上 MRAM。我的判断标准是写入频率高每秒几十次以上EEPROM 和 Flash 寿命扛不住。数据量小几百 KB 以内MRAM 容量够用。掉电不能丢必须有非易失性。环境恶劣高温、振动、电磁干扰大。如果只是存个配置参数一年写几次那 EEPROM 就够了没必要花 MRAM 的钱。如果是存大量历史数据那还是得上 Flash 或者外挂存储卡。选型这件事合适比先进重要。7. 后续可扩展的方向这套框架跑通之后其实还能往上加东西。比如把 MRAM 当 SPI 总线的共享存储多个主控轮流读写用 CS 和状态寄存器做互斥或者在 MRAM 里实现一个轻量级的文件系统按文件名存取上层就不用关心地址了。我最近在试的是把采集数据和事件日志分开存日志用追加写数据用环形缓冲两者互不干扰掉电恢复时各恢复各的。另外 PIC24 的 SPI 模块支持多主模式如果以后要加第二个主控做冗余MRAM 可以当共享存储用通过 CS 仲裁来避免冲突。这个思路在工业冗余系统里挺常见有兴趣的可以往这个方向试试。最后分享一个小技巧MRAM 的写保护引脚 WP 如果接一个 GPIO 而不是直接拉高可以在固件里动态控制写保护正常运行时打开写进入安全模式时关闭写防止误写关键配置区。这个改动成本很低但安全性提升明显我在几个项目里都加上了。
返回列表