ARTICLE DETAIL

资讯详情

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

MRAM与MCU组合:工业嵌入式高频数据存储方案解析

MRAM与MCU组合:工业嵌入式高频数据存储方案解析 前阵子做工业设备的数据记录模块选型时把存储方案从头到尾捋了一遍最后定下来用 Everspin 的 MR25H40CDF 这颗 SPI MRAM配合 NXP 的 MK24FN256VDC12 做主控。MR25H40CDF 是一颗 4Mbit 的 SPI 接口磁阻随机存储器MK24FN256VDC12 是 Cortex-M4F 内核的 Kinetis 系列 MCU这两个芯片搭在一起正好解决工业现场最头疼的既要频繁写、又要掉电不丢、还要能快速读出来的问题。这篇文章就把这套方案的选型逻辑、硬件接线、驱动实现和现场调优过程整理出来给想在工业和嵌入式项目里做存储和读取的同行一个可以直接参考的方案。MRAM 的全称是 Magnetoresistive Random Access Memory磁阻随机存储器。它不像 Flash 那样写入前必须先擦除也不像普通 SRAM 那样断电就丢数据。写入速度接近 SRAM数据保存又跟 Flash 一个级别所以在高端嵌入式存储方案里越来越常见。MR25H40CDF 属于 Everspin 的 SPI MRAM 产品线容量 4Mbit也就是 512KBMCU 通过标准 SPI 协议就能访问对单片机系统来说非常友好。MK24FN256VDC12 作为主控主频可以跑到 120MHz内部带 256KB Flash 和 64KB SRAM外设资源丰富有多个 SPI、UART、I2C、ADC 和 DMA 控制器工业级温度规格供电范围也适合 3.3V 系统。整套系统的核心思路很直接MK24 通过 SPI 总线把关键数据写入 MR25H40CDF掉电后数据不丢下次上电再把数据读出来用于设备状态分析、日志回溯或参数恢复。1. 为什么工业存储需要 MRAM 而不是继续用 Flash1.1 工业现场存储需求的特殊性嵌入式设备做数据存储最常见的选项是 SPI Flash 和 EEPROM。这两种芯片成本低、容量也够用但它们各自有让人头疼的地方。SPI Flash 写入前必须擦除擦除单位是扇区通常是 4KB如果只想改一个字节也得先把整个扇区读进 SRAM、擦掉、再写回去。EEPROM 虽然可以字节写但容量通常很小几 KB 到几十 KB而且写寿命一般在一百万次左右在频繁记录运行数据的场景下很快就会逼近寿命极限。工业设备里最典型的需求是设备运行参数、报警事件、故障码、维护记录要反复写入而且一写就是好几年。比如一台驱动器或者工业控制器每次上电要记录运行时间每次报警要记录故障代码每秒钟可能还要保存几个关键模拟量。这种高频次的写入如果交给 Flash 或者 EEPROM要么寿命不够要么擦除延迟太长导致数据来不及落盘。这个场景下 MRAM 的优势就很明显。MR25H40CDF 的写耐久性可以做到 10 的 14 次方以上写操作不需要擦除没有固定的擦除块限制字节写、连续写都行。写入时延基本是 SRAM 级别的SPI 时钟跑 40MHz 的时候连续写的有效吞吐可以做到好几 MB/s 的理论值实际用起来比普通 SPI Flash 的写流程快一个数量级。工业设备在选择存储方案时真正应该关注的不是容量大小而是写入频率、掉电保持和数据完整性的综合要求这也是我把 Flash 换掉的根本原因。1.2 组合方案的定位MK24FN256VDC12 在这里不是单纯当一颗单片机用它承担的是采集、组帧、校验、存储调度和读取索引这些工作。MRAM 只负责数据保存。用一颗带丰富外设的工业级 MCU 配合 MRAM可以把存储这件事做得非常透明上层应用只需要调用 write 和 read 接口不需要关心擦除、磨损、坏块这些 Flash 世界的历史包袱。这个组合特别适合做这几类产品工业数据记录仪、电力监控终端、PLC 扩展模块、车载记录设备、医疗仪器以及一切需要边采集边存储、掉电不丢、数据可以快速导出的嵌入式系统。如果项目的存储核心诉求是高频率写入和快速读取MRAM 几乎是绕不开的选择。我之前在一个设备上用 SPI Flash 做秒级采样记录运行一个多月就出现写入超时换成 MRAM 之后再也没出现过存储相关的故障。2. MR25H40CDF 和 MK24FN256VDC12 的选型细节2.1 MR25H40CDF 到底能提供什么MR25H40CDF 是 Everspin 的 3.3V SPI MRAM容量 4Mbit常见封装有 SOP-8 和 DFN-8。整个地址空间是 512KB通过 SPI 指令直接访问。标准指令集包括 Read Data、Fast Read、Write Data、Write Enable、Write Disable、Read Status Register、Write Status Register 这些对用过 SPI Flash 的工程师来说非常顺手。有一点需要特别注意MRAM 的写操作不是编程而是覆盖写。你发出去的数据就是最终存进去的数据不需要先擦除到 0xFF 再写。这意味着逻辑上可以把整个 MRAM 当成一个掉电不丢的大数组来用。修改任意字节都不会影响相邻数据也不用考虑 Flash 的 program/erase 循环和坏块管理问题这是 MRAM 在工程上最舒服的地方。MR25H40CDF 的核心指令如下表所示实际驱动开发时可以直接参考指令名称操作码功能说明WREN0x06写使能执行写操作前必须先发WRDI0x04写禁止关闭写使能状态READ0x03从指定地址连续读取数据FAST_READ0x0B带 dummy 时钟的快速读取WRITE0x02从指定地址连续写入数据RDSR0x05读状态寄存器WRSR0x01写状态寄存器我最初用这颗芯片做日志记录时最满意的是它没有写等待时间。SPI Flash 写完一页之后往往要等几毫秒到几十毫秒MRAM 写完一个字节或一页数据在时钟传输结束后就已经稳定了下一个操作可以立刻开始。在中断频繁、数据吞吐要求高的场景下这个特性会节省大量 CPU 时间也让代码逻辑简单很多。2.2 MK24FN256VDC12 的资源分配MK24FN256VDC12 是 NXP Kinetis K24 系列的成员Cortex-M4F 内核带 FPU主频 120MHz片上 Flash 256KBSRAM 64KB。外设方面有多个 SPI、UART、I2C、定时器、ADC、DMA 控制器和外部总线接口。工业级温度规格供电范围适合 3.3V 系统跟 MR25H40CDF 可以在同一个电压域工作不需要额外的电平转换。在实际项目里我这样分配资源一组 SPI 连接 MRAM专做存储读写串口或者以太网负责跟外部上位机通信DMA 负责把需要存储的数据从缓冲区搬到 SPI 发送寄存器减少 CPU 中断负担。如果还有剩余的 GPIO就用来做片选控制、掉电检测和看门狗反馈。Kinetis 系列的 FlexBus 接口可以用来外扩并行存储器但我个人觉得在这种方案里没有必要浪费 FlexBus。SPI 方式最大的好处是管脚少、走线简单、器件小在 PCB 面积紧张的工业板上很有优势。整个存储子系统的外部器件几乎就是一颗 MRAM、几个电阻电容硬件成本很低可靠性却比 Flash 方案高不少。2.3 为什么 512KB 容量够用工业存储的容量不是越大越好。512KB 对于记录结构化数据来说非常合适。举个例子如果一条运行记录定义成 64 字节包含时间戳、设备状态、三个模拟量、一个故障码和 CRC 校验那么 512KB 可以存 8192 条记录。如果每分钟存一条可以覆盖五到六天的数据每秒存一条也能覆盖一个多小时的精细数据。配合上位机定期导出这种容量在大多数设备里足够用。容量选型时还可以算一下写入频率和寿命的关系。MR25H40CDF 的耐久次数极高512KB 空间就算每天写满几十次用上十年也完全没问题。这点跟 Flash 完全不同Flash 的寿命模型会让系统设计者反复计算擦写次数MRAM 则可以直接忽略这个维度把精力花在数据格式和协议设计上。如果项目需要更大容量也可以在同一颗主控下接多片 MRAM驱动代码只需要扩展片选逻辑和地址位宽整体架构不用动。3. 硬件连接和存储区规划3.1 最小硬件电路MR25H40CDF 和 MK24FN256VDC12 的硬件连接非常简单最低只要四根线加一根片选。把 MK24 的 SPI_SCK、SPI_SOUT、SPI_SIN 分别接到 MRAM 的 SCK、SI、SO再用一个 GPIO 接到 MRAM 的 CS#。MRAM 的 WP# 和 HOLD# 在正常读写时不能悬空一般直接上拉到 3.3V防止意外触发写保护和暂停传输。电源方面两片都工作在 3.3V。MRAM 的电源脚旁边放一个 0.1uF 和一个 10uF 的电容尽量靠近芯片引脚。如果板子上有电机、继电器这类大电流干扰源建议在 MRAM 的电源入口再加一个磁珠或者小型 LC 滤波。因为 SPI 通信在高频干扰下最容易出现的是某个 bit 被拉偏而不是整片通信失败电源做干净一点能减少很多莫名其妙的偶发错误。片选脚的选择也有讲究。理论上任意 GPIO 都可以用但在 Kinetis 平台上建议把 CS# 接到支持 DMA 触发或者不影响中断响应的引脚上这样后续做 DMA 连续读写时可以自己控制 CS 的时序不会和 SPI 硬件自动片选产生冲突。为了调试方便还可以在 CS# 和 VCC 之间预留一个 10K 上拉电阻的位置某些板卡上这个电阻能改善上电瞬间的片选抖动。3.2 存储区规划拿到 512KB 空间后第一步不是写驱动而是先画一张存储地图。我常用的规划方式是把整个空间分成三块参数区、日志区、临时缓冲区。参数区放在最前面大约 8KB用来保存设备配置、校准系数、序列号等信息。这类数据写入频率低但每一笔都必须绝对可靠所以我会在参数区里做双份备份每份包含 CRC读取时先校验主备份失败再读备用备份。日志区是 MRAM 的主体根据单条日志长度算好总条数头部用一个 32 位变量记录当前写指针另一个 32 位变量记录已写总条数。上电时先读头部就能直接从上次的位置继续写不用对整个存储区进行扫描。临时缓冲区放在日志区后面大小随意用来做数据搬运和格式转换的暂存区也可以放一些需要临时保存的中间结果。区域名称起始地址大小用途参数区主备份0x000004KB设备参数、校准数据参数区备用备份0x010004KB参数备份和恢复日志区0x02000480KB运行记录、事件日志临时缓冲区0x7A00024KB数据搬运暂存存储地图必须固化在代码注释和设计文档里避免多个版本之间地址错位。尤其是后续要扩展功能时如果新需求直接往空地址写很可能覆盖旧数据而没有任何提示。工业产品的存储布局最好形成一套可版本管理的配置每次升级固件时同步核对地址偏移。3.3 读写接口的设计对上层应用我一般只暴露四个接口初始化、读数据、写数据、区域清空。虽然 MRAM 本身不需要擦除但逻辑上还是需要一个清空区域的操作用全 0xFF 或者全 0x00 填充即可方便调试和出厂初始化。每个接口的第一个参数是逻辑地址第二个参数是缓冲区指针第三个参数是长度。底层把逻辑地址转换成 MRAM 的实际地址按 SPI 指令格式发送。写接口内部会自动处理写使能、CS 时序和长度限制上层完全不感知。这样做的好处是如果将来换了其他型号的 MRAM甚至换成并行接口的 MRAM只需要修改底层驱动上层业务代码完全不用动。接口设计有一个小原则写接口内部不要做数据拼接。如果上层传入的缓冲区长 100 字节底层就直接连续写 100 字节不要在驱动里拆成多次小包。因为 MRAM 的写入没有页边界限制拆分反而增加出错概率。读取接口同理连续读一整块让上层自己决定如何解析。4. SPI 读写驱动实现细节4.1 SPI 初始化我用 MK24 的 SPI0 模块连接 MR25H40CDF。初始化时配置为主模式时钟极性 CPOL0相位 CPHA0也就是 SPI 模式 0。MR25H40CDF 支持 SPI 模式 0 和模式 3选模式 0 是最常见的做法别搞错否则读出来的数据全是乱码。时钟频率方面数据手册标称最高 40MHz。实际工程我建议先跑 20MHz等逻辑分析仪确认时序没问题再往上提。如果 PCB 走线较长或者板上有强干扰源降低到 10MHz 往往更稳。SPI 通信不是越快越好关键是保证每个数据沿都能被可靠采样一味追求高速反而容易给自己埋坑。初始化代码大致如下void mram_spi_init(void) { // 配置 SPI0 引脚复用 spi_mux_configure(SPI0_SCK_PIN, SPI0_SOUT_PIN, SPI0_SIN_PIN); // 主模式8bit 数据SPI Mode 0分频后时钟约 20MHz SPI0-CTAR0 SPI_CTAR_FMSZ(7) | SPI_CTAR_CPOL(0) | SPI_CTAR_CPHA(0) | SPI_CTAR_PBR(0) | SPI_CTAR_BR(1); // 设置为主模式关闭自动片选 SPI0-MCR SPI_MCR_MSTR_MASK | SPI_MCR_PCSIS_MASK; }使能主模式之后CS 由 GPIO 手动控制不用 SPI 硬件自动片选。手动片选的自由度更高也方便在 DMA 传输场景下控制时序。4.2 写数据流程写入一个缓冲区的流程是先发 WREN0x06让 MRAM 进入写使能状态然后拉低 CS发送 WRITE 指令0x02再发送两字节地址接着连续发送数据全部发完后拉高 CS。CS 必须在整个 WRITE 指令和后续数据期间保持低电平中途释放会导致写操作被终止。写数据前要先确认总线方向正确。MK24 的 SPI 是四线制SOUT 接 MRAM 的 SISIN 接 MRAM 的 SO不要接反。接反的结果往往是写进去的数据读出来全 FF。代码里可以加一个简单的 self-test上电后往地址 0 写一个 0x5A再读回来比对不一致直接报错。这个自检在工业现场非常实用能第一时间暴露总线问题。int mram_write(uint32_t addr, const uint8_t *buf, uint32_t len) { if (addr len MRAM_SIZE) { return -1; } spi_cs_low(); mram_wren(); // 发送 WREN 指令 spi_transfer_byte(0x02); // WRITE 指令 spi_transfer_byte((addr 8) 0xFF); spi_transfer_byte(addr 0xFF); for (uint32_t i 0; i len; i) { spi_transfer_byte(buf[i]); } spi_cs_high(); return 0; }另外要注意写使能状态在每次 CS 拉高后会被清除所以每次操作都要走WREN CS WRITE的完整流程不能在初始化时只使能一次。有些工程师第一次用 MRAM 会习惯性地省略 WREN导致写入无效这个细节值得反复强调。4.3 读数据流程读取流程比写入更简单拉低 CS发送 READ 指令0x03再发送两字节地址然后连续发送 0x00 作为时钟来读取数据读完拉高 CS。MRAM 的连续读没有任何页边界限制可以从任意地址一路读到存储空间末尾不需要像 Flash 那样考虑跨页分段问题。读取代码int mram_read(uint32_t addr, uint8_t *buf, uint32_t len) { if (addr len MRAM_SIZE) { return -1; } spi_cs_low(); spi_transfer_byte(0x03); // READ 指令 spi_transfer_byte((addr 8) 0xFF); spi_transfer_byte(addr 0xFF); for (uint32_t i 0; i len; i) { buf[i] spi_transfer_byte(0x00); } spi_cs_high(); return 0; }这个简单的连续读能力在数据导出时非常有用。我经常用它一次性把几个 KB 的日志块读出交给上位机解析整个过程没有任何卡顿。相比 Flash 需要按扇区读、跨页还要拼接MRAM 的读取手感更像在操作内存。4.4 用 DMA 提升吞吐如果日志记录频率很高比如每秒要存几百条数据CPU 逐字节搬运会浪费大量时间。这时可以使用 MK24 的 DMA 模块把内存里的日志缓冲区直接搬运到 SPI 的发送 FIFO同时 SPI 硬件自动产生时钟CPU 只需要在传输完成中断里做统计和清理工作。使用 DMA 后有两个点需要注意。第一CS 控制别交给 SPI 自动片选自己用 GPIO 在 DMA 传输前拉低、传输完成后拉高。第二DMA 传输长度和缓冲区有效长度要严格一致多传一个字节就会覆盖下一条记录的头部。我一般会在每个记录头部放一个 magic numberDMA 完成后再检查最后一个记录的完整性确保没有越界。DMA 的初始化需要联动配置 SPI 的发送 FIFO 和 DMA 请求源。这个过程不同芯片的 SDK 差异较大建议直接参考 NXP SDK 里的 spi_dma 例程再把例程里的缓冲区替换成 MRAM 的写入帧格式半天时间基本可以调通。5. 工业现场的数据可靠性设计5.1 记录格式和 CRC 校验工业现场环境恶劣SPI 线上偶尔受干扰出现一个坏 bit 是正常的。如果只写裸数据坏 bit 可能导致整条记录出现错误甚至影响下一条记录的解析。我的做法是每条记录采用固定帧格式头部放 magic中间放数据尾部放 CRC32 校验值。字段长度说明Magic2 字节固定值 0xA55A用来快速判断记录是否有效Timestamp4 字节秒级或毫秒级时间戳DataN 字节实际采集数据CRC324 字节对前面所有字段做 CRC32 校验读取时先检查 Magic再计算 CRC两者都通过才算有效记录。如果 Magic 不对通常意味着记录没有写完或者该区域从未写过直接跳过。这种设计让日志解析过程非常稳健即使存储区里混入了几条坏记录也不会导致整段数据不可读。CRC 计算可以放在 MCU 里用软件实现也可以用 Kinetis 系列的 CRC 硬件模块速度更快而且不占用 CPU 太多时间。如果记录数据量比较大建议把 CRC 硬件的 DMA 通道一起配好这样每条记录的校验开销几乎可以忽略。5.2 掉电保护和两阶段写入虽然 MRAM 本身是掉电不丢的但系统掉电如果恰好发生在写数据的过程中还是可能出现记录写了一半的情况。解决办法是两阶段提交先写一个临时数据区等数据完整写入后再更新记录头部的 Magic 和长度字段。读取时只认 Magic 完整的记录这样即使掉电发生在中间也不会产生半条有效记录。MRAM 的写入是覆盖写Magic 字段的更新非常快速所以两阶段提交的额外开销几乎可以忽略。如果系统把掉电检测也做起来比如 MK24 的引脚检测到掉电信号后立即停止上层任务把剩余数据快速写入 MRAM这就算是一套完整的掉电保护方案。MRAM 的优势在这里非常明显因为写入不需要等待MCU 来得及在电压跌落到下限之前完成最后一批数据的保存。参数区建议采用双备份机制。启动时先读主区CRC 校验失败就尝试备用区备用区有效就把备用区内容复制到主区。这样就算一次写参数时掉电也不会让设备变成无法起机的状态。这个机制在 Flash 里做起来很繁琐因为要考虑擦写次数和恢复流程在 MRAM 里做起来就是几行代码的事。5.3 上电自检和完整性检查上电后我会执行三段检查。第一段是 SPI 通信自检对固定地址写入已知 pattern 再读回验证总线和芯片正常。第二段是扫描日志区头部检查写指针是否在合法范围内。第三段是随机抽查几条日志做 CRC 校验评估存储区整体健康状态。如果项目对数据完整性要求更高比如医疗或电力设备可以考虑在软件层加一层汉明码或者简单奇偶校验。MR25H40CDF 内部本身有一些纠错能力但工业级现场还可以做得更稳。额外的 ECC 会占用一部分存储空间和 CPU 时间但显著提高数据可信度我一向主张这类可靠性设计宁可多花一点资源也不能让现场数据出错。6. 实际调试中遇到过的坑6.1 读回来的数据全是 0xFF这是 SPI 接反或者片选没动作的典型症状。先检查 SOUT 和 SIN 有没有接反再看 CS 的 GPIO 是否初始化成输出模式。MRAM 在 CS 为高时数据输出脚是高阻态如果读输入悬空读回的数据经常是 0xFF。另外还要检查 WP# 和 HOLD# 的上拉是否正常这两个脚如果被拉低写入操作会被直接拦截。6.2 写入后立刻读取数据仍是旧值先确认写操作前是否发了 WREN。MRAM 的写使能逻辑和 Flash 类似WREN 没发或者 CS 时序不对写指令可能被忽略。解决办法是写完 WREN 之后拉高 CS再拉低 CS 发写指令中间不能把两个指令连在一起发。这个细节我用逻辑分析仪抓时序时才发现因为从数据手册上看两段指令似乎可以连续但实际芯片就是要求 CS 有一次上升沿来锁存写使能状态。6.3 高速模式偶尔出现数据错位40MHz 在 3.3V 系统里容易受寄生电容影响。排查时把 SPI 时钟降到 20MHz 或者 10MHz这个问题基本能解决。同时注意 SCK 走线不要跟电源线、电机驱动线并行走地线要完整。MRAM 附近的地铜皮尽量直接连到主控地不要绕圈否则回流路径太长会引入噪声。6.4 记录被干扰导致日志解析中断如果日志区是环形覆盖的坏记录会直接导致后续解析错位。我的处理方式是每条记录头部带长度字段尾部带 CRC。解析时先按长度字段跳到下一条记录即使当前记录坏了也能接着扫不会因为一条坏记录把整个日志区废掉。这里再分享一个小技巧写日志时在每条记录里同时记下下一条记录的偏移量读取时直接按这个偏移量跳转牺牲 2 字节空间换取的解析稳定性非常划算。7. 这组芯片还能怎么扩展MR25H40CDF 加 MK24FN256VDC12 的组合并不局限于简单的日志存储。利用 MK24 的串口或以太网可以把 MRAM 里的数据实时导出到上位机。上位机端可以用 Python 写一个小工具通过串口按帧读取数据解析成 CSV 或者直接存数据库。这样就不需要每次到设备前插数据线拷贝也算是一种轻量级的远程数据读取方案。如果设备的日志需要长期保存还可以在主控上挂一个 TF 卡或者通过以太网把数据上传到网络存储。MRAM 在这里充当短时高速缓存先保证实时数据不掉再定期把数据批量搬到大容量存储里。这种两级存储结构在很多工业网关里非常实用既保证了实时性又解决了容量限制。另外如果要让数据进边缘网关或者做设备故障分析可以由 MCU 把 MRAM 中的数据通过 Modbus 等协议开放出去。MRAM 的快速读取特性让边缘网关在轮询历史数据时不会阻塞现场控制逻辑。很多号称支持远程诊断的设备底层其实都是这样做的现场控制器写 MRAM网关通过控制器接口读取 MRAM然后上云或者进入知识库做进一步处理。8. 一些个人经验这套方案我断断续续用了一年多最大的感受是存储方案不能只看芯片参数要看整个系统的写入模式。如果只是偶尔存几个配置参数EEPROM 够用如果需要频繁记录运行日志MRAM 的价值一下就体现出来了。MR25H40CDF 这颗 4Mbit SPI MRAM 在容量、接口、耐久度之间找到了一个很好的平衡点MK24FN256VDC12 的资源和外设又能把存储这件事做得非常灵活。最后给准备上手的朋友三条建议。第一先读数据手册里的 SPI 时序图和指令表每个控制位都要确认清楚不要想当然也不要只看网上零散的例程。第二上电自检一定要写进固件工业设备最怕表面正常、数据全错。第三电路设计时给 MRAM 的电源和 SPI 线留出滤波和阻抗匹配的余量现场出了问题再改板子成本比多花几个电容高得多。把这几步做扎实这个组合用起来会非常省心。
返回列表