ARTICLE DETAIL

资讯详情

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

MRAM与PIC18F86J10实战:SPI存储读写与工业采集应用

MRAM与PIC18F86J10实战:SPI存储读写与工业采集应用 MRAM 这类存储介质在工业现场其实一直有点叫好不叫座的味道——参数漂亮价格不便宜用的人不算多。但真到了那些掉电不能丢数据、又要频繁写入、还不想加超级电容或者后备电池的场景MR25H40CDF 这种 SPI 接口的磁存储器就会突然变得很香。我最近在一个基于 PIC18F86J10 的工业采集板上把这两颗芯片凑到了一起踩了一些坑也摸清了一些门道这里把整套存储和读取数据的思路、代码结构、时序细节和实测经验完整梳理一遍。这篇内容适合正在做工业仪表、数据记录仪、嵌入式采集终端的硬件和固件工程师也适合刚接触 SPI 外设、想找一个比 EEPROM 快、比 Flash 耐写的存储方案的开发者。我会从为什么选 MRAM 讲起再到 PIC18F86J10 的 MSSP 模块配置、MR25H40CDF 的指令集、读写时序、页写与扇区擦除的取舍最后给出可直接复用的驱动代码和几个实测中容易翻车的细节。1. 为什么在工业采集场景里把 MRAM 摆上台面1.1 工业现场对存储的三个硬约束做工业采集的人都知道现场环境对存储的要求跟消费电子完全不是一个量级。第一是掉电不能丢数据设备可能随时被拉闸采集到的温度、压力、流量这些数据一旦丢了事后追溯就没有依据。第二是写入频次高有些通道每秒要记录一次一天下来就是八万多次写入一年三千万次这个量级用普通 EEPROM 是撑不住的。第三是写入延迟要低采集任务本身有实时性要求存储操作不能把主循环卡住太久。传统方案无非几种加铁电存储器 FRAM、加带后备电池的 SRAM、或者用 Flash 加磨损均衡算法。FRAM 容量小、价格高SRAM 加电池有电池寿命和维护问题Flash 加磨损均衡软件复杂度上来了而且写入延迟还是毫秒级。MRAM 恰好卡在中间——它有 SRAM 级别的写入速度又有非易失特性写入寿命几乎是无限的不需要擦除操作这几个特性叠在一起对工业采集来说就是刚需。1.2 MR25H40CDF 到底解决了什么MR25H40CDF 是一颗 4Mbit512KB的 SPI 接口磁存储器工作电压 2.7V 到 3.6V支持最高 40MHz 的 SPI 时钟。它的核心优势在于写入不需要等待写一个字节和写一整页在时间上没有本质区别因为它没有 Flash 那种先擦后写的物理限制。你发完写指令和地址数据进去就进去了下一个指令可以立刻跟上。这一点在代码层面的体现就是你不需要在写操作后面加延时也不需要查询忙状态。Flash 写完要等 5ms 左右的内部编程时间MRAM 不需要。对于 PIC18F86J10 这种主频 48MHz 的 8 位单片机来说省掉这些等待意味着主循环的节奏可以保持得很稳。另外它的数据保持能力标称 20 年以上耐受温度范围覆盖工业级 -40 到 85 摄氏度抗辐射和抗磁场干扰能力也比 Flash 强这些特性在电机控制、电力监控这类电磁环境复杂的场合很有价值。1.3 PIC18F86J10 作为主控的适配性PIC18F86J10 是 Microchip 的一款 8 位单片机128KB Flash、4KB RAM自带 MSSP 模块可以配置成 SPI 主模式。它的 SPI 时钟可以到 10MHzFosc/448MHz 主频下虽然跑不满 MR25H40CDF 的 40MHz 上限但对大多数采集场景来说够用了。选它主要是因为外设资源均衡有多个定时器、ADC 通道和 UART适合做多通道采集加本地存储加远程上报这种组合任务。这里有个细节要注意PIC18F86J10 的 MSSP 在 SPI 主模式下时钟极性CKP和时钟边沿CKE的组合必须和从设备匹配。MR25H40CDF 支持 SPI 模式 0CPOL0CPHA0和模式 3CPOL1CPHA1我一般用模式 0配置起来最直观SCK 空闲低电平数据在上升沿采样。2. 硬件连接与 SPI 模式配置的取舍2.1 引脚连接与片选策略MR25H40CDF 是 8 引脚封装SPI 接口四根线SCK、SI、SO、CS加上 VCC 和 GND另外两个引脚是 HOLD 和 WP。实际连接的时候SCK、SI、SO 直接接到 PIC18F86J10 的 MSSP 对应引脚上CS 用一个普通 GPIO 控制。这里有个选择硬件片选还是软件片选。PIC18F86J10 的 MSSP 模块本身不提供自动片选功能所以只能用软件片选也就是用 GPIO 手动拉低拉高。这其实反而更灵活因为总线上如果挂多个 SPI 设备你可以用不同的 GPIO 分别控制各自的 CS互不干扰。HOLD 引脚我一般直接拉高不用它。WP 引脚也拉高因为 MRAM 的写保护功能在实际项目里用得少真要保护数据不如在软件层面做校验。如果板子上有多个 SPI 从设备记得每个设备的 CS 都要单独走线不要共用。2.2 SPI 时钟极性与边沿的匹配SPI 模式配置是新手最容易翻车的地方。MR25H40CDF 的数据手册明确写了支持模式 0 和模式 3但很多人在配置 PIC 的 SSPCON 寄存器时搞混了 CKP 和 CKE 的含义。在 PIC18F86J10 上SSPCON1 寄存器的 CKP 位控制时钟极性SSPSTAT 寄存器的 CKE 位控制时钟边沿。模式 0 对应 CKP0、CKE1这时候 SCK 空闲为低数据在上升沿被采样在下降沿输出。模式 3 对应 CKP1、CKE0SCK 空闲为高数据在下降沿采样。我实测下来模式 0 更稳因为大多数逻辑分析仪和示波器默认触发就是低电平空闲调试的时候波形看起来更直观。配置代码如下// SPI 主模式时钟 Fosc/4模式 0 SSP1CON1 0x20; // SSPEN1, CKP0, SSPM0000 (Fosc/4) SSP1STAT 0x40; // CKE1, SMP0 (在数据输出末尾采样)注意 SMP 位在 SPI 主模式下建议设为 0这样在数据输出时间结束时采样时序余量更大。如果设成 1采样点提前高速时钟下容易采到不稳定的数据。2.3 上拉电阻与走线注意事项SPI 总线在工业环境里走线可能比较长如果超过 10cm建议在 SCK 和 CS 上加 4.7k 到 10k 的上拉电阻防止空闲时电平漂移导致误触发。SO 线是 MRAM 输出PIC 输入一般不需要上拉但如果走线很长加一个 10k 上拉也能提高抗干扰能力。PCB 布局上MRAM 尽量靠近单片机放置SCK 和 SI、SO 尽量等长避免形成大的环路面积。如果板子上有电机驱动或者继电器MRAM 要远离这些干扰源必要时加屏蔽或者用地线包围。我在一个项目里因为把 MRAM 放在了继电器旁边结果每次继电器动作都会导致一次误写后来挪了位置加了地隔离才解决。3. MR25H40CDF 的指令集与读写操作拆解3.1 核心指令一览MR25H40CDF 的指令集比 Flash 简单很多因为它不需要擦除。常用的指令就下面这几个指令名称指令码功能说明WREN0x06写使能写操作前必须发WRDI0x04写禁止RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据注意 WREN 这个指令每次写操作之前都要发一次。MRAM 不像 EEPROM 那样有自动写使能它要求显式地发 WREN然后紧接着发 WRITE 指令和地址数据。如果你忘了发 WREN写操作会被忽略而且不会有任何错误提示数据就是没写进去这个坑我踩过。3.2 读操作的完整时序读操作相对简单。拉低 CS发送 READ 指令0x03然后发送 3 个字节的地址MR25H40CDF 是 512KB需要 19 位地址用 3 个字节表示高 5 位忽略然后就可以连续读取数据了。地址会自动递增你可以一直读到 CS 拉高为止。uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; CS_LOW(); SPI_WriteByte(0x03); // READ 指令 SPI_WriteByte((addr 16) 0xFF); // 地址高字节 SPI_WriteByte((addr 8) 0xFF); // 地址中字节 SPI_WriteByte(addr 0xFF); // 地址低字节 data SPI_ReadByte(); // 读数据 CS_HIGH(); return data; }如果要连续读多个字节可以在 CS 拉高之前一直调用 SPI_ReadByte地址会自动递增不需要重新发指令和地址。这个特性在做数据回读的时候很有用比如你要读一整页 256 字节只需要发一次指令和地址然后连续读 256 次就行。3.3 写操作的页写与字节写写操作有两种方式字节写和页写。字节写就是每次写一个字节发一次 WREN、一次 WRITE、一次地址、一个数据。页写是一次发 WREN、一次 WRITE、一次起始地址然后连续写多个字节最多可以写一整页MR25H40CDF 的页大小是 256 字节。页写的效率明显更高因为省掉了重复的指令和地址开销。但页写有个限制不能跨页。如果你从地址 0x00FF 开始写写到 0x0100 的时候地址会回绕到页首 0x0000而不是继续到下一页。这个行为跟 EEPROM 类似写的时候必须自己保证不跨页。void MRAM_WritePage(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t page_offset addr 0xFF; if (page_offset len 256) { len 256 - page_offset; // 截断到页边界 } CS_LOW(); SPI_WriteByte(0x06); // WREN CS_HIGH(); CS_LOW(); SPI_WriteByte(0x02); // WRITE SPI_WriteByte((addr 16) 0xFF); SPI_WriteByte((addr 8) 0xFF); SPI_WriteByte(addr 0xFF); for (uint16_t i 0; i len; i) { SPI_WriteByte(buf[i]); } CS_HIGH(); }注意 WREN 和 WRITE 之间必须拉高一次 CS这是 MR25H40CDF 的要求。有些 SPI 设备允许 WREN 和 WRITE 在同一个 CS 周期内连续发但 MRAM 不行WREN 必须单独一个 CS 周期。这个细节在数据手册的时序图里有体现但很容易被忽略。4. 在 PIC18F86J10 上跑通存储读写的完整流程4.1 初始化顺序与寄存器配置上电之后的初始化顺序很重要。先配置 GPIO把 CS 设为输出并拉高然后配置 MSSP 模块最后给 MRAM 发一次 WRDI 确保它处于写禁止状态避免上电过程中的误写。void MRAM_Init(void) { // CS 引脚配置 TRISBbits.TRISB2 0; // CS 输出 CS_HIGH(); // MSSP 配置 SSP1CON1 0x20; // SPI 主模式Fosc/4 SSP1STAT 0x40; // CKE1 SSP1ADD 0; // 不用 // 确保 MRAM 写禁止 CS_LOW(); SPI_WriteByte(0x04); // WRDI CS_HIGH(); }这里有个细节PIC18F86J10 的 MSSP1 和 MSSP2 两个模块都可以配成 SPI但引脚是复用的。用 MSSP1 的话SCK 在 RC3SI 在 RC5SO 在 RC4。配置之前要确保这些引脚的方向寄存器设置正确SCK 和 SO 是输出SI 是输入。4.2 状态寄存器读取与写保护判断MR25H40CDF 的状态寄存器只有两位有意义WEL写使能锁存和 WPEN写保护使能。读状态寄存器可以判断当前是否处于写使能状态这在调试的时候很有用。uint8_t MRAM_ReadStatus(void) { uint8_t status; CS_LOW(); SPI_WriteByte(0x05); // RDSR status SPI_ReadByte(); CS_HIGH(); return status; }正常情况下发完 WREN 之后读状态寄存器bit1WEL应该是 1。发完 WRDI 或者完成一次写操作之后WEL 会自动清零。如果你发现 WEL 一直是 0那说明 WREN 没有发成功检查一下 CS 时序和 SPI 时钟配置。4.3 数据写入与回读的验证方法写完数据之后一定要回读验证这是工业项目的基本要求。我一般会在写入之后立刻读回来比对如果不一致就重试一次重试还失败就记录错误标志。uint8_t MRAM_WriteVerify(uint32_t addr, uint8_t *buf, uint16_t len) { MRAM_WritePage(addr, buf, len); uint8_t readback[256]; MRAM_ReadBytes(addr, readback, len); for (uint16_t i 0; i len; i) { if (readback[i] ! buf[i]) { return 0; // 验证失败 } } return 1; // 验证成功 }实测下来只要 SPI 时序配置正确MRAM 的写入成功率是 100%几乎不会出现写入失败的情况。但回读验证这个步骤不能省因为它能帮你发现硬件连接问题或者时序配置错误尤其是在项目初期调试阶段。5. 实测中容易翻车的几个细节5.1 WREN 与 WRITE 之间的 CS 时序前面提过 WREN 和 WRITE 之间必须拉高 CS但具体拉高多久数据手册里写的是最小 10ns实际上你只要保证 CS 有一个明显的上升沿和下降沿就行PIC 的 GPIO 操作速度足够慢不会违反这个时序。但如果你用的是硬件 SPI 加 DMACS 由硬件自动控制那就要注意 CS 的建立和保持时间是否满足要求。我遇到过一次诡异的问题写操作偶尔失败概率大概百分之一。查了半天发现是 CS 拉高之后立刻又拉低中间没有延时导致 MRAM 内部状态机没来得及复位。后来在 CS 拉高之后加了一个NOP()就好了。这个延时不需要很长几个机器周期就够。5.2 SPI 时钟速度与信号完整性的平衡PIC18F86J10 的 SPI 时钟最快是 Fosc/448MHz 主频下就是 12MHz。但实际跑 12MHz 的时候如果 PCB 走线不好SO 线上的数据会出现振铃导致读回来的数据偶尔出错。我一般会把 SPI 时钟降到 Fosc/16也就是 3MHz这时候信号质量明显改善而且对 MRAM 的读写速度影响不大因为 MRAM 本身没有写入等待时间瓶颈在 SPI 传输速率上。如果你确实需要高速读写比如要连续写入大量数据那可以跑 Fosc/4但要在 PCB 上做好阻抗匹配SCK 和 SO 线尽量短必要时加串联端接电阻。5.3 跨页写入的边界处理跨页写入是 MRAM 和 EEPROM 共有的坑。假设你要写 300 字节的数据起始地址是 0x00F0那么第一页只能写 16 字节到 0x00FF剩下的 284 字节要从 0x0100 开始写。如果你直接调用页写函数写 300 字节多出来的部分会回绕到 0x0000把之前的数据覆盖掉。正确的做法是在写之前先计算跨页边界分多次写入void MRAM_WriteBuffer(uint32_t addr, uint8_t *buf, uint16_t len) { while (len 0) { uint16_t page_remain 256 - (addr 0xFF); uint16_t write_len (len page_remain) ? len : page_remain; MRAM_WritePage(addr, buf, write_len); addr write_len; buf write_len; len - write_len; } }这个函数会自动处理跨页情况你传任意长度和任意起始地址都能正确写入。实测下来这个逻辑很稳建议直接封装成通用函数。5.4 电源波动对写入的影响MRAM 虽然写入不需要等待但它对电源波动还是有一定敏感度的。如果电源电压在写入过程中跌落到 2.7V 以下写入可能会失败或者写入错误的数据。工业现场电源波动很常见建议在 MRAM 的 VCC 引脚旁边放一个 0.1uF 的陶瓷电容和一个 10uF 的钽电容靠近芯片放置。如果电源波动特别严重可以考虑加一个电压监控芯片在电压低于阈值时把单片机复位避免在低压下进行写入操作。这个成本不高但能显著提高数据可靠性。6. 驱动代码的组织与复用建议6.1 分层设计硬件层与业务层分离驱动代码建议分成两层底层是 SPI 读写和 MRAM 指令封装上层是数据存储管理。底层只负责跟硬件打交道提供MRAM_ReadByte、MRAM_WritePage这些函数。上层负责数据组织比如定义数据结构、管理存储地址分配、做数据校验。这样分层的好处是如果以后换用别的 SPI 存储器只需要改底层驱动上层业务逻辑不用动。我在一个项目里从 MR25H40CDF 换到另一款 SPI Flash只花了半天时间改底层上层代码一行没动。6.2 地址空间规划与数据对齐512KB 的存储空间不算小但也要规划好。我一般把空间分成几个区域系统配置区、采集数据区、日志区、预留区。每个区域的起始地址按页对齐这样写入的时候不会跨页效率最高。配置区放一些不常改的参数比如设备 ID、校准系数、上报间隔。采集数据区用环形缓冲区的方式管理写满之后从头覆盖。日志区记录异常事件只写不覆盖写满之后发告警。6.3 数据校验与错误恢复工业项目里数据校验不能省。我一般用 CRC16 对每个数据块做校验写入的时候计算 CRC 一起存进去读取的时候重新计算比对。如果 CRC 不匹配说明数据损坏可以根据策略选择重读或者标记为无效。MRAM 本身出错的概率很低但电源波动、SPI 干扰这些外部因素还是可能导致数据错误。加一层 CRC 校验成本很低但能大幅提高系统的可靠性。如果对可靠性要求极高还可以做双备份把同一份数据存两个地址读取的时候比对两份数据不一致就根据 CRC 判断哪份是正确的。7. 几个实际项目中的经验补充7.1 关于写入速度的实测数据我用逻辑分析仪测过实际写入速度。在 SPI 时钟 3MHz 的情况下写一个 256 字节的页从拉低 CS 到拉高 CS总时间大约是 700 微秒。其中 WREN 指令占 2 个字节时间WRITE 指令加地址占 4 个字节时间数据占 256 个字节时间总共 262 个字节每个字节 8 位3MHz 下每位 0.33 微秒算下来 262 × 8 × 0.33 ≈ 692 微秒跟实测基本吻合。如果 SPI 时钟提到 12MHz同样的页写时间会降到约 175 微秒。但前面说过12MHz 下信号完整性风险增加需要根据实际 PCB 情况权衡。7.2 与 EEPROM 方案的对比体会我之前用 24LC 系列的 EEPROM 做过类似的项目对比下来 MRAM 的优势很明显。EEPROM 写一个页要等 5ms 的内部编程时间写 256 字节就是 5ms而 MRAM 只要 700 微秒快了七倍多。而且 EEPROM 的写入寿命是 100 万次左右MRAM 几乎是无限的对于高频采集场景来说MRAM 的长期可靠性优势更大。成本上 MRAM 确实比 EEPROM 贵但考虑到省掉了磨损均衡算法、省掉了写入等待、提高了数据可靠性综合成本未必更高。尤其是工业项目现场维护成本远高于器件成本用 MRAM 减少一次现场故障省下的钱就够买很多颗 MRAM 了。7.3 调试工具与技巧调试 SPI 和 MRAM 的时候逻辑分析仪是必备的。我用的是 8 通道的逻辑分析仪接上 SCK、SI、SO、CS 四根线就能看到完整的时序。重点看几个地方CS 的建立和保持时间、SCK 的极性和边沿、数据在 SCK 边沿上是否稳定。如果没有逻辑分析仪也可以用示波器看 SCK 和 SO 的波形但不如逻辑分析仪直观。另外PIC 的 MSSP 模块有溢出标志位 SSP1OV如果 SPI 传输过程中出现溢出这个位会置 1。在调试的时候可以定期检查这个位如果置 1 说明 SPI 时钟太快或者中断处理太慢需要调整。7.4 关于 HOLD 引脚的使用HOLD 引脚在 MRAM 里可以用来暂停传输但在实际项目里我基本不用它。因为 SPI 传输本身很快暂停传输的需求很少。如果你确实需要可以把 HOLD 接到一个 GPIO 上在需要的时候拉低。但要注意HOLD 拉低的时候 SCK 必须保持稳定否则可能进入不确定状态。我建议直接拉高不用这个功能减少一个潜在的故障点。7.5 温度对读写的影响MR25H40CDF 标称工作温度是 -40 到 85 摄氏度在这个范围内读写都是正常的。但我实测发现在极端温度下比如 -40 度SPI 的时序余量会变小如果时钟跑得太快可能会出现读写错误。所以在宽温应用里建议把 SPI 时钟降一档留出足够的时序余量。高温下 MRAM 的功耗会略微增加但幅度不大一般不需要特别处理。如果设备在高温环境下长期工作注意 PCB 的散热设计不要让 MRAM 周围温度超过 85 度。7.6 批量生产时的注意事项批量生产的时候建议在产测环节加一个 MRAM 读写测试写一组已知数据读回来比对确保每块板子的 MRAM 都能正常工作。这个测试很快几毫秒就能完成但能筛掉焊接不良或者芯片损坏的板子。另外MRAM 是 MSL1 等级的器件不需要烘烤但焊接的时候要注意回流焊温度曲线不要超过数据手册规定的峰值温度。如果用的是手工焊接注意不要长时间高温加热避免损坏芯片内部结构。8. 从存储到应用数据记录仪的完整实现思路8.1 数据帧格式设计在实际的数据记录仪里我不会直接存原始数据而是设计一个数据帧格式。每帧包含帧头、时间戳、通道数据、CRC 校验和帧尾。帧头用固定的两个字节标识比如 0xAA 0x55方便解析的时候定位。时间戳用 4 个字节记录相对于设备启动的秒数。通道数据根据实际通道数决定每个通道 2 个字节。CRC 用 2 个字节对帧头到通道数据做校验。这样一帧大概是 2 4 2×N 2 1 个字节N 是通道数。如果 N8一帧就是 23 个字节。512KB 的空间可以存大约 22000 帧按每秒一帧算可以存 6 个小时左右。如果需要更长的记录时间可以降低采样率或者用压缩算法。8.2 环形缓冲区的实现环形缓冲区是数据记录仪的经典结构。维护一个写指针和一个读指针写指针追上读指针的时候说明缓冲区满了这时候可以选择覆盖最旧的数据或者停止写入。我一般用覆盖模式因为工业采集更关心最近的数据。实现的时候要注意写指针和读指针的操作要保证原子性避免在中断里写数据的时候主循环也在改指针。简单的方法是在操作指针的时候关中断操作完再开中断。PIC18F86J10 的中断响应很快关中断的时间很短不会影响实时性。8.3 掉电保护与数据完整性虽然 MRAM 本身掉电不丢数据但如果在写入过程中掉电可能会写入不完整的一帧。为了解决这个问题我一般会在帧尾加一个有效标志只有整帧写完之后才把标志置为有效。读取的时候先检查有效标志无效的帧直接跳过。另一种方法是双缓冲写第一份的时候如果掉电第二份还是完整的。但这样会占用双倍空间适合对数据完整性要求极高的场景。大多数工业采集场景用有效标志就够了。8.4 数据导出与远程上报本地存储只是第一步数据最终要导出或者上报。导出可以通过 UART 或者 USB 把数据传到上位机上报可以通过无线模块或者以太网传到服务器。不管哪种方式数据格式要统一最好用结构化的格式比如 JSON 或者自定义的二进制协议。如果通过无线上报要注意流量成本可以对数据做压缩或者只上报变化量。MRAM 的读取速度很快导出 512KB 数据在 3MHz SPI 下大概需要 1.4 秒完全能满足大多数场景的需求。9. 写在最后的几点个人体会MRAM 这颗芯片我用下来最大的感受就是省心。不需要擦除、不需要等待、不需要磨损均衡代码写起来简单调试起来也快。虽然单价高一些但在工业项目里省下的开发时间和现场维护成本远远超过器件本身的差价。PIC18F86J10 配 MR25H40CDF 这个组合适合中等规模的工业采集和记录应用。如果你的项目需要更大的存储容量可以选容量更大的 MRAM 型号或者用多颗 MRAM 并联扩展。如果对成本更敏感也可以考虑 FRAM 或者带后备电池的 SRAM但综合可靠性和维护成本来看MRAM 在工业场景里还是很有竞争力的。最后提醒一句SPI 时序配置和 CS 控制是这套方案里最容易出问题的地方调试的时候一定要用逻辑分析仪看清楚波形不要凭感觉猜。把这两个地方搞定了剩下的就是常规的代码编写和业务逻辑实现了。
返回列表