ARTICLE DETAIL

资讯详情

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

工业嵌入式非易失存储:MRAM替换SRAM与PIC18驱动实践

工业嵌入式非易失存储:MRAM替换SRAM与PIC18驱动实践 1. 为什么工业现场还在用并行SRAM而MRAM已经悄悄替换了它如果你拆过工业PLC的板子或者修过电力监控终端大概率见过那种带电池座的SRAM芯片——一颗512K×8的异步SRAM旁边焊着一颗CR2032纽扣电池再配一个电源切换逻辑。这套方案用了快三十年稳定、便宜、工程师闭着眼都能画原理图。但它有个致命问题电池会漏液会没电会在高低温循环里提前失效。一旦掉电标定参数、累计运行时长、故障日志全部归零现场维护人员得带着笔记本重新灌参数。MR25H40CDF就是冲着这个场景来的。它是Everspin出品的一颗4Mbit512K×8磁性随机存储器SPI接口DFN封装工作温度覆盖-40℃到85℃工业级甚至到105℃。和传统SRAM电池方案相比它不需要备用电源写入即非易失读写寿命没有实际限制写入前也不需要擦除。和EEPROM、NOR Flash相比它的写入速度快了几个数量级没有擦写等待也没有块擦除的概念——你可以把它当成一颗永远不掉电的SRAM来用。PIC18LF47K42是Microchip的PIC18系列里比较新的一颗8位MCU带SPI外设、DMA、可配置逻辑单元CLC、以及丰富的中断源。它的SPI模块支持主机模式和从机模式时钟极性/相位可配波特率通过BRG寄存器分频。用它来驱动MR25H40CDF硬件上只需要四根线SCK、SDI、SDO、CS软件上就是标准的SPI读写时序封装。这套组合解决的核心问题是在无备用电池、宽温、强振动、频繁掉电的工业环境中可靠地保存和读取关键数据。适合谁看做工业仪表、电力终端、车载记录仪、医疗设备数据日志的嵌入式工程师尤其是那些被电池漏液和EEPROM写入慢折磨过的人。我前后在三四个项目里用过MRAM从早期的MR25H256到现在的MR25H40CDF踩过的坑不算少。下面把选型逻辑、硬件设计、SPI驱动、数据管理策略、以及实测中遇到的怪问题完整拆一遍。2. MR25H40CDF的引脚、时序与SPI模式选择2.1 引脚定义与硬件连接要点MR25H40CDF是8引脚DFN封装引脚定义如下引脚名称类型说明1CS输入片选低有效2SO输出数据输出MISO3WP输入写保护低有效4VSS电源地5SI输入数据输入MOSI6SCK输入时钟7HOLD输入保持低有效8VDD电源2.7V~3.6V实际布线时WP和HOLD如果不用直接上拉到VDD。我见过有人把WP悬空结果在电磁干扰强的场合偶尔触发写保护数据写不进去排查了半天。WP和HOLD必须明确上拉或下拉不能悬空这是第一条经验。CS引脚建议由MCU的普通GPIO控制不要用SPI外设的硬件片选。原因后面讲软件片选与硬件片选的区别时会展开。VDD端建议放一颗0.1μF陶瓷电容紧贴芯片再并一颗1μF的储能电容。MRAM写入瞬间的电流脉冲比SRAM略大电源纹波控制不好会导致写入失败。2.2 SPI模式Mode 0还是Mode 3MR25H40CDF支持SPI Mode 0CPOL0CPHA0和Mode 3CPOL1CPHA1。数据在SCK上升沿采样下降沿移出。我一般用Mode 0因为PIC18LF47K42的SPI模块默认配置就是Mode 0省得改。PIC18LF47K42的SPI配置寄存器关键位// SPI1CON1 配置 SPI1CON1bits.CKP 0; // 空闲时SCK为低 SPI1CON1bits.CKE 1; // 数据在SCK上升沿采样 SPI1CON1bits.SMP 0; // 中间采样 SPI1CON1bits.MSTEN 1; // 主机模式 SPI1CON1bits.SSPEN 1; // 使能SPI // 波特率 Fosc / (2 * (BRG 1)) // 假设Fosc 64MHz目标8MHz SPI1BRG 3; // 64 / (2 * 4) 8MHzMR25H40CDF的最高SCK频率是40MHz读和20MHz写。实际跑的时候我建议先降到1MHz调通再逐步往上提。PCB走线长、有干扰的场合8MHz以上就要注意信号完整性了。2.3 命令集读、写、状态寄存器MR25H40CDF的命令集很精简常用的就几条命令编码说明WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据状态寄存器只有bit0WEL和bit1BP0有意义。WEL在WREN后置1写入完成后自动清0。BP0是块保护位配合WP引脚使用。写操作的完整流程是拉低CS → 发WREN0x06→ 拉高CS → 拉低CS → 发WRITE0x02→ 发24位地址 → 发数据 → 拉高CS。注意WREN和WRITE之间必须拉高CS这是很多新手容易漏掉的步骤。我见过有人把WREN和WRITE连在一起发结果数据死活写不进去查了一下午。读操作简单拉低CS → 发READ0x03→ 发24位地址 → 读数据 → 拉高CS。地址是24位的因为4Mbit对应19位地址但芯片要求发满24位高5位忽略。3. PIC18LF47K42的SPI驱动封装与DMA优化3.1 基础读写函数先写最底层的字节收发函数。PIC18LF47K42的SPI1BUF寄存器读写就是收发一体uint8_t SPI1_ExchangeByte(uint8_t txData) { SPI1BUF txData; while(!PIR5bits.SPI1RXIF); // 等待接收完成 return SPI1BUF; }这个函数在8MHz SCK下单字节耗时约1μs。如果一次要读512字节就是512μs对于大多数工业应用可以接受。但如果你的系统有实时性要求比如每10ms要存一次数据那就得考虑DMA或者优化循环。3.2 页写与地址自动递增MR25H40CDF内部地址会自动递增所以你不需要每写一个字节都发地址。一次WRITE命令可以连续写整页甚至整颗芯片。但要注意写入过程中CS不能拉高否则写入会提前终止而且已经写入的数据可能处于不确定状态。我一般封装成这样的写函数void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { CS_LOW(); SPI1_ExchangeByte(0x06); // WREN CS_HIGH(); CS_LOW(); SPI1_ExchangeByte(0x02); // WRITE SPI1_ExchangeByte((addr 16) 0xFF); SPI1_ExchangeByte((addr 8) 0xFF); SPI1_ExchangeByte(addr 0xFF); for(uint16_t i 0; i len; i) { SPI1_ExchangeByte(buf[i]); } CS_HIGH(); // 等待写入完成 while(MRAM_IsBusy()); }MRAM的写入时间很短典型值几十纳秒但状态寄存器的WEL位清除需要一点时间。实测下来写完最后一个字节后拉高CS再读状态寄存器WEL已经清了。所以MRAM_IsBusy()其实可以省略但为了保险我还是留着。3.3 用DMA减轻CPU负担PIC18LF47K42有DMA控制器可以配置成SPI1接收触发。如果你要连续读大块数据比如上电时加载整个参数区假设4KB用DMA可以省下大量CPU周期。配置思路DMA源地址设为SPI1BUF目标地址设为RAM缓冲区触发源设为SPI1RX传输计数设为要读的字节数。然后启动SPI连续读DMA会自动搬运。具体寄存器配置比较繁琐这里不展开但思路是清晰的。不过说实话对于大多数工业数据存储场景数据量不大几十字节到几KB用轮询方式完全够用。DMA更适合高速数据采集场景比如振动监测。不要为了用DMA而用DMA先把轮询版本调稳再考虑优化。4. 数据管理策略如何组织参数区、日志区与掉电保护4.1 分区规划4Mbit 512KB对于存储参数和日志来说绰绰有余。我一般这样分区区域起始地址大小用途配置区A0x000004KB系统参数带CRC配置区B0x010004KB配置区A的备份标定区0x020008KB传感器标定系数日志区0x04000448KB循环日志带序号和时间戳保留区0x7400048KB固件升级缓存、预留配置区做双备份写入时先写B再写A读取时校验CRC哪个有效用哪个。这是工业设备的标准做法防止写入过程中掉电导致配置丢失。4.2 掉电保护写入原子性MRAM虽然写入快但如果在写入过程中掉电数据仍然可能只写了一半。解决办法是双缓冲CRC写入标志。具体做法在配置区头部放一个4字节的魔数比如0xAA55AA55和一个2字节的CRC。写入流程计算新配置的CRC写入备份区写入完成后写入魔数和CRC验证备份区写入主区写入完成后写入魔数和CRC验证主区读取时先读主区检查魔数和CRC如果无效则读备份区。这样即使主区写入时掉电备份区仍然是完整的。日志区的掉电保护更简单每条日志带一个序号写入时序号递增。读取时按序号排序遇到序号不连续的地方就是掉电点后面的数据丢弃。4.3 日志循环覆盖与磨损均衡MRAM的写入寿命是无限的官方标称10^14次写入所以不需要像Flash那样做复杂的磨损均衡。但日志区循环覆盖时仍然要注意写指针的原子更新。我的做法是日志区头部放一个写指针每次写日志前先更新指针再写数据。如果写数据时掉电指针已经指向下一条最多丢失一条日志不会导致整个日志区混乱。日志条目格式typedef struct { uint32_t seq; // 序号 uint32_t timestamp; // 时间戳 uint16_t event_id; // 事件ID uint8_t data[8]; // 事件数据 uint16_t crc; // CRC16 } LogEntry_t;每条16字节448KB可以存约28000条。按每分钟一条算可以存19天。如果不够可以压缩data字段或者降低记录频率。5. 实测中遇到的三个怪问题与排查过程5.1 问题一写入后立即读取数据偶尔错误现象写完一个字节紧接着读回来大约每几千次出现一次错误读到的值是0xFF或0x00。排查过程先用逻辑分析仪抓SPI波形发现写入时序完全正确CS、SCK、MOSI都符合手册。然后怀疑是电源问题用示波器看VDD发现写入瞬间有一个约200mV的跌落持续时间约50ns。虽然MRAM的工作电压范围是2.7V~3.6V但电源跌落可能导致内部电荷泵工作异常。解决在VDD引脚旁边加了一颗1μF的X7R陶瓷电容紧贴芯片引脚跌落降到50mV以内问题消失。MRAM对电源纹波比SRAM敏感这是第二条经验。5.2 问题二高温下写入失败率上升现象常温下一切正常放到85℃烘箱里跑写入失败率从0上升到约0.1%。排查过程先查时序高温下SCK频率没有变化。再查电源高温下LDO输出正常。最后查MRAM的数据手册发现写入电流在高温下会增大而我的LDO最大输出电流只有100mAMRAM写入瞬间的峰值电流可能超过这个值。解决换了一颗300mA的LDO问题解决。选LDO时要注意峰值电流不能只看平均电流这是第三条经验。5.3 问题三SPI通信偶尔死锁现象系统运行几天后SPI通信突然停止MCU一直在等SPI1RXIF标志。排查过程用调试器读寄存器发现SPI1STAT的SPI1RXIF没有置位但SPI1BUF里有数据。怀疑是中断优先级问题SPI接收中断被更高优先级中断打断导致标志位处理异常。解决在SPI读写函数里加了超时机制uint8_t SPI1_ExchangeByte_Timeout(uint8_t txData) { uint16_t timeout 10000; SPI1BUF txData; while(!PIR5bits.SPI1RXIF) { if(--timeout 0) { return 0xFF; // 超时返回 } } return SPI1BUF; }超时后重新初始化SPI模块系统恢复。任何等待循环都要加超时这是第四条经验适用于所有嵌入式外设驱动。6. 和EEPROM、FRAM、NOR Flash的横向对比选型时我做过一轮对比把结论列出来供参考特性MR25H40CDF (MRAM)24LC512 (EEPROM)FM25V05 (FRAM)W25Q64 (NOR Flash)容量512KB64KB64KB8MB接口SPII2CSPISPI写入速度无延迟5ms/页无延迟3ms/页擦除需求不需要不需要不需要需要写入寿命无限100万次10^14次10万次掉电保持20年200年10年20年工作温度-40~105℃-40~85℃-40~85℃-40~85℃单价千片约$3.5约$0.8约$2.5约$0.5MRAM的优势在于容量比FRAM大速度比EEPROM快不需要擦除温度范围宽。缺点是价格贵。如果你的应用只需要存几百字节参数EEPROM就够了。如果需要存大量日志且写入频繁MRAM是更好的选择。FRAM和MRAM很像但FRAM的容量通常较小而且某些型号的掉电保持时间只有10年。MRAM的保持时间更长适合需要长期保存数据的场景。NOR Flash适合存固件和大量不常改的数据但不适合频繁写入。如果你用NOR Flash存日志需要做磨损均衡复杂度高很多。7. 从裸机到RTOS驱动层的可移植封装7.1 硬件抽象层设计为了让驱动能在不同MCU之间移植我把SPI底层操作抽象成几个函数指针typedef struct { void (*cs_low)(void); void (*cs_high)(void); uint8_t (*exchange_byte)(uint8_t); void (*delay_ms)(uint32_t); } MRAM_HAL_t;上层驱动只调用这些函数指针不直接操作寄存器。换MCU时只需要重新实现HAL层。这个思路在多个项目里帮我省了大量移植时间。7.2 在FreeRTOS下的互斥保护如果系统跑RTOS多个任务可能同时访问MRAM必须加互斥锁。我用的是二值信号量SemaphoreHandle_t mram_mutex; void MRAM_Write_Task(uint32_t addr, uint8_t *buf, uint16_t len) { if(xSemaphoreTake(mram_mutex, pdMS_TO_TICKS(100)) pdTRUE) { MRAM_Write(addr, buf, len); xSemaphoreGive(mram_mutex); } }注意SPI总线本身也需要互斥如果多个SPI设备共享总线要把CS操作也纳入互斥范围。我见过有人只锁了MRAM驱动结果另一个任务在操作SPI Flash时把CS拉错了导致数据写飞。7.3 低功耗模式下的数据保存PIC18LF47K42支持休眠模式休眠前要把MRAM的CS拉高WP拉高避免休眠期间被干扰。如果系统有掉电检测中断在掉电中断里要尽快把关键数据写入MRAM。MRAM写入快几十微秒就能完成比EEPROM的几毫秒快得多这是MRAM在掉电保护场景的另一个优势。掉电中断服务程序里要注意不要调用RTOS的API不要做复杂计算只做最关键的写入。我一般只写一个掉电标志和几个关键参数其他数据在正常运行时定期保存。8. 几个容易被忽略的细节WP引脚在上电初始化完成前要保持低电平写保护等MCU配置好SPI后再拉高。这样可以防止上电瞬间的干扰导致误写。MRAM的CS拉低后第一个SCK上升沿之前SO引脚会输出一个无效字节通常是0xFF或0x00这是芯片内部逻辑决定的。读数据时要把这个字节丢弃。我见过有人直接读结果数据整体偏移了一个字节。如果PCB上MRAM离MCU较远超过10cm建议在SCK和CS上串22Ω电阻减少反射。SDO和SDI可以串33Ω。这是高速数字信号布线的常规做法但在SPI频率不高时容易被忽略。MRAM的DFN封装底部有散热焊盘必须焊接到地平面。不仅为了散热也为了机械强度。我见过因为散热焊盘虚焊导致芯片在振动环境下失效的案例。最后MRAM虽然写入寿命无限但读取次数也是无限的所以不用担心读操作会磨损。这一点和Flash不同Flash的读取虽然不磨损但读取干扰read disturb在极端情况下可能影响数据。MRAM没有这个问题。我在最近一个电力终端项目里用PIC18LF47K42MR25H40CDF的方案替换了原来的SRAM电池方案BOM成本增加了约2元人民币但省掉了电池座、电池、电源切换电路整体成本反而下降了而且可靠性大幅提升。现场运行一年多没有出现过数据丢失。这个方案值得在类似场景里推广。
返回列表