ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

MRAM与PIC18F4550工业数据存储方案:SPI驱动与可靠性设计

MRAM与PIC18F4550工业数据存储方案:SPI驱动与可靠性设计 1. 为什么MRAM在工业嵌入式场景里越来越受关注做工业控制和嵌入式数据采集的朋友最近几年应该都有一个共同的感受传统EEPROM和NOR Flash在频繁写入场景下越来越不够用了。EEPROM的擦写寿命通常在100万次左右NOR Flash大概10万次而工业现场很多设备要求每秒甚至每毫秒记录一次关键状态一年下来写入次数轻松突破千万级别。这时候MR25H40CDF这颗MRAM磁性随机存储器就进入了视野。MR25H40CDF是Everspin推出的一款4Mbit SPI接口MRAM采用8引脚DFN或SOIC封装工作电压3.3V支持最高40MHz的SPI时钟。它最大的特点就是近乎无限的擦写寿命官方标称10^14次以上和无延迟写入——不需要像Flash那样先擦后写也不需要等待内部写周期完成。这两点对于工业数据记录来说价值非常大。而PIC18F4550是一颗经典的Microchip 8位单片机带全速USB 2.0接口40引脚PDIP封装拥有32KB Flash程序存储器、2KB SRAM、256字节EEPROM内置MSSP模块可以配置为SPI主模式。它虽然不是新片子但在工业仪表、数据采集模块、USB通信设备中存量巨大很多老项目还在持续维护和升级。把MR25H40CDF挂到PIC18F4550的SPI总线上做工业现场的数据存储和读取是一个非常务实的组合。这篇文章主要面向正在做工业嵌入式数据记录、或者手头有PIC18F4550老平台需要扩展非易失存储的工程师。我会从硬件连接、SPI驱动、读写时序、数据管理策略几个层面把整个方案拆开讲清楚包括我在实际项目中踩过的坑和验证过的参数配置。2. 方案整体设计与关键选型考量2.1 为什么选MRAM而不是FRAM或NVSRAM在工业数据存储这个领域可选的非易失方案其实不少EEPROM、NOR Flash、FRAM铁电存储器、NVSRAM带电池的SRAM、MRAM。我当初做选型的时候把这几种都过了一遍最终选MR25H40CDF主要基于以下几个维度的权衡。FRAM的写入寿命也很高10^12到10^14次写入速度也快但FRAM的容量通常偏小常见的是256Kbit到2Mbit而且价格按位算比MRAM贵不少。NVSRAM虽然写入无限快但内部靠电池维持数据工业现场高温环境下电池寿命是个隐患而且封装里带电池的器件在振动场景下可靠性存疑。NOR Flash容量大、便宜但擦写寿命和写入速度是硬伤做频繁数据记录需要配合磨损均衡算法软件复杂度上去了。MR25H40CDF的4Mbit容量512KB对于工业数据记录来说刚好够用——可以存几万条带时间戳的状态记录或者做环形缓冲区存最近的历史数据。SPI接口简单PIC18F4550的MSSP模块直接支持不需要额外的地址总线。最关键的是写入不需要等待SPI发送完数据数据就已经在MRAM单元里了没有内部写周期这对实时性要求高的场景非常友好。2.2 PIC18F4550的SPI资源分配PIC18F4550的MSSP模块可以工作在SPI主模式或从模式。我们这里用主模式时钟由单片机产生。引脚分配如下PIC18F4550引脚功能连接到MR25H40CDFRC3 (SCK)SPI时钟SCKRC5 (SDO)主出从入SIRC4 (SDI)主入从出SORA5 (SS)片选软件控制CSVDD/VSS电源VDD/VSS这里有个细节需要注意PIC18F4550的MSSP模块在SPI主模式下SS引脚RA5可以被配置为普通IO片选由软件控制。我建议一定要用软件片选不要用硬件片选自动控制。原因后面讲时序的时候会详细说。另外PIC18F4550的SPI时钟极性CKP和时钟边沿CKE需要根据MR25H40CDF的时序要求来配置。MR25H40CDF支持SPI模式0CPOL0CPHA0和模式3CPOL1CPHA1。我一般用模式0因为大多数逻辑分析仪和调试工具默认就是模式0抓波形方便。2.3 电源与去耦设计MR25H40CDF的工作电压是2.7V到3.6V典型3.3V。PIC18F4550可以工作在5V或3.3V。如果PIC18F4550用5V供电SPI信号电平就是5V直接连到MRAM的3.3V引脚会超过绝对最大额定值。这时候需要电平转换可以用74LVC245或者简单的电阻分压但电阻分压在高频下波形会变差。我的建议是整个系统统一用3.3V供电PIC18F4550在3.3V下可以跑到20MHz需要配置PLLSPI时钟也能满足MRAM的40MHz上限。这样省掉电平转换硬件简单可靠性高。去耦电容方面MR25H40CDF的VDD引脚旁边必须放一个0.1uF的陶瓷电容越近越好。如果SPI走线超过10cm建议再并一个1uF的电容。工业现场电磁干扰大电源上的毛刺可能导致MRAM写入错误这个电容不能省。3. MR25H40CDF的SPI驱动实现细节3.1 指令集与操作码解析MR25H40CDF的指令集和标准SPI Flash很像但有几个关键区别。先看操作码表指令名称操作码说明WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据SLEEP0xB9进入低功耗模式WAKE0xAB唤醒和Flash最大的区别是MRAM的WRITE指令不需要先擦除。Flash写之前必须发WREN然后发扇区擦除指令等待擦除完成再发WREN再发WRITE。MRAM只需要发WREN然后直接发WRITE指令加地址和数据就行。整个流程简化了很多写入延迟也从毫秒级降到微秒级。状态寄存器的bit0是WEL写使能锁存bit1是BP0bit2是BP1bit7是SRWD。上电后WEL是0任何写操作之前必须先发WREN把WEL置1。写操作完成后WEL自动清零。这个机制和Flash一样是为了防止误写入。3.2 PIC18F4550的SPI初始化代码下面是我在实际项目中用的SPI初始化函数基于XC8编译器void SPI_Init(void) { // 配置SPI引脚 TRISCbits.TRISC3 0; // SCK输出 TRISCbits.TRISC5 0; // SDO输出 TRISCbits.TRISC4 1; // SDI输入 TRISAbits.TRISA5 0; // CS输出 // 片选默认拉高 CS_MRAM 1; // MSSP配置SPI主模式时钟Fosc/16 // SSPCON1: WCOL0, SSPOV0, SSPEN1, CKP0, SSPM0000 SSPCON1 0x20; // 使能SPI模式0时钟Fosc/16 // SSPSTAT: SMP0(中间采样), CKE0(空闲到激活时钟) SSPSTAT 0x00; // 时钟速度计算Fosc48MHz时SPI时钟3MHz // 如果需要更快可以改SSPM为0001(Fosc/4)12MHz }这里解释一下时钟配置。PIC18F4550的SPI时钟由SSPCON1的SSPM位决定SSPM0000Fosc/4SSPM0001Fosc/16SSPM0010Fosc/64SSPM0011TMR2溢出如果系统时钟是48MHzUSB应用常用Fosc/16就是3MHzFosc/4就是12MHz。MR25H40CDF支持40MHz所以12MHz完全没问题。但实际用的时候要考虑PCB走线质量和信号完整性我一般先用3MHz调通再逐步提高到12MHz。3.3 字节读写函数实现SPI发送和接收在PIC18F4550上是同一个寄存器SSPBUF。发送时写SSPBUF接收时读SSPBUF。注意读之前要等BFBuffer Full标志置位否则读到的可能是旧数据。uint8_t SPI_Transfer(uint8_t data) { SSPBUF data; // 写入发送数据 while(!PIR1bits.SSPIF); // 等待传输完成 PIR1bits.SSPIF 0; // 清除中断标志 return SSPBUF; // 返回接收数据 } void MRAM_WriteEnable(void) { CS_MRAM 0; SPI_Transfer(0x06); // WREN指令 CS_MRAM 1; } void MRAM_Write(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WriteEnable(); CS_MRAM 0; SPI_Transfer(0x02); // WRITE指令 SPI_Transfer((addr 16) 0xFF); // 地址高8位 SPI_Transfer((addr 8) 0xFF); // 地址中8位 SPI_Transfer(addr 0xFF); // 地址低8位 for(uint16_t i 0; i len; i) { SPI_Transfer(buf[i]); } CS_MRAM 1; } void MRAM_Read(uint32_t addr, uint8_t *buf, uint16_t len) { CS_MRAM 0; SPI_Transfer(0x03); // READ指令 SPI_Transfer((addr 16) 0xFF); SPI_Transfer((addr 8) 0xFF); SPI_Transfer(addr 0xFF); for(uint16_t i 0; i len; i) { buf[i] SPI_Transfer(0x00); // 发送哑数据接收返回 } CS_MRAM 1; }注意MRAM_Read里发送的是0x00这是哑数据dummy byte因为SPI是全双工读的时候也要有时钟主机发送什么无所谓从机不关心。但有些SPI从机要求哑数据是特定值MR25H40CDF不挑0x00或0xFF都行。3.4 片选时序的关键细节前面提到一定要用软件片选原因在这里。MR25H40CDF的CS引脚有两个作用一是选中芯片二是界定指令边界。每个SPI事务必须以CS拉低开始以CS拉高结束。如果在事务中间CS抖动了一下MRAM会认为指令结束后续数据就丢了。PIC18F4550的硬件SS功能在SPI主模式下如果SSPCON1的SSPM0000且SSEN使能SS引脚会被自动控制。但硬件SS的时序和MRAM要求的CS时序不一定完全匹配而且硬件SS在多从机场景下容易出问题。所以我的做法是把SS功能关掉RA5配置为普通IO手动控制CS。手动控制CS的时候要注意CS拉低之后要等至少10ns再发第一个时钟MRAM的tCSS参数。CS拉高之前要确保最后一个时钟已经完成tCSH至少10ns。在3MHz SPI时钟下一个时钟周期333ns这些纳秒级的延时自然满足。但如果SPI跑到40MHz周期25ns就需要检查一下代码里CS操作的顺序确保没有额外的指令延迟导致时序违规。4. 工业数据存储策略与实操流程4.1 数据帧格式设计工业现场的数据记录不能只存原始字节要有结构。我一般设计一个固定长度的数据帧包含时间戳、设备ID、数据类型、数据载荷和校验。下面是我在一个电机监控项目里用的帧格式字段长度说明帧头2字节0xAA55用于同步时间戳4字节秒级Unix时间戳设备ID1字节区分不同设备数据类型1字节0x01温度0x02电流0x03转速数据载荷8字节浮点数或整型CRC162字节校验帧头到载荷帧尾1字节0x0D一帧总共19字节。512KB的MRAM可以存大约27000帧。如果每秒记录一帧可以存7.5小时。如果每分钟记录一帧可以存18天。实际项目中我会根据需求调整记录频率或者用环形缓冲区覆盖最旧的数据。4.2 环形缓冲区实现环形缓冲区是工业数据记录的经典方案。MRAM的地址空间从0x00000到0x7FFFF512KB。我把它分成两部分前4KB存元数据写指针、读指针、总记录数、设备配置后面508KB存数据帧。元数据区的结构typedef struct { uint32_t write_ptr; // 下一个写入位置相对于数据区起始 uint32_t read_ptr; // 下一个读取位置 uint32_t total_count; // 总写入帧数 uint32_t magic; // 0x4D52414D (MRAM) uint8_t reserved[240]; // 预留 } MRAM_Meta_t;每次写入一帧之前先读元数据计算写入地址写数据帧然后更新write_ptr和total_count再把元数据写回。这里有个关键点元数据的更新必须原子化。如果写完数据帧但元数据没更新下次上电后write_ptr还是旧的新数据会被覆盖。我的做法是元数据存两份地址0x0000和0x0800各一份每份带CRC。写入时先写数据帧再写第一份元数据再写第二份元数据。上电初始化时读两份元数据比较CRC和write_ptr取有效的那份。如果两份都无效说明MRAM是全新的或者数据损坏就初始化元数据。4.3 写入速度实测与优化我用逻辑分析仪抓过实际波形在SPI时钟3MHz下写一帧19字节的时间构成如下CS拉低到第一个时钟约1us指令地址4字节 × 8bit / 3MHz 10.7us数据载荷19字节 × 8bit / 3MHz 50.7usCS拉高到下一个事务约2us元数据更新读写约200us总计约265us。如果SPI时钟提到12MHz数据部分可以缩短到12.7us但元数据更新的时间主要花在SPI事务的固定开销上提升有限。所以瓶颈不在SPI时钟而在元数据管理策略。优化方法不要每写一帧就更新元数据。可以在SRAM里缓存write_ptr每写10帧或者每1秒更新一次元数据。这样元数据更新频率降低10倍整体写入速度提升明显。但代价是掉电时可能丢失最近几帧的元数据不过数据帧本身已经写进MRAM了只是write_ptr没更新下次上电后新数据会被覆盖。如果对数据完整性要求极高可以每帧都更新元数据牺牲速度换可靠性。4.4 读取流程与数据导出读取数据有两种模式实时读取和历史导出。实时读取是设备正常运行时上位机通过USB或串口发送指令单片机从MRAM里读最新几帧返回。历史导出是设备停机维护时把MRAM里的数据全部读出来上传到PC。实时读取的实现比较简单根据read_ptr计算地址读一帧CRC校验通过USB CDC发送。PIC18F4550的USB模块可以配置成CDC类在PC上显示为虚拟串口波特率随便设USB CDC不受波特率限制。历史导出的数据量比较大508KB全部读出来在3MHz SPI下需要约1.4秒508K × 8 / 3M。如果通过USB全速12Mbps传输理论上一秒多能传完。实际测试中加上协议开销和PC端处理大概3到5秒。这个速度对于维护场景完全可以接受。导出的时候要注意读取过程中设备不能写入否则read_ptr和write_ptr会冲突。我的做法是进入导出模式前先停止数据记录任务把write_ptr和read_ptr快照到SRAM然后按快照读取。导出完成后恢复记录任务。5. 常见问题与排查技巧实录5.1 写入后读出来全是0xFF或0x00这是新手最常遇到的问题。原因通常有三个一是WREN指令没发或者发完WREN后CS拉高了WEL位被清除二是地址发错了写到了未使用的区域三是SPI模式配置不对数据采样边沿错了。排查步骤先用逻辑分析仪抓SPI波形看WREN指令0x06后面CS是否保持低电平直到WRITE指令发完。MRAM要求WREN和WRITE可以在同一个CS低电平周期内完成也可以分开。我一般分开做WREN一个CS周期WRITE另一个CS周期。但要注意WREN之后WEL位保持直到下一个WRITE完成或者WRDI。如果波形没问题读一下状态寄存器RDSR0x05看bit0WEL是否为1。如果WEL是0说明WREN没生效。检查CS时序WREN指令发送时CS必须拉低发送完可以拉高WEL会保持。5.2 SPI通信不稳定偶尔读到错误数据工业现场电磁干扰大SPI走线长的时候容易出现位错误。我遇到过的情况是MRAM和单片机在同一块板上但SPI走线经过了电机驱动区域电机启动时SPI数据偶尔出错。解决方法一是降低SPI时钟从12MHz降到3MHz给信号建立更多时间二是在SCK和MOSI上串22欧姆电阻抑制反射三是在MRAM的VDD和VSS之间加0.1uF和1uF电容靠近芯片引脚四是软件上加CRC校验发现错误就重读。CRC校验我用的是一帧一校验读出来先算CRC16和帧尾的CRC比较不匹配就丢弃或者重读。重读最多3次3次都失败就记录错误日志。实际跑下来加了CRC之后数据可靠性提升非常明显误码率从万分之一降到百万分之一以下。5.3 掉电后数据丢失或元数据损坏MRAM本身是非易失的掉电数据不会丢。但元数据更新过程中掉电可能导致write_ptr和实际数据不一致。比如数据帧写完了元数据还没更新掉电后write_ptr指向旧位置下次上电后新数据覆盖了刚写的那帧。解决方案是双份元数据加CRC。写入顺序写数据帧 → 写元数据A → 写元数据B。上电时读A和B如果A的CRC有效且write_ptr B的write_ptr用A否则用B。如果两个都无效用默认值初始化。还有一种情况是掉电时正在写元数据导致元数据A写了一半。这时候A的CRC肯定无效B还是旧的但有效用B恢复。最坏情况是A和B都写了一半但概率极低因为元数据只有几十字节写入时间几十微秒掉电恰好落在两个元数据写入之间的概率很小。5.4 常见问题速查表现象可能原因排查方法解决措施读出来全0xFFCS时序错误逻辑分析仪抓CS和SCK确保CS在事务期间保持低写入不生效WREN未执行读状态寄存器bit0写前先发WREN数据偶尔错误SPI时钟太快降低时钟测试降到3MHz或加CRC元数据损坏掉电时写入读双份元数据双份CRC恢复逻辑通信完全无响应电源或接线测VDD和CS电压检查3.3V和地连接读取速度慢SPI时钟低测SCK频率提高SSPM配置5.5 实操心得先调通再优化我刚开始做这个方案的时候想一步到位把SPI配到12MHz结果波形惨不忍睹数据错误率很高。后来退回到3MHz先把读写逻辑调通确认数据正确再逐步提高时钟。每提高一档就用逻辑分析仪看波形用CRC统计误码率。最后在12MHz下稳定运行但3MHz到12MHz之间试了四五次才找到稳定的配置。另一个心得是MRAM的写入不需要等待但SPI事务之间的间隔不能太短。我试过连续写CS拉高后立刻拉低发下一个指令结果偶尔出错。后来在CS拉高后加了一个NOP延时约1us就稳定了。查手册发现MRAM的tSHSLCS高电平最短时间是10ns理论上不需要延时但实际PCB上有寄生电容CS上升沿需要时间加个小延时更保险。6. 性能优化与扩展思路6.1 SPI时钟提升的边界条件MR25H40CDF标称最高40MHz SPI时钟但实际能跑多快取决于PCB走线、电源质量和单片机SPI输出能力。PIC18F4550在48MHz系统时钟下SPI最高是Fosc/412MHz。如果想更快需要换更高级的单片机比如PIC32系列或者STM32SPI可以跑到40MHz以上。在12MHz下我实测写一帧19字节含元数据更新约120us读一帧约60us。如果去掉元数据更新写一帧只要约30us。这个速度对于大多数工业数据记录场景已经足够了。如果要求更高可以考虑用DMA或者双缓冲但PIC18F4550没有DMA只能靠优化代码。6.2 数据压缩与存储效率508KB的数据区如果每帧19字节能存27000多帧。如果数据有规律可以做压缩。比如温度数据变化缓慢可以只存变化量delta编码每帧从8字节载荷压缩到2字节。这样一帧变成13字节存储量提升30%以上。但压缩会增加CPU负担和代码复杂度。PIC18F4550的指令周期是48MHz/412MIPS做简单的delta编码没问题做复杂的压缩算法就吃力了。我的建议是如果数据变化确实缓慢用简单的差分编码如果数据随机性大不要压缩直接存原始数据可靠性优先。6.3 多设备SPI总线扩展一个PIC18F4550的SPI总线可以挂多个MRAM芯片用不同的CS引脚选择。比如挂4片MR25H40CDF总容量2MB。CS引脚可以用RA5、RB0、RB1、RB2软件控制。但要注意SPI总线的负载效应每增加一个从机SCK和MOSI上的电容就增加高速下波形会变差。如果挂4片以上建议加SPI缓冲器或者用多路复用器。另一个扩展方向是用MRAM做程序存储器的扩展。PIC18F4550只有32KB Flash如果程序超过32KB可以把不常用的函数或数据表放到MRAM里运行时按需读取。但MRAM是SPI接口不能直接执行代码只能做数据存储。如果需要执行代码得用并行接口的MRAM或者带XIP功能的Flash。6.4 低功耗设计考虑工业现场有些设备是电池供电的低功耗很重要。MR25H40CDF支持SLEEP模式0xB9指令进入后电流降到几微安。PIC18F4550也有多种低功耗模式比如Sleep模式电流可以降到0.1uA以下。我的做法是数据记录间隔期间单片机进入SleepMRAM进入SLEEP。定时器或者外部中断唤醒后先发WAKE指令0xAB唤醒MRAM等待tWAK约100us再开始SPI通信。这样平均功耗可以做到毫安级以下用一节18650电池能撑几个月。但要注意MRAM从SLEEP唤醒后需要重新发WREN才能写入。而且SLEEP期间CS必须保持高电平否则MRAM可能被意外唤醒。这些细节在低功耗调试的时候很容易忽略我当初就因为CS在Sleep期间被拉低导致MRAM一直处于唤醒状态功耗降不下来。7. 写在最后的一些个人体会这个方案我从2019年第一次用在电机监控项目上到现在陆续改了三四版中间踩过的坑基本都写在上面了。MR25H40CDF这颗芯片给我的感觉是贵有贵的道理。单片价格比同容量的NOR Flash贵好几倍但在频繁写入、高可靠性要求的场景下省下来的软件复杂度和维护成本远超硬件差价。PIC18F4550虽然老但生态成熟XC8编译器稳定USB功能开箱即用配合MRAM做数据记录模块整体BOM成本可控开发周期短。如果你手头有PIC18F4550的存量项目需要扩展存储或者正在选型工业数据记录方案这个组合值得认真考虑。最后分享一个小技巧MRAM的WRITE指令支持跨页写入不像Flash那样有页边界限制。你可以一次写任意长度只要不超过芯片容量。但实际用的时候我建议单次写入不要超过256字节因为PIC18F4550的SRAM只有2KB缓冲区太大容易和其他任务冲突。256字节的缓冲区加上元数据和栈空间SRAM占用在可控范围内。
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表