ARTICLE DETAIL

资讯详情

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

MRAM替代EEPROM实战:MR25H40CDF与PIC18F96J94的SPI存储方案

MRAM替代EEPROM实战:MR25H40CDF与PIC18F96J94的SPI存储方案 MRAM 这东西我第一次接触的时候心里是打问号的。EEPROM 和 Flash 用了这么多年突然冒出来一个磁阻随机存储器宣传语写的是无限次擦写、纳秒级写入、掉电不丢数据听起来像是把 SRAM 的速度和 Flash 的非易失性捏在了一起。直到我在一个工业数据采集项目里被 EEPROM 的擦写寿命和写入延迟折腾得够呛才真正认真去研究 MR25H40CDF 这颗芯片并把它和手头的 PIC18F96J94 搭起来跑了一套完整的存储读取方案。这篇内容适合谁看如果你正在做工业控制、嵌入式数据记录、频繁掉电场景下的参数保存或者你单纯想找一个比 EEPROM 更靠谱的非易失存储方案那这篇从选型思路到 SPI 时序、从硬件连接到软件驱动的完整记录应该能帮你少走不少弯路。我下面会围绕 MR25H40CDF 和 PIC18F96J94 这对组合把整个存储和读取数据的实现过程拆开讲清楚包括我踩过的坑和实测下来的经验。1. 为什么在这个项目里放弃 EEPROM 转向 MRAM1.1 工业场景对存储器的真实需求先说说我遇到的具体问题。那是一个工业现场的数据采集设备主控用的是 PIC18F96J94需要每隔几百毫秒记录一次传感器数据包括温度、压力、流量这些参数。设备可能随时断电所以数据必须实时写入非易失存储器不能等攒够一批再写。一开始我用的是传统的 SPI EEPROM容量够、接口简单、成本低。但跑了一段时间发现两个致命问题第一EEPROM 的字节写入周期典型值在 5ms 左右我每 200ms 要写几十个字节虽然用页写入能缓解但写入期间 CPU 基本被阻塞影响了采集的实时性第二EEPROM 的擦写寿命通常在 100 万次左右按每天写入 10 万次算不到两周就到寿命了虽然可以做磨损均衡但软件复杂度上去了可靠性反而下降。后来换成 SPI Flash写入速度确实快了但 Flash 有个更麻烦的特性必须先擦除再写入擦除的最小单位是扇区通常 4KB而且擦除寿命只有 10 万次左右。对于我这种小数据量、高频写入的场景Flash 的架构根本不合适。MRAM 就不一样了。MR25H40CDF 是 Everspin 出的 4Mbit512KBSPI MRAM它的存储单元是磁性隧道结写入靠的是电流改变磁化方向不需要擦除操作也没有电荷泄漏的问题。官方标称的擦写次数是 10 的 14 次方以上写入周期在纳秒级数据保持时间超过 20 年。这几个参数一摆出来我就知道它是我要找的东西。1.2 MR25H40CDF 的关键参数拆解在决定用之前我把 MR25H40CDF 的数据手册翻了好几遍几个关键参数值得单独拎出来说参数数值实际意义容量4Mbit (512K x 8)存参数和短时数据记录足够接口SPI最高 40MHz和 PIC18F96J94 的 SPI 模块兼容写入周期无延迟纳秒级写入后无需等待可立即读擦写寿命10^14 次基本不用考虑寿命问题数据保持20 年工业设备生命周期内无忧工作电压2.7V ~ 3.6V注意是 3.3V 系统不是 5V工作温度-40°C ~ 85°C工业级温度范围这里有个细节要特别注意MR25H40CDF 的工作电压是 2.7V 到 3.6V典型值 3.3V。而 PIC18F96J94 虽然可以在 5V 下工作但它的 SPI 引脚在 5V 模式下输出高电平接近 5V直接连到 MRAM 的输入引脚会超过其绝对最大额定值。所以要么把 PIC 也跑在 3.3V要么做电平转换。我选择的是前者把整个系统统一到 3.3V省去了电平转换芯片也降低了功耗。1.3 和 FRAM、nvSRAM 的横向对比在选型阶段我还对比了另外两种非易失存储方案FRAM铁电存储器和 nvSRAM非易失 SRAM。FRAM 的写入速度也很快擦写寿命同样很高但它的容量通常偏小常见的是 4Kbit 到 256Kbit而且价格按位算比 MRAM 贵不少。对于我需要 512KB 容量的场景FRAM 要么容量不够要么成本太高。nvSRAM 本质上是 SRAM 加了一颗 EEPROM 做备份掉电时把 SRAM 内容拷贝到 EEPROM。它的优点是 SRAM 接口简单、速度极快但缺点也很明显需要额外的电容维持掉电拷贝过程而且 EEPROM 的擦写寿命问题依然存在只是被 SRAM 屏蔽了大部分写入操作。对于频繁掉电的场景nvSRAM 的可靠性不如 MRAM。综合下来MR25H40CDF 在容量、速度、寿命、成本之间取得了最好的平衡。它的 SPI 接口也意味着我不需要改动 PIC18F96J94 的硬件设计直接用现有的 SPI 模块就能驱动。2. PIC18F96J94 的 SPI 模块配置与硬件连接2.1 硬件连线与片选策略MR25H40CDF 和 PIC18F96J94 的连接很直接标准的 SPI 四线制加上片选SCKPIC 的 SPI 时钟输出接到 MRAM 的 SCKSDIPIC 的 SPI 数据输入接到 MRAM 的 SO数据输出SDOPIC 的 SPI 数据输出接到 MRAM 的 SI数据输入CS片选信号用 PIC 的一个普通 GPIO 控制这里有个容易搞混的地方PIC 的 SDO 要接 MRAM 的 SIPIC 的 SDI 要接 MRAM 的 SO因为一个是主机发送从机接收另一个是主机接收从机发送。我见过有人把 SDO 接 SDO、SDI 接 SDI结果怎么都读不出数据。片选信号我用的是 PIC18F96J94 的 PORTB 上的一个引脚具体是 RB0。为什么不用 SPI 模块自带的 SS 引脚因为 PIC18F96J94 的 SPI 模块在主机模式下SS 引脚的功能比较受限而且我需要用软件精确控制片选的时序特别是在多字节连续读写的时候片选必须在整个传输过程中保持低电平。用普通 GPIO 手动拉低拉高控制起来更灵活。注意MR25H40CDF 的 CS 引脚内部有上拉但为了可靠起见建议在 PCB 上再加一个 10K 的上拉电阻到 3.3V防止上电瞬间的误触发。2.2 SPI 模式选择与时序参数MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1。这两种模式的区别在于时钟空闲电平和数据采样边沿。模式 0 下时钟空闲为低电平数据在上升沿采样模式 3 下时钟空闲为高电平数据在下降沿采样。我选择的是模式 0因为 PIC18F96J94 的 SPI 模块在模式 0 下配置最简单而且大多数 SPI 从设备都默认支持模式 0。配置的时候PIC 的 SSPCON1 寄存器要这样设置SSPM3:SSPM0 0000主机模式时钟 Fosc/4CKP 0时钟空闲低电平CKE 0数据在时钟上升沿发送对应模式 0关于时钟频率PIC18F96J94 在 3.3V、40MHz 晶振下Fosc/4 就是 10MHz。MR25H40CDF 最高支持 40MHz所以 10MHz 完全在它的能力范围内。实测下来10MHz 的 SPI 时钟读写都很稳定没有出现数据错误。但这里有个细节PIC18F96J94 的 SPI 模块在发送数据时如果连续发送多个字节字节之间会有一定的间隔这个间隔取决于 CPU 的执行速度。对于 MRAM 来说它不关心字节之间的间隔只要片选保持低电平它就会一直等待时钟。所以连续读写的时候我通常是一次拉低片选然后连续发送命令字节、地址字节和数据字节最后再拉高片选。2.3 初始化代码与寄存器配置下面是我实际使用的 SPI 初始化代码基于 PIC18F96J94 的寄存器配置void SPI_Init(void) { // 配置 SPI 引脚 TRISCbits.TRISC3 0; // SCK 输出 TRISCbits.TRISC4 1; // SDI 输入 TRISCbits.TRISC5 0; // SDO 输出 // 片选引脚配置 TRISBbits.TRISB0 0; // CS 输出 LATBbits.LATB0 1; // CS 默认高电平 // SSPCON1 配置 SSPCON1 0x00; // 先清零 SSPCON1bits.SSPM3 0; // 主机模式 SSPCON1bits.SSPM2 0; SSPCON1bits.SSPM1 0; SSPCON1bits.SSPM0 0; // Fosc/4 SSPCON1bits.CKP 0; // 时钟空闲低电平 SSPCON1bits.SSPEN 1; // 使能 SPI // SSPSTAT 配置 SSPSTATbits.CKE 0; // 数据在上升沿发送 SSPSTATbits.SMP 0; // 在数据输出末尾采样 }这段代码里SSPSTAT 的 SMP 位我设成了 0意思是在数据输出时间的末尾采样输入数据。对于 10MHz 的时钟来说这个设置能提供更充裕的建立时间减少误码率。如果 SPI 时钟跑得更高比如 20MHz 以上可能需要把 SMP 设成 1在数据输出中间采样但那样对信号完整性的要求更高。3. MR25H40CDF 的读写命令与操作流程3.1 命令集概览与常用指令MR25H40CDF 的命令集和标准 SPI Flash 很像但少了擦除相关的指令因为 MRAM 不需要擦除。常用的命令有这几个命令名称命令码功能说明WREN0x06写使能写入前必须发送WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据这里要重点说一下 WREN 命令。MR25H40CDF 在上电后默认是写禁止状态任何写入操作之前必须先发送 WREN0x06命令把内部的写使能锁存器置位。写入完成后写使能锁存器会自动复位所以每次写入都要重新发送 WREN。这个机制和 EEPROM、Flash 是一样的目的是防止误写入。状态寄存器的读取也很重要。RDSR0x05返回一个字节其中 bit0 是 WIPWrite In Progress位。对于 MRAM 来说写入是纳秒级的所以 WIP 位几乎永远是 0但为了代码的通用性和可靠性我还是会在写入后读一下状态寄存器确认 WIP 为 0 再进行下一步操作。3.2 单字节写入的完整时序单字节写入的流程是这样的拉低 CS发送 WREN 命令0x06拉高 CS拉低 CS发送 WRITE 命令0x02发送 24 位地址MR25H40CDF 是 4Mbit需要 19 位地址但按 24 位发送高 5 位忽略发送 1 字节数据拉高 CS注意第 2 步和第 3 步WREN 命令必须单独发送发送完后要拉高 CS 再拉低才能开始写入操作。我一开始把 WREN 和 WRITE 连在一起发结果写不进去后来看数据手册才发现 WREN 需要独立的片选周期。下面是单字节写入的代码实现void MRAM_WriteByte(uint32_t addr, uint8_t data) { // 发送写使能 CS_LOW(); SPI_ExchangeByte(0x06); // WREN CS_HIGH(); // 发送写入命令和地址数据 CS_LOW(); SPI_ExchangeByte(0x02); // WRITE SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); SPI_ExchangeByte(data); CS_HIGH(); // 等待写入完成实际 MRAM 无需等待但保留此步骤 while (MRAM_ReadStatus() 0x01); }SPI_ExchangeByte 函数的作用是发送一个字节并接收一个字节对于只发送不接收的场景接收到的数据直接丢弃uint8_t SPI_ExchangeByte(uint8_t txData) { SSPBUF txData; while (!SSPSTATbits.BF); // 等待接收完成 return SSPBUF; }3.3 页写入与连续读取的实现MR25H40CDF 支持页写入页大小是 256 字节。页写入的流程和单字节写入类似只是在发送完地址后可以连续发送多个数据字节MRAM 会自动递增内部地址。但要注意页写入不能跨页如果起始地址在页的中间写入的数据长度超过了页边界地址会回绕到页首覆盖之前的数据。连续读取就更简单了发送 READ 命令0x03和起始地址后可以一直发送时钟MRAM 会从起始地址开始连续输出数据地址自动递增直到片选拉高。这个特性非常适合批量读取数据记录。void MRAM_ReadData(uint32_t addr, uint8_t *buffer, uint16_t len) { CS_LOW(); SPI_ExchangeByte(0x03); // READ SPI_ExchangeByte((addr 16) 0xFF); SPI_ExchangeByte((addr 8) 0xFF); SPI_ExchangeByte(addr 0xFF); for (uint16_t i 0; i len; i) { buffer[i] SPI_ExchangeByte(0x00); } CS_HIGH(); }这里有个小技巧在连续读取时SPI_ExchangeByte 发送的是 0x00但实际上发送什么数据都无所谓因为 MRAM 在读取模式下只输出数据不关心主机发送的内容。发送 0x00 只是为了让时钟继续运行。4. 实际项目中的数据存储策略与踩坑记录4.1 数据分区与地址规划在工业数据采集项目里我把 512KB 的 MRAM 空间做了分区规划地址范围用途大小0x00000 - 0x000FF系统配置参数256 字节0x00100 - 0x001FF设备校准数据256 字节0x00200 - 0x00FFF保留区3.5KB0x01000 - 0x7FFFF数据记录区508KB系统配置参数包括设备 ID、通信波特率、采样周期这些写入频率很低只在设备配置变更时写入。设备校准数据包括传感器零点、满量程系数也是低频写入。数据记录区是循环使用的每写满一页就跳到下一页写到最后再回到开头形成一个环形缓冲区。这种分区方式的好处是配置参数和校准数据不会因为数据记录区的频繁写入而受到影响即使数据记录区写满了也不会覆盖关键参数。4.2 掉电保护与数据完整性MRAM 的一个巨大优势是写入无延迟掉电时不会像 EEPROM 那样出现写了一半的情况。但为了保险起见我还是在数据记录区加了一个简单的完整性校验机制。每条数据记录包含时间戳4 字节、传感器数据8 字节、CRC16 校验2 字节总共 14 字节。写入的时候先写时间戳和传感器数据最后写 CRC。读取的时候先读 CRC再读数据计算 CRC 并比对。如果 CRC 不匹配说明这条记录不完整直接丢弃。这个机制在 MRAM 上其实有点多余因为 MRAM 的写入是原子性的不会出现部分写入的情况。但加上 CRC 校验后代码的健壮性更好即使遇到 SPI 通信错误也能及时发现。4.3 我踩过的三个坑第一个坑是片选时序。我一开始用 PIC 的 SPI 模块自带的 SS 引脚控制片选结果发现连续读写的时候SS 引脚会在每个字节之间自动拉高导致 MRAM 认为一次操作结束了后续的数据全部丢失。后来改用普通 GPIO 手动控制片选问题解决。第二个坑是地址字节数。MR25H40CDF 是 4Mbit 容量需要 19 位地址但 SPI 协议是按字节传输的所以地址要发 3 个字节24 位高 5 位忽略。我一开始只发了 2 个字节地址结果只能访问前 64KB 的空间后面的地址全部错乱。第三个坑是写使能命令的独立性。前面提到过WREN 命令必须单独发送发送完后要拉高片选再拉低才能开始写入。我一开始把 WREN 和 WRITE 连在一起发结果写不进去调试了半天才发现这个问题。提示如果你在调试时发现写入不成功先检查 WREN 命令是否独立发送再检查片选时序是否正确最后检查地址字节数是否足够。4.4 读写速度实测与优化在 10MHz 的 SPI 时钟下我实测了 MRAM 的读写速度单字节写入约 2.4 微秒包括 WREN 命令的开销连续读取 256 字节约 210 微秒连续写入 256 字节约 215 微秒这个速度对于我的数据采集应用来说绰绰有余。如果换成 EEPROM写入 256 字节需要至少 5ms差了 20 多倍。优化的空间主要在 WREN 命令上。每次写入都要发送 WREN增加了约 1 微秒的开销。如果连续写入多个不连续的数据块可以考虑在第一个数据块写入前发送 WREN然后连续写入多个数据块最后再发送 WRDI。但要注意MRAM 在每次写入完成后会自动复位写使能锁存器所以这个优化只适用于连续写入的场景而且需要确认 MRAM 是否支持在一次 WREN 后连续写入多个地址。根据我的实测MR25H40CDF 在一次 WREN 后只能完成一次写入操作写入完成后写使能锁存器自动复位所以这个优化行不通。5. 从 EEPROM 迁移到 MRAM 的代码改造要点5.1 驱动层的接口抽象如果你手头有现成的 EEPROM 驱动代码迁移到 MRAM 其实不难关键是要把驱动层抽象好。我定义了一组通用的存储接口typedef struct { void (*Init)(void); uint8_t (*ReadByte)(uint32_t addr); void (*WriteByte)(uint32_t addr, uint8_t data); void (*ReadBuffer)(uint32_t addr, uint8_t *buf, uint16_t len); void (*WriteBuffer)(uint32_t addr, uint8_t *buf, uint16_t len); } Storage_Driver_t;然后针对 MRAM 实现这些接口。这样上层的数据记录逻辑不需要改动只需要替换底层的驱动实现。如果以后要换成 FRAM 或其他存储器也只需要实现同样的接口。5.2 写入等待逻辑的简化EEPROM 驱动里通常有一个等待写入完成的循环检查状态寄存器的 WIP 位或者固定延时 5ms。迁移到 MRAM 后这个等待逻辑可以大幅简化因为 MRAM 的写入是纳秒级的不需要等待。但我的建议是保留一个简单的状态检查不要完全去掉。原因有两个第一保留状态检查可以让代码更通用万一以后换成其他存储器不需要改代码第二状态检查可以帮助发现 SPI 通信故障如果读状态寄存器返回 0xFF说明 SPI 通信有问题可以及时报错。5.3 磨损均衡逻辑的取舍EEPROM 和 Flash 的驱动里通常会有磨损均衡逻辑把写入操作分散到不同的地址延长存储器寿命。迁移到 MRAM 后磨损均衡逻辑完全可以去掉因为 MRAM 的擦写寿命超过 10^14 次按每天写入 100 万次算也能用上几百年。去掉磨损均衡逻辑后代码复杂度大幅降低而且写入地址的计算也更简单。我的做法是直接把磨损均衡相关的代码删除同时在注释里说明原因方便以后维护的人理解。6. SPI 通信的硬件设计与信号完整性6.1 PCB 布局的注意事项SPI 通信在 10MHz 下对 PCB 布局的要求不算高但有几个点还是要注意走线尽量短SPI 的 SCK、SDI、SDO、CS 四条线尽量靠近走线长度控制在 10cm 以内。如果走线太长时钟信号会产生反射和振铃导致数据错误。地线要完整MRAM 的 GND 引脚要就近接地最好在芯片下方铺一块地铜减少回流路径。电源去耦MRAM 的 VDD 引脚旁边要放一个 0.1uF 的陶瓷电容距离越近越好。如果电源纹波较大可以再加一个 1uF 的电容。我第一版 PCB 的时候MRAM 离 PIC 有点远走线大概 15cm结果在 10MHz 下偶尔出现读写错误。后来把 MRAM 挪近到 5cm 以内问题就消失了。所以走线长度这个事宁可短不可长。6.2 上拉电阻与片选信号CS 引脚的上拉电阻我前面提过这里再强调一下。MR25H40CDF 的 CS 引脚内部有弱上拉但在上电瞬间PIC 的 GPIO 处于高阻态CS 引脚可能会被外部干扰拉低导致 MRAM 误触发。加一个 10K 的外部上拉电阻可以确保 CS 在上电期间保持高电平。另外SCK、SDI、SDO 这三条线不需要上拉电阻因为它们是推挽输出不需要外部上拉。如果 PIC 的 SPI 引脚配置为开漏输出才需要上拉但 PIC18F96J94 的 SPI 引脚是推挽输出所以不需要。6.3 信号完整性实测与示波器观察我用示波器观察了 SCK 和 SDO 的信号波形在 10MHz 下上升沿和下降沿都很干净没有明显的过冲和振铃。但在 CS 信号上我观察到拉低瞬间有一个小的振铃幅度大概 0.5V持续时间约 10ns。这个振铃不影响 MRAM 的工作因为 MRAM 在 CS 拉低后需要一定的建立时间才会开始响应时钟10ns 的振铃在这个建立时间之内。如果你在调试时发现 SPI 通信不稳定建议用示波器看一下 SCK 和 CS 的波形重点观察上升沿和下降沿是否有过冲、振铃以及 CS 拉低到第一个 SCK 上升沿之间的时间是否足够。MR25H40CDF 要求 CS 建立时间最小 5ns一般都能满足但如果 PIC 的 GPIO 驱动能力不足可能需要调整。7. 常见问题排查与调试经验7.1 读出的数据全是 0xFF 或 0x00这是最常见的 SPI 通信故障。如果读出的数据全是 0xFF说明 MISO 线一直处于高电平可能是 MRAM 没有响应或者片选没有正确拉低。如果读出的数据全是 0x00说明 MISO 线一直处于低电平可能是 MRAM 没有供电或者 SPI 模式配置错误。排查步骤用万用表测量 MRAM 的 VDD 引脚确认电压在 3.3V 左右用示波器观察 CS 引脚确认在读写操作时能正确拉低和拉高用示波器观察 SCK 引脚确认有 clock 输出检查 SPI 模式配置确认 CPOL 和 CPHA 与 MRAM 匹配检查 MISO 和 MOSI 是否接反7.2 写入后读出的数据不一致如果写入成功但读出的数据不一致可能是以下原因地址错误检查地址字节数是否正确MR25H40CDF 需要 3 字节地址WREN 命令未发送每次写入前必须发送 WREN片选时序错误WREN 命令必须独立发送发送完后拉高 CS 再拉低SPI 时钟太快降低 SPI 时钟频率试试比如从 10MHz 降到 5MHz7.3 SPI 通信偶尔出错如果 SPI 通信大部分时间正常但偶尔出错通常是硬件问题电源纹波用示波器观察 VDD 引脚确认纹波在 50mV 以内走线过长缩短 SPI 走线长度或者降低 SPI 时钟频率地线不完整检查 PCB 的地线是否完整MRAM 的 GND 是否就近接地干扰源检查 MRAM 附近是否有高频开关电源、电机驱动等干扰源我在实际项目中遇到过一次偶发读写错误排查了很久才发现是 MRAM 的电源引脚去耦电容虚焊导致电源纹波偏大。重新焊接电容后问题解决。所以遇到偶发问题先检查硬件焊接再查软件配置。7.4 调试工具的使用建议调试 SPI 通信逻辑分析仪比示波器更好用。逻辑分析仪可以同时抓取 CS、SCK、MOSI、MISO 四条线的信号并且能直接解码 SPI 协议显示出命令码、地址和数据。我用的是 Saleae Logic 8配合它的 SPI 解码器能快速定位问题。如果没有逻辑分析仪也可以用示波器的双通道模式同时观察 CS 和 SCK确认片选和时钟的时序关系。但这种方式看不到数据内容只能判断通信是否发生。8. 写在最后的几点实操体会MR25H40CDF 这颗芯片我用了一年多跑了几个不同的项目整体稳定性很好。它的 SPI 接口和标准 Flash 兼容驱动代码改动量小对于需要高频写入、频繁掉电的工业场景来说是一个非常靠谱的选择。如果你打算在自己的项目里用 MRAM我有几个建议第一先确认你的主控 SPI 电平是否和 MRAM 匹配3.3V 系统可以直接连5V 系统必须做电平转换第二PCB 布局时把 MRAM 尽量靠近主控走线短一点电源去耦电容不要省第三驱动代码里保留状态寄存器检查虽然 MRAM 不需要等待但状态检查能帮你发现通信故障第四WREN 命令一定要独立发送这个坑我踩过希望你不要再踩。还有一点MRAM 的价格比 EEPROM 和 Flash 贵不少所以选型的时候要算一下成本。如果你的应用写入频率不高EEPROM 或 Flash 可能更划算。但如果你的应用需要高频写入、频繁掉电、长寿命那 MRAM 多出来的成本是值得的。我在项目里算过一笔账用 EEPROM 的话每两年要更换一次存储器芯片加上停机维护的成本三年下来就超过 MRAM 的差价了。所以对于工业设备这种长生命周期、维护成本高的场景MRAM 的综合成本反而更低。
返回列表