ARTICLE DETAIL

资讯详情

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

MRAM与PIC18F96J65的SPI存储方案:工业掉电保护与高频写入实践

MRAM与PIC18F96J65的SPI存储方案:工业掉电保护与高频写入实践 1. 项目缘起与方案选型思考工业现场的数据存储有个很尴尬的处境用 EEPROM 吧写入速度慢、擦写寿命也就百万次级别高频采集场景下没几个月就写废了用 SRAM 加后备电池吧电池在高温高湿环境下撑不了几年换电池还得停机用 NOR Flash 吧写之前要擦除整个扇区掉电瞬间还可能丢数据。我前两年做一个工业数据采集终端的项目就卡在这个存储环节上后来接触到 MRAM 才算是找到了一个比较舒服的解法。这次要聊的这套组合主角是MR25H40CDF这颗 4Mbit 的磁阻随机存储器MRAM搭配PIC18F96J65这颗带以太网控制器的 8 位单片机通过SPI总线完成数据的存储和读取。说白了就是拿 MRAM 当数据缓冲区或者日志存储介质用 PIC18F96J65 做主控靠 SPI 把两边连起来。这套方案特别适合工业仪表、数据记录仪、嵌入式黑匣子这类需要频繁写入、掉电不能丢数据、又不想伺候电池的场景。MR25H40CDF 这颗芯片有几个特性值得先摆出来因为它直接决定了整个方案的架构。它是 512K×8 的组织结构也就是 4Mbit 容量SPI 接口最高能跑到 40MHz读写都是字节级操作不需要擦除写入次数官方标称 10^14 次以上数据保持时间超过 20 年。这几个数字放在一起意味着什么意味着你可以把它当成一个永远不会坏的 SRAM来用写一个字节就是一个字节没有擦除周期没有写放大掉电了数据还在。对于工业场景里那种每秒写几十次、连续跑几年的需求这个特性是决定性的。PIC18F96J65 这边它是 Microchip PIC18 家族里比较特殊的一颗带 10/100M 以太网 MAC 和 PHY96KB Flash3936 字节 SRAM64 引脚 TQFP 封装工作电压 2.0V 到 3.6V。它有两个 MSSP 模块都能配置成 SPI 主模式这给硬件设计留了余地。选它做这个方案的主控一方面是它的 SPI 外设成熟稳定另一方面是它自带以太网方便把存储的数据通过网口传出去省掉一颗外部的以太网控制器。为什么不用 STM32 或者 ESP32 来做这个事这问题我被问过很多次。STM32 的 SPI 当然也能驱动 MR25H40CDFESP32 更不在话下。但工业现场有个现实问题很多老设备、老产线的控制板就是 PIC 架构工程师熟悉的是 MPLAB 和 XC8 编译器换平台意味着重新做认证、重新培训、重新写驱动。PIC18F96J65 在这个生态里是个稳妥的选择而且它的宽温版本能到 -40°C 到 85°C工业级应用够用。所以这套组合不是性能最优解而是工程上最省心的解。提示MRAM 虽然叫磁阻随机存储器但它和传统的磁性存储比如硬盘完全不是一回事。它是利用磁隧道结的电阻变化来存储数据属于非易失性半导体存储器没有机械运动部件抗震动、抗冲击能力很强这一点在工业现场很重要。2. 硬件连接与 SPI 总线设计要点2.1 引脚连接与信号完整性MR25H40CDF 是 8 引脚 SOIC 封装引脚定义很简洁CS片选、SO数据输出、SI数据输入、SCK时钟、VDD、VSS加上 WP写保护和 HOLD保持。实际接线的时候CS 接 PIC18F96J65 的一个普通 IO 或者 SPI 的 SS 引脚SO 接单片机的 SDISI 接单片机的 SDOSCK 接 SCK。WP 和 HOLD 如果不用的话直接拉到 VDD别悬空悬空容易受干扰导致误写。这里有个细节很多人会忽略MR25H40CDF 的 SPI 模式支持 Mode 0CPOL0CPHA0和 Mode 3CPOL1CPHA1。PIC18F96J65 的 MSSP 模块配置成 SPI 主模式时默认是 Mode 0但你可以通过 SSPCON1 寄存器的 CKP 位来调整时钟极性。我一般用 Mode 0因为示波器上看波形最直观SCK 空闲低电平数据在上升沿采样。如果你用 Mode 3SCK 空闲高电平数据在下降沿采样两种都能用关键是两边要一致。PCB 布线这块SPI 虽然不像 USB 或者以太网那么娇气但在 40MHz 时钟下也不能太随意。我的经验是SCK 走线尽量短最好不超过 5 厘米SCK 和 SO/SI 之间不要平行走太长距离容易串扰CS 线如果比较长可以在单片机端加一个 22Ω 到 33Ω 的串联电阻抑制过冲。电源去耦方面MR25H40CDF 的 VDD 引脚旁边放一个 0.1μF 的陶瓷电容离引脚越近越好再并一个 1μF 的钽电容做低频滤波。2.2 片选方式的选择硬件片选还是软件片选PIC18F96J65 的 MSSP 模块支持硬件片选但实际用下来我更推荐用普通 IO 做软件片选。原因有几个第一硬件片选在 SPI 使能的时候会自动拉低但如果你总线上挂了多个 SPI 从设备硬件片选的管理会比较麻烦第二软件片选你可以精确控制 CS 的拉低和拉高时机对于 MR25H40CDF 这种需要 CS 下降沿启动命令、上升沿结束写入的芯片来说时序控制更灵活第三调试的时候你可以用逻辑分析仪清楚地看到 CS 的每一次翻转排查问题方便。软件片选的操作逻辑很简单拉低 CS发送命令字节发送地址读写数据拉高 CS。注意 MR25H40CDF 的 CS 拉高之后芯片内部需要一点时间来完成写入操作这个时间叫 tWR典型值是 10 毫秒左右。如果你连续写多个字节可以在一次 CS 拉低期间连续写写完再拉高这样只需要等一次 tWR。但如果你写完一个字节就拉高 CS再写下一个字节又拉低那每个字节都要等 10 毫秒效率会非常低。注意MR25H40CDF 的写操作是在 CS 上升沿之后才真正开始内部写入的。如果你在 CS 还低的时候就去读状态寄存器读到的可能是旧数据。正确的做法是拉高 CS延时 tWR然后再发命令读状态或者读数据。2.3 电源与电平匹配PIC18F96J65 的工作电压是 2.0V 到 3.6VMR25H40CDF 的工作电压是 2.7V 到 3.6V。如果你用 3.3V 供电两边可以直接连不需要电平转换。但如果你用 2.5V 给单片机供电那就要注意了MR25H40CDF 在 2.5V 下可能工作不正常因为它的最低工作电压是 2.7V。这种情况下要么把单片机也提到 3.3V要么加电平转换芯片。我一般建议整个系统统一用 3.3V 供电这样最省事。电源的纹波要控制在 50mV 以内MRAM 虽然抗干扰能力比 Flash 强但电源太脏的话写入过程中如果电压跌落超过阈值还是可能导致写入失败。可以在电源入口加一个 LC 滤波L 用 10μH 的功率电感C 用 10μF 的陶瓷电容效果很明显。3. SPI 通信协议与 MRAM 操作命令详解3.1 MR25H40CDF 的命令集MR25H40CDF 的命令集不复杂常用的就那么几条。我整理了一个表格方便你对照着看命令名称命令码十六进制功能说明地址字节数数据字节数WREN0x06写使能00WRDI0x04写禁止00RDSR0x05读状态寄存器01WRSR0x01写状态寄存器01READ0x03读数据31 到 NWRITE0x02写数据31 到 N状态寄存器只有最低两位有意义bit0 是 WEL写使能锁存bit1 是 BP0块保护。WEL 位在每次 WREN 命令之后置 1在 WRITE 命令执行完之后自动清零。BP0 位用来保护上半部分或者整个存储区不被写入具体保护范围看数据手册的表格。这里有个坑要注意MR25H40CDF 的地址是 19 位因为 512K 字节需要 19 根地址线。但 SPI 一次传 8 位所以地址要分 3 个字节发第一个字节的高 5 位是无效的实际有效的地址位是 A18 到 A0。很多人第一次用的时候会搞错把地址当成 24 位发结果读写位置全错。正确的做法是地址的高 3 位放在第一个字节的低 3 位中间 8 位放在第二个字节低 8 位放在第三个字节。3.2 读操作的时序与实现读操作是最简单的流程是拉低 CS发送 0x03发送 3 字节地址然后就可以连续读数据了。MR25H40CDF 支持地址自动递增所以你读了一个字节之后地址会自动加 1不需要每次都发新地址。如果你想读 100 个字节就连续发 100 个时钟每个时钟读一个字节。用 PIC18F96J65 的 MSSP 模块实现读操作代码大概长这样unsigned char MRAM_ReadByte(unsigned long addr) { unsigned char data; CS 0; // 拉低片选 SSPBUF 0x03; // 发送读命令 while(!SSPIF); // 等待发送完成 SSPIF 0; SSPBUF (addr 16) 0x07; // 发送地址高3位 while(!SSPIF); SSPIF 0; SSPBUF (addr 8) 0xFF; // 发送地址中8位 while(!SSPIF); SSPIF 0; SSPBUF addr 0xFF; // 发送地址低8位 while(!SSPIF); SSPIF 0; SSPBUF 0x00; // 发送空字节产生时钟 while(!SSPIF); SSPIF 0; data SSPBUF; // 读取数据 CS 1; // 拉高片选 return data; }这段代码里有个细节发送地址之后需要再发一个空字节来产生时钟才能把数据读进来。因为 SPI 是全双工的你发一个字节出去同时也会收一个字节进来。发空字节的目的是产生 8 个时钟脉冲让从设备把数据推出来。3.3 写操作的时序与注意事项写操作比读操作多了一步先发 WREN 命令再发 WRITE 命令。WREN 命令的作用是把状态寄存器的 WEL 位置 1相当于解锁写操作。如果你不发 WREN 直接发 WRITE数据是写不进去的芯片会忽略这次写操作。写操作的流程是拉低 CS发送 0x06WREN拉高 CS再拉低 CS发送 0x02WRITE发送 3 字节地址发送数据拉高 CS延时 tWR典型 10ms等待内部写入完成。这里有个效率问题如果你要写 100 个字节可以一次性发完然后等一次 tWR。但如果你写一个字节就拉高 CS再写下一个字节又拉低 CS那每个字节都要等 10ms100 个字节就是 1 秒太慢了。所以正确的做法是在一次 CS 拉低期间连续写多个字节地址自动递增写完再拉高 CS等一次 tWR。void MRAM_WritePage(unsigned long addr, unsigned char *buf, unsigned int len) { unsigned int i; CS 0; SSPBUF 0x06; // WREN while(!SSPIF); SSPIF 0; CS 1; CS 0; SSPBUF 0x02; // WRITE while(!SSPIF); SSPIF 0; SSPBUF (addr 16) 0x07; while(!SSPIF); SSPIF 0; SSPBUF (addr 8) 0xFF; while(!SSPIF); SSPIF 0; SSPBUF addr 0xFF; while(!SSPIF); SSPIF 0; for(i 0; i len; i) { SSPBUF buf[i]; while(!SSPIF); SSPIF 0; } CS 1; __delay_ms(10); // 等待 tWR }提示MR25H40CDF 没有页的概念你可以从任意地址开始连续写一直写到存储区末尾。但实际使用中我建议还是按页对齐来写比如每 256 字节一页这样管理起来方便也便于做磨损均衡虽然 MRAM 不需要磨损均衡但逻辑上分页有利于数据组织。4. 数据存储策略与掉电保护设计4.1 为什么 MRAM 适合做掉电保护工业现场最怕的就是掉电丢数据。传统的方案是用铁电存储器FRAM或者带电池的 SRAM但 FRAM 容量小、价格高带电池的 SRAM 又有电池寿命问题。MRAM 在这两者之间找到了一个平衡点容量比 FRAM 大价格比 FRAM 低不需要电池写入速度还快。MR25H40CDF 的写入过程是这样的CS 上升沿触发内部写入写入时间典型值 10ms。在这 10ms 内如果电源掉电数据可能会丢。但如果你在 CS 拉高之前就把数据发完了那数据已经在芯片的输入缓冲里了掉电也不影响。所以关键是要保证 CS 拉高之后的那 10ms 内电源稳定。我的做法是在 MRAM 的 VDD 引脚旁边放一个大电容比如 100μF这样即使主电源掉了电容里的电还能撑几十毫秒足够完成写入。4.2 数据组织方式4Mbit 的容量也就是 512KB怎么组织数据要看你的应用场景。如果是做数据记录仪我一般会分成几个区域第一个区域存系统配置参数比如设备 ID、采样频率、校准系数这些数据不常变但必须可靠第二个区域存实时数据按时间顺序循环写入第三个区域存事件日志比如报警记录、操作记录。循环写入的时候需要一个写指针记录当前写到哪个位置了。写指针本身也要存到 MRAM 里但不用每次都存可以每隔一段时间存一次或者掉电前存一次。掉电检测可以用 PIC18F96J65 的 LVD低电压检测模块当电压低于阈值时触发中断在中断里把写指针存到 MRAM 里。// 掉电中断服务函数 void __interrupt(high_priority) HighISR(void) { if(PIR1bits.LVDIF) { PIR1bits.LVDIF 0; // 保存写指针到 MRAM MRAM_WriteByte(WRITE_PTR_ADDR, write_ptr 0xFF); MRAM_WriteByte(WRITE_PTR_ADDR 1, (write_ptr 8) 0xFF); MRAM_WriteByte(WRITE_PTR_ADDR 2, (write_ptr 16) 0xFF); } }4.3 数据校验与恢复MRAM 虽然可靠但也不是绝对不会出错。工业现场有强电磁干扰SPI 通信过程中如果受到干扰可能会写错数据。所以我一般会在每个数据块后面加一个 CRC 校验读的时候先校验校验不通过就丢弃或者重读。CRC 可以用软件实现也可以用 PIC18F96J65 的硬件 CRC 模块如果型号支持的话。软件 CRC 的话推荐用 CRC-16/CCITT 算法查表法实现速度快占用资源少。每个数据块比如 64 字节后面跟 2 字节 CRC这样每 66 字节一个块512KB 可以存大约 7900 个块。注意CRC 校验只能发现错误不能纠正错误。如果你需要纠错可以用汉明码或者 Reed-Solomon 码但那会增加存储开销和计算复杂度。对于大多数工业应用来说CRC 加自动重读就够了。5. 常见问题排查与实操避坑指南5.1 SPI 通信失败排查流程SPI 通信失败是最常见的问题表现是读出来的数据全是 0xFF 或者 0x00或者数据不稳定。排查的时候按这个顺序来第一步用示波器或者逻辑分析仪看 SCK、CS、SI、SO 四根线的波形。先看 SCK 有没有时钟输出如果没有说明单片机的 SPI 模块没配置好检查 SSPCON1 寄存器的 SSPEN 位有没有置 1。再看 CS 有没有拉低如果没有检查片选 IO 的配置。第二步看 SI 上的数据是不是你发的命令。如果 SI 上没有数据说明 SSPBUF 没写进去或者 SPI 模块没使能。如果 SI 上有数据但不对检查波特率设置可能是时钟太快了从设备跟不上。第三步看 SO 上有没有数据返回。如果 SO 一直是高阻态说明从设备没响应检查 CS 是不是真的拉低了电源是不是正常WP 和 HOLD 引脚是不是拉高了。第四步如果波形都正常但数据不对检查 SPI 模式。MR25H40CDF 支持 Mode 0 和 Mode 3PIC18F96J65 默认是 Mode 0但如果你改了 CKP 位就可能变成 Mode 3。两边模式要一致。5.2 写入失败的原因分析写入失败的表现是发了 WRITE 命令也发了数据但读回来还是旧数据。原因可能有这几个一是没发 WREN 命令。WREN 是写使能不发的话 WEL 位是 0WRITE 命令会被忽略。检查你的代码确认每次写之前都发了 WREN。二是 tWR 等待时间不够。MR25H40CDF 的 tWR 典型值是 10ms但最大值可能到 15ms。如果你只等了 5ms 就去读可能读到的还是旧数据。建议等 15ms 以上或者读状态寄存器的 WEL 位等 WEL 变成 0 再读数据。三是 WP 引脚没拉高。WP 是写保护低电平有效。如果你把 WP 接地了那所有写操作都会被禁止。检查 WP 引脚的电平确保它是高电平。四是电源电压不够。MR25H40CDF 的最低工作电压是 2.7V如果你用 2.5V 供电写入可能失败。用万用表量一下 VDD 引脚的电压确保在 2.7V 到 3.6V 之间。5.3 常见问题速查表现象可能原因排查方法解决方法读数据全 0xFFCS 没拉低示波器看 CS 波形检查片选 IO 配置读数据全 0x00SCK 没时钟示波器看 SCK 波形检查 SSPEN 位数据不稳定SPI 模式不匹配对比两边模式设置统一为 Mode 0写入无效没发 WREN逻辑分析仪看命令序列写之前加 WREN写入后读回旧数据tWR 等待不够测量 CS 拉高到读的时间延时 15ms 以上偶尔写错电源纹波大示波器看 VDD 纹波加去耦电容高温下出错时序余量不足高温箱测试降低 SPI 时钟5.4 实操心得与避坑技巧第一个心得SPI 时钟不要一上来就跑 40MHz。虽然 MR25H40CDF 标称支持 40MHz但实际布线如果不好40MHz 下波形会很难看。我一般先用 1MHz 调通再逐步提高到 10MHz、20MHz找到稳定的最高频率。工业现场建议留余量跑 10MHz 就够了稳定比速度重要。第二个心得CS 的拉高拉低之间加一点延时。有些单片机 IO 翻转太快CS 和 SCK 的时序可能不满足从设备要求。在 CS 拉低之后、发第一个时钟之前加 1μs 的延时在最后一个时钟之后、CS 拉高之前也加 1μs。这个延时用 __delay_us(1) 就行不影响整体速度。第三个心得MRAM 的写入次数虽然标称 10^14 次但那是整片寿命。如果你盯着同一个地址反复写比如每秒写 1000 次一年就是 3×10^10 次虽然还没到 10^14但也没必要这么干。做循环写入的时候尽量让写指针均匀分布在整个存储区不要老写同一个地方。第四个心得调试的时候用逻辑分析仪比示波器好用。示波器只能看模拟波形逻辑分析仪能直接解码 SPI 协议把命令、地址、数据都显示出来一眼就能看出哪里不对。我用的是一款 8 通道的 USB 逻辑分析仪采样率 24MHz价格不贵但调试 SPI 的时候帮了大忙。第五个心得MR25H40CDF 的 HOLD 引脚如果不用一定要拉到 VDD。我遇到过一个问题HOLD 引脚悬空的时候偶尔会误触发导致 SPI 通信中断。后来把 HOLD 拉到 VDD问题就消失了。WP 引脚同理不用就拉高。6. 性能优化与扩展思路6.1 SPI 时钟频率的权衡SPI 时钟频率直接决定了读写速度。MR25H40CDF 支持最高 40MHzPIC18F96J65 的 MSSP 模块在 64MHz 系统时钟下SPI 时钟最高可以到 16MHzFosc/4。所以实际能跑的最高频率是 16MHz不是 40MHz。如果你需要更高的速度可以考虑用 PIC18F96J65 的另一个 MSSP 模块或者换用带 SPI 高速模式的单片机。16MHz 的 SPI 时钟一个字节 8 位传输时间是 0.5μs。加上命令和地址的开销读一个字节大概需要 1μs写一个字节加上 tWR 等待大概需要 10ms。所以读操作很快写操作受限于 tWR速度上不去。如果你需要高速写入可以考虑用 MRAM 的页写模式一次写多个字节只等一次 tWR。6.2 数据压缩与存储效率512KB 的容量说大不大说小不小。如果你存的是原始 ADC 数据16 位一个采样点512KB 只能存 256K 个点。如果采样率是 1kHz那只能存 256 秒不到 5 分钟。所以实际应用中数据压缩是必须的。简单的压缩方法有差分编码存相邻两个采样点的差值差值通常比原始值小可以用更少的位数表示游程编码如果数据有很多重复值可以存值和重复次数查表法如果数据范围有限可以建一个索引表存索引而不是原始值。我做过一个项目用差分编码加游程编码把 16 位 ADC 数据压缩到平均 6 位一个点存储时间从 5 分钟延长到了 13 分钟。压缩算法不复杂在 PIC18F96J65 上跑压缩一个点大概 20μs完全跟得上 1kHz 的采样率。6.3 通过以太网导出数据PIC18F96J65 自带以太网 MAC 和 PHY这是它的一大优势。你可以把 MRAM 里存的数据通过网口传出去用 TCP 或者 UDP 协议。TCP 可靠但开销大UDP 简单但可能丢包。工业现场一般用 TCP因为数据完整性比速度重要。实现的时候可以用 Microchip 的 TCP/IP 协议栈或者自己写一个简单的 TCP 服务器。数据导出的时候先读 MRAM 里的数据打包成 TCP 报文发出去。注意 TCP 的窗口大小和 MTU别一次发太多不然会分片。我一般一次发 1460 字节正好是一个以太网帧的载荷。提示PIC18F96J65 的以太网 PHY 需要外接一个 25MHz 的晶振和一个网络变压器。网络变压器的作用是隔离和阻抗匹配不能省。我见过有人为了省成本把变压器去掉结果网口经常丢包后来加回去就好了。6.4 多片 MRAM 级联扩展容量512KB 如果不够用可以挂多片 MR25H40CDF。SPI 总线可以挂多个从设备每个从设备用独立的 CS 片选。PIC18F96J65 的 IO 口够多挂 4 片 MRAM 用 4 个 CS 引脚容量扩展到 2MB。软件上只需要在读写之前选对应的 CS其他操作都一样。多片级联的时候要注意SPI 总线上所有从设备的 SCK、SI、SO 是并联的只有 CS 是独立的。所以同一时刻只能有一个 CS 拉低否则数据会冲突。软件上要保证 CS 的互斥别同时拉低两个 CS。7. 实际项目中的经验沉淀这套 MR25H40CDF 加 PIC18F96J65 的方案我在两个工业项目里实际用过。一个是电力监测终端每秒采集 64 个点的三相电压电流数据存 MRAM通过以太网上传。另一个是环境监测仪每分钟采集一次温湿度和气压数据存 MRAM通过 4G 模块上传。两个项目都跑了两年多没出现过数据丢失的问题。电力监测那个项目一开始用的是 EEPROM结果三个月就写坏了一片。后来换成 MRAM到现在两年多了写入次数早就超过了 EEPROM 的寿命但 MRAM 一点问题没有。这个对比让我对 MRAM 的可靠性有了直观的认识。环境监测那个项目遇到过一个坑MRAM 的 WP 引脚在 PCB 上忘了拉高结果所有写操作都失败。排查了半天最后用万用表量 WP 引脚电压发现是 0V才找到原因。所以我现在画 PCB 的时候WP 和 HOLD 引脚都会明确标注不用时拉高避免再犯同样的错误。还有一个经验MRAM 的 tWR 等待时间数据手册上写的是典型值 10ms但我在高温箱里测试的时候发现 85°C 下 tWR 会变长最长到 12ms。所以如果你要在高温环境下用等待时间要留够余量建议等 15ms 以上。或者你可以读状态寄存器的 WEL 位等它变成 0 再继续这样最可靠。最后分享一个小技巧如果你觉得每次写都等 10ms 太慢可以用写缓冲的方式。在 SRAM 里开一个缓冲区数据先写到 SRAM等缓冲区满了或者掉电前再一次性写到 MRAM。这样写操作的次数大大减少整体效率提高很多。但要注意掉电前必须把缓冲区里的数据写进 MRAM所以掉电检测电路要可靠电容要够大保证有足够的时间完成最后一次写入。
返回列表