ARTICLE DETAIL

资讯详情

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

工业嵌入式MRAM存储方案:MR25H40CDF与PIC18LF45K50实战

工业嵌入式MRAM存储方案:MR25H40CDF与PIC18LF45K50实战 1. 为什么 MRAM 在工业嵌入式场景里越来越香搞过工业数据采集或者电力终端的朋友应该都有体会选存储芯片这件事很多时候比选主控还让人头疼。EEPROM 写入慢、擦写寿命有限FRAM 容量小价格高NOR Flash 有擦除块限制还得做磨损均衡铁电和 SRAM电池的方案又要考虑电池寿命和环保问题。我前两年做一个配电终端的项目要求每秒记录一次关键参数掉电不能丢环境温度从零下四十度到零上八十五度还要连续跑十年以上。当时试了好几种方案最后锁定了 MRAM 这条路线用的就是MR25H40CDF这颗 4Mb 的磁性随机存储器主控搭配PIC18LF45K50通过SPI接口通信。MRAM 这东西全称是 Magnetoresistive Random Access Memory磁阻随机存储器。它的存储单元用的是磁性隧道结靠电子自旋方向来存数据而不是电荷。这个原理上的差异带来了几个非常实在的好处写入速度是纳秒级的跟 SRAM 差不多擦写寿命几乎无限官方标称 10^14 次以上掉电数据不丢不需要备用电池宽温范围轻松覆盖工业级甚至车规级。你可以把它理解成一个披着存储器外衣的 SRAM读写行为跟普通内存一样随意但断电之后数据还在。MR25H40CDF 的具体规格是这样的容量 4Mb也就是 512K 字节组织方式是 512K × 8 位。供电电压 2.7V 到 3.6VSPI 接口最高支持 40MHz 时钟。封装是 8 引脚的 DFN 或者 SOIC占板面积很小。工作温度范围有工业级-40°C 到 85°C和更宽的版本可选。它支持 SPI 模式 0 和模式 3指令集兼容标准 SPI Flash 的操作码比如 0x03 读、0x02 写、0x06 写使能、0x05 读状态寄存器等。这意味着如果你之前用过 SPI Flash迁移过来基本不需要改硬件软件上把擦除相关的逻辑去掉就行。PIC18LF45K50 是 Microchip 的一款 8 位单片机44 引脚32KB Flash2KB RAM1KB EEPROM。它最大的特点是低功耗LF 版本工作电压 1.8V 到 3.6V非常适合电池供电或者对功耗敏感的工业节点。它自带一个 MSSP 模块可以配置成 SPI 主模式硬件片选可以用普通的 GPIO 来控制。我选这个组合的原因很简单PIC18 系列在工业现场存量巨大开发工具成熟代码移植方便MR25H40CDF 的 SPI 接口跟它对接几乎零障碍而且 3.3V 供电可以直接匹配不需要电平转换。这套方案适合什么人参考呢如果你在做工业数据记录仪、智能电表、PLC 扩展模块、医疗设备参数存储、车载黑匣子这类需要高频写入、掉电保护、长寿命、宽温运行的嵌入式项目那这篇文章的内容应该能帮你少走不少弯路。即使你用的是 STM32 或者其他平台SPI 操作 MRAM 的核心逻辑是一样的改改底层驱动就能用。2. 硬件设计从原理图到 PCB 的实操细节2.1 引脚连接与信号完整性MR25H40CDF 的引脚定义很标准1 脚是片选 CS2 脚是 SPI 时钟 SCK3 脚是数据输入 SI4 脚是地 VSS5 脚是数据输出 SO6 脚是时钟 SCK 的另一个位置有些封装会重复7 脚是写保护 WP8 脚是电源 VDD。实际接线的时候CS 接 PIC18LF45K50 的任意一个 GPIO我一般用 RC2 或者 RA5方便布局。SCK、SI、SO 分别接 MSSP 模块的 SCK、SDO、SDI 引脚。WP 引脚如果不用硬件写保护直接上拉到 VDD 就行但我在工业项目里建议还是接到一个 GPIO 上万一程序跑飞了可以硬件锁死防止误写。这里有个细节很多人会忽略MRAM 的 SPI 时序跟 Flash 很像但它没有页编程的概念也不需要擦除。你发写命令之后数据是直接写进去的每个字节写完就可以立刻读回来验证。这意味着你不需要像操作 Flash 那样先擦后写也不需要考虑擦除块边界对齐的问题。但要注意写操作期间 CS 必须保持低电平直到整个写序列完成中间不能拉高否则写入会中断。PCB 布局方面SPI 信号线尽量等长SCK 走线不要有太多过孔。如果时钟跑到 20MHz 以上建议在 SCK 和 SO 上串 22 欧姆到 33 欧姆的电阻抑制反射。MRAM 芯片的电源引脚旁边放一个 0.1uF 的陶瓷电容再并一个 1uF 的钽电容或者 MLCC位置越靠近芯片越好。地平面要完整不要被其他信号割裂。我遇到过一个问题MRAM 和一颗 DC-DC 芯片共用 3.3V 电源DC-DC 的开关噪声耦合到了 SPI 总线上导致偶尔读出来的数据错一位。后来在 MRAM 的 VDD 前面加了一个磁珠和 10uF 电容组成的 LC 滤波问题就解决了。2.2 电源与去耦策略PIC18LF45K50 和 MR25H40CDF 都是 3.3V 供电看起来很简单但工业现场的电源质量往往很差。我建议在电源入口处放一个 TVS 管防止浪涌。然后经过一个 LDO 稳压到 3.3V再给数字部分供电。LDO 的选型要注意压差和静态电流如果项目是电池供电选静态电流几个微安的型号比如 MCP1700 系列。MRAM 的写入电流比待机电流大不少虽然平均功耗很低但瞬间电流可能达到十几毫安LDO 的瞬态响应要够快否则电压跌落可能导致写入失败。去耦电容的配置我一般是这样每个电源引脚配一个 0.1uF芯片整体再配一个 4.7uF 或者 10uF。如果板子上有多个 SPI 从设备每个从设备都要独立去耦不要共用。还有一点MRAM 的 WP 引脚和 CS 引脚在上电初始化之前状态不确定如果这时候主控的 GPIO 是浮空的可能会产生意外的写操作。所以我在 CS 和 WP 上各加了一个 10K 的上拉电阻确保上电默认是高电平芯片处于未选中状态。2.3 与 PIC18LF45K50 的硬件 SPI 配置PIC18LF45K50 的 MSSP 模块配置成 SPI 主模式需要设置几个寄存器。SSPCON1 的 SSPEN 位使能模块CKP 位选择时钟极性SSPCON1 的 SSPM3:SSPM0 设置为 0000 表示主模式时钟由 Fosc/4 分频或者 0001 表示 Fosc/16等等。我一般用 Fosc/4 先跑低速调试稳定之后再提高。SSPSTAT 的 SMP 位决定采样相位CKE 位决定时钟边沿。MR25H40CDF 支持模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我习惯用模式 0配置起来简单CKP0CKE0SMP0。实际配置的时候如果系统时钟是 16MHzFosc/4 就是 4MHz 的 SPI 时钟对于 MRAM 来说完全没问题它支持到 40MHz。但如果你用长排线连接或者板子上有干扰建议先降到 1MHz 调试确认读写正常之后再逐步提高。我试过在 20MHz 时钟下用 10 厘米的杜邦线连接误码率很高换成短线或者降低时钟就正常了。所以 SPI 时钟不是越高越好要看实际布线。3. 软件驱动从底层字节读写到数据管理3.1 SPI 底层读写函数先写最基础的字节收发函数。PIC18 的 MSSP 模块发送一个字节的过程是把数据写入 SSPBUF然后等待 BF 位SSPSTAT 的 bit0被置位表示接收完成然后读走 SSPBUF。注意这里有个坑PIC18 的 SPI 是全双工的发送和接收同时进行。如果你只想发一个字节读回来的数据是对方同时发过来的可能是无效数据直接忽略就行。但如果你要读一个字节必须先发一个哑字节比如 0xFF来产生时钟然后读 SSPBUF。// SPI 字节交换函数 uint8_t SPI_ExchangeByte(uint8_t data) { SSPBUF data; // 写入发送缓冲 while (!PIR1bits.SSPIF); // 等待传输完成 PIR1bits.SSPIF 0; // 清除中断标志 return SSPBUF; // 返回接收到的数据 }这个函数是阻塞式的简单可靠。如果你用中断或者 DMA逻辑会复杂一些但对于 MRAM 这种高速器件阻塞式完全够用。注意 SSPIF 标志的清除时机有些编译器或者库会自动处理但手动清除更稳妥。3.2 MRAM 读写的完整时序MR25H40CDF 的读操作时序是这样的拉低 CS发送读命令 0x03然后发送 3 个字节的地址24 位地址因为 512K 字节需要 19 位地址高位补零接着就可以连续读取数据了。每发一个时钟数据就从 SO 引脚移出。地址会自动递增所以你可以一次读很多字节不需要每次都发地址。读完之后拉高 CS。写操作稍微复杂一点拉低 CS发送写使能命令 0x06拉高 CS。然后再拉低 CS发送写命令 0x02发送 3 字节地址然后发送要写入的数据。数据在时钟的上升沿被采样写入 MRAM 单元。写完之后拉高 CS写入生效。注意写使能命令是独立的每次写之前都要发一次。如果你连续写多个字节只要 CS 保持低电平地址会自动递增不需要重复发写使能。// 读取 MRAM 数据 void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { CS_LOW(); SPI_ExchangeByte(0x03); // 读命令 SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { buf[i] SPI_ExchangeByte(0xFF); } CS_HIGH(); } // 写入 MRAM 数据 void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { CS_LOW(); SPI_ExchangeByte(0x06); // 写使能 CS_HIGH(); 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 len; i) { SPI_ExchangeByte(buf[i]); } CS_HIGH(); }这里有个细节写使能命令之后必须拉高 CS 再拉低这是 MRAM 内部状态机的要求。如果你连续发 0x06 和 0x02 而不拉高 CS写使能可能不生效。我一开始没注意这个调试了半天发现写不进去后来看时序图才明白。3.3 状态寄存器与写保护MR25H40CDF 有一个状态寄存器可以通过 0x05 命令读取。状态寄存器的 bit0 是 WIPWrite In Progress但 MRAM 的写入是纳秒级的实际上你读的时候 WIP 几乎永远是 0不像 Flash 那样需要等待。bit1 是 WELWrite Enable Latch发过写使能之后这个位会置 1写完自动清零。bit7 是 SRWDStatus Register Write Disable配合 WP 引脚可以实现硬件写保护。如果你把 WP 引脚拉低并且 SRWD 位是 0那么状态寄存器可以被写入但存储阵列仍然可以写。如果 SRWD 位是 1 且 WP 引脚拉低那么状态寄存器和存储阵列都被硬件保护任何写操作都会被忽略。这个功能在工业场景很有用设备出厂前把 WP 拉低并设置 SRWD现场就无法篡改关键参数了。需要修改的时候用主控的一个 GPIO 把 WP 拉高写完再拉低。3.4 数据组织与掉电保护策略MRAM 虽然掉电不丢数据但如果你在写一个多字节的数据结构时突然掉电可能会写到一半导致数据不一致。比如你要存一个 32 位的累计电量值写到第 3 个字节时断电了读出来就是一个错误的值。解决这个问题有几种常用方法。第一种是双备份加校验。把数据存两份每份后面跟一个 CRC 校验值。读的时候先读第一份CRC 对就用第一份不对再读第二份。写的时候先写第二份再写第一份。这样即使写第一份时断电第二份还是完整的。缺点是占用双倍空间但 MRAM 有 512K对于大多数参数存储来说绰绰有余。第二种是日志式存储。把每次写入追加到日志区每条记录带序号和 CRC。读的时候找序号最大且 CRC 正确的记录。这种方法适合高频写入的场景比如每秒记录一次数据。MRAM 的擦写寿命几乎无限你不用担心写坏但日志区需要循环覆盖要管理好写指针。第三种是原子操作模拟。用一个标志位来标记写入状态写数据之前先把标志位置为忙写完数据再把标志位改为空闲。读的时候如果发现标志位是忙说明上次写入可能没完成数据不可信需要用备份恢复。这个方法需要 MRAM 支持位操作实际上你可以把标志位单独放在一个字节里写标志位和写数据是两个独立的 SPI 事务中间断电的概率很小但不是零。我实际项目里用的是第一种加第三种结合关键参数双备份加 CRC同时用一个状态字节标记写入过程。实测下来在每秒写入一次、连续运行半年的情况下没有出现过数据丢失或错误。4. 性能实测与优化经验4.1 读写速度实测数据我用 PIC18LF45K50 在 16MHz 系统时钟下SPI 时钟配置为 Fosc/4 即 4MHz实测 MRAM 的连续读写速度。读操作发送命令和地址需要 4 个字节的时间然后每个字节数据需要 8 个时钟。在 4MHz 时钟下每个字节 2 微秒加上命令开销读 1KB 数据大约需要 2.1 毫秒。写操作因为要发写使能多了一个 CS 拉高拉低的过程写 1KB 大约需要 2.3 毫秒。如果把 SPI 时钟提高到 16MHzFosc/1读 1KB 可以降到 0.6 毫秒左右。对比一下 EEPROM典型的 24C512 在 400kHz I2C 下写 1KB 需要几十毫秒而且有 5 毫秒的写周期等待。MRAM 的速度优势非常明显。对比 FRAMFM25V05 在 20MHz SPI 下速度跟 MRAM 差不多但容量只有 512Kb价格还更贵。所以在大容量、高频写入的场景MRAM 的性价比很高。4.2 功耗表现与低功耗设计PIC18LF45K50 的低功耗模式配合 MRAM 可以做到非常省电。MRAM 的待机电流典型值只有几十微安读写电流在 10 毫安左右。如果你每秒只写一次每次写 32 字节写入时间大约 70 微秒平均电流增加可以忽略不计。我实测过主控进入 Sleep 模式MRAM 保持供电但 CS 拉高整个系统待机电流大约 5 微安。定时器唤醒后写一次数据然后继续睡平均电流不到 10 微安。用一颗 2000mAh 的锂亚电池理论寿命可以超过 20 年。这里有个省电技巧MRAM 没有写周期等待写完立刻可以进入低功耗模式不需要像 EEPROM 那样等 5 毫秒。所以你的固件可以写完之后马上让主控休眠不用空转等待。另外SPI 时钟在空闲时保持低电平或者高电平都行MRAM 不挑但为了省电建议配置成空闲低电平。4.3 常见问题与排查技巧问题一写入后读出来全是 0xFF 或者 0x00。首先检查 CS 时序确保写使能命令之后 CS 有拉高再拉低。其次检查 WP 引脚是不是被拉低了如果 WP 低且 SRWD 置位写入会被忽略。最后检查电源电压低于 2.7V 时 MRAM 可能工作不正常。问题二偶尔读出错位数据。大概率是 SPI 时序问题。降低 SPI 时钟试试如果降速后正常说明布线或者信号完整性有问题。检查 SCK 和 SO 上是否有过冲或者振铃必要时串电阻。另外PIC18 的 SPI 在高速下采样相位可能需要注意SMP 位设置不对会导致采样点偏移。问题三多字节写入时只有第一个字节生效。检查 CS 是否在写入过程中被意外拉高。有些 GPIO 操作库会在每次字节发送后自动操作 CS如果你用了这种库要改成手动控制 CS。另外地址递增是芯片内部自动完成的你不需要手动改地址但如果你在循环里重新发命令和地址就会覆盖前面的数据。问题四系统复位后数据丢失。MRAM 掉电不丢数据但如果你的复位电路有问题比如复位期间 CS 引脚浮空导致误写数据就可能被破坏。确保 CS 和 WP 有上拉电阻复位期间保持高电平。另外PIC18 的 MCLR 引脚要接好复位电路避免反复复位。问题五长时间运行后数据出错。检查电源纹波MRAM 对电源噪声比较敏感。用示波器看 VDD 引脚纹波应该小于 50mV。如果纹波大增加去耦电容或者换 LDO。另外检查是否有其他 SPI 设备跟 MRAM 共用总线如果其他设备的 CS 控制不当可能会干扰 MRAM 的通信。4.4 与 Flash 方案的对比与选型建议特性MR25H40CDF (MRAM)W25Q64 (SPI Flash)24C512 (EEPROM)容量4Mb (512KB)64Mb (8MB)512Kb (64KB)接口SPI 40MHzSPI 104MHzI2C 400kHz写入速度纳秒级无等待毫秒级需擦除毫秒级有写周期擦写寿命10^14 次10^5 次10^6 次掉电保持20 年以上20 年40 年写入功耗低无擦除高擦除耗电中等价格较高低低适用场景高频写入、关键数据大容量、低频写入小容量、参数存储选型逻辑很简单如果你需要频繁写入每秒几次到几千次、掉电不能丢、环境温度宽、寿命要求长选 MRAM。如果你只是存个配置参数、固件备份、日志文件写入频率很低那 Flash 更划算。EEPROM 适合存几十到几百字节的校准参数写入次数不多的情况。我一般建议客户关键运行数据用 MRAM历史日志用 Flash出厂参数用 EEPROM各取所长。5. 工业现场部署的实战心得5.1 电磁兼容与抗干扰措施工业现场的电磁环境很恶劣变频器、继电器、接触器都在旁边SPI 总线很容易受到干扰。我在一个钢铁厂的项目里就遇到过MRAM 数据偶尔会跳变查了很久发现是旁边一台大功率变频器启停时产生的干扰。后来做了几件事SPI 信号线改用双绞线SCK 和 GND 绞在一起SI 和 GND 绞在一起MRAM 的电源加了两级滤波第一级是共模电感第二级是 LC 滤波PCB 上 MRAM 下面铺完整地平面信号线包地处理。改完之后连续运行三个月没有再出现数据错误。另外SPI 的 CS 信号在长距离传输时容易受到干扰导致误选中。如果 MRAM 离主控比较远超过 10 厘米建议在 CS 上加一个 RC 滤波比如 1K 电阻加 100pF 电容时间常数 100 纳秒不影响正常通信但能滤掉窄脉冲干扰。5.2 数据可靠性的多重保障除了前面说的双备份加 CRC我还建议在 MRAM 里划出一块区域做健康记录。每次系统启动时往健康记录区写一条启动日志包括启动原因、时间戳、复位次数。这样如果设备在现场出问题拿回来一读就知道是掉电重启还是看门狗复位。MRAM 容量大写这些日志毫无压力。还有一个技巧把关键数据的地址分散开不要连续存放。比如参数 A 存在 0x00000参数 B 存在 0x10000参数 C 存在 0x20000。这样即使某个地址区域出现物理损伤虽然 MRAM 概率极低也不会影响所有数据。同时分散存储也能减少地址译码电路同时翻转的概率降低功耗和噪声。5.3 固件升级与数据迁移产品出厂后可能需要升级固件新固件的数据结构可能跟旧版本不一样。这时候 MRAM 里的数据需要迁移。我的做法是在 MRAM 的固定位置存一个版本号固件启动时先读版本号如果跟当前固件不匹配就执行迁移程序。迁移程序把旧格式的数据读出来转换成新格式写到新的地址区域然后更新版本号。整个过程是原子操作先写新区域校验通过后再更新版本号最后擦除旧区域MRAM 不需要擦除直接标记为无效即可。这个机制我用了好几个项目升级过三次数据结构现场设备没有丢过数据。关键点是版本号要放在最可靠的位置并且迁移程序要有回滚机制万一迁移过程中断电下次启动还能重新迁移。5.4 生产测试与工装设计批量生产的时候每台设备都要测试 MRAM 的读写功能。我设计了一个简单的工装用 PIC18LF45K50 的串口跟电脑通信电脑发命令单片机执行 MRAM 的读写测试返回结果。测试内容包括全片写 0x55 读回校验、全片写 0xAA 读回校验、地址线测试写特定图案到不同地址、数据线测试写 0x01、0x02、0x04 等单比特图案。整个测试过程大约 10 秒可以覆盖 99% 的硬件缺陷。工装设计要注意测试夹具的接触电阻要小SPI 线要短电源要稳定。我见过因为夹具接触不良导致误判的情况后来改用弹簧针加镀金焊盘问题就没了。另外测试程序要记录每台设备的测试结果方便追溯。6. 从这套方案延伸出去的几个方向这套 MRAM PIC18 的组合本质上是一个高可靠数据存储节点的参考设计。你可以根据具体需求做很多变形。比如把 PIC18 换成 STM32L4 系列低功耗性能更强SPI 速度更快适合处理更复杂的数据协议。或者把 MRAM 换成容量更大的 MR2A16A4Mb 并口或者 MR4A16B8Mb 并口适合需要更高带宽的场景。如果项目要求无线传输可以在 PIC18 上加一个 LoRa 或者 NB-IoT 模块MRAM 作为本地缓存网络恢复后把数据补传上去。软件层面你可以把前面说的日志式存储做成一个通用的小型文件系统支持按时间索引、循环覆盖、坏块管理虽然 MRAM 几乎不会坏。我目前正在整理一个轻量级的 MRAM 驱动库把读写、校验、日志、迁移这些功能封装成 API方便在不同项目里复用。等整理好了可以开源出来有兴趣的可以关注后续。最后分享一个我在实际调试中总结的小技巧每次修改 MRAM 驱动代码之后先做一个全片写-读-校验的回归测试确保底层读写没问题再测试上层的数据管理逻辑。这样可以快速定位问题是出在驱动层还是应用层。我踩过的坑是有一次改了 SPI 时钟分频忘了同步修改延时函数导致读写时序错乱数据全错。后来加了回归测试这种低级错误就再也没犯过。
返回列表