首页 / 资讯中心 / 文章详情

工业存储选型:用MRAM替代EEPROM和Flash的PIC18F4620方案详解

工业存储选型:用MRAM替代EEPROM和Flash的PIC18F4620方案详解 ★ FEATURED ARTICLE
现场设备第三次因为电池掉电被人叫去维护时我开始认真考虑换掉电池供电的SRAM。最后定下来的方案是两颗芯片存储用 Everspin 的 MR25H40CDF这是一颗 4Mbit 的 SPI 接口 MRAM主控用 Microchip 的 PIC18F4620用内置的 MSSP 外设跑 SPI 总线。这套组合在产线上已经跑了一年多参数、日志、校准数据都没有再丢过。这篇内容就是这份方案的完整复盘从选型原因、硬件连接、驱动细节到调试踩坑适合做工业仪表、运动控制器或车载数据记录的朋友参考也适合那些正在 EEPROM 和 Flash 之间纠结的人。1. 为什么是MRAM工业存储选型的第一课1.1 传统存储为什么在工业现场不够用做嵌入式的人提到存数据脑子里第一反应通常是 EEPROM比如 24Cxx 系列或者 SPI Flash比如 W25Q64。这两类器件在消费类产品里没什么毛病但一放到工业现场问题就接二连三。EEPROM 的寿命标称 100 万次擦写但这通常是在理想温度下的数值。设备如果装在振动电机旁边机箱长期超过 70℃实际磨损会比想象快很多。另一个麻烦是 EEPROM 写一个字节也要等几毫秒如果数据量稍微大一点比如一次存几 KB 的故障波形代码就不得不陷入漫长的轮询等待系统其它任务全被拖住。SPI Flash 容量大、成本低但内部结构是扇区-块-页写数据之前必须先把目标扇区擦成 0xFF。擦除操作很慢几百毫秒一次是常态万一掉电刚好赶在擦除过程中运气差就是整个扇区的旧数据全变成乱码。更麻烦的是 Flash 有擦写寿命上限通常几千到十万次你必须自己做磨损均衡、坏块管理和掉电恢复。我在早期项目里为这事维护过一套 2KB 的标记表每次上电都要全盘扫描最后依然避免不了某些扇区提前报废。1.2 MR25H40CDF 内部到底做了什么MRAM 是磁阻随机存储器的缩写存储单元是一层磁性隧道结MTJ靠自由层和固定层的磁化方向呈现高电阻或低电阻用来表示 0 和 1。它不靠电荷保存数据所以断电后数据不会凭空消失写数据时只需要改变磁性方向不需要像 Flash 那样先擦除再编程也没有高压泵这是它能做到快速写入的关键。MR25H40CDF 就是 Everspin 这颗 4Mbit 的 SPI MRAM。4Mbit 换算下来是 512KB对大多数记录型应用来说是够用的。它支持 SPI Mode 0 和 Mode 3时钟最高可以跑到 20MHz工作电压 2.7V 到 3.6V和 3.3V 系统可以直接对接。芯片体积很小SOIC-8 封装的引脚间距和普通模拟芯片一样贴片生产没有任何额外成本也不会占用多少 PCB 面积。1.3 寿命、速度和功耗对比一张表看明白项目EEPROM24CxxSPI FlashW25Q64MRAMMR25H40CDF电池SRAM写入寿命约100万次擦写约10万次擦写可视为无磨损无磨损写前擦除无必须整扇区擦除无无单字节写时间3-5ms先擦后写整体数百ms与SPI时钟同速与时钟同速掉电数据保持10年以上10年以上不依赖电池没电池就丢坏块管理不需要需要不需要不需要驱动复杂度很低高低低看完这张表我当时的判断很简单只要能接受容量是 512KB、价格比 Flash 略高MRAM 在工业存储里就是最省心的选择。它把擦除、磨损均衡、坏块管理这件事全部拿掉上层驱动只需要做一件事地址来了就写地址来了就读。2. PIC18F4620 的外围搭建硬件连接里的门道2.1 为什么主控选 PIC18F4620PIC18F4620 是一颗 40 脚 8 位 MCU内置 64KB 程序 Flash 和 3.8KB SRAM主频最高可以到 40MHz外设里有 MSSP 模块可以配置成 SPI。对于只做数据采集、存储和通讯的设备这颗芯片足够而且 MPLAB X IDE 和 xc8 编译器都是免费开发工具链不用折腾。相比 STM32PIC18F4620 的引脚更少板上布线简单中断响应也更直观相比某些精简指令集单片机Microchip 的 8 位生态算稳定规格书和参考设计都很齐全。用在工业环境里还有一个隐形优势抗干扰能力经过了很多现场项目的验证电源抖动时不像某些低功耗内核那样容易复位。2.2 最小硬件连接背靠背的四根线SPI 接法非常固定。MR25H40CDF 的 CS 接 PIC18F4620 的一个普通 GPIO我这里用的是 RC2SCK 接 RC3也就是 SCK1SI 接 RC5也就是 SDO1SO 接 RC4也就是 SDI1。注意两个芯片之间的 SI/SO 正好交叉主控的 SDO 输出给 MRAM 的 SIMRAM 的 SO 输出给主控的 SDI。这两根线接反是新手最容易犯的错现象就是写入好像成功但读出来全是乱码。如果板上有多个 SPI 器件CS 用普通 GPIO 控制会比硬件 SS 灵活很多。MSSP 模块的 SS 功能和普通 IO 是复用的你完全可以在软件里决定片选时序。初始化时先把 CS 拉高防止上电瞬间 MRAM 被误选中启动阶段出现意外的写操作。2.3 3.3V还是5V电平匹配这一个问题MR25H40CDF 的供电范围是 2.7V 到 3.6V所以在 5V 系统里不能直接把 VDD 接 5V。我这里用的是标准 PIC18F4620工作电压按 5V 设计存储那边单独用一颗 3.3V LDO 供电。数字信号之间加了一路双向电平转换芯片比如 74LVC4245把 SCK、SDO、CS 从 5V 转到 3.3V再把 MISO 从 3.3V 转回 5V。如果直接把 5V 高电平拿去推 MRAM 的引脚短时间可能不出问题长期可靠性和引脚耐压一定受影响。如果你不想处理电平转换选 PIC18LF4620 这个低压版本整板统一用 3.3V 供电系统会简单很多。不管哪种方案两块芯片的地必须共地否则 SPI 信号没有参考电平高速时会看到各种随机误码而且很难排查。电源去耦是这个项目里最不起眼却最关键的地方。我在 MRAM 的 VDD 和 GND 之间放了 0.1uF 和 10uF 两个电容靠近引脚放置SCK、SI、CS 线上各串了 33Ω 电阻用来抑制振铃。实际用示波器看不串电阻时边沿非常陡过冲能到几百毫伏串了电阻之后波形干净很多EMI 也跟着降下来。3. 驱动编写从底层 SPI 时序到完整读写接口3.1 MSSP 初始化的那些位PIC18F4620 的 MSSP 模块可配置的 SPI 模式中需要先弄清楚两个位CKP 和 CKE。MR25H40CDF 支持 SPI Mode 0也就是空闲时 SCK 为低数据在第一个时钟沿采样。对应到 MSSP 寄存器CKP0、CKE0。下面这段初始化代码在我的项目里直接可用#define MRAM_CS LATCbits.LATC2 #define MRAM_CS_TRIS TRISCbits.TRISC2 #define MRAM_SCK_TRIS TRISCbits.TRISC3 #define MRAM_MISO_TRIS TRISCbits.TRISC4 #define MRAM_MOSI_TRIS TRISCbits.TRISC5 void MRAM_Init(void) { MRAM_CS_TRIS 0; MRAM_SCK_TRIS 0; MRAM_MISO_TRIS 1; MRAM_MOSI_TRIS 0; SSP1CON1 0x20; // SPI Master, Fosc/4, CKP0 SSP1STAT 0xC0; // SMP1, CKE0 MRAM_CS 1; }这里把 SPI 时钟配成 Fosc/4如果主频是 32MHzSPI 就是 8MHz对于大多数工业应用已经足够。SMP1 是让主控在 SCK 末端采样 MISO时序余量更大。要是读回的数据一直是 0xFF八成就是 CKE 或者 SMP 这两个位没配对。3.2 读数据0x03 指令的完整时序读操作不需要 Write Enable只要 CS 拉低发送读命令 0x03再发送 24 位地址然后从 MISO 上取数据每取一个字节需要主控产生 8 个时钟所以发送端要补 0x00。uint8_t MRAM_SPI_Transfer(uint8_t byte) { SSP1BUF byte; while (!SSP1STATbits.BF); return SSP1BUF; } void MRAM_ReadBytes(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_CS 0; MRAM_SPI_Transfer(0x03); MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); while (len--) { *buf MRAM_SPI_Transfer(0x00); } MRAM_CS 1; }MR25H40CDF 是 4Mbit实际有效地址是 512KB也就是 19 位地址。为什么代码里还是发 24 位因为 SPI MRAM 的指令格式就是定长 3 字节地址高位补 0 就好这样驱动将来换成同系列的更大容量芯片协议层不用改。3.3 写数据WREN 先行CS 低到高是命令边界MRAM 写数据不需要擦除但必须先发一条 Write Enable 命令。WREN 命令本身很简单CS 拉低发送 0x06CS 拉高。关键是 CS 必须有一个完整的上升沿芯片才认为这条命令结束。void MRAM_WriteEnable(void) { MRAM_CS 0; MRAM_SPI_Transfer(0x06); MRAM_CS 1; } void MRAM_WriteBytes(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WriteEnable(); MRAM_CS 0; MRAM_SPI_Transfer(0x02); MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); while (len--) { MRAM_SPI_Transfer(*buf); } MRAM_CS 1; }有人会问一次最多连续写多少字节我在驱动里做了一个限制单次写不超过 256 字节。原因是 MRAM 内部地址计数器在到达最高地址后可能会回卷到 0如果一次写太长数据就可能写到不想写的开头地址去。虽然实际使用中很少一次写满 256 字节但驱动层加一个地址越界判断可以避免很多隐蔽问题。3.4 状态寄存器与写保护排错要从这里入手MRAM 的状态寄存器里有一个 WEL 位只有 WREN 命令执行成功之后才会置 1。可以通过 RDSR 命令 0x05 把状态字节读出来判断上次 Write Enable 到底有没有生效。uint8_t MRAM_ReadStatus(void) { uint8_t st; MRAM_CS 0; MRAM_SPI_Transfer(0x05); st MRAM_SPI_Transfer(0x00); MRAM_CS 1; return st; }我的经验是写数据时只要 WREN 后 CS 正常拉高再发 WRITE 命令基本不会出现写不进去的情况。常见问题反而出在硬件保护引脚上如果板子上有外部写保护引脚被拉低0x06 这条命令会被芯片忽略WEL 位一直是 0后面所有写操作都静默失败。排错时先读状态寄存器确认 WEL 是 1 再往下查能省很多时间。4. 工业现场的真实考验抗干扰、掉电与数据完整性4.1 随机掉电测试我做了 2000 次断电选 MRAM 最大的动机不是速度快而是掉电不丢数据。为了验证这一点我做了个比较极端的测试用继电器控制整块板卡电源MCU 上电后立刻往 MRAM 固定地址写一组特征数据写完瞬间把电源切断然后重新上电读回校验。这样连续跑了 2000 次没有出现一次旧数据被改写成随机值的情况。这符合 MRAM 的物理特性写入过程是改变磁性方向如果中途掉电导致电流不够最坏结果只是这个字节没有写进去不会像 Flash 那样在擦除过程中把整个扇区都弄脏。这个差别对现场维护来说意义很大因为它意味着不需要设计复杂的掉电恢复协议。4.2 高低温循环下的一致性我还把板子放进了高低温箱在 -30℃ 到 85℃ 之间做了循环。每个温度点停留半小时写入一批随机数再读回比对连续三个循环都没有发现存储单元本身的数据翻转。这里要说明温度范围要按器件规格来卡MR25H40CDF 的工业级型号能覆盖大部分户外柜体环境。真正会出问题的往往不是存储芯片而是 SPI 线上的信号完整性。温度升高后线缆阻抗和芯片内部驱动能力都会变化所以我在高低温测试时重点盯 MISO 波形保证采样点还有足够余量。4.3 应用层还是得留一道 CRCMRAM 不会坏但 MCU 和 MRAM 之间的 SPI 信号经过连接器和线缆时插拔和振动仍可能让个别 bit 传错。我为每条记录末尾加了 CRC16读出来校验不过就重读一次连续两次失败才报警。很多人觉得有了 MRAM 就不需要校验这是误区。MRAM 保证的是数据在存储单元里不掉、不被磨损但它不能保证 SPI 总线上没有噪声。加 CRC 的成本很低带来的安全感却很高。尤其用长排线连接的时候这条建议值得直接抄进代码。4.4 不需要坏块表这对经验少的人最友好传统 SPI Flash 用久了会出现坏块需要在出厂时扫描并建立坏块表运行期间还要动态更新。MRAM 没有这个问题因为它没有电荷俘获、没有浮栅磨损这些机构写入寿命和读次数都远远超过实际使用需求。这意味着代码里不需要维护复杂的状态位也不需要对某个地址做特殊回避。数据区可以像 SRAM 一样连续分配这对维护老项目的人来说体验是质的提升。5. 调试中我踩过的三个坑解法和复现路径5.1 读回全是 FFSPI 时钟相位没对上第一次接 MR25H40CDF 时我上电后读了状态寄存器返回 0xFF读数据也是 0xFF。第一反应是芯片坏了后来用逻辑分析仪抓波形才发现SCK 的空闲电平和我预期的不一样数据采样点落在了错误的边沿上。这颗芯片支持 Mode 0 和 Mode 3只要硬件接好问题大概率出在 MCU 侧初始化。PIC18F4620 的 MSSP 里CKE 和 CKP 的组合决定了采样边沿。我最后是按 CKE0、CKP0 配置的也就是空闲低电平、第一个边沿采样波形才对。如果你也遇到读回 FF先把这两个位翻一遍比反复换芯片有效得多。5.2 中断把写序列打断了CS 低到高前被拆散另一个很隐蔽的坑出现在启用定时中断之后。跑写函数时CS 已经拉低命令和地址也发送了但数据还没传完高优先级中断进来把 SPI 传输打断了几个毫秒。MRAM 内部状态机等不到完整输入写指令被作废但代码里 CS 直到最后才拉高所以从外部看时序就是拖了很长一条尾巴。解决方式也很简单写操作期间把相关中断关掉或者用临界区保护整段 CS 拉低到拉高的过程。PIC 的 MSSP 在传输过程中被中断不会破坏当前字节但两个字节之间停顿过长SCK 长时间不动芯片就会认为时序异常。这个坑让我意识到MRAM 驱动看起来简单但片选窗口内的操作必须一次性完成。5.3 逻辑分析仪跑一遍指令波形问题立刻现形调试 SPI 最实用的工具是逻辑分析仪不用多贵支持 24MHz 采样率的就够看 8MHz SPI。抓取的信号至少四根CS、SCK、MOSI、MISO。正确波形应该是这样的CS 拉低后SCK 开始输出时钟MOSI 上依次出现 0x03、地址高字节、地址中字节、地址低字节每个字节高位在前字节之间没有长时间停顿MISO 在地址结束后出现数据和 SCK 边沿有正确的建立保持关系。我抓了波形后发现以前的代码少发了一个字节地址整体错位读回来的数据全是乱的。有波形图在手上排错速度能快好几倍。6. 性能与扩展把这颗 4Mbit 的 MRAM 用得更彻底6.1 实测读写吞吐量一个字节和一块数据的差异SPI 配在 8MHz 时批量读 512 字节的时间大约 0.52ms换算下来接近 1MB/s。为什么不是 8MHz 就是 1MB/s因为还要算上命令和地址的额外字节512 字节加 4 字节开销总共 516 字节传输每字节约 1us所以 0.516ms。单字节随机读就慢一些因为每次都要发 4 字节命令和地址再收 1 字节数据总共 5 字节大约 5us换算下来每秒能执行 20 万次左右。写操作多了一条 WREN 命令批量写的吞吐量会略低于读但也足够支撑几十 Hz 的波形采样。如果追求更高性能可以把 SPI 时钟从 Fosc/4 提高到更高分频但要注意 PCB 走线长度和逻辑分析仪层信号质量。6.2 用 MRAM 做环形日志没有擦除的快乐实际项目里我把它的一部分空间做成了环形日志。先划分成若干个 512 字节的数据块每个块头部写序号和 CRC固定地址存最新写位置指针。写入时从头到尾顺序写写满后直接覆盖最旧块不需要擦除也不需要磨损均衡代码逻辑就跟操作普通数组一样。同样的功能如果放在 SPI Flash 上必须先找到可擦除扇区擦除旧块再写新块中间还要防止掉电。MRAM 把这个复杂度直接消灭了这对我这种现场维护任务很重的人来说省下的时间不是一点点。6.3 容量不够怎么办MR25H40CDF 的 512KB 在数据记录场景里不算大。如果未来要扩展到 1Mbit 或者 2Mbit可以看同系列的更高容量型号它们的 SPI 指令格式基本一致驱动层通常只需要改容量宏和地址掩码。如果数据量继续涨到几十 MB那就要考虑 SPI NAND 加掉电保护电容的方案了但那是另一个工程问题。至少在当下512KB 的 MRAM 配合 PIC18F4620 已经覆盖了绝大多数仪表参数、故障录波和运行日志需求而且让存储相关 bug 降到了极低水平。回头再看MR25H40CDF 与 PIC18F4620 这套组合并没有用什么高深技术最大价值是把不可靠因素一个个从系统里拆掉了不用电池了不用擦除管理了不用磨损均衡了剩下的事情自然就简单。设备在现场跑了一年多最让我放心的不是它的速度而是每次开机读到的数据和关机前一模一样。如果你也在为数据丢失折腾我觉得值得先画一块小板子跑通读写再做掉电测试最后再上量场验证。数据手册里的 SPI 时序图建议像读小说一样一行一行过一遍——这块芯片好说话但不会替你把时序错误纠正过来。
阅读完成 · 觉得有帮助?
咨询建站