ARTICLE DETAIL

资讯详情

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

PIC18F85K90 驱动 MR25H40CDF MRAM 的 SPI 实现与掉电保护

PIC18F85K90 驱动 MR25H40CDF MRAM 的 SPI 实现与掉电保护 1. 项目缘起与方案选型为什么是 MRAM 加 PIC181.1 一个真实场景引出的存储需求前阵子帮一个做工业数据采集的朋友处理一块老设备的主控板板子上跑着一颗 PIC18F85K90外挂了一颗 EEPROM 用来存配置参数和掉电前的关键数据。问题出在现场设备每次断电重启后偶尔会丢几条记录尤其是掉电瞬间正在写的那一笔直接变成乱码。排查下来是 EEPROM 写入速度太慢加上掉电时电源跌落时间不够写操作没完成就被打断了。这个场景其实在工业和嵌入式领域非常典型。很多设备需要频繁记录运行状态、累计计数、故障日志而且要求掉电不丢、上电即读、写入次数足够多。传统方案无非几种EEPROM 写入慢、寿命有限SPI Flash 写入前要擦除、有块管理开销、擦写寿命也就十万次量级铁电存储器 FRAM 速度快、寿命高但容量小、价格偏高。而 MRAM磁性随机存储器这几年逐渐成熟像 Everspin 的 MR25H40CDF 就是一颗 4Mbit 的 SPI MRAM兼具 SRAM 级的写入速度、近乎无限的擦写寿命和掉电非易失特性正好补上了这个缺口。所以这个项目的核心目标很明确用 PIC18F85K90 通过 SPI 总线驱动 MR25H40CDF实现工业现场数据的可靠存储与读取。关键词里出现的 MR25H40CDF、PIC18F85K90、嵌入式、SPI、MRAM基本就是这条技术链的全部要素。适合谁看做工业控制、数据采集、仪表设备的嵌入式工程师尤其是被掉电丢数据折磨过的人这篇内容应该能帮你少走弯路。1.2 为什么选 MR25H40CDF 而不是 EEPROM 或 Flash先把三者的关键差异摆出来选型逻辑就清楚了。特性EEPROM如 25LC 系列SPI Flash如 W25Q 系列MRAMMR25H40CDF写入速度毫秒级页写 5ms 左右毫秒级且需先擦除纳秒级无擦除擦写寿命约 100 万次约 10 万次近乎无限10^14 量级掉电非易失是是是写入前擦除不需要必须不需要接口SPISPISPI容量小大中等4Mbit单价低低偏高从表里能看出MR25H40CDF 最大的价值在于“写入不用等、不用擦、不怕写坏”。工业现场那种每秒都在更新计数值、随时可能断电的场景用 EEPROM 你得做复杂的掉电保护电路和写缓存用 Flash 你得做磨损均衡和块管理而 MRAM 直接像写 RAM 一样写进去就完事掉电数据还在。这就是我最终选它的核心理由。1.3 PIC18F85K90 的角色与 SPI 资源分配PIC18F85K90 是 Microchip 的一款 8 位增强型单片机自带 MSSP 模块可以配置成 SPI 主模式。它在这个项目里干三件事一是作为 SPI 主机发起读写时序二是管理数据帧格式和地址映射三是处理上电初始化与掉电前的数据落盘。选它而不是更高级的 ARM主要是成本和生态考虑。工业设备很多还是 8 位或 16 位主控PIC18 系列资料全、工具链成熟、抗干扰能力也不错。它的 MSSP 模块支持 SPI 模式 0 和模式 1时钟极性 CPOL 和相位 CPHA 可配正好匹配 MR25H40CDF 的 SPI 模式 0CPOL0CPHA0。这一点很关键后面配置寄存器时会详细说。提示MR25H40CDF 只支持 SPI 模式 0 和模式 3PIC18F85K90 的 MSSP 配置时务必确认 CPOL/CPHA 组合配错了读出来全是 0xFF 或 0x00。2. 硬件连接与 SPI 时序细节2.1 引脚连接与片选处理MR25H40CDF 是 8 引脚封装关键引脚包括 VDD、VSS、SCK、SI、SO、CS#、WP#、HOLD#。和 PIC18F85K90 连接时我用的映射如下SCK 接 PIC 的 SCK 引脚RC3MSSP 时钟SI 接 PIC 的 SDORC5SO 接 PIC 的 SDIRC4CS# 接一个普通 GPIO我用的是 RA5软件控制片选WP# 和 HOLD# 直接上拉到 VDD禁用写保护和保持功能这里有个细节值得说CS# 我用的是软件片选而不是硬件片选。原因有两个一是 PIC18F85K90 的 MSSP 在 SPI 主模式下硬件片选SS主要用于从模式主模式下用软件控制更灵活二是 MRAM 的片选时序要求 CS# 拉低后到第一个时钟沿之间要有一定建立时间软件控制能精确插入延时。实测下来软件片选在 10MHz 时钟下依然稳定。注意WP# 和 HOLD# 千万不要悬空悬空会导致写入被意外保护或通信被挂起直接上拉到 VDD 是最稳妥的做法。2.2 SPI 模式与时序参数核算MR25H40CDF 的数据手册明确写了支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我选模式 0因为 PIC18F85K90 的 MSSP 在模式 0 下配置最简单SCK 空闲为低数据在上升沿采样、下降沿变化。时序上要关注几个参数SCK 周期最小 20ns对应最高 50MHzCS# 建立时间最小 5nsCS# 保持时间最小 5ns数据输出有效时间等。PIC18F85K90 在 64MHz 系统时钟下SPI 时钟可以做到 16MHzFosc/4完全在 MRAM 的能力范围内。我实际跑的是 10MHz留足余量给 PCB 走线和干扰。计算一下SPI 时钟 10MHz一个字节 8 位传输时间 0.8μs。MR25H40CDF 的读操作是发一个命令字节加三个地址字节然后连续读数据所以读一个字节的总开销约 4 字节命令开销加 1 字节数据约 4μs。写操作类似命令加地址加数据约 4μs 写完一个字节。这个速度比 EEPROM 的毫秒级快了三个数量级。2.3 PCB 布局与信号完整性要点SPI 虽然简单但在 10MHz 以上还是要讲究布局。我的经验是SCK 走线尽量短避免和 SI/SO 长距离平行走线减少串扰CS# 走线单独走不要和时钟捆在一起MRAM 的电源引脚旁边放 0.1μF 陶瓷电容越近越好如果板子上还有其他 SPI 从设备每个设备的 CS# 独立控制不要共用有一次我偷懒把 MRAM 和一颗 Flash 共用 CS#结果两个设备同时被选中数据总线打架读出来全是错。后来改成独立片选问题立刻消失。这个坑希望大家别踩。3. 软件驱动实现从初始化到读写3.1 MSSP 模块初始化配置PIC18F85K90 的 MSSP 初始化分几步设置 TRIS 方向、配置 SSPCON1、设置时钟速率、使能模块。下面是我实际用的初始化代码片段。// PIC18F85K90 MSSP SPI 主模式初始化 // 系统时钟 64MHzSPI 时钟 Fosc/4 16MHz实际用 Fosc/16 4MHz 起步 TRISC3 0; // SCK 输出 TRISC5 0; // SDO 输出 TRISC4 1; // SDI 输入 TRISA5 0; // CS# 输出 CS_MRAM 1; // 初始拉高不选中 SSPSTAT 0x40; // 数据在上升沿采样下降沿变化模式0 SSPCON1 0x20; // SSPEN1SPI 主模式时钟 Fosc/4 // 如需更慢时钟改 SSPCON1 低两位0x21Fosc/16, 0x22Fosc/64这里 SSPSTAT 的 SMP 位和 CKE 位决定采样时刻。模式 0 要求 SMP0上升沿采样、CKE0下降沿变化所以 SSPSTAT 写 0x40 是设置 SMP0、CKE0同时保留其他位默认值。SSPCON1 的 SSPM3:0 设为 0b0000 是 Fosc/40b0001 是 Fosc/16我一般先用慢速调通再提速。3.2 字节收发函数与片选控制SPI 收发在 PIC18 上就是写 SSPBUF 然后等 BF 标志。我封装了两个函数一个发一个收实际读的时候是发一个哑字节同时收。unsigned char SPI_ExchangeByte(unsigned char data) { SSPBUF data; while (!PIR1bits.SSPIF); // 等待传输完成 PIR1bits.SSPIF 0; return SSPBUF; } void MRAM_CS_Low(void) { CS_MRAM 0; // 插入短延时满足 CS# 建立时间 __delay_us(1); } void MRAM_CS_High(void) { __delay_us(1); CS_MRAM 1; }片选拉低后我加了 1μs 延时虽然手册只要求 5ns但实际 PCB 和引脚电容会让边沿变缓多等一点更稳。这个延时在 10MHz 下对吞吐影响可以忽略。3.3 MR25H40CDF 命令集与地址映射MR25H40CDF 的命令集很简洁常用的就几条命令编码功能WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据地址是 24 位因为 4Mbit 等于 512KB需要 19 位地址但 MRAM 用 24 位地址格式高 5 位忽略。读操作时序是CS# 拉低发 0x03发 24 位地址3 字节然后连续读数据CS# 拉高结束。写操作类似但写之前要先发 WREN。这里有个和 EEPROM 不同的地方MRAM 写操作不需要等待写完成发完数据拉高 CS# 就结束了没有轮询状态寄存器那一套。这是它速度快的重要原因。3.4 读写的完整代码实现下面是我实际用的读写函数读和写都封装好了。void MRAM_WriteByte(unsigned long addr, unsigned char data) { MRAM_CS_Low(); SPI_ExchangeByte(0x06); // WREN MRAM_CS_High(); MRAM_CS_Low(); SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); SPI_ExchangeByte(data); MRAM_CS_High(); } unsigned char MRAM_ReadByte(unsigned long addr) { unsigned char data; MRAM_CS_Low(); SPI_ExchangeByte(0x03); // READ SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); data SPI_ExchangeByte(0x00); // 哑字节换数据 MRAM_CS_High(); return data; }写的时候先单独发 WREN 再拉高 CS#然后再拉低发 WRITE 命令这是 MRAM 的要求。有些朋友会想省掉中间那次拉高直接把 WREN 和 WRITE 连在一起发实测不行MRAM 不认。这个细节手册里写得清楚但容易忽略。4. 数据可靠性与掉电保护策略4.1 为什么 MRAM 还需要掉电保护有人会问MRAM 写入是纳秒级、掉电非易失那还需要掉电保护吗答案是MRAM 本身不需要但系统需要。因为掉电瞬间单片机可能正在执行写操作如果电源跌落太快SPI 时序没走完MRAM 收到的可能是半个命令或半个数据导致写入不完整。所以掉电保护的重点不是 MRAM而是保证写操作的原子性。我的做法是在电源端加一个大电容比如 1000μF配合一个电压检测电路。当检测到输入电压低于阈值时立刻触发中断在电容供电的几毫秒内完成当前写操作并停止新写入。因为 MRAM 写一个字节只要几微秒几毫秒足够写完几十个字节。4.2 数据帧格式与校验设计工业数据不能只存原始值我设计了一个简单的帧格式每个记录 16 字节2 字节帧头0xAA554 字节时间戳或序号8 字节数据区2 字节 CRC16 校验写入时先写数据区再写帧头最后写校验。读取时先读帧头如果帧头不对就跳过帧头对了再读数据和校验校验通过才认为有效。这样即使掉电导致某条记录写了一半上电后也能识别出来并跳过不会把乱码当数据用。CRC16 我用的是 Modbus 多项式 0xA001计算速度快8 位机上跑几十微秒就够。这个校验在工业现场非常必要我遇到过好几次因为电源波动导致的数据位翻转有 CRC 就能发现。4.3 写入次数与寿命管理虽然 MRAM 寿命近乎无限但也没必要往死里写。我的策略是分区域循环写把 512KB 分成若干块每块存固定条数的记录写满一块后跳到下一块全部写满后回到第一块覆盖最旧的数据。这样既实现了日志滚动又避免了单一地址被反复写。具体实现时我在 MRAM 的第一个扇区存一个写指针记录当前写到哪个块、哪个偏移。每次上电先读指针然后从指针位置继续写。指针本身也是 MRAM更新它同样不怕掉电。提示写指针更新频率高的话可以每写 10 条记录更新一次指针减少指针写次数。虽然 MRAM 不怕写但减少操作总是好的。5. 常见问题与排查实录5.1 读出来全是 0xFF 或 0x00这是最常见的现象原因通常有三个一是 SPI 模式配错CPOL/CPHA 不对二是片选没拉低或拉低时机不对三是 SI/SO 接反。排查顺序是先量 SCK 有没有波形再量 CS# 有没有拉低最后用示波器看 SI 上有没有命令字节。我遇到过一次是 SO 和 SI 焊反了读出来全是 0xFF换了就好了。5.2 写入后读出来不对如果写进去和读出来不一致先确认 WREN 有没有发成功。可以读状态寄存器RDSR看 WEL 位是不是 1。如果 WEL 是 0说明 WREN 没生效检查 CS# 时序。另一个可能是地址算错了24 位地址的高位没处理好导致写到别的地址去了。5.3 高速时钟下通信不稳定10MHz 以上如果出现偶发错误多半是信号完整性问题。先降速到 1MHz 试试如果降速后稳定就是布局或走线问题。可以在 SCK 上串一个 22Ω 电阻减缓边沿减少反射。另外检查电源去耦电容有没有放够。5.4 常见问题速查表现象可能原因排查方法读出全 0xFFSI/SO 接反、模式错检查接线和 CPOL/CPHA读出全 0x00CS# 没拉低、MRAM 没供电量 CS# 和 VDD写入无效WREN 没发、CS# 时序错读状态寄存器 WEL 位偶发错误时钟太快、干扰降速、加去耦、串电阻掉电丢数据电源跌落太快加大电容、加电压检测5.5 几个实操心得第一调试 SPI 一定要有逻辑分析仪或示波器光看代码看不出时序问题。我用的是一台便宜的逻辑分析仪抓 SPI 时序足够。第二MRAM 的 WP# 和 HOLD# 一定要处理我见过有人悬空导致写入被保护查了半天。第三上电初始化时先读一次 MRAM 的 ID 或固定地址确认通信正常再开始业务逻辑这样能早发现问题。第四如果系统里有多个 SPI 设备每个设备的 CS# 独立控制初始化时全部拉高避免总线冲突。6. 性能实测与扩展思路6.1 实测数据我在 10MHz SPI 时钟下做了连续读写测试结果如下操作耗时写一个字节约 4.2μs读一个字节约 4.0μs写 16 字节记录约 68μs读 16 字节记录约 64μs连续写 1000 条记录约 68ms这个速度对于工业数据采集完全够用每秒可以写上万条记录。对比 EEPROM 的毫秒级写入提升非常明显。6.2 后续可以怎么扩展如果数据量更大可以换更大容量的 MRAM比如 MR25H40 是 4Mbit还有 16Mbit 的型号接口兼容改一下地址位数就行。如果主控升级到 16 位或 32 位SPI 时钟可以拉到 50MHz速度还能再快几倍。软件上可以加一个简单的文件系统把 MRAM 当成一个小型磁盘支持按文件名读写。不过对于大多数工业场景直接按地址读写加帧格式就够了没必要上文件系统增加复杂度。我在实际使用中的体会是MRAM 这种器件最大的价值不是速度快而是让嵌入式工程师从“怕掉电、怕写坏”的焦虑中解放出来。以前用 EEPROM 要算寿命、做缓存、加掉电检测现在这些都可以简化。当然成本确实高一些但对于数据可靠性要求高的场合这个投入是值得的。
返回列表