ARTICLE DETAIL

资讯详情

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

MRAM替代EEPROM与Flash:工业嵌入式存储方案实战

MRAM替代EEPROM与Flash:工业嵌入式存储方案实战 做工业控制或者嵌入式数据记录的人一定经历过这种尴尬EEPROM容量小SPI Flash写入前要先擦除一整块频繁写参数又总担心寿命耗尽。之前调一个需要每秒钟记录一次电流值的项目用普通Flash存运行参数算下来十几万次擦写寿命居然撑不了一年最后只能改成分批轮询麻烦得要命。后来换成了磁阻随机存取存储器方案才真正把这件事做痛快了。这套方案的材料就是标题里的两颗芯片一颗是Everspin的MR25H40CDF4Mbit容量、SPI接口的MRAM另一颗是Microchip的PIC18F66K408位低功耗MCU。两者组合用来做工业与嵌入式场景下的数据存储和读取非常顺。这篇文章我会完整复盘一遍这款组合方案的选型逻辑、硬件连接、底层时序、可靠性设计和现场踩坑记录。适合正在搞参数存储、掉电记录、事件日志、黑匣子数据采集的嵌入式工程师参考尤其如果你被EEPROM和Flash的擦除寿命折磨过这篇内容应该能帮你省下不少时间。1. 项目概述与方案选型思路1.1 为什么要用 MR25H40CDF 而非 EEPROM/Flash先直接说结论MRAM最大的价值在于它把RAM的读写速度和ROM的非易失性结合在了一起。MR25H40CDF这颗4Mbit MRAM写入时不需要先擦除数据可以直接覆盖写入也没有写次数明显寿命的概念。它标称的读写次数在10^14次量级这是什么概念我做了一个粗略的账假如一秒钟写一条数据一年约3153万次10^14次意味着理论上可以连续写三千多年。相比之下普通EEPROM写十万次最多几十个小时就磨完了SPI NOR Flash虽然也好一点但页擦除的时间代价和磨损问题始终躲不掉。工业场景里设备往往要跑五到十年参数表每天更新几次事件记录可能频繁写入。用Flash做日志不仅要专门设计磨损均衡算法还要考虑掉电瞬间正好碰上擦写的窗口。MRAM写入周期短到纳秒级而且无擦除过程掉电损坏的数据窗口被压缩到了一个非常小的范围这种特性对现场设备来说比单纯的大容量更有意义。当然MRAM价格比同容量的Flash贵但在工控产品里一颗MRAM换来可靠性这个成本通常是可以接受的。1.2 PIC18F66K40 在这套方案里的角色PIC18F66K40不是一颗多炫的MCU但它在这个方案里非常合适。它属于Microchip的8位Enhanced Mid-range系列内置64KB程序Flash、3.5KB SRAM和256B EEPROM而且带新一代MSSP模块跑SPI主模式很稳。更重要的是这颗芯片的外设引脚支持PPS重映射PCB布线上可以把SPI信号从默认引脚挪到更合理的位置避开功率器件和高压区域这一点在做工业控制板时价值很大。还有几个不能忽略的片内外设BOR和LVD可以做掉电检测这在掉电写日志的场景里尤其重要硬件CRC模块可以快速计算校验值不用软件绕来绕去内部振荡器频率最高可以跑到64MHz给SPI提供灵活的分频时钟源。另外它还保留了256B EEPROM适合放一些启动标志和配置指纹。整个系统不需要外加昂贵器件MRAM和MCU之间的连接就是六根信号线。1.3 这套方案最合适的落地场景以我实际接触过的项目来看MR25H40CDF和PIC18F66K40的组合主要集中在三类应用上。第一类是运行参数和标定数据存储。比如变频器、伺服驱动器、温度控制器的PID系数、电流环参数、校准补偿表这些数据需要长期保存又需要厂家或现场随时改写。MRAM的随机访问特性让“读出一个参数、修改其中一个字节、再写回原位”这样的操作变得非常简单不需要像Flash那样整页搬运。第二类是事件记录和黑匣子数据。比如设备掉电时间、过流报警、传感器故障码、运行状态快照这类数据通常是循环覆盖的。MRAM的高写寿命让日志区不需要复杂的磨损均衡直接按顺序覆盖写就行程序逻辑简化了一大截。第三类是高频周期数据缓存。有些采集系统要求每个控制周期记录几个关键变量MRAM写周期短甚至可以当作掉电不丢失的临时缓冲区用。明白这颗芯片能做什么之后再看硬件和软件怎么落地。2. 硬件设计接口连接与PCB注意事项2.1 SPI接口连接与引脚规划MR25H40CDF是标准SPI接口一共六根有效信号线片选CS、时钟SCK、数据输入SI、数据输出SO、写保护WP和暂停HOLD。连接到PIC18F66K40时典型的对应关系如下。MR25H40CDF引脚功能PIC18F66K40连接说明/CS片选GPIO输出每个读写事务都靠CS拉低开始建议独立引脚控制SCKSPI时钟SPI1时钟引脚最快40MHz工业场景建议先以1MHz起步SI主机输出/从机输入SDO1引脚MCU发出的命令和数据SO从机输出/主机输入SDI1引脚MRAM返回的状态和数据/WP写保护GPIO或直接接VCC低电平时禁止写命令执行/HOLD暂停传输上拉VCC或GPIO控制低电平时挂起SPI通信绝不能悬空很多工程师喜欢把CS和HOLD合成一个或者把WP直接接地这在调试阶段容易踩坑。我的建议是CS和WP尽量都用GPIO控制后面会讲原因。关于PIC18F66K40的引脚映射这颗芯片的外设引脚选择PPS功能非常自由。SPI1的时钟、数据输入、数据输出都可以映射到不同的引脚。选择时要注意避开与ADC复用冲突的引脚尽量选在板子边缘、远离继电器和功率管的位置。具体PPS映射值要对照你手头的PIC18F66K40数据手册和MPLAB Code Configurator生成的配置这里不写死某个引脚号因为不同板子设计差别很大。2.2 供电、去耦和电平匹配MR25H40CDF工作电压范围是2.7V到3.6V一般直接给3.3V。PIC18F66K40如果供电5VMCU侧的SI和SCK输出高电平时可能超过MRAM的输入电压范围必须做电平转换或者用电阻分压。最简单的方案是整个板子统一用3.3V供电这样SPI电平完全兼容少一堆麻烦。如果非要5V系统建议在MCU的SDO1和SCK1输出串联分压电阻SDI1再串联限流电阻而不是只靠一颗电平转换芯片来解决。电源去耦方面MRAM虽然静态电流不大但写入瞬间电流变化还是需要关注的。常规做法是在MRAM的VCC引脚旁边放一颗100nF高频瓷片电容再在板级放一颗10uF钽电容或MLCC。电容要尽量靠近芯片电源引脚过孔直接进地平面不要把去耦电容离得很远否则高频噪声滤不掉。PCB布局上SPI信号线尽量短特别是SCK和数据线不要在板子上绕一大圈。如果MRAM离MCU超过5厘米建议在SCK和SI线路上各串一颗22到33欧姆的端接电阻放在发送端。这个电阻能明显抑制时钟信号的振铃尤其是靠近电机驱动板的场合。还有一点HOLD线如果只是简单上拉走线时尽量远离继电器输出线和PWM驱动线避免噪声耦合导致MRAM误进入暂停状态。2.3 WP与HOLD引脚的正确处理HOLD引脚的处理比较直接必须保证它在通信过程中是高电平。我的做法是HOLD引脚接10kΩ上拉到VCC同时在靠近引脚处加一颗1nF电容到地形成一个简单的低通滤波。这样既保证上电默认状态不会误触发暂停又能滤掉一部分现场噪声。如果非要MCU控制HOLD上电后第一件事就是把对应GPIO输出高电平不要让它悬空。WP引脚则建议用GPIO控制而不是直接焊死接高。平时MCU输出低电平把写保护打开这样即使程序跑飞或者SPI线上有毛刺也不会误写MRAM。真正需要写入参数或日志时先把WP拉高写完再拉低。这个动作的成本只有几行代码但对工业环境下的数据保护是很有意义的。如果实在省引脚把WP直接接VCC也可以只要你对现场电磁环境足够有信心。3. 软件实现读写时序与代码示例3.1 SPI配置与MRAM命令集PIC18F66K40的SPI模块配置不算复杂但有几个参数必须选对。MR25H40CDF支持SPI Mode 0和Mode 3也就是CPOL、CPHA可以两种组合我习惯固定用Mode 0也就是CPOL0、CPHA0时钟空闲为低电平数据在上升沿采样。调试时命令字和手册时序图能直接对上。时钟频率方面芯片标称支持最高40MHz但我在工业项目中很少一上来就跑极限。初始调试阶段建议把SPI时钟设置在1MHz左右等逻辑全部验证通过再逐步提高到4MHz或10MHz。PIC18F66K40运行在64MHz内部振荡器时SPI1BAUD寄存器写15实际SPI时钟就是64MHz除以4再除以16等于1MHz这个值非常稳。后面想提速把SPI1BAUD改成3就能到4MHz。MR25H40CDF的命令集比较简单常用的就这几个命令名称命令字节功能说明WREN0x06写使能必须在该命令后才能在后续写命令中写入数据WRDI0x04写禁用RDSR0x05读状态寄存器bit7是写使能锁存WENWRSR0x01写状态寄存器可用于配置块保护等READ0x03读数据支持连续读出WRITE0x02页写入每次最多128字节这里有一个特别要强调的点MRAM和Flash不同它没有“忙等待”状态。Flash写一个扇区之后需要查询状态寄存器忙位等几毫秒甚至几十毫秒才能进行下一次操作MR25H40CDF的页写命令发出后数据是立即写入的不需要任何擦除等待。很多从Flash转过来的工程师会习惯性去找WIP位结果在MRAM里找不到反而造成困惑。3.2 页写、连续读与核心代码下面这段代码是我在这类项目里常用的MRAM驱动核心逻辑。这里的SPI_Write和SPI_Read是底层SPI收发函数可以用MCC生成也可以自己根据数据手册写。CS和WP的GPIO控制宏按实际板子定义。void MR25_WriteEnable(void) { MR25_WP_GPIO_SetHigh(); // 先解除写保护 MR25_CS_SetLow(); SPI_Write(0x06); // WREN命令 MR25_CS_SetHigh(); } uint8_t MR25_ReadSR(void) { uint8_t sr; MR25_CS_SetLow(); SPI_Write(0x05); // RDSR命令 sr SPI_Read(); MR25_CS_SetHigh(); return sr; } void MR25_WritePage(uint32_t addr, const uint8_t *buf, uint8_t len) { // 注意len最大128且起始地址到页末不能跨128字节页 MR25_WriteEnable(); MR25_CS_SetLow(); SPI_Write(0x02); // WRITE页写命令 SPI_Write((addr 16) 0xFF); SPI_Write((addr 8) 0xFF); SPI_Write(addr 0xFF); while (len--) { SPI_Write(*buf); } MR25_CS_SetHigh(); MR25_WP_GPIO_SetLow(); // 写完重新打开写保护 } void MR25_Read(uint32_t addr, uint8_t *buf, uint32_t len) { MR25_CS_SetLow(); SPI_Write(0x03); // READ命令 SPI_Write((addr 16) 0xFF); SPI_Write((addr 8) 0xFF); SPI_Write(addr 0xFF); while (len--) { *buf SPI_Read(); } MR25_CS_SetHigh(); }读操作比较简单CS拉低发送READ命令再发三字节地址之后每个时钟周期从SO引脚读回一个字节。读数据时可以连续读地址自动递增跨到最大地址后会回卷到0x00000这个和许多SPI Flash类似。页写操作有一个关键细节MR25H40CDF每页是128字节页写命令一次最多写128字节而且如果起始地址在页中间写入的数据到达页末尾后不会自动进入下一页而是会回卷到本页开头把已经写过的数据覆盖掉。这是最常见的翻车点写数据前一定要检查当前地址在页内的偏移量。bool MR25_WriteSafe(uint32_t addr, const uint8_t *buf, uint32_t len) { uint16_t page_remain 128 - (addr 0x7F); if (len page_remain) { MR25_WritePage(addr, buf, (uint8_t)len); return true; } // 拆分写避免跨页回卷 MR25_WritePage(addr, buf, (uint8_t)page_remain); buf page_remain; addr page_remain; len - page_remain; while (len 128) { MR25_WritePage(addr, buf, 128); buf 128; addr 128; len - 128; } if (len 0) { MR25_WritePage(addr, buf, (uint8_t)len); } return true; }在实际项目中我通常把参数区设计成固定长度的记录块比如64字节一条这样一条记录整好落在半个页里读写都不需要考虑分页问题省心很多。3.3 数据组织结构与校验存储区域不能像一张白纸一样随便写尤其是工控设备要面对意外断电、程序升级、参数被篡改等情况。我习惯把MR25H40CDF的4Mbit空间按用途划分区域至少分成参数区和日志区参数区再进一步划分成主区、备份区和厂家区。每条参数记录的结构建议包含校验魔数、版本号、参数长度、参数数据和CRC校验值。伪代码如下不用我细说。参数区写入时先在RAM中计算好CRC再一次性写入MRAM写完立即回读并比对。如果回读比对失败说明写入过程中出了问题可以切到备份区重试并在日志区记一条错误事件。typedef struct { uint16_t magic; // 固定为0xA55A uint8_t version; uint8_t length; uint8_t data[32]; uint16_t crc16; } ParamRecord;PIC18F66K40自带硬件CRC模块可以直接配置成CRC-16-CCITT多项式0x1021把数据按字节灌进去几个周期就能算出结果比纯软件查表法还快。硬件CRC模块用起来不复杂关键是初始值和输出异或参数要和产品定义保持统一不然两边算法对不上。日志区则建议使用定长记录加序列号的设计。每条日志记录64字节头部包含序列号、时间戳、日志类型和长度尾部是CRC16。上电时扫描日志区找到序列号最新的记录作为有效末尾新日志从后面继续写。MRAM写寿命极高日志区不需要Flash那种复杂的磨损均衡直接从低地址往高地址顺序写写满后从起始地址重新开始覆盖就可以。4. 工业应用的可靠性设计要点4.1 掉电保护与BOR联动工业设备最怕的不是正常断电而是突然断电瞬间正好在写数据。PIC18F66K40有Brown-Out Reset和Low-Voltage Detect外设可以把BOR触发电压配置在3.0V左右当电源电压跌到阈值以下时先触发中断而不是立刻复位。掉电中断处理程序建议只做一件最紧急的事把一份关键状态记录写入MRAM的应急区。因为掉电后电源维持时间可能只有几十毫秒程序里不能做任何耗时操作。MRAM的写周期短一个64字节日志块在1MHz SPI时钟下大约需要几百微秒写完绰绰有余。如果是Flash或者EEPROM掉电中断里执行页擦除、充电泵升压这些操作几乎不可能稳定完成这也是我坚持用MRAM做掉电日志的原因。这里有个细节掉电中断里写数据时SPI时钟不要用太高的频率。如果平时是4MHz掉电ISR里可以先临时把SPI1BAUD改大让时钟降到500kHz到1MHz换取更强的抗干扰能力和更稳的通信。主程序跑完写操作后再恢复。4.2 参数分区与磨损均衡策略虽然MRAM寿命极高但产品设计上我仍然建议做简单的主备分区理由不是怕写坏而是为了抗“写了一半断电”的情况。即使MRAM原子性很强万一写入过程中程序刚好被复位数据也可能处于新旧混合状态。比较通用的做法是准备两个参数槽位槽A和槽B。每条记录头部加一个2字节递增计数器。上电时同时读槽A和槽B选择计数更大且CRC校验通过的那个作为当前有效参数。写参数时先读当前有效参数更新内容再写到非有效槽位然后更新计数器。这样即使写新槽时掉电旧槽仍然是可用的完整数据。这种双槽切换逻辑在EEPROM方案里很常见在MRAM上实现反而更简单因为不需要考虑擦除时间。MRAM的高写寿命也让计数器不会很快溢出我算过一秒钟写一次2字节计数器可以撑一百多亿秒远远超过设备寿命。4.3 上电自检与在线CRC设备上电后不要直接使用MRAM里的参数。先做一个完整的自检流程第一步读取MRAM的状态寄存器确认WEN位置正常芯片通信没有异常第二步读取参数区magic和版本号校验CRC第三步如果CRC失败尝试备份区第四步备份区也失败则写入出厂默认参数并记录一条故障日志。这套自检流程在工业设备里非常必要。我见过不少设备因为现场强干扰导致MRAM里的参数个别字节翻转设备没有自检直接用了损坏的PID参数结果机器一上电就猛冲差点酿成事故。MRAM本身有良好的数据保持特性但任何半导体存储都无法完全杜绝异常物理环境下的写入错误所以软件层面的校验始终要保留。在线运行过程中如果参数区允许在线修改可以在每次修改后延迟10毫秒再回读校验一次确认数据稳定写入。对日志区则不需要频繁校验因为日志写坏一条不影响整体运行只要CRC能识别就够了。5. 常见问题与调试实录5.1 读回全 0xFF 或写入无效这个问题出现的频率最高。MR25H40CDF上电后如果CS、SCK、SI、SO四根线没接对读任何地址都会得到全0xFF。遇到这种情况我会按顺序排查先量MRAM的VCC是否为3.3V再量HOLD引脚是否高电平WP引脚是否处于非保护状态然后用示波器或逻辑分析仪抓CS拉低后SCK是否有时钟SI线上是否有正确的命令字节0x03和地址。有一个常见错误是把WP引脚直接接地当普通引脚处理。WP为低电平时所有写命令都被芯片拒绝但读操作正常所以表现为“能读、写不进”。如果遇到能读回老数据、但新数据写不进去的情况十有八九是WP拉低了或者写命令之前没有执行0x06写使能。5.2 写入后读回数据错位如果读回来的数据每位都错位或者第一个字节对、后面全是重复第一个字节通常不是芯片问题而是SPI模式时钟相位配置不对。MR25H40CDF在Mode 0和Mode 3下都能工作但PIC18F66K40的SPI模块配置不同模式时CPOL和CPHA的组合必须和芯片预期一致。我的调试方法是先确认SCK空闲电平Mode 0应该是低电平Mode 3是高电平再看数据采样沿Mode 0在上升沿采样Mode 3在下降沿采样。如果抓到的波形是数据线在SCK下降沿之后才稳定那说明采样沿选错了。还有一种错位是连续读时CS拉高的时机不对。有些从Flash转过来的工程师习惯在连续读N个字节后再读一个垃圾字节把最后一个有效数据“顶”出来这在某些Flash驱动里是必须的但MR25H40CDF的SPI接口没有这个要求。读最后一个字节时CS直接拉高就行多读一个字节反而会把地址推到下一个地址。5.3 页写跨页覆盖问题这个问题是逻辑设计上的不是硬件问题。前面说过MR25H40CDF页写命令的寻址范围限制在128字节页内跨页是回卷而不是跳转到下一页。如果有一个参数表长度是100字节起始地址刚好在页偏移60的位置直接一次写100字节前68字节写到本页末尾剩余32字节就回卷到本页开头把最前面的参数覆盖掉了。解决的办法已经在代码里给出核心就是写之前计算页内剩余空间需要拆分时就拆成多次写事务。我的习惯是所有固定长度参数都设计成64字节或者96字节并且从页边界开始存放这样基本不需要拆页逻辑代码简单很多出错概率也低。5.4 工业现场偶发读写异常如果设备在实验室一切正常一到现场就偶尔出现读写错误大概率是干扰问题。我最先查的是HOLD线因为HOLD一旦被噪声拉低SPI通信会中途暂停MCU还在继续收发字节结果整个数据包错乱。处理方案是把HOLD上拉电阻阻值减小到4.7k同时加1nF电容到地滤高频。其次要查WP线如果WP直接接VCC写保护完全开放任何毛刺都可能触发写入把MRAM里的数据改坏。改成GPIO控制后非写状态保持低电平噪声无法写入数据这个改动在电机驱动现场效果很明显。还有一例是SCK信号过冲引起的误采样。板子上SCK走线较长又没加端接示波器上看上升沿有振荡SPI数据偶尔错位。给SCK和SI各串一颗33欧姆电阻后波形立刻干净了。最后在项目交付前建议把SPI时钟从4MHz降到2MHz再进行一轮长时间拷机因为现场的电磁环境对信号的恶化幅度很难在实验室完全模拟出来。最后再分享一个我个人的习惯。用MRAM这种器件时我会刻意把驱动代码写得像操作内存一样简单直接不要把Flash那套“先擦除、后校验、再等待”的思维带进来。MRAM的价值就是让数据存储这个环节从流程里隐掉工程师只需要关心数据本身这带来的省心程度在我做过的掉电记录项目里体验非常明显。后续如果项目需要更大容量MRAM家族还有更高密度的型号可以无缝替换驱动代码基本不用大改。
返回列表