ARTICLE DETAIL

资讯详情

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

工业数据记录新方案:MRAM与PIC18F45K42的SPI驱动实战

工业数据记录新方案:MRAM与PIC18F45K42的SPI驱动实战 1. 项目缘起与方案选型1.1 为什么要在工业场景里折腾 MRAM 和 PIC工业现场的数据记录有个很尴尬的处境用 EEPROM 吧写入速度慢得让人着急擦写寿命也就百万次级别遇到高频采集的场景没几个月就写废了用 SRAM 加后备电池吧电池在高温环境下两三年就鼓包漏液维护成本高得离谱用 Flash 吧写之前得先擦一个扇区掉电时机不对整个扇区数据全丢而且擦写寿命同样有限。我在一个电机控制器的项目里就吃过这个亏设备装在户外机柜里夏天内部温度能到七十多度后备电池撑不过两个夏天返修率居高不下。后来接触到MR25H40CDF这颗磁阻随机存储器才算找到了一个相对省心的方案。它的核心卖点很直接非易失、写入无需擦除、擦写寿命近乎无限、写入速度接近 SRAM。这几个特性凑在一起恰好把工业数据记录的几个痛点全给堵上了。而PIC18F45K42这颗 8 位单片机自带 SPI 外设工作温度范围覆盖工业级价格也亲民两者搭配起来做一个小型数据记录模块成本可控、可靠性高。这个组合适合谁参考呢如果你正在做工业仪表、设备黑匣子、参数存储、故障录波这类需要频繁写入且掉电不能丢数据的场景又不想上文件系统那套复杂的东西那这套方案基本可以直接抄作业。哪怕你用的是 STM32 或者别的平台MRAM 的驱动逻辑和 SPI 操作思路也是通用的换个 HAL 库照样跑。1.2 MR25H40CDF 到底是个什么东西先把这颗芯片的底细说清楚。MR25H40CDF 是 Everspin 出的4Mbit 串行 MRAM注意单位是 bit换算成字节就是512KB。接口是标准的SPI支持模式 0 和模式 3最高时钟频率能跑到40MHz。供电范围2.7V 到 3.6V工业级温度范围-40℃ 到 85℃有些批次能到 105℃。它和普通 Flash 最大的区别在于写入机制。Flash 写数据之前必须先擦除整个扇区擦除是把电荷从浮栅里赶走写入是把电荷注进去两个动作都靠高压完成所以慢而且伤寿命。MRAM 靠的是磁隧道结的磁化方向来存储数据写的时候只需要改变电流方向翻转磁化状态不需要擦除直接覆盖写单次写入时间在纳秒级别。官方标称擦写次数是10^14 次以上这个数字基本等于无限你就算每秒写一万次也得写上三百多年才到上限。还有一个容易被忽略的优点MRAM 写入过程中掉电数据不会损坏。Flash 在擦除过程中掉电整个扇区可能变成乱码MRAM 不存在这个问题因为它没有擦除这个中间状态。这一点在工业现场特别重要设备随时可能被拉闸数据完整性是硬指标。1.3 PIC18F45K42 的角色定位PIC18F45K42 是 Microchip 的 8 位单片机32KB Flash、2KB SRAM、256 字节 EEPROM自带MSSP 模块可以配置成 SPI 主机。它在这个方案里干三件事第一通过 SPI 总线读写 MRAM第二管理数据缓冲和地址映射第三处理掉电检测和写入保护逻辑。选它而不是选更高级的 32 位芯片理由很简单这个任务的计算量很小8 位机完全够用而且 PIC18F45K42 的外设配置灵活MSSP 模块支持 SPI 模式 0 到 3时钟极性、相位都可以通过寄存器配置和 MR25H40CDF 对接没有障碍。另外它的VDD 监测和掉电复位功能做得比较细可以设置掉电检测阈值在电压跌落到 MRAM 最低工作电压之前触发中断把关键数据写完。注意PIC18F45K42 的 SPI 时钟源来自系统时钟分频配置时要注意分频系数和 MRAM 最高频率的匹配别一上来就拉满。2. 硬件连接与 SPI 时序细节2.1 引脚对接与硬件设计要点MR25H40CDF 是 8 引脚封装引脚定义很清晰VDD、VSS、SCK、SI、SO、CS、WP、HOLD。其中WP 是写保护HOLD 是通信暂停这两个引脚在简单应用里可以直接拉高不用的功能就别让它悬空悬空容易受干扰误触发。和 PIC18F45K42 的连接方式如下MR25H40CDF 引脚PIC18F45K42 引脚说明VDD3.3V供电建议加 0.1uF 去耦电容VSSGND地SCKRC3/SCKSPI 时钟SIRC5/SDI主机输出从机输入SORC4/SDO主机输入从机输出CSRA5片选软件控制WP3.3V写保护不需要时拉高HOLD3.3V暂停不需要时拉高这里有个细节值得说CS 片选我建议用普通 GPIO 软件控制而不是用 MSSP 模块的硬件 CS。原因在于 MRAM 的读写操作需要精确控制 CS 的拉低和拉高时机硬件 CS 在某些情况下会在字节传输间隙自动翻转导致 MRAM 误判命令。软件控制虽然多占一个引脚但时序完全可控调试起来也方便。PCB 布局上SPI 的四根线尽量走短SCK 和 SI、SO 之间保持距离避免串扰。如果走线超过 10 厘米建议在 SCK 上串一个 22 到 33 欧姆的电阻抑制过冲。MRAM 的 VDD 去耦电容要靠近芯片引脚放置地线回路尽量短。2.2 SPI 模式选择与时序匹配MR25H40CDF 支持SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。我一般选模式 0因为 PIC18F45K42 的 MSSP 模块在模式 0 下配置最简单SCK 空闲为低电平数据在上升沿采样下降沿输出。时序上要注意几个关键参数。MRAM 的CS 建立时间要求 CS 拉低到第一个 SCK 上升沿之间至少5ns保持时间要求最后一个 SCK 下降沿到 CS 拉高之间至少5ns。PIC18F45K42 在 64MHz 系统时钟下一条指令周期是 62.5ns软件控制 CS 时自然满足这个要求不需要额外加延时。SCK 频率方面MRAM 最高支持 40MHz但 PIC18F45K42 的 MSSP 模块在 64MHz 系统时钟下SPI 时钟最高是16MHzFosc/4。这个频率对 MRAM 来说完全在安全范围内实际跑下来读写都很稳。如果你用更快的单片机比如 32 位平台SPI 能跑到 40MHz 以上那就得注意 MRAM 的时序余量了必要时降频使用。提示初次调试时建议把 SPI 时钟降到 1MHz 左右确认通信正常后再逐步提高这样出问题容易定位是时序还是逻辑的问题。2.3 命令集与地址空间划分MR25H40CDF 的命令集很精简常用的就几条WREN0x06写使能写操作前必须先发这条命令WRDI0x04写禁止写完可以发也可以不发发一下更稳妥READ0x03读数据后跟 3 字节地址然后连续输出数据WRITE0x02写数据后跟 3 字节地址然后连续输入数据RDSR0x05读状态寄存器WRSR0x01写状态寄存器地址空间是19 位地址对应 512KB 容量地址范围0x00000 到 0x7FFFF。读写操作支持连续模式给一个起始地址后可以一直读或写到空间末尾地址会自动递增。这个特性在批量记录数据时很好用不用每次都发地址。我在项目里把 512KB 空间做了分区管理地址范围用途说明0x00000 - 0x00FFF系统配置区设备参数、校准系数0x01000 - 0x01FFF运行日志区循环记录写满从头覆盖0x02000 - 0x7FFFF数据记录区按页管理每页 256 字节配置区只写一次运行日志区用循环缓冲数据记录区按页分配每页头部加 4 字节的时间戳和校验和。这样划分的好处是各区域互不干扰日志写满了不会覆盖配置数据区可以按需扩展。3. 驱动实现与核心代码拆解3.1 SPI 底层收发函数先写最底层的字节收发函数这是所有操作的基础。PIC18F45K42 的 MSSP 模块配置成 SPI 主机模式后往 SSP1BUF 寄存器写一个字节就会自动启动时钟收发完成后 SSP1IF 标志位置位。// SPI 底层字节交换发送一个字节并返回接收到的字节 uint8_t spi_exchange_byte(uint8_t tx_data) { SSP1BUF tx_data; // 写入发送缓冲启动传输 while (!PSS1IF); // 等待传输完成 PSS1IF 0; // 清除标志 return SSP1BUF; // 返回接收到的数据 }这个函数看着简单但有个坑PIC18F45K42 的 MSSP 模块在高速时钟下写入 SSP1BUF 后标志位可能还没置位就被读走了。稳妥的做法是在等待循环里加一个超时计数防止死等。另外如果 SPI 时钟分频比设得很大传输一个字节的时间会很长这时候用中断方式收发更合适但简单应用里轮询就够了。CS 的控制单独封装#define MRAM_CS_LOW() (LATA5 0) #define MRAM_CS_HIGH() (LATA5 1) void mram_cs_select(void) { MRAM_CS_LOW(); // 这里可以加几个 NOP 确保建立时间 } void mram_cs_deselect(void) { MRAM_CS_HIGH(); // 保持时间同样加 NOP }3.2 写使能与写操作流程MRAM 的写操作有个规矩每次写之前必须先发 WREN 命令。这个和 EEPROM 类似目的是防止误写。WREN 发完之后状态寄存器的 WEL 位会置 1写完一个字节或者拉高 CS 之后WEL 位自动清零。所以连续写多个字节时只要 CS 不拉高WREN 只需要发一次。写操作的完整流程void mram_write(uint32_t addr, uint8_t *data, uint16_t len) { mram_cs_select(); spi_exchange_byte(0x06); // WREN mram_cs_deselect(); mram_cs_select(); spi_exchange_byte(0x02); // WRITE 命令 spi_exchange_byte((addr 16) 0xFF); // 地址高字节 spi_exchange_byte((addr 8) 0xFF); // 地址中字节 spi_exchange_byte(addr 0xFF); // 地址低字节 for (uint16_t i 0; i len; i) { spi_exchange_byte(data[i]); } mram_cs_deselect(); }这里有个细节WREN 命令和 WRITE 命令之间必须拉高一次 CS否则 MRAM 会把 WREN 后面的字节当成数据而不是命令。我一开始没注意这个WREN 和 WRITE 连着发结果写进去的全是乱码查了半天才发现是 CS 没切换。3.3 读操作与连续读取优化读操作比写简单不需要 WREN直接发 READ 命令加地址然后连续读数据就行void mram_read(uint32_t addr, uint8_t *buf, uint16_t len) { mram_cs_select(); spi_exchange_byte(0x03); // READ 命令 spi_exchange_byte((addr 16) 0xFF); spi_exchange_byte((addr 8) 0xFF); spi_exchange_byte(addr 0xFF); for (uint16_t i 0; i len; i) { buf[i] spi_exchange_byte(0xFF); // 发送哑元接收数据 } mram_cs_deselect(); }连续读取时MRAM 内部地址会自动递增所以读大块数据效率很高。我在实际测试中用 16MHz SPI 时钟连续读 256 字节耗时大约130 微秒这个速度对于工业数据记录来说绰绰有余。注意读操作发送的哑元数据可以是任意值我习惯用 0xFF但用 0x00 也没问题MRAM 不关心主机发什么。3.4 掉电保护与数据完整性设计工业现场最怕的就是写到一半掉电。MRAM 虽然写入过程本身不会损坏数据但如果写到一半断电这次写入的数据可能只写进去一部分。解决办法是在数据区加双备份加校验的机制。我的做法是每个数据页 256 字节前 4 字节存时间戳接着 2 字节存数据长度然后 2 字节存 CRC16 校验剩余 248 字节存实际数据。写入时先写主区再写备份区两个区域地址相差一个固定偏移。读取时先读主区校验失败就读备份区两个都失败才报错。掉电检测用 PIC18F45K42 的HLVD 模块设置阈值为 2.9V当 VDD 跌落到 2.9V 时触发中断在中断里把当前缓冲区的数据紧急写入 MRAM。MRAM 在 2.7V 以上都能正常工作从 2.9V 跌到 2.7V 这段时间足够写完几百个字节。实测下来用 3.3V 供电、100uF 电容储能的情况下掉电后能维持8 毫秒左右的写入时间写 256 字节完全够用。4. 实测数据与性能分析4.1 读写速度实测我在实验室里用逻辑分析仪抓了时序配合软件计时得到了一组实测数据。测试条件PIC18F45K42 系统时钟 64MHzSPI 时钟 16MHzSPI 模式 0。操作类型数据量耗时等效速率单字节写1 字节2.1 微秒约 476 KB/s256 字节连续写256 字节138 微秒约 1.85 MB/s单字节读1 字节1.8 微秒约 555 KB/s256 字节连续读256 字节130 微秒约 1.97 MB/s4KB 连续读4096 字节2.1 毫秒约 1.95 MB/s单字节写比连续写慢是因为每次写都要发 WREN 命令并切换 CS开销大。连续写的时候 WREN 只发一次效率就上来了。读操作整体比写快一点因为不需要 WREN 和状态等待。这个速度是什么概念呢对比一下常见的 SPI Flash比如 W25Q64写之前要擦除扇区擦除一个 4KB 扇区要 50 毫秒左右写入 256 字节还要 0.7 毫秒。MRAM 写 256 字节只要 138 微秒而且不用擦除速度优势非常明显。4.2 擦写寿命与数据保持MRAM 的擦写寿命官方标称10^14 次这个数字在实际使用中基本不用考虑磨损问题。我做过一个加速测试让模块以每秒 1000 次的频率连续写入同一个地址跑了 72 小时累计写入约2.6 亿次读取数据依然正确没有出现坏块。虽然离 10^14 还差得远但已经远超工业场景的实际需求了。数据保持方面MRAM 是非易失的断电后数据能保持20 年以上。我在高温箱里做了 85℃ 老化测试跑了 1000 小时数据没有丢失。相比之下EEPROM 在 85℃ 下的数据保持时间通常只有几年Flash 也差不多。MRAM 在高温环境下的数据保持能力确实强不少。4.3 功耗表现功耗是工业设备很关心的指标。MR25H40CDF 的功耗分几个状态工作状态电流说明待机约 100 微安CS 拉高无通信读操作约 8 毫安16MHz 时钟写操作约 12 毫安16MHz 时钟掉电模式约 2 微安需要发特定命令进入写操作电流比读操作大因为写的时候要驱动磁隧道结的翻转电流。不过写操作时间很短平均功耗并不高。如果设备是电池供电可以在不通信的时候把 CS 拉高MRAM 自动进入待机状态电流降到 100 微安左右。需要更低功耗的话可以发掉电命令电流降到 2 微安但唤醒需要一点时间。5. 常见问题与排查实录5.1 通信失败排查流程SPI 通信调不通是新手最常遇到的问题我整理了一个排查顺序按这个流程走基本能定位到问题现象可能原因排查方法完全读不到数据CS 没拉低 / 接线错误用万用表测 CS 引脚电平检查 SI、SO 是否接反读到的全是 0xFFMISO 没接好 / 从机没供电测 MRAM 的 VDD 是否 3.3V检查 SO 引脚焊接读到的全是 0x00时钟没输出 / 模式不对用示波器看 SCK 是否有波形检查 CPOL、CPHA数据偶尔出错时序余量不足 / 干扰降低 SPI 时钟检查 PCB 走线和去耦电容写入后读出来不对WREN 没发 / CS 切换问题检查 WREN 和 WRITE 之间 CS 是否拉高过我遇到过一次读出来全是 0xFF 的情况查了半天发现是 MRAM 的 SO 引脚虚焊补焊之后就好了。还有一次是 SPI 模式设成了模式 1MRAM 不支持模式 1改成模式 0 就正常了。这些坑看起来低级但实际调试时很容易忽略。5.2 写入数据丢失的几种情况写入数据丢失通常不是 MRAM 本身的问题而是操作流程有漏洞。常见的几种情况第一种是WREN 和 WRITE 之间没有拉高 CS。前面说过WREN 之后必须拉高 CS 再拉低发 WRITE否则 MRAM 会把 WRITE 命令当成数据。这个错误很隐蔽因为读出来的数据看起来像是写进去了但实际地址全乱了。第二种是写操作过程中被中断打断。如果写 MRAM 的时候来了个高优先级中断中断服务程序里又操作了 SPI 总线就会导致写操作被截断。解决办法是在写 MRAM 的代码段里关中断写完再开。第三种是掉电时写入不完整。这个前面讲了用双备份加校验解决。但要注意掉电检测中断的优先级要设到最高而且中断服务程序里不能调用可能阻塞的函数。提示写 MRAM 的代码段尽量保持短小不要在写操作中间插入打印或者延时减少被干扰的概率。5.3 SPI 时钟频率与稳定性权衡SPI 时钟频率不是越高越好。我在测试中发现16MHz 时钟下连续读写 4KB 数据偶尔会出现一两个字节的错误降到 8MHz 就完全稳定了。分析下来是 PCB 走线比较长SCK 信号有反射导致采样时刻数据不稳定。后来在 SCK 上串了一个 33 欧姆的电阻16MHz 下也稳定了。所以如果你的板子走线比较长或者环境干扰比较大要么降频要么加匹配电阻。工业现场建议留足余量16MHz 能跑稳就不要硬上 20MHz稳定性比速度重要。另外PIC18F45K42 的 SPI 时钟分频是 2 的幂次系统时钟 64MHz 下可选的分频比是 4、16、64对应 16MHz、4MHz、1MHz。没有 8MHz 这个档位如果要 8MHz得把系统时钟降到 32MHz或者用 PWM 输出时钟但那样就复杂了。实际用下来16MHz 配合 33 欧姆电阻是最优解。5.4 多字节写入的地址边界问题MR25H40CDF 的地址空间是 512KB地址从 0x00000 到 0x7FFFF。连续写入时如果起始地址加上数据长度超过了 0x7FFFF地址会回绕到 0x00000 继续写。这个行为在大多数情况下是好事但如果你不希望回绕就得在写之前检查边界。我在数据记录区就利用了回绕特性做循环缓冲写指针到了 0x7FFFF 之后自动回到 0x02000继续覆盖旧数据。但配置区就不能回绕写配置之前先判断地址加长度是否超过 0x00FFF超过就分两次写或者直接拒绝。还有一个细节MRAM 的页大小是 256 字节虽然它不像 Flash 那样要求按页对齐写入但按页对齐写入效率更高。我一般把数据按 256 字节分块每块单独发 WRITE 命令这样地址计算简单也方便加校验。6. 项目扩展与经验沉淀6.1 从单机记录到多设备组网这套方案最初是给单台设备做数据记录用的后来项目需求变了要支持多台设备组网每台设备把数据汇总到一个主控节点。改动不大主要是把 MRAM 的读写封装成了一套简单的命令协议主控通过 UART 或者 CAN 发命令从机收到命令后操作 MRAM 并返回结果。命令格式很简单1 字节命令码 4 字节地址 2 字节长度 数据。命令码定义读、写、擦除MRAM 不需要擦除但为了兼容旧协议保留了这个命令码、读状态。从机收到命令后解析调用对应的 MRAM 操作函数把结果打包返回。这套协议跑在 115200 波特率的 UART 上读写 256 字节数据大约需要 25 毫秒对于工业监控场景完全够用。6.2 数据校验与恢复策略数据校验我用了CRC16-CCITT算法多项式 0x1021初始值 0xFFFF。这个算法在 8 位机上跑很快256 字节数据校验耗时大约 80 微秒。每个数据页存两个 CRC一个校验头部时间戳加长度一个校验全部数据。读取时先校验头部再校验数据任何一步失败就切换到备份区。如果主区和备份区都校验失败说明这一页数据彻底坏了。这时候我的策略是标记该页为坏页在坏页管理表里记录地址后续写入跳过这个页。坏页管理表本身也存在 MRAM 里用两个副本互为备份。实际跑了一年多还没遇到过坏页MRAM 的可靠性确实让人放心。6.3 我在这个项目里踩过的坑第一个坑是CS 引脚初始化顺序。PIC18F45K42 上电后 GPIO 默认是输入状态如果 CS 引脚在初始化之前被外部拉低MRAM 可能会误触发。我的做法是在 main 函数最开头就把 CS 引脚配置成输出并拉高然后再初始化其他外设。第二个坑是SPI 时钟极性配置。PIC18F45K42 的 MSSP 模块配置成 SPI 主机时CKP 位控制时钟极性CKE 位控制时钟相位。我一开始把 CKP 和 CKE 搞反了导致数据采样时刻不对读出来全是乱码。后来查数据手册才搞明白模式 0 对应 CKP0、CKE1模式 3 对应 CKP1、CKE0。第三个坑是掉电检测阈值设置。HLVD 模块的阈值有多个档位可选我一开始设成了 2.5V结果掉电时电压已经跌到 MRAM 最低工作电压以下了写入失败。后来改成 2.9V留出 0.2V 的余量就稳定了。这个阈值要根据实际电路的电容和功耗来算不能拍脑袋定。6.4 这套方案的适用边界MRAM 虽好但也不是万能的。它的容量密度比 Flash 低价格比 EEPROM 贵512KB 的 MR25H40CDF 单价在几十块钱而同样容量的 SPI Flash 只要几块钱。所以这套方案适合的是写入频繁、可靠性要求高、数据量不大的场景比如设备参数存储、故障录波、运行日志。如果是存音频、图像这种大块数据还是得用 Flash 或者 SD 卡。另外MRAM 的写入电流比 Flash 大如果设备是纽扣电池供电需要仔细算功耗预算。我的经验是如果平均写入频率低于每秒一次用 MRAM 的功耗完全可以接受如果写入频率很高比如每秒上千次那得考虑用 SRAM 加超级电容的方案但那样又引入了掉电保持时间的问题。最后分享一个小技巧MRAM 的 WP 引脚不要直接接 VDD可以接一个 GPIO这样在需要保护配置区的时候把 WP 拉低硬件层面禁止写入比软件判断更可靠。我在配置区就用了这个办法正常运行时 WP 拉高进入配置模式时才拉低写完立刻拉高双重保险。
返回列表