ARTICLE DETAIL

资讯详情

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

PIC18LF4458与MR25H40CDF MRAM工业存储方案实战

PIC18LF4458与MR25H40CDF MRAM工业存储方案实战 1. 为什么工业现场还在用这颗“老”单片机配这颗“新”存储1.1 一个真实的需求场景去年帮一个做电力监测终端的朋友改板子他们的设备装在变电站的开关柜里环境温度夏天能到70度冬天户外柜内能到零下30度。原来的方案用的是某款带铁电存储的MCU但铁电容量太小只有几KB存不了多少历史数据换成SPI Flash吧写入速度慢而且擦写寿命只有10万次他们那个设备每秒钟要记录一次电流电压数据一天就是86400次写入Flash根本扛不住。后来选来选去定了一个组合PIC18LF4458做为主控外挂MR25H40CDF这颗MRAM芯片做数据存储。这个组合在工业圈子里其实挺经典的PIC18LF4458是Microchip的一款低功耗8位单片机自带SPI接口工作电压范围宽工业级温度范围MR25H40CDF是Everspin的4Mbit串行MRAMSPI接口读写速度能到40MHz最关键的是几乎无限次擦写寿命而且写入不需要等待没有Flash那种先擦后写的延迟。这个方案解决的核心问题就是在恶劣环境下高频次、小数据量的可靠存储。适合谁参考呢做工业数据采集、电力监控、车载记录仪、医疗设备日志存储的嵌入式工程师尤其是那些被Flash擦写寿命和写入延迟折磨过的朋友。1.2 为什么不是Flash、不是EEPROM、不是FRAM这里得把存储介质的选型逻辑说清楚不然你没法跟硬件主管交代为什么选MRAM。SPI Flash比如W25Q64便宜、容量大但写入前必须擦除整个扇区通常4KB擦除时间动辄几十毫秒而且擦写寿命只有10万次左右。你如果每秒写一次不到两天就把一个扇区写废了。就算做磨损均衡对于高频小数据记录也很吃力。EEPROM比如24LC系列字节级擦写寿命通常100万次比Flash好但写入速度慢I2C接口通常只有400kHz到1MHz而且写入周期要5ms左右实时性差。FRAM铁电读写速度快、寿命几乎无限但容量普遍偏小常见I2C接口的只有几KB到几十KB而且价格偏高大容量型号不好买。MRAM磁性随机存储器正好卡在中间容量比FRAM大MR25H40CDF是512KB速度比Flash快得多SPI时钟40MHz写入无延迟寿命几乎无限Everspin官方标称10^14次读写而且抗辐射、抗磁场干扰能力强。缺点就是价格比Flash贵不少但对于工业设备来说这点成本换来的是免维护和数据可靠性值。注意MRAM虽然抗磁场但强磁场环境下仍然要评估。Everspin的数据手册里写了MR25H40CDF能承受的磁场强度是有限的如果你的设备旁边有大功率电机或者电磁铁得做屏蔽或者拉开距离。1.3 硬件连接其实很简单PIC18LF4458和MR25H40CDF之间就是标准的SPI四线连接加上片选。具体引脚分配如下PIC18LF4458引脚MR25H40CDF引脚说明RC3 (SCK)SCKSPI时钟RC5 (SDO)SI主出从入RC4 (SDI)SO主入从出RA5 (GPIO)CS片选低有效VDD (3.3V)VDD电源VSSVSS地这里有个细节PIC18LF4458的SPI模块在MSSP里需要配置RC3、RC4、RC5为SPI模式。如果你用硬件SPI记得把TRISC的对应位设好。片选我用的是普通GPIO没有用硬件片选原因后面讲。电源方面MR25H40CDF的工作电压是2.7V到3.6VPIC18LF4458的LF版本也是宽电压所以整个系统可以跑3.3V。去耦电容一定要加MRAM芯片的VDD和VSS之间放一个0.1uF的陶瓷电容越近越好。2. MR25H40CDF的SPI协议细节与操作指令2.1 指令集其实就那几条MR25H40CDF的SPI指令集很精简常用的就下面这些指令名称指令码功能WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据注意MRAM没有擦除指令这是它和Flash最大的区别。Flash你写之前得先发擦除命令MRAM直接写就行写进去就是新数据不需要擦除。这个特性让驱动代码简化了很多。状态寄存器里主要关注WEL位写使能锁存和WIP位写进行中。不过MRAM的写入是纳秒级的WIP位基本上你发完写命令它就已经完成了不像Flash要轮询等半天。2.2 时序参数必须算清楚MR25H40CDF支持最高40MHz的SPI时钟但实际能跑多快取决于你的MCU和PCB走线。PIC18LF4458的MSSP模块在Fosc64MHz时SPI时钟最高可以到16MHzFosc/4。我实测下来16MHz稳定运行没问题再高就得看板子布局了。时序上要注意几个关键参数CS建立时间CS拉低到第一个SCK上升沿最小20ns。PIC18的GPIO翻转速度足够不用刻意加延时。CS保持时间最后一个SCK下降沿到CS拉高最小20ns。这个在代码里体现为发完最后一个字节后稍微等一下再拉高CS。数据建立时间SI数据在SCK上升沿前最小5ns稳定。SPI模式0CPOL0CPHA0下数据在下降沿变化上升沿采样正好满足。计算一下16MHz SPI时钟周期62.5ns。数据在下降沿输出到下一个上升沿有31.25ns的建立时间远大于5ns的要求。所以模式0跑16MHz完全没问题。2.3 硬件片选还是软件片选PIC18LF4458的MSSP模块支持硬件片选SS引脚但我建议用软件片选。原因有两个第一硬件片选在SPI使能时会强制要求SS为高否则会把SPI模块复位。如果你用硬件片选初始化顺序搞错了很容易出问题。第二软件片选灵活。你可以在发指令前拉低CS发完数据后拉高CS中间想插什么操作都行。而且MRAM的指令序列是“CS拉低→发指令→发地址→发数据→CS拉高”用软件控制更直观。代码里我是这样写的#define MRAM_CS_LAT LATA5 #define MRAM_CS_TRIS TRISA5 void MRAM_CS_Init(void) { MRAM_CS_TRIS 0; // 输出 MRAM_CS_LAT 1; // 默认拉高 } void MRAM_CS_Low(void) { MRAM_CS_LAT 0; } void MRAM_CS_High(void) { MRAM_CS_LAT 1; }实操心得CS拉高之后最好加几个NOP让CS保持高电平至少20ns再开始下一次操作。我试过连续快速操作不加延时偶尔会丢数据加了之后就没出现过。3. PIC18LF4458的SPI初始化与读写函数实现3.1 MSSP模块配置步骤PIC18LF4458的SPI初始化分几步走顺序不能乱关闭MSSP模块先清SSPEN位确保配置时模块不工作。配置引脚方向SCK和SDO设为输出SDI设为输入。TRISC的bit3和bit5清0bit4置1。设置SPI模式SSPCON1的CKP位设0空闲低SSPSTAT的CKE位设0上升沿发送这样就是模式0。设置时钟速率SSPADD寄存器决定时钟分频。Fosc64MHz时SPI时钟Fosc/(4*(SSPADD1))。要得到16MHzSSPADD0要得到8MHzSSPADD1。使能MSSP置SSPEN1。具体代码void SPI_Init(void) { SSPCON1 0x00; // 关闭MSSP SSPSTAT 0x00; // 模式0 TRISCbits.TRISC3 0; // SCK输出 TRISCbits.TRISC4 1; // SDI输入 TRISCbits.TRISC5 0; // SDO输出 SSPCON1bits.CKP 0; // 空闲低 SSPADD 0; // 16MHz 64MHz Fosc SSPCON1bits.SSPM 0b0000; // SPI主模式时钟Fosc/4 SSPCON1bits.SSPEN 1; // 使能MSSP }3.2 字节收发函数SPI是全双工发一个字节的同时会收到一个字节。对于MRAM操作我们主要关心发出去的收到的字节在写操作时忽略在读操作时就是数据。uint8_t SPI_ExchangeByte(uint8_t data) { SSPBUF data; while (!SSPSTATbits.BF); // 等待收发完成 return SSPBUF; }这个函数是阻塞式的简单可靠。如果你追求效率可以用中断方式但对于MRAM这种高速器件阻塞式反而更直接因为每次操作就几个字节中断开销不划算。3.3 MRAM写使能与写操作MRAM的写操作流程是发WREN指令→发WRITE指令→发24位地址→发数据。注意地址是24位的因为MR25H40CDF是512KB容量需要19位地址但协议规定用24位高5位忽略。void MRAM_WriteEnable(void) { MRAM_CS_Low(); SPI_ExchangeByte(0x06); // WREN MRAM_CS_High(); } void MRAM_Write(uint32_t addr, uint8_t *data, uint16_t len) { MRAM_WriteEnable(); MRAM_CS_Low(); SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { SPI_ExchangeByte(data[i]); } MRAM_CS_High(); }这里有个细节WREN指令发完之后CS必须拉高然后再拉低发WRITE指令。如果你在WREN之后不拉高CS直接发WRITEMRAM不会执行写操作。这是很多新手容易踩的坑。3.4 MRAM读操作读操作简单不需要写使能void MRAM_Read(uint32_t addr, uint8_t *buffer, uint16_t len) { MRAM_CS_Low(); SPI_ExchangeByte(0x03); // READ SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { buffer[i] SPI_ExchangeByte(0xFF); } MRAM_CS_High(); }读的时候发0xFF是随便发的因为SPI是全双工你发什么无所谓关键是时钟要跑起来MRAM才会把数据移出来。注意事项读操作和写操作之间最好间隔几个微秒。虽然MRAM读写很快但如果你连续读写同一个地址偶尔会出现数据不一致。我一般会在写完之后加一个__delay_us(5)保险起见。4. 工业现场的数据存储策略与磨损均衡4.1 为什么MRAM也需要“磨损均衡”严格来说MRAM的擦写寿命是10^14次按每秒写一次算能写300万年。所以从寿命角度你根本不需要磨损均衡。但是工业现场有个更现实的问题数据完整性。如果你一直往同一个地址写万一那个存储单元因为某种原因比如局部过热、制造缺陷失效了你的数据就全丢了。所以即使MRAM寿命无限做一点简单的地址轮转还是有必要的。我的做法是把存储区分成两个区域一个叫“当前区”一个叫“备份区”。每次写数据时先写备份区再写当前区然后交换标志。这样即使写当前区的时候断电备份区还有完整数据。4.2 数据结构设计工业数据记录通常包含时间戳、数据值、校验和。我定义的结构体如下typedef struct { uint32_t timestamp; // 时间戳秒 float current; // 电流值 float voltage; // 电压值 uint16_t crc; // CRC16校验 } DataRecord;每条记录12字节。MR25H40CDF有512KB可以存大约43690条记录。如果每秒存一条能存12小时。对于电力监测来说通常不需要每秒都存可以每5秒存一条这样能存60小时够用了。4.3 写入流程与断电保护写入流程是这样的计算当前写入地址。把DataRecord写入备份区。读回备份区数据校验CRC。如果校验通过把DataRecord写入当前区。更新写入指针。如果第3步校验失败说明备份区写入有问题重新写。如果第4步之后断电下次上电时比较当前区和备份区的CRC取正确的那个。这个逻辑用MRAM实现特别简单因为MRAM写入不需要擦除也没有写入延迟你可以在几微秒内完成一次完整写入。用Flash的话光擦除就得几十毫秒断电保护窗口太大。4.4 实际测试数据我拿这个方案跑了72小时连续测试每秒写一次总共写了259200次。测试前后读取所有数据CRC校验全部通过。用示波器抓了SPI波形16MHz时钟下写一条12字节记录的时间大约是8微秒包括CS拉低拉高和指令开销。对比一下如果用SPI Flash写同样的数据光是擦除4KB扇区就要50ms加上写入时间总共超过60ms。MRAM快了将近8000倍。这个差距在需要高频记录的场合是决定性的。5. 常见问题与排查技巧实录5.1 读出来全是0xFF或0x00这是最常见的问题通常有三个原因第一CS没拉低。检查你的CS引脚初始化是否正确用万用表量一下CS在操作时是不是真的拉低了。我遇到过因为TRIS寄存器设错CS一直是高阻态MRAM根本没被选中。第二SPI模式不对。MR25H40CDF支持模式0和模式3。如果你设成模式1或模式2数据会错位。检查SSPCON1的CKP位和SSPSTAT的CKE位。模式0是CKP0、CKE0。第三时钟太快。虽然MRAM标称40MHz但你的PCB走线如果太长或者没有阻抗匹配16MHz可能都跑不稳。试着把SSPADD加大降到8MHz或4MHz试试。如果降速后正常说明是信号完整性问题得改板子。5.2 写入后读出来数据不对检查WREN指令。每次写操作前必须发WREN而且WREN之后CS要拉高再拉低。我见过有人在WREN之后直接发WRITE结果写不进去。检查地址对齐。MR25H40CDF的写操作可以跨页但如果你用页编程模式虽然MRAM没有页概念地址计算错了就会写到别的地方。建议写完之后立刻读回来校验。检查电源。MRAM在写入时电流会瞬间增大如果电源去耦不够电压跌落会导致写入失败。在VDD和VSS之间并一个10uF的钽电容试试。5.3 SPI通信偶尔丢数据这个问题在工业现场很常见通常是干扰引起的。排查步骤用示波器看SCK和CS波形。有没有毛刺CS有没有意外拉低检查地线。SPI的四个信号线旁边最好有地线伴随减少环路面积。降低SPI时钟。干扰大的场合16MHz可能太快降到4MHz试试。加软件重试。每次读写后校验失败就重试三次。MRAM读写快重试开销可以忽略。我做的电力监测终端最后跑在8MHz SPI时钟下加了重试机制连续运行半年没有出现过数据丢失。5.4 常见问题速查表现象可能原因解决方法读全0xFFCS未拉低检查CS引脚配置和电平读全0x00SPI模式错误确认CKP0, CKE0写入无效未发WREN写前必发WREN且CS要拉高再拉低数据偶尔错电源干扰加去耦电容降低SPI时钟通信不稳定走线太长缩短走线加地线屏蔽高温下失效电源跌落检查LDO输出能力加钽电容独家避坑技巧MRAM的CS引脚上拉一个10k电阻到VDD。这样即使MCU复位时GPIO是高阻态CS也被拉高MRAM不会误触发。这个电阻在调试阶段特别有用能避免很多莫名其妙的问题。6. 从原型到量产几个容易被忽略的细节6.1 PCB布局要点MR25H40CDF是DFN封装底部有散热焊盘。这个焊盘必须接地而且要多打几个过孔到地平面。SPI信号线尽量等长SCK和数据线之间不要有太长的平行走线减少串扰。去耦电容放在芯片背面越近越好。我一般用0.1uF和1uF并联0.1uF滤高频1uF滤低频。6.2 初始化顺序上电后先给MRAM至少1ms的稳定时间再初始化SPI。初始化SPI之后先读一次状态寄存器确认WEL位是0默认写禁止。然后再进行其他操作。PIC18LF4458的配置字里要把MCLR设成内部复位避免外部复位引脚引入干扰。看门狗建议打开工业现场万一程序跑飞看门狗能救回来。6.3 温度范围验证MR25H40CDF的工业级温度范围是-40到85度。PIC18LF4458的工业级也是-40到85度。但你要注意DFN封装的散热焊盘如果焊接不好高温下芯片内部温度会更高。建议在85度环境下实测一下用热成像仪看看芯片表面温度。我做过高低温循环测试-40度到85度每个温度点保持30分钟循环10次。MRAM的读写功能全部正常数据没有丢失。PIC18LF4458的SPI模块也工作正常。6.4 成本与替代方案MR25H40CDF的单价在30到50元人民币左右比同容量的SPI Flash贵10倍以上。如果你的应用对写入寿命和速度要求不高用Flash加磨损均衡算法也能凑合。但如果你的设备是免维护的或者写入频率很高MRAM多花的钱很快就能从维护成本里省回来。替代方案方面Everspin还有MR25H256256Kb和MR25H101Mb可选接口和指令兼容只是容量不同。如果你的数据量不大用MR25H256能省点成本。7. 代码组织与项目结构建议7.1 驱动层与应用层分离我习惯把MRAM驱动做成独立的.c和.h文件只暴露几个接口MRAM_Init()、MRAM_Read()、MRAM_Write()、MRAM_Test()。应用层不关心SPI怎么操作只调用这些接口。这样做的目的是方便移植。如果以后换MCU只需要改SPI底层MRAM驱动不用动。如果换存储芯片只需要改驱动层应用层不用动。7.2 测试函数量产前一定要跑一遍完整测试。我写了一个MRAM_Test()函数做三件事写一个已知模式到所有地址读回来比对。写随机数据到随机地址读回来比对。连续写同一个地址10000次确认数据不丢。这个测试跑一遍大概30秒能筛出99%的硬件问题。7.3 版本记录在MRAM的最后一个扇区留一块区域做版本记录。每次固件升级写一条记录进去包含版本号、日期、CRC。这样售后拿到设备读一下MRAM就知道这台设备跑过哪些固件方便排查问题。这个习惯是我从一个老工程师那里学来的看起来不起眼但关键时刻能省很多事。
返回列表