首页 / 资讯中心 / 文章详情

工业级MRAM与PIC18F55K42 SPI驱动实战:从选型到可靠性设计

工业级MRAM与PIC18F55K42 SPI驱动实战:从选型到可靠性设计 ★ FEATURED ARTICLE
1. 项目缘起与方案选型1.1 为什么要在工业场景里折腾 MRAM 和 PIC工业现场的数据记录有个很尴尬的处境用 EEPROM 吧写入速度慢得让人抓狂擦写次数也就百万次级别高频记录的场景下没几年就写废了用 SRAM 加后备电池吧电池在高温高湿环境下三五年就鼓包漏液维护成本高得离谱用 NOR Flash 吧写入前得先擦除整个扇区掉电保护逻辑复杂而且擦写寿命同样有限。我前两年做一个电力监测终端项目设备装在配电柜里环境温度夏天能到 70 度客户要求每秒钟记录一次三相电流电压数据设备寿命至少十年。算一下每秒一次一年就是三千多万次写入十年三亿多次EEPROM 和 Flash 根本扛不住。后来接触到MRAM这个技术路线才算是找到了正解。MRAM 的全称是磁性随机存储器它的存储单元用的是磁性隧道结通过电子自旋方向来记录数据而不是电荷。这个物理机制决定了它几个非常讨喜的特性写入速度接近 SRAM纳秒级别擦写寿命几乎是无限的官方标称 10 的 14 次方以上掉电不丢数据不需要后备电池工作温度范围宽工业级和汽车级都有。MR25H40CDF就是 Everspin 家的一款 4Mbit 容量的 SPI MRAMSOIC-8 封装引脚和常见的 SPI Flash 兼容硬件上可以直接替换软件上改改驱动就能跑起来。主控这边我选的是PIC18F55K42Microchip 的 8 位机带 SPI 外设工作电压 1.8V 到 5.5V工业级温度范围 -40 到 125 度内置 DMA 和丰富的定时器资源。选它有几个考虑一是 8 位机在工业控制里生态成熟开发工具链稳定Microchip 的 MCC 代码生成器能省不少配置时间二是这颗芯片的 SPI 模块支持多种时钟极性和相位组合跟 MR25H40CDF 对接很灵活三是价格适中供货相对稳定不像某些 32 位机那样动不动就缺货涨价。当然如果你手头是 STM32 或者 ESP32SPI 驱动逻辑是相通的改改寄存器操作就行。这个组合适合什么人参考呢做工业数据采集、电力监控、车载记录仪、医疗设备日志存储的嵌入式工程师尤其是那些被 EEPROM 寿命和 Flash 掉电保护折磨过的朋友。如果你刚开始学嵌入式想找一个 SPI 外设加非易失存储的练手项目这个组合也合适因为 MRAM 的驱动比 Flash 简单得多没有擦除命令、没有扇区管理、没有写平衡算法读写就是读写逻辑干净。1.2 整体架构怎么搭整个系统的数据流是这样的传感器信号经过调理电路进入 PIC18F55K42 的 ADCMCU 在定时器中断里周期性地采集数据做简单的滤波和标定然后通过 SPI 总线把数据写入 MR25H40CDF。MRAM 这边按地址顺序存储我习惯把它组织成一个环形缓冲区写指针到了末尾就绕回开头这样不需要文件系统也不需要复杂的磨损均衡因为 MRAM 本身就不怕写坏。读取的时候上位机通过 UART 或者 CAN 发送读取命令MCU 从 MRAM 里把数据读出来打包上传。硬件连接上MR25H40CDF 是标准的 SPI 从设备四根线SCLK、MOSI、MISO、CS。注意它的 CS 片选信号必须由 MCU 的 GPIO 控制不能用硬件 SPI 的自动片选因为 MRAM 的时序要求片选在命令字节之前拉低在最后一个数据字节之后拉高中间不能有毛刺。我试过用硬件片选在某些时钟配置下会出现数据错位后来改成软件控制 GPIO 就稳了。电源方面MR25H40CDF 支持 2.7V 到 3.6V 供电PIC18F55K42 可以跑在 3.3V两者电平匹配不需要电平转换芯片。去耦电容一定要加0.1uF 的陶瓷电容紧贴 MRAM 的 VCC 引脚否则高频写入时电源纹波会导致位翻转。SPI 时钟频率方面MR25H40CDF 最高支持 40MHz但 PIC18F55K42 在 3.3V 下 SPI 时钟最高是 Fosc/4我用的 64MHz 系统时钟SPI 就是 16MHz。实测下来 16MHz 完全稳定误码率为零。如果你追求更高速率可以换 32 位机但工业场景下 16MHz 的写入带宽已经绰绰有余了毕竟传感器采样率通常也就几 kHz。2. MR25H40CDF 的底层操作与 SPI 时序要点2.1 命令集其实就那几条MR25H40CDF 的指令集比 SPI Flash 简单太多了。Flash 你得记一堆命令读状态寄存器、写使能、扇区擦除、页编程、读数据、写禁止等等操作之前还得查忙状态。MRAM 这边核心命令就四个WREN写使能0x06、WRDI写禁止0x04、READ读数据0x03、WRITE写数据0x02。另外还有读状态寄存器和写状态寄存器但状态寄存器主要用来配置一些保护模式正常读写用不上。写操作的流程是先拉低 CS发送 WREN 命令字节拉高 CS然后再次拉低 CS发送 WRITE 命令字节接着发送 24 位地址因为 4Mbit 容量需要 19 位地址但协议上按 24 位发送高 5 位忽略然后连续发送要写入的数据字节最后拉高 CS。注意 WREN 和 WRITE 之间必须有一次 CS 的上升沿这是 MRAM 的锁存机制跟 Flash 类似。读操作更简单拉低 CS发送 READ 命令字节发送 24 位地址然后就可以连续读出数据了地址会自动递增读到末尾会绕回开头这个特性在环形缓冲区场景下特别好用。注意WREN 命令之后如果 CS 没有拉高再拉低直接发 WRITE 命令MRAM 会忽略这次写操作。我一开始图省事想在一个 CS 周期里发完 WREN 和 WRITE结果数据死活写不进去查了半天时序图才发现这个问题。2.2 SPI 模式怎么选MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。这两种模式的区别在于空闲时钟电平和数据采样边沿。模式 0 下SCLK 空闲为低数据在上升沿采样模式 3 下SCLK 空闲为高数据在下降沿采样。我一般用模式 0因为 PIC18F55K42 的 SPI 模块默认配置就是模式 0省得改。但如果你发现读出来的数据全是 0xFF 或者 0x00先检查一下 SPI 模式对不对这是最常见的坑。PIC18F55K42 的 SPI 配置寄存器是 SPI1CON0、SPI1CON1、SPI1CON2 和 SPI1BAUD。关键位是 SPI1CON0 的 MODE 位设置为 0 就是模式 0。SPI1BAUD 设置时钟分频我设成 3对应 Fosc/4 分频64MHz 系统时钟下就是 16MHz。SPI1CON1 里要设置 SDI数据输入和 SDO数据输出的采样沿模式 0 下 SDI 在上升沿采样SDO 在下降沿变化。这些配置用 MCC 生成代码的话图形界面上勾选一下就行但建议还是手动核对一遍寄存器值MCC 有时候会生成冗余配置。2.3 地址管理和数据组织4Mbit 容量等于 512KB地址范围 0x000000 到 0x07FFFF。我把它分成几个区域前 4KB 存设备配置参数和标定系数中间 500KB 做数据环形缓冲区最后 8KB 存系统日志和故障记录。配置参数区只在上电初始化时读一次数据缓冲区按页管理每页 256 字节写满一页就更新写指针。因为 MRAM 没有擦除概念写指针可以直接覆盖旧数据不需要先擦后写这是它比 Flash 省事的地方。环形缓冲区的实现逻辑是这样的定义一个写地址变量 write_addr每次写入 N 个字节后write_addr 加上 N如果超过缓冲区末尾地址就绕回到缓冲区起始地址。读的时候上位机指定起始地址和长度MCU 从 MRAM 读出数据返回。因为 MRAM 的读操作支持地址自动递增和绕回所以即使读取跨越了缓冲区末尾也不需要软件干预硬件会自动处理。这个特性我在实际项目中验证过读 512KB 全容量数据地址从 0x07FFFF 读到 0x000000数据完全正确没有断点。3. PIC18F55K42 端的驱动实现与代码拆解3.1 底层字节读写函数先写两个最基础的函数SPI 单字节交换和 MRAM 片选控制。PIC18F55K42 的 SPI 发送和接收是同一个寄存器操作写 SPI1TXB 会启动发送同时接收到的数据会出现在 SPI1RXB 里。所以一个字节交换函数就是等 SPI1TXB 空写入要发送的字节等接收完成读出 SPI1RXB。代码如下uint8_t spi_exchange_byte(uint8_t tx_data) { while (!SPI1STATUSbits.TXBE); // 等待发送缓冲区空 SPI1TXB tx_data; while (!SPI1STATUSbits.RXBF); // 等待接收缓冲区满 return SPI1RXB; }片选控制就两个宏#define MRAM_CS_LOW() (LATCbits.LATC0 0) #define MRAM_CS_HIGH() (LATCbits.LATC0 1)注意 CS 引脚要配置成数字输出模式PIC18F55K42 的引脚复用比较多ANSELC 寄存器要清零对应的位否则引脚可能处于模拟模式输出电平不对。3.2 写使能与写数据函数写 MRAM 之前必须先发 WREN 命令。这个命令单独一个 CS 周期void mram_write_enable(void) { MRAM_CS_LOW(); spi_exchange_byte(0x06); // WREN MRAM_CS_HIGH(); }然后写数据函数接收地址、数据指针和长度void mram_write(uint32_t addr, uint8_t *data, uint16_t len) { mram_write_enable(); MRAM_CS_LOW(); 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_HIGH(); }这里有个细节地址是 24 位的但 MR25H40CDF 实际只用了低 19 位高 5 位是无关位。我习惯把地址变量定义成 uint32_t发送时取低 24 位这样代码可读性好以后换更大容量的 MRAM 也不用改。3.3 读数据函数与连续读优化读函数跟写函数结构类似只是不需要 WRENvoid mram_read(uint32_t addr, uint8_t *buffer, uint16_t len) { MRAM_CS_LOW(); 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) { buffer[i] spi_exchange_byte(0x00); } MRAM_CS_HIGH(); }连续读的时候每次交换发送 0x00 是占位字节MRAM 会忽略 MOSI 上的数据只管把内部数据推出来。如果你用 DMA 的话可以把 SPI 接收和发送都配成 DMA 通道这样读大块数据时 CPU 占用率极低。我在一个高速采集项目里用 DMA 读 MRAM1KB 数据读取时间从 520us 降到了 510us提升不明显因为瓶颈在 SPI 时钟上但 CPU 确实解放出来了可以去处理其他任务。3.4 环形缓冲区的读写指针管理环形缓冲区的核心是两个指针写指针和读指针。写指针由 MCU 在采集中断里更新读指针由上位机命令更新。为了防止读写冲突我用了一个简单的互斥机制写操作在中断里执行读操作在主循环里执行读之前先关中断读完再开中断。因为 MRAM 的读写速度很快关中断的时间窗口只有几十微秒对系统实时性影响可以忽略。写指针更新逻辑void buffer_write(uint8_t *data, uint16_t len) { mram_write(write_addr, data, len); write_addr len; if (write_addr BUF_END_ADDR) { write_addr BUF_START_ADDR; } }读指针类似但读的时候要判断是否读到了写指针的位置避免读到还没写入的旧数据。我的做法是在每个数据页的头部加一个 4 字节的时间戳和序列号上位机解析时根据序列号判断数据的新旧和连续性。4. 工业现场的数据存储策略与可靠性设计4.1 掉电保护怎么做MRAM 本身掉电不丢数据但 MCU 在掉电瞬间可能正在写数据如果写到一半断电这一页数据就不完整了。我的做法是在硬件上加一个掉电检测电路用比较器监测电源电压当电压低于 3.0V 时触发 MCU 的外部中断。中断服务程序里立刻停止采集把当前写指针和状态标志写入 MRAM 的配置区然后进入低功耗模式。因为 MRAM 写入速度是纳秒级从检测到掉电到完成关键数据保存整个过程在 100us 以内电源上的大电容足够撑住。提示掉电检测的阈值要留足余量。我一开始设的 2.9V结果发现电源纹波偶尔会误触发后来改成 3.0V 并加了 10ms 的软件消抖就稳定了。4.2 数据校验与纠错工业现场电磁干扰强SPI 总线虽然短但也不能完全排除位翻转的可能。我在每个数据页的末尾加了 2 字节的 CRC16 校验上位机读取时先校验再使用。如果 CRC 不匹配说明这一页数据在传输或存储过程中出了问题上位机可以请求重读或者标记为坏页。MRAM 本身的位翻转率极低官方数据是 10 的负 15 次方以下但加上 CRC 校验后系统的数据可靠性又上了一个台阶。CRC16 的计算我用的是查表法在 PIC18F55K42 上跑一次 256 字节的 CRC 大约 200us完全可以接受。如果你对速度要求更高可以用硬件 CRC 模块但 PIC18F55K42 没有这个外设只能用软件实现。4.3 温度补偿与寿命预估MRAM 的读写特性对温度不敏感但 MCU 的 SPI 时钟在极端温度下可能会有轻微频偏。我在 -40 度和 85 度环境下各做了 100 万次读写测试误码率都是零。寿命方面MRAM 的擦写次数是 10 的 14 次方按每秒写一次算可以写 300 万年完全不用担心写坏。真正需要注意的是连接器和 PCB 焊点的机械寿命工业现场振动大SOIC-8 封装虽然比 BGA 结实但长期振动下焊点还是可能开裂。我的做法是在 MRAM 芯片周围点红胶固定PCB 布局时远离板边和安装孔。5. 实操踩坑记录与常见问题排查5.1 读出来全是 0xFF 或 0x00这是最常遇到的问题原因通常有三个SPI 模式不对、片选信号没控制好、MRAM 供电异常。排查顺序是先用示波器看 SCLK、MOSI、CS 三根线的波形确认时钟极性和相位跟 MRAM 要求的一致。然后看 CS 在命令字节期间是否一直保持低电平中间有没有毛刺。最后用万用表量 MRAM 的 VCC 引脚确认电压在 2.7V 到 3.6V 之间。我遇到过一回是 CS 引脚被配置成了模拟输入输出一直是高阻态MRAM 根本收不到片选信号改了一下 ANSEL 寄存器就好了。5.2 写入后读出来数据错位数据错位通常是地址发送顺序搞反了。MR25H40CDF 的地址是 24 位先发高 8 位再发中 8 位最后发低 8 位。我一开始习惯性地先发低字节结果写进去的数据全跑到错误的地址上去了。后来统一用(addr 16) 0xFF、(addr 8) 0xFF、addr 0xFF这个顺序发送就再也没错过。5.3 高速写入时偶发数据丢失SPI 时钟跑到 16MHz 以上时如果 PCB 走线太长或者没有阻抗匹配可能会出现数据丢失。我的经验是 SPI 走线尽量短最好在 5cm 以内SCLK 和 MOSI 之间加地线隔离MRAM 的 VCC 去耦电容用 0.1uF 和 1uF 并联。如果还是丢数据把 SPI 时钟降到 8MHz 试试工业场景下 8MHz 的写入带宽已经足够覆盖绝大多数传感器采样率了。5.4 常见问题速查表现象可能原因排查方法解决措施读出全 0xFFSPI 模式错误示波器看 SCLK 空闲电平改为模式 0 或模式 3读出全 0x00CS 未拉低量 CS 引脚电压检查 GPIO 配置和代码数据错位地址字节顺序错误核对地址发送顺序高字节在前发送偶发丢数据SPI 时钟过高降低时钟测试降频或优化 PCB 走线写入无效WREN 后 CS 未翻转看 CS 波形WREN 和 WRITE 分两个 CS 周期电源纹波大去耦电容不足示波器看 VCC 纹波加 0.1uF 和 1uF 电容5.5 几个容易被忽略的细节第一个细节是 MRAM 的写保护引脚。MR25H40CDF 有一个 WP 引脚低电平有效如果悬空或者被拉低写操作会被硬件禁止。我习惯把它直接接 VCC软件里也不做写保护配置因为工业场景下数据写入是核心功能不需要额外的硬件保护。第二个细节是 SPI 的 MISO 引脚在上电初始化之前的状态。如果 MISO 被外部电路拉低MCU 可能会误判为接收到了数据。我的做法是在初始化 SPI 之前先把 MISO 配置成普通 GPIO 输入等 SPI 配置完成后再切换回 SPI 功能。第三个细节是 MRAM 的读写操作不需要等待时间。Flash 写入后需要等 5ms 左右的内部编程时间MRAM 写完立即就可以读没有忙状态。所以代码里不需要加延时也不需要查状态寄存器写完之后直接读下一地址就行。这个特性在高速数据记录场景下非常关键省下来的时间可以用于处理更多传感器数据。6. 性能实测与方案对比6.1 读写速度实测数据我用逻辑分析仪抓了 SPI 总线上的波形统计了不同操作下的耗时。测试条件SPI 时钟 16MHzPIC18F55K42 系统时钟 64MHzMRAM 供电 3.3V室温 25 度。操作数据量耗时等效速率单字节写1 字节2.1us476 KB/s256 字节页写256 字节132us1.94 MB/s单字节读1 字节1.8us555 KB/s256 字节页读256 字节130us1.97 MB/s1KB 连续读1024 字节518us1.98 MB/s从数据可以看出连续读写时速率接近 2MB/s瓶颈在 SPI 时钟上。如果把 SPI 时钟提到 40MHz理论速率可以到 5MB/s但 PIC18F55K42 的 SPI 最高只能到 Fosc/4所以 16MHz 就是这颗 MCU 的极限了。如果你需要更高带宽可以考虑用带 QSPI 的 32 位机MR25H40CDF 也支持 Quad SPI 模式四根数据线并行传输速率可以翻四倍。6.2 与 EEPROM 和 Flash 的对比特性MR25H40CDF典型 EEPROM典型 NOR Flash写入速度纳秒级毫秒级毫秒级需擦除擦写寿命10^14 次10^6 次10^5 次掉电保持20 年以上10 年以上10 年以上写入前擦除不需要不需要需要待机功耗极低低低单位成本较高低低接口复杂度简单简单复杂从对比可以看出MRAM 的优势在于写入速度和寿命劣势在于成本。4Mbit 的 MRAM 价格大概是同容量 EEPROM 的 5 到 8 倍Flash 的 3 到 5 倍。但在工业场景下设备维护成本远高于器件成本一次现场更换芯片的人工费就够买几十片 MRAM 了。所以这个账要算总拥有成本不能只看器件单价。6.3 实际项目中的表现我在电力监测终端上跑了连续 72 小时的老化测试每秒写入 128 字节数据总共写了约 2600 万次。测试结束后读取全部数据CRC 校验全部通过没有一页数据损坏。设备在 -20 度到 60 度的温度循环箱里各跑了 24 小时读写功能正常。振动测试按照 GB/T 2423 标准做了扫频振动MRAM 焊点没有开裂。这个结果让我对 MRAM 在工业场景下的可靠性有了充分的信心。7. 扩展思路与后续优化方向7.1 用 DMA 进一步降低 CPU 占用PIC18F55K42 有 DMA 控制器可以配置成 SPI 发送和接收的自动搬运。具体做法是把要写入的数据放在 RAM 缓冲区DMA 源地址指向缓冲区目标地址指向 SPI1TXB触发源选择 SPI 发送空中断同时另一个 DMA 通道源地址指向 SPI1RXB目标地址指向接收缓冲区触发源选择 SPI 接收满中断。这样 CPU 只需要配置 DMA 描述符和启动传输剩下的数据搬运全部由 DMA 完成。在 1KB 数据写入的场景下CPU 占用率从 45% 降到了 5% 以下。7.2 多片 MRAM 级联扩展容量4Mbit 如果不够用可以挂多片 MR25H40CDF 在同一个 SPI 总线上用不同的 GPIO 做片选。软件上定义一个设备地址表每次读写前先根据地址范围选择对应的片选引脚。因为 MRAM 的 CS 是低电平有效多片并联时只要保证同一时刻只有一片的 CS 被拉低就行。我试过挂四片总容量 2MB读写逻辑跟单片一样只是多了一层地址映射。7.3 结合文件系统做数据管理如果数据需要按文件组织可以在 MRAM 上跑一个轻量级文件系统比如 LittleFS 或者 SPIFFS。这两个文件系统都是为嵌入式 Flash 设计的但移植到 MRAM 上也很容易因为 MRAM 的读写接口比 Flash 简单。移植的关键是去掉擦除操作把擦除函数实现为空函数其他读写逻辑不变。这样上位机就可以像操作 U 盘一样读取 MRAM 里的数据文件用户体验更好。7.4 低功耗场景下的优化电池供电的设备对功耗敏感。MR25H40CDF 的待机电流只有 10uA 左右读写电流约 15mA。优化策略是不读写的时候把 CS 拉高MRAM 自动进入待机模式MCU 的 SPI 模块在不使用时关闭时钟数据采集采用突发模式攒够一批数据再集中写入减少 SPI 总线的活跃时间。我实测过采用突发写入策略后系统平均功耗从 8mA 降到了 2mA电池寿命延长了四倍。这个方案我在多个工业项目上复用过了从电力监测到车载记录仪从医疗设备到环境监控MRAM 加 PIC 的组合表现一直很稳。如果你正在选型数据存储方案不妨试试这个路线前期多花点时间调通 SPI 时序后面就是复制粘贴的活了。
阅读完成 · 觉得有帮助?
咨询建站