ARTICLE DETAIL

资讯详情

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

SPI MRAM工业存储实战:用MR25H40CDF替代EEPROM的完整方案

SPI MRAM工业存储实战:用MR25H40CDF替代EEPROM的完整方案 最近做一批工业采集板的存储方案改造把原来的I2C EEPROM换成了Everspin的MR25H40CDF主控是Microchip的PIC18LF47K42。这颗MRAM是4Mbit、SPI接口的磁性随机存储器最大卖点是写数据不需要擦除、不需要等待掉电后数据也不丢非常适合工业现场做参数备份、事件记录和断电续传。这篇文章把选型思路、硬件连接、驱动代码和现场踩到的坑整体讲一遍做数据记录仪、网关配置存储、设备校准参数备份的朋友可以直接参考。我会先讲为什么这么选再给完整电路和代码最后把我在实际项目中遇到过的时序问题和排查方法列出来。整套逻辑在MPLAB X XC8环境下跑通换其他MCU也很容易移植核心就是一套SPI命令。1. 方案选型MRAM、EEPROM与NOR Flash之间怎么选1.1 三种存储器的本质差异很多工程师一提到掉电存储第一反应就是EEPROM或者NOR Flash。说实话在容量小、写入频率低的场合这两类芯片完全够用但一旦遇到“高频写日志”或“现场维护成本高”这类工业需求它们的短板就会非常明显。我把三种存储器的核心差异拉了一个表方便对照对比维度EEPROMNOR FlashSPI MRAM写入单元字节/页按页/扇区操作字节或连续写写前擦除部分型号需要必须整扇区擦除不需要典型写周期单字节3~5ms页编程几十到几百微秒外加擦除纳秒级锁存无等待轮询擦写寿命10^5~10^6次10^4~10^5次极高规格书标称无限写读速度较快快快接近SRAM读掉电数据保持10年以上10年以上20年高温常温更长常见容量几Kbit到几Mbit几百Kbit到Gbit1~几十Mbit单体密度偏低单位bit成本中低相对高从表里能清楚看到EEPROM的主要问题在写速度和寿命。工业设备如果每秒记录一组数据一组64字节EEPROM单字节写入要3~5ms64字节写满可能要几百毫秒这还没算重复擦写带来的寿命焦虑。NOR Flash虽然吞吐高但必须先擦除再写而且按扇区管理写一条64字节日志要先搬移整个扇区代码复杂度一下就上去了。MRAM的定位很清晰它适合写频繁、数据量不大但要求可靠、掉电不能丢、不允许等待的场合。用个不太严谨但好记的类比EEPROM像一张改字必须先用橡皮擦干净再写的纸NOR Flash像必须整页擦掉再写字的挂图MRAM则像一块随时能写、关灯也不消失的白板。这个特性决定了它在工业嵌入式里最适合做“运行日志、配置参数、掉电续传指针、校准数据”这类任务。1.2 MR25H40CDF 的关键特性MR25H40CDF是Everspin的4Mbit SPI MRAM规格上属于MR25H40系列。4Mbit拆开算就是512K字节工业场景下用来做中小规模数据记录非常合适不会像Flash那样动辄几MB浪费空间也不会像普通EEPROM那样只有几十KB还要省着写。这颗芯片上值得注意的参数有几个容量4Mbit即512KB地址范围0x00000~0x7FFFF。接口标准SPI支持模式0和模式3最高时钟40MHz。供电2.7V~3.6V典型3.3V。写操作不需要擦除没有页编程概念CS上升沿提交数据无忙标志轮询。数据保持工业级后缀宽温范围高温下数据保持20年以上。指令架构兼容常见SPI NOR Flash的READ、WRITE、WREN、RDSR、WRSR指令但不需要擦除指令也没有WIP忙位。指令集这块我多说一句因为很多人第一次接触MRAM会下意识去找Flash的擦除流程但MR25H40CDF的驱动逻辑其实比Flash简单得多核心指令只有五六个。指令名操作码作用WREN0x06写使能锁存WRDI0x04写禁止READ0x03读数据支持连续读WRITE0x02写数据支持连续写RDSR0x05读状态寄存器WRSR0x01写状态寄存器状态寄存器里有个和Flash非常不一样的地方MRAM没有WIP写忙位因为写入在电气上瞬间完成不需要用户去轮询“本次写完了没”。寄存器里常见的是WEL写使能锁存位以及块保护BP位。驱动设计时不要参照Flash去查WIP否则你会发现读回来的状态位对不上手册。1.3 为什么搭配 PIC18LF47K42PIC18LF47K42属于Microchip的K42系列8位MCU里算是外设比较现代的一批。选它搭配MR25H40CDF主要看中的是这么几点第一工作电压范围宽1.8V到5.5V都能跑工业控制板上供电环境往往不干净宽压设计能少很多电源焦虑。配合MRAM时我会采用3.3V供电两者电平直接兼容不需要电平转换电路。第二K42系列带硬件MSSP模块也就是SPI/I2C外设还有外围引脚可选PPS功能SPI信号不一定要固定在某个引脚画PCB时走线可以灵活很多。对做小批量工业板的人来说这个灵活性很实用。第三这代MCU带了硬件CRC、窗口看门狗、内存扫描、掉电检测等适合工业可靠性设计的外设。后面做数据完整性校验、掉电保护逻辑时这些硬件模块能派上用场不用全靠软件硬扛。当然整套方案换成STM32、GD32或者其他带SPI的MCU也完全没问题PIC18LF47K42不是唯一解。它只是在这类工业存储场景里能用一个低成本8位MCU把MRAM的潜力发挥得比较完整。2. 硬件连接与SPI初始化先把链路跑通2.1 最小电路连接与上拉/去耦细节MR25H40CDF标准封装引出CS、SCK、SI、SO、WP、HOLD这些引脚配合PIC18LF47K42时我的连接方式如下MR25H40CDF引脚功能连接到PIC18LF47K42CS片选RC2SCKSPI时钟RC3复用SCK1SI串行输入RC5复用SDO1SO串行输出RC4复用SDI1WP写保护接10kΩ上拉到3.3VHOLD保持接10kΩ上拉到3.3VVDD电源3.3V就近0.1μF去耦电容GND地公共地有两个引脚极其容易被忽略一个是WP一个是HOLD。WP拉低会使芯片进入硬件写保护状态这时候即使发送WREN指令写操作也会被拒绝表现就是“能读、不能写”。HOLD拉低会让芯片暂停SPI通信SO脚输出高阻、忽略SCK和SI输入如果你把HOLD悬空线上一点噪声就可能导致通信中断。所以这两个引脚必须固定上拉到高电平别只依赖内部上拉。CS脚我建议也加一个10kΩ上拉电阻到3.3V目的是防止MCU上电瞬间引脚处于未初始化状态时CS出现低电平毛刺导致MRAM误认为一次通信已经开始。工业现场电磁环境复杂这种上拉成本很低但能省掉很多肉眼难查的偶发问题。电源去耦方面0.1μF电容要尽量靠近VDD引脚。如果MRAM和MCU距离较远或者板上有继电器、电机这类干扰源建议再并联一个4.7μF电容给瞬态电流提供缓冲。MRAM写入时的瞬态电流并不大但这种抗干扰冗余对工业板来说值得做。2.2 SPI模式与MSSP1初始化MR25H40CDF支持SPI模式0和模式3区别在于空闲时SCK电平。模式0空闲SCK为低模式3空闲SCK为高。为了统一起见代码和硬件我都固定用模式0。PIC18LF47K42的SPI由MSSP1外设承载初始化时主要配置主从模式、时钟极性、采样相位和波特率。我建议把SPI时钟先压到1MHz左右调通再根据实际走线情况逐步提高最高不要超过40MHz的芯片规格上限。初始化代码大致如下void MRAM_SPI_Init(void) { // 引脚方向RC2 CS 输出RC3 SCK 输出 // RC5 SDO1 输出RC4 SDI1 输入 TRISCbits.TRISC2 0; TRISCbits.TRISC3 0; TRISCbits.TRISC5 0; TRISCbits.TRISC4 1; ANSELC 0x00; // 关闭RC口模拟功能避免影响数字IO LATCbits.LATC2 1; // CS默认高电平 // 以下寄存器配置按PIC18LF47K42手册MSSP1说明填写 // 如果使用MPLAB Code Configurator也可以直接用它生成。 SSP1CON1 0x00; SSP1STAT 0x00; // SPI模式0配置基础 SSP1CON1bits.SSP1EN 1; SSP1CON1bits.CKP 0; // 空闲时钟为低 SSP1STATbits.CKE 0; // 对应模式0的采样沿 SSP1CON1bits.SSP1MODE 0b0010; // SPI主模式 SSP1ADD 15; // 分频值配合时钟源得到约1MHz SPI时钟 }注意PIC18F-K42系列SPI主模式在SSP1ADD非零时时钟频率公式基本上是FCLK/(4*(SSP1ADD1))。如果你跑64MHz系统时钟SSP1ADD填15算出来就是64MHz/641MHz。如果SSP1ADD填0分频最小能跑到比较高的速率。第一次调板我强烈建议先跑1MHz别急着冲高速。MSSP1的收发是同一个缓冲区所以读写函数是统一的uint8_t MRAM_SPI_Transfer(uint8_t tx) { SSP1BUF tx; while (!PIR1bits.SSP1IF) { // 等待SPI传输完成 } PIR1bits.SSP1IF 0; return SSP1BUF; }读数据时主控通过发送0x00来产生SCK时钟这一点对不熟悉SPI全双工机制的读者要特别点出来在SPI里主设备想要读到从设备数据就必须持续输出时钟哪怕发送的内容没有实际意义。2.3 最容易翻车的CS时序与写使能MRAM操作和EEPROM、Flash一样大多数写指令前必须先发WREN写使能。这个“必须先发”不是随便说说时序上有一个非常容易踩的坑WREN必须作为一个独立SPI帧完成也就是CS拉低、发送0x06、CS拉高。CS上升沿把写使能锁存进芯片接下来才能打开写操作的大门。有人会图省事把WREN和WRITE连续发在同一个CS低电平窗口里结果WEL位没有建立写指令被拒绝读回来的数据还是旧值。我第一次调试时也踩过这个坑逻辑分析仪一抓波形就明白了CS必须有一个完整的“低-高”动作。写数据时的完整时序是CS拉低。发送WREN指令0x06。CS拉高锁存写使能。CS再次拉低。发送WRITE指令0x02。依次发送3字节地址高字节在前。连续发送数据字节。CS拉高提交写入。这里还要注意CS拉高的时机。MRAM在CS上升沿提交数据如果CS拉高瞬间SCK线上正好有一个毛刺或者数据线还没有稳定最后一位数据可能被错误采样。稳妥的做法是在最后一个字节的SCK沿结束后稍微保持几十到几百纳秒再拉高CS。速度要求不高时可以直接在软件里插入几个NOP或者拉高前做一个短暂延时。3. 驱动代码与工业级封装3.1 基础读写与状态寄存器操作先把最底层的驱动函数给全。状态寄存器的读写是排查问题时的关键工具尤其是WEL写使能位。#define MRAM_CS_L() LATCbits.LATC2 0 #define MRAM_CS_H() LATCbits.LATC2 1 // 读状态寄存器 uint8_t MRAM_ReadSR(void) { uint8_t sr; MRAM_CS_L(); MRAM_SPI_Transfer(0x05); // RDSR sr MRAM_SPI_Transfer(0x00); MRAM_CS_H(); return sr; } // 写状态寄存器 void MRAM_WriteSR(uint8_t sr) { MRAM_CS_L(); MRAM_SPI_Transfer(0x06); // WREN MRAM_CS_H(); MRAM_CS_L(); MRAM_SPI_Transfer(0x01); // WRSR MRAM_SPI_Transfer(sr); MRAM_CS_H(); }读回来状态寄存器后bit0是WEL写使能锁存位。如果执行完WREN后读WEL发现始终是0基本可以判定CS时序有问题或者WREN指令之后CS没有正确拉高。这里再次强调MRAM没有Flash那种WIP忙标志不要在代码里死等一个“写忙状态”否则逻辑会卡死。3.2 写数据连续写入与地址边界写数据函数是最常用的一块。MR25H40CDF支持连续写CS保持低电平期间地址会自动递增。和NOR Flash不一样的是它没有页缓冲和页大小限制你不必在跨页时把数据拆成好几段这是MRAM做日志型应用时特别舒服的一点。void MRAM_WriteBytes(uint32_t addr, const uint8_t *p, uint32_t len) { // 地址边界保护4Mbit 0x80000字节 if (addr 0x80000UL) { return; } if ((addr len) 0x80000UL) { len 0x80000UL - addr; } MRAM_CS_L(); MRAM_SPI_Transfer(0x06); // WREN MRAM_CS_H(); MRAM_CS_L(); MRAM_SPI_Transfer(0x02); // WRITE MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); while (len--) { MRAM_SPI_Transfer(*p); } MRAM_CS_H(); }地址是按高字节到低字节顺序发送的这一点和很多人的习惯相反。我见过有同事把地址按低、中、高顺序发结果数据全部写到“看似随机”的位置检查了半天才发现是地址字节顺序搞反了。另一个容易忽略的点是连续写跨过芯片末尾时会发生地址回卷。比如从0x7FFF0开始写32字节在容量512KB的MRAM上会有一部分落到0开头的位置这种错误比地址顺序反了更难查。所以上面的代码做了地址边界截断如果超出末尾就只写还能容纳的长度宁可不写也不能回卷乱写。实际项目中应用层还应该再加一层越界判断驱动层这个截断只是兜底。3.3 读数据随时读、连续读读数据不需要写使能也不需要擦除只要地址正确随时可以读。void MRAM_ReadBytes(uint32_t addr, uint8_t *p, uint32_t len) { if (addr 0x80000UL) { return; } if ((addr len) 0x80000UL) { len 0x80000UL - addr; } MRAM_CS_L(); MRAM_SPI_Transfer(0x03); // READ MRAM_SPI_Transfer((addr 16) 0xFF); MRAM_SPI_Transfer((addr 8) 0xFF); MRAM_SPI_Transfer(addr 0xFF); while (len--) { *p MRAM_SPI_Transfer(0x00); } MRAM_CS_H(); }读函数的底层就是不断交换字节主控每次发送0x00给MRAM提供时钟同时从SO脚收数据。整个过程没有任何等待也不需要查询状态这一点比EEPROM读要快很多体感上和直接读SRAM没有区别。如果要在轮询或中断环境下使用可以把MRAM_ReadBytes和MRAM_WriteBytes做成带状态机的中断版本。但基础版本先把功能跑通后续再优化性能不迟。3.4 工业应用建议带协议和校验的读写封装裸读写MRAM在实验室里跑没问题放到工业现场就必须加一层协议封装。原因很简单MRAM本身再可靠也防不住通信线干扰、地址越界、逻辑错误导致的数据错乱。我实际使用的数据帧格式大概是这样的// 一条记录 // [0xA5] [0x5A] [类型] [长度] [数据...] [CRC16低] [CRC16高]帧头用0xA5、0x5A是为了在扫描数据时能快速找到记录边界。类型字段区分参数记录、日志记录、运行状态记录。长度字段记录有效数据长度读取时先确认长度合理再继续读。最后加两个字节CRC16校验或者直接上PIC18LF47K42的硬件CRC模块把所有字节算一遍再比对。这个封装对工业场景的意义在于如果某条记录因为掉电或其他原因没写完整读取端扫描时会发现帧头对不上或者CRC不对然后跳过这条记录继续找下一条而不是把垃圾数据当成有效值用。实际项目里我还会在固定地址保存一个“当前有效记录序号”每次写完一条记录后更新这个序号。MCU重启后先读序号直接从上次末尾继续写不会把历史数据覆盖掉。4. 工业场景可靠性设计与问题排查实录4.1 掉电保护与数据完整性工业设备最怕的就是写数据写到一半突然掉电然后重开机发现配置全丢了。MRAM对比EEPROM和Flash在这个环节有天然优势但也不能完全裸奔。先说芯片层面。MR25H40CDF的写提交发生在CS上升沿没有Flash那种“整页编程进行中掉电导致整页报废”的问题。如果一个多字节写操作还没走到CS上升沿就掉电最多是这一帧数据没提交成功已经提交过的历史数据完全不受影响。从这一点来看MRAM的掉电鲁棒性比Flash好一个数量级。但从系统层面看还是要做两道保险。第一道是数据帧校验。每条记录都带帧头、长度、CRC开机时按地址扫描发现CRC不对就判定为“写了一半的坏记录”直接跳过。工业现场不需要写操作做到“原子级完美”只要能把坏记录识别出来并丢弃系统就能自愈。第二道是双槽位指针技术。对于“当前写入位置”这种关键信息不要只存一份开两个固定地址轮流写。比如第一次写槽A第二次写槽B第三次写回槽A。读取时比较两份指针选最新的那一份如果掉电发生在写指针过程中至少还有上一份是完整的。PIC18LF47K42本身有BOR掉电检测模块和外部中断引脚可以做到检测到电压跌落时立即把紧急参数写入MRAM再停机。但要注意从检测到掉电到主控完全失去供电通常只有几毫秒所以紧急写操作的代码要精简到极致只写最关键的几十个字节别在掉电流程里做复杂计算。4.2 块保护与WP引脚的实际用法前面讲过WP引脚要上拉那是为了不误触发保护。反过来在工业应用里我们也可以主动利用保护功能通过WRSR设置状态寄存器里的BP0、BP1块保护位把存放核心系统参数的存储区设置为只读防止应用程序跑飞时乱写覆盖。具体思路是这样把MRAM地址空间划分成两段比如高16KB存设备配置和引导参数低496KB做日志区。设置块保护位把高16KB区域锁住。正常系统运行时日志区随便写。修改设备配置时先解除保护写入新配置再第一时间把保护恢复。这个做法的好处是即便单片机固件因为电磁干扰复位、程序跑飞也不可能把配置区冲掉因为硬件保护位挡在那里。代价只是每次修改配置要执行一次WRSR这个频率一般很低完全可接受。有一个细节要记住启用了块保护之后向保护区域写数据是不会成功的而且不会报错只是数据写不进去。排查问题时如果出现“某个区域能读不能写”第一件事就是查状态寄存器里的BP位和WP引脚电平而不是怀疑芯片坏了。4.3 常见问题排查速查表我在实际调试MR25H40CDF和PIC18LF47K42时整理过一张排查表很多问题其实都是同一个根源。现象可能原因排查与解决写后读回全是0xFF未成功写使能检查WREN后CS是否拉高检查WEL位状态写后读回全是0x00数据线接反或SCK极性错误检查SI/SO连接尝试切换SPI模式0/3能读不能写WP引脚被拉低、块保护开启测WP电平读状态寄存器查BP位只有第一个字节对后面全错地址字节顺序错误、长度参数错误确认地址按高、中、低字节发送偶发数据错乱HOLD悬空、电源毛刺、SCK/CS间距过近HOLD上拉加强去耦降低SPI速率WEL位始终为0WREN时序不完整用逻辑分析仪看CS波形确认低-高完整这里想单独展开一下SPI速率导致的偶发错乱。MR25H40CDF标称最高40MHz看起来很快但实际板上能不能跑满取决于走线长度、过孔数量、MCU驱动能力、SCK和CS的串扰等多种因素。我调试时遇到过在10MHz下稳定、一升到20MHz就开始随机多字节错乱的情况。把SCK速率降回10MHz抓波形确认采样点不在数据翻转附近问题就消失了。所以首次调通后先别急着追求高时钟成本最低的做法就是降速排查。4.4 实测速度与量产测试建议我在这套组合上做过一次简单实测PIC18LF47K42跑8MHz SPI时钟连续写64字节日志数据从第1个字节发送到CS拉高整个耗时大约在几十微秒级别读回64字节再做CRC校验也基本是同样量级。对比原来用的I2C EEPROM写64字节要分多次写光等待时间就得几百毫秒。MRAM带来的体验提升是代差级别的那些原本需要用SRAM缓冲后再批量搬运的数据现在完全可以考虑“直接写MRAM”代码结构还能更简单。量产测试阶段我建议给每块板子加一个“全片写读回环”用例。具体做法是用伪随机数填充整个512KB地址空间调用MRAM_WriteBytes全片写入然后全片读回逐字节比对。PIC18LF47K42在1MHz SPI时钟下全片写读也就一两秒时间作为出厂测试项完全可接受。这个用例能一次性覆盖地址线、数据线、CS控制、SPI时序、焊接质量等多个环节比只测固定地址可靠得多。最后补充一点个人看法MR25H40CDF这类SPI MRAM设计思路上不应该拿它当Flash用它更像一块“永不失忆的SRAM”。驱动初始化、写使能、读写命令要按SPI存储器的规矩来但上层应用完全可以用内存的思维去读写它随时改、随时存、不需要考虑擦除对齐、不需要考虑磨损均衡。这样用才能把它的价值发挥到最大。如果你首次点亮这颗芯片先把SPI速率降到1MHz跑一个连续512KB的写读回环确认整片地址都能随机读写再提速度、做掉电测试你会发现后面排查问题的难度会小很多。
返回列表