ARTICLE DETAIL

资讯详情

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

工业存储选型与掉电保护实践:MR25H40CDF + PIC18LF45K80 详解

工业存储选型与掉电保护实践:MR25H40CDF + PIC18LF45K80 详解 到了工业现场的数据可靠性存储这块往往是最后才被想起来的需求但恰恰是掉电、重启、日志异常这类问题最后都会指向存储器件不够硬。最近在做的一个项目里我用 Everspin 的 MR25H40CDF4Mbit 串行 MRAM搭配 Microchip 的 PIC18LF45K80把参数存储、实时日志、掉电数据快照都跑通了。这篇文章就把整个选型思路、电路设计、软件读写流程和踩坑记录完整拆开讲。1. 工业存储选型复盘为什么最后选了 MRAM 而不是铁电、Flash 或并行 SRAM1.1 现场工况给存储器件出的三道难题先交代一下项目的实际工况控制器运行环境温度范围要求 -40℃ 到 85℃存在频繁的上下电操作现场有变频器等强干扰源而且系统需要每 50ms 左右更新一次运行状态快照掉电瞬间还要能保存最后的关键数据。这个组合基本淘汰掉了一批常规方案。NOR Flash 首先出局原因不复杂擦写寿命。常规 SPI NOR Flash 的擦写周期在 10 万次量级50ms 更新一次快照意味着一天就是 170 多万次写入就算只更新运行参数不更新快照日志类数据也会迅速逼近寿命上限。而且 Flash 写入前要擦除擦除粒度是扇区通常 4KB写一个 16 字节的参数要连带擦掉一大块从工程角度看又慢又浪费。铁电存储器FRAM比如富士通的 MB85RS256我也认真对比过寿命够、写入快单字节写不需要擦除按字节随机写可以到 10 的 12 次方次。问题出在容量和 SPI 频率上常见的串行 FRAM 容量是 256Kbit 到 1Mbit大容量的型号很难找当时需要的存储规划是运行参数区 8KB、日志环形区 24KB、掉电快照区 4KB还要留出固件升级时的临时暂存区。1Mbit 的 FRAM 只有 128KB勉强够用但扩展空间太小。而且 FRAM 的 SPI 时钟上限普遍在 25MHz 左右和 MRAM 的 40MHz 上限比高速连续采集场景下有差距。带电池的 SRAM 方案我也考虑过但电池在高温下寿命衰减严重工业现场更换电池本身就是维护负担在线更换还涉及数据保持电路的问题最终被我否掉。这时候 MRAM 的优势就很明显了无限次写入寿命实际上 MRAM 的写耐久度大约是 10 的 15 次方量级工程上可以认为是无限、写入无需擦除、掉电数据保持 20 年以上、SPI 接口直接替换型封装、工作温度范围可以覆盖工业级。1.2 MRAM、FRAM、NOR Flash、带电池 SRAM 的对比对比表格先摆出来方便做选型的直接参照对比项目MRAM (MR25H40CDF)FRAMNOR Flash带电池 SRAM写耐久度约 10^15 次工程上视为无限约 10^12 次约 10^5 次无限依赖供电写入方式单字节/页写无需擦除单字节写无需擦除写前必须扇区擦除单字节写写入速度SPI 时钟最高 40MHz通常最高 25MHz擦除慢写入受擦除限制总线直接读写快数据保持20 年以上10 年左右10 年以上依赖电池容量选项256Kbit 到 16Mbit 常见典型 256Kbit 到 1Mbit1Mbit 到 128Mbit视型号而定温度范围工业级 -40 到 105℃ 可选工业级工业级电池受限功耗读写约 50mA 峰值待机低低写时功耗高持续供电待机功耗大主要风险单位成本偏高大容量型号少寿命和擦除粒度问题电池维护与高温隐患从这个表能看出MRAM 的最核心价值就是把非易失和无限写同时做到不用像 Flash 那样做擦写均衡的复杂逻辑也不用像 SRAM 那样担心掉电。单位成本确实比 Flash 高但工业设备通常不以存储颗粒成本为首选指标可靠性优先。1.3 关于 MR25H40CDF 的关键选型细节MR25H40CDF 是 Everspin 的 4Mbit512KB串行 MRAMSPI 接口单电源 3.3V 供电工作电流在读写时大概 50mA 级别待机电流只有几毫安对工业 3.3V 系统很友好。具体型号后缀 CDF 我核对过规格书和批次信息H 代表 3.3V 供电系列40 代表密度 4MbitCDF 封装为 8 脚 DFN宽体 6mm x 5mm。其实还有 SOIC-8 等封装选项但 DFN 占板面积小适合当前板卡密度较高的设计。选型时另外一个容易忽略的点是 MRAM 的字节序。MRAM 内部存储按字节组织没有像 Flash 那样的块结构从第一个字节到最后一个字节可以连续随机访问这让数据区的规划变得非常灵活。读取时也无需先发命令码之外的延迟等待这一点和 Flash/FRAM 的读流程一样但 MRAM 的连续读支持更彻底发完 0x03 命令、24 位地址后可以连续读任意长度的数据SPI 时钟一直拉就行因为 MRAM 没有页边界的概念。对比之下 SPI NOR Flash 有 256 字节页的天然限制跨页读需要重新发地址这在日志读取这种大块数据传输场景下会多出不少冗余操作。2. MR25H40CDF 与 PIC18LF45K80 的硬件连接和电路设计要点2.1 最小系统搭建PIC18LF45K80 这颗 MCU 是 8 位架构里比较少见的带 ECAN 外设的型号工作在 1.8V 到 3.6V配内部 16MHz 振荡器集成了 MSSP 模块可以跑 SPI以及 I2C对于不能直接用大封装芯片的小型控制器来说很实用。因为系统其他外设是 3.3V 逻辑MRAM 和 MCU 共用 3.3V 电源即可不需要电平转换。我和 MR25H40CDF 的连接一共用到了 6 条线/CS片选接 PIC18LF45K80 的 RC2 引脚SS注意这个引脚和 MSSP 的从机选择复用在主机模式下面我们主动控制高低电平。SCK时钟接 RC3SCK。SDI数据输入MRAM 视角接 RC4SDO这是 MCU 的 SPI 数据输出。SDO数据输出MRAM 视角接 RC5SDI这是 MCU 的 SPI 数据输入。/WP写保护接一个可控 GPIO低电平禁止写高电平允许写。/HOLD保持固定拉高不要浮动。这个脚很容易被疏忽如果悬空SPI 通信时可能因为干扰导致 MRAM 进入保持状态时钟和数据被忽略表现为偶发读写失败。另外 MR25H40CDF 的 /WP 和 /HOLD 都是低电平有效内部有弱上拉但为了抗干扰板级上还是加 10K 上拉电阻到 3.3V 更稳妥。电路上还有一些细节值得注意。常规 SPI 通信频率我最终设定在 10MHz。理论上 MRAM 可以跑 40MHz但考虑到 PIC18LF45K80 的 MSSP 在主机模式的时钟源来自 FOSC/4而 FOSC 我用的内部 16MHz那么 SCK 最高就是 4MHz。如果后续要提升速率需要外接更高频的晶振并把 MSSP 配置成适当的分频模式。PIC18LF45K80 的 MSSP 模块在 SPI 主机模式下时钟极性CKP和相位CKE需要和 MRAM 匹配。我直接用模式 0CKP0空闲时 SCK 为低电平CKE1数据在 SCK 上升沿改变、下降沿采样。这个组合和大多数 SPI 存储器件默认推荐的 CPOL0、CPHA0 是对应的不容易出错。2.2 去耦与布局经验工业环境电磁干扰是个绕不开的话题。MRAM 的 VDD 引脚旁边我放了一个 0.1uF 陶瓷电容加一个 10uF 钽电容的组合靠近电源引脚布放走线尽量短粗。同时 MRAM 的电源平面和 MCU 的数字电源之间用电感隔离或者至少做磁珠隔离防止 SPI 高速翻转的电流噪声干扰模拟电路。PCB 布局上我把 MRAM 放在 MCU 附近SPI 走线控制在 20mm 以内并且用地线包围减少相邻信号线的串扰。如果板子空间允许建议在 SCK、SDI、SDO 上各串一个 22Ω 电阻这是经验值可以抑制振铃、改善信号完整性特别是当 SPI 时钟高于 5MHz 时效果比较明显。2.3 读写控制引脚的硬件策略前面提到了 /WP 引脚接 GPIO 可控这里补充一下原因在系统设计里我希望正常运行期间可以写日志但固件升级期间锁定参数区。与其让软件反复改写 MRAM 内部的状态寄存器来实现保护不如硬件上直接由 MCU 的 GPIO 控制 /WP 更直白程序逻辑也简单。这在多任务系统里能避免因为 SPI 总线被其他外设占用导致错误写入。/HOLD 引脚拉高后我特意确认了 MRAM 在 /HOLDLOW 时会冻结内部数据输出并且忽略 SCK 变化这在调试瞬间没有发现明显问题但板级上必须处理不然 DFN 封装引脚间距很小如果该脚走线悬空弱上拉可能不足以抵抗板面脏污或水汽导致的漏电从而误进入保持状态。这是我在一次环境试验中读回数据异常后发现的低级坑后面专门讲。3. 软件实现从 SPI 初始化到可靠的读写时序3.1 SPI 模式与速率配置PIC18LF45K80 的 MSSP 配置我用的是库函数直接操作寄存器的写法因为这样可控性最强。以下是 SPI 主机模式初始化的核心代码// SPI Master Mode 0FOSC16MHzSCKFOSC/44MHz void MRAM_SPI_Init(void) { // 设置 SCK、SDO 为输出SDI 为输入/CS 为输出/WP 为输出 TRISCbits.TRISC3 0; // SCK TRISCbits.TRISC4 0; // SDO TRISCbits.TRISC5 1; // SDI TRISCbits.TRISC2 0; // /CS TRISCbits.TRISC1 0; // /WP // 空闲时 SCK 低电平数据在 SCK 下降沿采样 - Mode 0 SSPCON1 0x2A; // 0b00101010: SSPEN1, CKP0, SSPM0000 (SPI Master, clockFOSC/4) SSPCON1bits.SSPEN 1; SSPSTATbits.CKE 1; // 数据在 SCK 上升沿改变下降沿采样 SSPSTATbits.SMP 0; // 中间采样 }时钟极性配置这一步最容易出错。PIC18LF45K80 里 CKP 位和 CKE 位的组合逻辑有点反直觉对于模式 0CPOL0、CPHA0需要 CKP0 并且 CKE1才能实现数据在 SCK 上升沿改变、下降沿采样。如果按惯性思维直接参考某些示例配置成 CKP0、CKE0那采样点会错半个时钟周期导致读回的数据整体移位这在 SPI 调试中表现非常隐蔽——第一次读写可能正常高低温或走线变长后偶发错误。3.2 状态寄存器与写使能WREN的正确时序MR25H40CDF 和普通的 SPI Flash 类似也有一个状态寄存器STATUS Register最低位 WEL写使能锁存表示是否允许写操作。任何写操作Write Status Register、Write Memory之前都必须先发 WREN0x06命令把 WEL 置 1。这里有一个容易踩坑的时序细节WREN 命令之后必须紧跟着完成写操作期间不能再发其他 SPI 命令否则 WEL 会在任何其他命令完成后被清除。也就是说不能在代码里先发 WREN然后中间插一个读状态寄存器的操作再去写数据那样写会被直接忽略。规范的流程是拉低 /CS。发送 0x06WREN。拉高 /CS。紧接着拉低 /CS。发送 0x02写数据命令、24 位地址、待写数据。拉高 /CS。软件上我封装了一个简单的Write Enable Write Byte函数把 WREN 和实际写命令合并避免中间被别的任务打断。// 向指定地址写入一个字节 void MRAM_WriteByte(uint32_t addr, uint8_t data) { // 1. WREN CS_LOW(); SPI_ExchangeByte(0x06); CS_HIGH(); // 2. 写数据 CS_LOW(); SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr 16) 0xFF); // A23-A16 SPI_ExchangeByte((addr 8) 0xFF); // A15-A8 SPI_ExchangeByte(addr 0xFF); // A7-A0 SPI_ExchangeByte(data); CS_HIGH(); }读操作不需要 WREN直接发 0x03 命令加地址就能连续读。MRAM 没有写周期等待的概念写命令后不需要轮询状态寄存器等待完成可以直接进行下一次操作这对 CPU 资源的节省非常明显。3.3 按字节、按页和连续读的实现差异MR25H40CDF 虽然是按字节随机写但它也支持页写Page Write页大小按照数据手册是 256 字节。页写命令0x02配合页内地址一次最多写 256 字节超过页边界会回卷wraparound这一点需要特别注意如果跨页写数据会写到页起始地址而不是按线性地址继续。因此批量写逻辑里必须自己处理页边界拆分。虽然可以逐字节写规避回卷问题但为了效率我还是实现了页写拆分#define MRAM_PAGE_SIZE 256 void MRAM_WritePage(uint32_t addr, const uint8_t *buf, uint16_t len) { // 处理跨页从 addr 开始到当前页末尾能写的最大长度 while (len 0) { uint16_t page_remain MRAM_PAGE_SIZE - (addr % MRAM_PAGE_SIZE); uint16_t chunk (len page_remain) ? len : page_remain; // WREN CS_LOW(); SPI_ExchangeByte(0x06); CS_HIGH(); // Page Write CS_LOW(); SPI_ExchangeByte(0x02); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i chunk; i) { SPI_ExchangeByte(buf[i]); } CS_HIGH(); addr chunk; buf chunk; len - chunk; } }连续读则不用考虑页边界MRAM 不像 NOR Flash 有读时的页限制可以把整个数据区当做一个线性内存读void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { CS_LOW(); SPI_ExchangeByte(0x03); SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint32_t i 0; i len; i) { buf[i] SPI_ExchangeByte(0x00); } CS_HIGH(); }这个连续读的特性在处理 24KB 日志环形区时会省很多事。3.4 用软件 CRC 做完整包校验工业场景不能假设存储内容永远不会出错。虽然 MRAM 本身非常可靠但 SPI 总线上的干扰、MCU 侧程序跑飞、电源跌落等外部因素仍可能造成数据错误。所以我给每个数据区都设计了 16 字节长的包头包含魔数、数据长度、版本号、CRC32 校验值以及时间戳。CRC32 用软件查表法实现直接嵌入到驱动文件里没有额外依赖。每次读数据后校验 CRC不通过就走错误处理状态机而不是直接把错误数据用于控制逻辑。这样做的成本很低但能显著提高整机在复杂电磁环境下的容错能力。4. 实测踩坑记录一次凌晨的掉电写入丢失排查4.1 现象在 -10℃ 低温箱里日志区出现单字节错误项目快结题时做环境试验低温箱温度降到 -10℃系统执行连续 2000 次掉电重启循环。大概跑到第 1300 次左右上位机下载日志文件时发现有一段日志的时间戳字段出现连续两个字节的异常值。日志本身是环形覆盖写理论上即使掉电时正在写某个字节也只应该影响当前这半条日志不会导致前一段已经写完的日志损坏。第一次排查直接把怀疑对象排在 MRAM 硬件上毕竟低温最容易让存储器件现原形。我用编程器把整个 512KB 空间全部读出来和预期数据比对结果发现损坏的字节几乎都出现在当时正在覆盖写的日志块边缘。这个特征已经很接近SPI 时序在低温下变差导致写入错位或掉电时 /CS 被拉低扔进了半截命令。4.2 排查链路从波形复现到 /HOLD 悬空风险我用示波器同时抓 SCK、SDO、/CS 三条线在掉电瞬间的时序。正常掉电流程是 MCU 检测到 VDD 跌落到阈值进入掉电中断先把 /CS 拉高再让 SPI 总线空闲。但实际波形显示掉电瞬间 /CS 并不是干净利落地拉高而是有一个约 3us 的中间态——电平在 0.8V 到 1.2V 之间抖动。这时候如果 MRAM 正在等待接收命令字节中间态会被解释成一次完整的引脚操作相当于输入了一个随机命令字节。另一个问题是我在用万用表量 /HOLD 引脚电平的时候意外发现低温时该引脚电位并不是稳定的 3.3V而是 2.3V 左右。主要原因是我当时为了省事把 /HOLD 直接悬空依赖 MRAM 的内部弱上拉。手册上写有内部上拉但在低温下内部上拉的阻值漂移比较明显而且 DFN 封装引脚和环氧树脂之间在低温下的漏电流变化足以把该引脚拉低到逻辑阈值附近导致 MRAM 偶发进入保持状态。4.3 根因修复与验证修复动作一共做了两处。第一是在 /HOLD 引脚上增加强上拉10K 电阻直接拉到 3.3V保证任何温度下都是稳定的高电平。第二是改进掉电处理流程MCU 检测到掉电后并不是立刻把 /CS 拉高而是先把所有 SPI 片选输出配置为普通 GPIO 推挽输出并强制输出高电平确保不经过三态转换的中间状态。这实际上是把软件控制 /CS变成GPIO 直连推挽的硬保障。修复后重新做了高低温循环试验-40℃、25℃、85℃ 三个温度点各做 500 次掉电重启日志区没有再出现任何损坏字节。同时我用多台样机连续跑了一周不间断写入每天写入约 2GB 数据量MRAM 依然稳如泰山。这个坑说明MRAM 本身没问题问题往往出在控制时序和板级设计的不规范上。5. 工业级应用加固与后续扩展5.1 数据区划分与伪磨损均衡虽然 MRAM 写入寿命理论上无限但我还是在软件层做了数据区规划避免所有写入集中在同一地址因为你无法预知某一个地址在极端环境下的长期行为主要是考虑到长期高温、alpha 粒子等极端因素对单个 bit 的潜在影响。数据区规划如下参数区0x00000 - 0x01FFF8KB保存设备配置和校准系数用 CRC 保护。日志区0x02000 - 0x07FFF24KB环形缓冲日志包含时间戳、事件类型、数据体。快照区0x08000 - 0x08FFF4KB保存掉电瞬间的关键运行数据双份冗余交替写入。固件暂存区0x09000 - 0x0FFFF28KB给 Bootloader 做固件校验和临时存储。快照区做的是双份交替策略两个地址轮流写入每次都先写新数据再更新有效标记地址。这样万一写入过程中掉电至少上一份完整的快照还在。日志区则是环形覆盖所以老日志会被逐渐冲掉设计时注意这一点。5.2 掉电检测与写入时序的配合PIC18LF45K80 内部有比较器模块和可编程欠压复位BOR功能我把掉电检测阈值设置在 2.7V当 VDD 低于 2.7V 时触发中断。留给我们的掉电处理时间非常有限毕竟电源储能电容不可能无限大一般按 10ms 设计。为了在这 10ms 内稳定写完 4KB 快照我在系统里加了独立的掉电检测中断在中断里做两件事一是把 SPI 时钟切换到最高优先级二是直接把快照区数据用 DMA 方式PIC18LF45K80 没有 DMA实际上是按顺序写写入 MRAM。因为 MRAM 写无需擦除和等待写 4KB 数据在 4MHz SPI 下大约只要 10ms 左右如果是 40MHz 那只需要 1ms完全来得及。这个场景如果用 Flash 就麻烦了掉电时如果赶上扇区擦除几乎是必死无疑。5.3 后续扩展思路MRAM 的可喜特性让我后续把同样的架构用在两个不同项目里一个用于伺服驱动器参数存储另一个用于智能仪表的数据记录模块。每次切换平台只需要调整 SPI 引脚和初始化数据读写逻辑几乎不用改。如果读者用的是 Cortex-M 系列 MCU配合硬件 SPI DMA 还能进一步降低 CPU 占用让日志记录变成后台任务。另外MR25H40CDF 还有配套的 MR25H40CDC-40℃ 到 105℃ 版本等工业宽温型号如果产品需要耐更高温度可以直接替换引脚和驱动完全兼容。6. 关于器件驱动与调试的个人体会最后分享一点实际操作中的小技巧。我在调试 SPI 读写 MRAM 的时候习惯先写一个极小的回环测试固件把所有地址空间按 0xAA、0x55、0x00、0xFF、随机数等固定图案反复写入再读回同时监控状态寄存器。如果这一步能跑 100 万次全对再进入正式业务逻辑后续的排错成本会大幅降低。还有一点经验是用逻辑分析仪而不是示波器来抓 SPI 时序。SPI 这种多线低速总线逻辑分析仪的协议解码功能可以直接标出命令帧、数据帧和错误帧比盯着模拟波形一个个数时钟快得多。我第一次定位 /HOLD 问题时就是靠逻辑分析仪看 CS 低电平期间的 SDO 数据异常发现 MRAM 直接没有响应才反查引脚电平。这套MR25H40CDF PIC18LF45K80的组合我实际用了大半年整体结论是对于需要频繁写入、掉电保持、高可靠性的工业场景MRAM 确实是最省心的存储方案。它不需要复杂的写均衡算法不需要等待擦除完成更不用担心写坏颗粒。只要把硬件引脚的坑避开、软件时序理顺它就能稳定地服务很多年。
返回列表