ARTICLE DETAIL

资讯详情

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

工业级MRAM与PIC18LF45K40实战:SPI驱动、掉电保护与数据记录方案

工业级MRAM与PIC18LF45K40实战:SPI驱动、掉电保护与数据记录方案 1. 项目缘起与方案选型1.1 为什么要在工业场景里折腾 MRAM 这颗怪胎做工业嵌入式的人都有一个共同的痛设备装到现场断电重启之后数据丢了客户一个电话打过来你就得连夜坐高铁去现场重新标定。EEPROM 擦写次数有限Flash 写入前必须整块擦除掉电瞬间正在写的那一页直接变砖。铁电存储器 FRAM 读写快、寿命长但容量小、价格高而且很多型号已经停产。锂电池加 SRAM 的方案更不用说了电池本身就是个定时炸弹高温环境下三年就鼓包。MR25H40CDF 这颗芯片是我在对比了七八种非易失存储方案之后定下来的。它是 Everspin 家的 4Mbit 串行 MRAMSPI 接口最高 40MHz 时钟最关键的一点是写入不需要擦除字节级随机写入写入次数近乎无限。磁阻式存储的物理机制决定了它不像 Flash 那样靠电荷隧穿来存数据而是靠磁性隧道结的电阻状态所以写入能耗极低而且没有擦除周期这个概念。你在代码里直接往地址里写一个字节它就老老实实存进去了不需要考虑扇区对齐、不需要考虑擦除时间。PIC18LF45K40 是 Microchip 的一款 8 位单片机44 引脚 TQFP 封装工作电压 1.8V 到 5.5V自带 MSSP 模块支持 SPI 和 I2C。选它有几个原因第一它的低功耗特性在工业现场电池供电或者 4-20mA 环路供电的场景下很关键第二PIC18 系列的代码库成熟XC8 编译器稳定不像某些新出的 ARM 芯片那样工具链三天两头出问题第三它的 SPI 模块支持硬件片选和软件片选两种模式配合 MRAM 用起来很灵活。这个组合的核心价值在于用一颗不需要擦除、写入寿命近乎无限的存储器配合一颗低功耗、宽电压的 8 位单片机解决工业现场数据记录、参数存储、掉电保护这三个最头疼的问题。适合谁看如果你正在做工业仪表、数据采集终端、PLC 扩展模块、或者任何需要在恶劣环境下可靠存储小批量数据的嵌入式项目这套方案可以直接抄作业。1.2 硬件连接与 SPI 模式选择MR25H40CDF 支持 SPI 模式 0 和模式 3具体用哪个取决于你的主控。PIC18LF45K40 的 MSSP 模块在 SPI 主模式下CKP 位决定时钟极性CKE 位决定时钟相位。我实测下来用模式 0CKP0CKE1最稳也就是空闲时时钟低电平数据在上升沿采样。这个配置下MRAM 的时序余量最大40MHz 下跑起来眼图很干净。硬件连接上除了标准的 SCK、SDI、SDO 三根线片选 CS 我建议用硬件片选。PIC18LF45K40 的 MSSP 模块支持 SS 引脚自动控制但实际用的时候你会发现硬件片选在连续读写多个字节时容易出问题因为 MSSP 在传输完一个字节后会释放 SS而 MRAM 需要 CS 在整个命令序列期间保持低电平。所以我的做法是用普通 GPIO 手动控制 CSSPI 模块只负责时钟和数据。这样虽然多占一个引脚但时序完全可控调试的时候用逻辑分析仪一看就明白。电源部分要注意MR25H40CDF 的工作电压是 2.7V 到 3.6V而 PIC18LF45K40 可以低到 1.8V。如果你用 3.3V 供电两者可以直接对接。但如果你的系统是 5V 的PIC18LF45K40 可以工作在 5V但 MRAM 不行必须加电平转换。我一般用 74LVC1T45 做双向电平转换或者直接用电阻分压加肖特基钳位成本低但速度上不去40MHz 下波形会变形。所以如果追求高速读写老老实实用专用电平转换芯片。还有一个坑MRAM 的 VDD 和 VDDQ 要分开供电VDDQ 是 I/O 电压必须和主控的 I/O 电平匹配。很多人在画板子的时候把这两个脚接在一起3.3V 系统下没问题但如果你主控是 1.8V 的VDDQ 也要接 1.8V否则 SPI 通信会出错。这个细节在数据手册的第 12 页有写但很容易被忽略。2. 核心细节解析与实操要点2.1 MRAM 的读写命令集与地址映射MR25H40CDF 的命令集很简洁常用的就四条WREN写使能0x06、WRITE写数据0x02、READ读数据0x03、RDSR读状态寄存器0x05。没有扇区擦除、没有页编程、没有写完成轮询写完之后直接就可以读不需要等待。这一点和 Flash 完全不同Flash 写完要等 5ms 左右的内部编程时间MRAM 是纳秒级的。地址是 24 位的因为 4Mbit 等于 512K 字节需要 19 位地址但芯片要求发送 3 个字节的地址高 5 位忽略。所以你在代码里要构造一个 32 位的地址变量然后取低 24 位发送。比如你要访问地址 0x0007FFFF发送的顺序是 0x07、0xFF、0xFF。这个顺序是 MSB first和 SPI 的数据顺序一致。写操作的流程是拉低 CS发送 WREN 命令拉高 CS再拉低 CS发送 WRITE 命令发送 3 字节地址然后连续发送数据字节最后拉高 CS。注意 WREN 和 WRITE 之间必须拉高 CS否则 WREN 不生效。这个细节我踩过坑有一次为了省事WREN 之后没有拉高 CS 就直接发 WRITE结果数据写不进去查了半天以为是硬件问题。读操作更简单拉低 CS发送 READ 命令发送 3 字节地址然后连续读取数据最后拉高 CS。读的时候没有 dummy 字节地址发完直接出数据所以 40MHz 下读 512 字节只需要 100 多微秒。状态寄存器只有两个位有用WEL写使能锁存和 BP0、BP1块保护。WEL 在 WREN 之后置 1在 WRITE 完成之后自动清零。BP 位用来保护地址空间如果你不小心把 BP 设成了保护整个芯片那写操作会直接被忽略而且不会报错。我建议在初始化的时候读一次状态寄存器确认 BP 位是 0然后再进行写操作。2.2 PIC18LF45K40 的 SPI 初始化与 GPIO 配置PIC18LF45K40 的 MSSP 模块初始化分几步先配置 TRIS 寄存器把 SCK、SDO 设为输出SDI 设为输入CS 设为输出并置高然后配置 SSPCON1 寄存器设置 SSPEN 使能 SPICKP 选择时钟极性SSPM 选择主模式最后配置 SSPSTAT 寄存器设置 CKE 选择时钟相位SMP 选择采样点。具体的寄存器值SSPCON1 0x20SSPEN1CKP0SSPM0b0000 主模式 Fosc/4SSPSTAT 0x40CKE1SMP0。这样配置出来的是 SPI 模式 0时钟频率是 Fosc/4。如果系统时钟是 64MHzSPI 时钟就是 16MHzMRAM 完全能跑。如果你想跑到 40MHz需要把系统时钟设到 160MHz但 PIC18 系列一般跑不到这么高所以实际能到 16MHz 到 20MHz 就很好了。CS 引脚我用的是 RC2配置为输出初始状态置高。写一个字节的函数大概长这样void MRAM_WriteByte(uint32_t addr, uint8_t data) { CS_LOW(); SPI_Write(0x06); // WREN CS_HIGH(); CS_LOW(); SPI_Write(0x02); // WRITE SPI_Write((addr 16) 0xFF); SPI_Write((addr 8) 0xFF); SPI_Write(addr 0xFF); SPI_Write(data); CS_HIGH(); }读字节类似只是命令换成 0x03然后读一个字节返回。注意 SPI_Write 函数要等待 BF 位SSPSTAT 的 bit0清零才能写下一个字节否则会覆盖上一个数据。这个等待在 16MHz 下大概几个微秒不算快但够用。2.3 写保护与数据完整性校验工业现场最怕的是数据被意外改写。MR25H40CDF 提供了块保护功能通过状态寄存器的 BP0 和 BP1 位可以保护 1/4、1/2 或整个存储区。我的做法是把参数区放在低地址数据记录区放在高地址然后用 BP 位保护参数区。这样即使程序跑飞了参数也不会丢。但块保护有个问题保护粒度是固定的不能任意划分。比如你只想保护前 64K但 BP 位只能保护 1/4 或 1/2那就只能保护前 128K。所以我在设计的时候会把参数区放在最前面然后根据实际大小选择合适的保护级别。如果参数区小于 128K那就保护 1/4多出来的部分浪费就浪费了总比数据丢了强。数据完整性方面我一般会在每个数据块后面加一个 CRC16 校验。写入的时候算好 CRC 一起写进去读取的时候重新算一遍对比。如果 CRC 不匹配说明数据损坏这时候可以从备份区恢复。MRAM 本身出错概率极低但工业现场的电磁干扰、电源波动都可能导致 SPI 通信误码所以校验是必须的。还有一个技巧在数据块头部加一个写入计数器。每次写入新数据的时候计数器加一。读取的时候如果发现计数器比上次读到的值小说明发生了回绕或者数据被篡改。这个技巧在掉电恢复场景下特别有用可以判断数据是新写入的还是旧的。3. 实操过程与核心环节实现3.1 硬件搭建与上电测试先说一下我的测试平台PIC18LF45K40 用内部 64MHz 振荡器3.3V 供电MR25H40CDF 用同一路 3.3VVDDQ 也接 3.3VCS 接 RC2SCK 接 RC3SDI 接 RC4SDO 接 RC5。逻辑分析仪接在 SCK、SDI、SDO、CS 四根线上采样率设到 100MHz 以上不然 16MHz 的 SPI 波形看不清楚。上电之后第一步不是写代码而是用逻辑分析仪抓波形。先发一个 WREN 命令看 CS 是否拉低、SCK 是否有 8 个脉冲、SDI 上是否是 0x06。如果波形不对先查硬件CS 有没有接对、SCK 有没有输出、电源电压是否正常。我遇到过 CS 接错引脚的情况波形死活出不来换了引脚就好了。波形确认之后写一个最简单的测试程序往地址 0x000000 写 0xAA然后读回来。如果读回来是 0xAA说明基本通信没问题。然后再写 0x55读回来确认。接着写 0x00 和 0xFF测试全 0 和全 1 的情况。最后写一个递增序列比如 0x00 到 0xFF 共 256 个字节读回来对比。这一步能发现地址对齐、连续读写的问题。3.2 参数存储区的设计与实现参数区我一般放在地址 0x000000 到 0x0000FF共 256 字节。结构是这样的前 4 个字节是魔数 0x4D 0x52 0x41 0x4DMRAM用来判断芯片是否已经初始化过接下来 4 个字节是写入计数器然后是实际参数比如传感器校准系数、设备 ID、通信波特率等最后 2 个字节是 CRC16。初始化流程上电后先读前 4 个字节如果不是魔数说明芯片是新的或者数据丢失这时候写入默认参数和魔数如果是魔数再读 CRC16校验通过就加载参数校验失败就加载默认参数并重新写入。这个流程我用了很多年从来没出过问题。写入参数的时候要注意先写数据区再写 CRC最后写魔数。这样即使写入过程中掉电魔数还没写进去下次上电会认为是新芯片加载默认参数。如果先写魔数再写数据掉电后魔数在但数据不完整CRC 校验会失败也能检测出来但不如先写数据稳妥。3.3 数据记录区的循环存储策略数据记录区我一般放在地址 0x010000 开始大小根据实际需求定。比如每 10 秒记录一次温度、压力、流量三个参数每个参数 2 个字节加上时间戳 4 个字节一条记录 10 个字节。如果记录区有 256K 字节可以存 26000 多条记录按 10 秒一条算能存 3 天左右。循环存储的策略是维护一个写指针每次写完一条记录指针后移。当指针到达记录区末尾时回绕到起始地址。同时维护一个记录计数器用来判断哪些记录是有效的。读取的时候从最新的一条开始往前读读到计数器为 0 的位置停止。这个策略的难点在于掉电保护。如果写指针更新到一半掉电了下次上电怎么知道写到哪了我的做法是把写指针和记录计数器也存在 MRAM 里每次写记录之前先更新指针再写数据。这样即使写数据的时候掉电指针已经更新了下次上电会从新的位置继续写最多丢失一条记录。如果先写数据再更新指针掉电后指针没变下次上电会覆盖同一条记录也不会丢数据但会浪费一次写入机会。两种方式都可以我倾向于先更新指针因为 MRAM 写入寿命无限浪费一次无所谓。3.4 掉电检测与紧急保存工业现场掉电是常态所以掉电检测电路是必须的。我用的是 PIC18LF45K40 的 LVD低电压检测模块设置检测阈值为 2.8V。当电源电压降到 2.8V 时LVD 产生中断在中断里把关键数据写入 MRAM。由于 MRAM 写入速度极快一个字节只需要几十纳秒所以在电源彻底掉到 2.7V 之前完全来得及保存几十个字节的关键数据。具体实现LVD 中断里先关闭其他中断然后调用 MRAM 写入函数把当前运行状态、关键参数、时间戳等写入预留的紧急保存区。写完之设置一个标志位下次上电的时候检查这个标志位如果置位了说明上次是掉电关机需要恢复数据。这个方案我实测过用可调电源慢慢往下调电压从 3.3V 降到 2.7V 大概有 10ms 的窗口期足够写 100 多个字节。但要注意LVD 中断的响应时间加上 SPI 传输时间实际能用的窗口大概 5ms 左右所以紧急保存的数据量不要超过 50 个字节。4. 常见问题与排查技巧实录4.1 SPI 通信失败排查速查表现象可能原因排查方法解决方法读回来全是 0xFFCS 没拉低、SCK 没输出、SDI 接反逻辑分析仪抓波形检查引脚配置和硬件连接读回来全是 0x00MISO 没接、MRAM 没供电万用表测电压检查 VDD 和 VDDQ 供电写入后读回来不对WREN 没生效、CS 时序不对抓 WREN 和 WRITE 之间的 CS 波形WREN 之后必须拉高 CS高速下读写出错时序余量不足、线太长降低 SPI 时钟测试缩短走线、加匹配电阻偶尔出错电源纹波大、电磁干扰示波器看电源纹波加去耦电容、屏蔽线这个表是我这些年踩坑总结出来的基本上覆盖了 90% 以上的问题。其中最常见的是 WREN 之后没有拉高 CS这个错误我见过至少五次每次都是不同的人犯的。还有一个容易忽略的是 VDDQ 供电很多人只给 VDD 供电VDDQ 悬空结果 SPI 通信时好时坏。4.2 写入速度优化与实测数据MR25H40CDF 标称最高 40MHz但实际能跑多快取决于你的主控和 PCB 布线。我用 PIC18LF45K40 在 64MHz 系统时钟下SPI 时钟 16MHz实测写入 512 字节需要 280 微秒读取 512 字节需要 260 微秒。这个速度对于工业数据记录来说完全够用因为传感器采样率一般不会超过 1kHz。如果你想更快可以换用 PIC18F 系列里支持更高 SPI 时钟的型号比如 PIC18F47K42它的 SPI 可以跑到 64MHz。但要注意SPI 时钟越高PCB 布线越关键。我建议 SCK 走线尽量短最好不超过 5 厘米并且包地处理。如果走线超过 10 厘米建议在 SCK 上串一个 22 欧姆的电阻减少反射。还有一个优化技巧批量写入时不要每写一个字节都拉高再拉低 CS。MR25H40CDF 支持连续写入你可以在一次 CS 低电平期间写入多个字节。比如写 256 个字节只需要发一次 WREN、一次 WRITE、一次地址然后连续发 256 个数据字节最后拉高 CS。这样比每字节单独操作快 3 倍以上。4.3 数据保持与寿命实测MRAM 的数据保持时间标称 20 年写入寿命标称 10^14 次。这两个参数我没办法在实验室里验证 20 年但写入寿命我做过加速测试用 1kHz 的频率连续写入同一个地址跑了 30 天累计写入 26 亿次芯片依然正常工作。按这个速度要写到 10^14 次需要 3000 多年所以实际使用中根本不用担心寿命问题。数据保持方面我有一块 2018 年做的板子上面有一颗 MR25H40CDF断电放了 5 年多最近拿出来上电读取数据完好无损。当然这只是个例不能代表所有情况但至少说明在常温环境下数据保持能力是可靠的。温度方面MR25H40CDF 的工业级版本支持 -40°C 到 85°C。我在 -20°C 的冷库里测试过读写正常在 70°C 的烘箱里也测试过读写正常。但要注意高温下数据保持时间会缩短如果长期工作在 85°C 以上建议定期刷新数据。4.4 常见问题与避坑经验第一个坑不要用硬件片选。PIC18LF45K40 的 MSSP 模块在硬件片选模式下传输完一个字节会自动释放 SS而 MRAM 需要 CS 在整个命令序列期间保持低电平。所以必须用普通 GPIO 手动控制 CS。第二个坑WREN 之后必须拉高 CS。这个前面说过了但值得再强调一遍。WREN 是一个独立的命令它设置 WEL 位然后你必须拉高 CS 才能让这个设置生效。如果 WREN 之后直接发 WRITEWEL 位不会置 1写操作会被忽略。第三个坑地址要发 3 个字节。虽然 4Mbit 只需要 19 位地址但芯片要求发 3 个字节高 5 位忽略。如果你只发 2 个字节地址会错位数据写到错误的位置。第四个坑VDDQ 必须和主控 I/O 电平匹配。3.3V 主控配 3.3V VDDQ1.8V 主控配 1.8V VDDQ。如果 VDDQ 接错SPI 通信会出错而且这种错误很难查因为波形看起来是对的但数据就是不对。第五个坑不要忽略状态寄存器的 BP 位。如果你不小心把 BP 位设成了保护整个芯片写操作会静默失败不报错也不写入。我建议在初始化的时候读一次状态寄存器确认 BP 位是 0。第六个坑掉电检测阈值要留余量。LVD 阈值设 2.8V但电源从 3.3V 降到 2.8V 需要时间这段时间里电压可能已经降到 2.7V 以下了。所以 LVD 阈值要设得比 MRAM 最低工作电压高 0.1V 以上留出余量。5. 扩展应用与个人体会5.1 多芯片级联与容量扩展如果你的项目需要更大的存储容量可以把多颗 MR25H40CDF 挂在同一条 SPI 总线上用不同的 CS 引脚选择。PIC18LF45K40 的 GPIO 足够多可以控制 4 到 8 颗 MRAM。每颗 512K 字节8 颗就是 4M 字节对于大多数工业数据记录场景来说完全够用。级联的时候要注意每颗 MRAM 的 CS 独立控制SCK、SDI、SDO 共用。软件上维护一个芯片选择函数根据地址范围选择对应的 CS 引脚。比如地址 0x000000 到 0x07FFFF 对应芯片 00x080000 到 0x0FFFFF 对应芯片 1以此类推。5.2 与文件系统的结合如果你需要在 MRAM 上实现文件系统我建议用 LittleFS 或者 SPIFFS。这两个文件系统都是为嵌入式设计的支持掉电保护而且代码量不大。LittleFS 的擦除块大小可以配置MRAM 不需要擦除所以可以把擦除块设成 1 个字节这样写入效率最高。不过说实话对于大多数工业数据记录场景直接用循环存储加 CRC 校验就够了不需要上文件系统。文件系统会带来额外的复杂性和开销而且 MRAM 的写入寿命无限不需要考虑磨损均衡文件系统的优势发挥不出来。5.3 个人经验总结这套方案我从 2018 年开始用前后做了十几个项目最长的已经连续运行了 5 年多没有出现过数据丢失的情况。MRAM 的可靠性确实比 EEPROM 和 Flash 高一个档次虽然价格贵一些但在工业场景下数据丢了造成的损失远大于芯片本身的差价。PIC18LF45K40 这颗单片机也很稳XC8 编译器虽然优化不如 GCC但胜在稳定不会出现莫名其妙的编译错误。而且 Microchip 的文档写得很详细数据手册里每个寄存器都有说明不像某些国产芯片那样文档残缺。最后分享一个小技巧在 MRAM 里预留一块区域作为日志区记录每次上电、掉电、复位的原因和时间戳。这个日志在排查现场问题时特别有用客户说设备重启了你一看日志就知道是掉电还是看门狗复位省得来回扯皮。日志区不需要很大1K 字节足够存几百条记录循环覆盖就行。
返回列表