ARTICLE DETAIL

资讯详情

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

工业控制器存储升级:MRAM替代NOR Flash的实战解析

工业控制器存储升级:MRAM替代NOR Flash的实战解析 上一版伺服驱动器改款的时候我被存储方案卡住了将近两周。控制板既要存几百个整定参数和厂商校准值还要在运行过程中不间断记录故障波形和累计运行数据旧方案用 SPI NOR Flash往页里写数据必须先擦除擦除一次几十毫秒写寿命标称十万次工业现场常年带电运行心里一直不踏实。后来我把存储介质换成了 Everspin 的 MR25H40CDF MRAM主控芯片统一升级到 NXP 的 MKV44F256VLH16整套存储逻辑一下子清爽了很多。这篇文章不聊芯片 datasheet 的翻译只讲我自己从选型、画板、写驱动到跑老化测试踩完的坑和最终沉淀下来的做法给同样在做工业控制器、仪表和电机驱动类产品的朋友做个参考。1. 为什么我在工业控制器里把 Flash 换成了 MRAM1.1 传统存储方案在工业现场的三个难言之隐先交代一下项目背景。我们做的是伺服驱动器主控板MCU 选型要满足三件事电机控制环路算力足够、ADC 能同步采样电流和母线电压、存储接口要简单可靠。之前的产品用一颗 Cortex-M4F 的 MCU 配合 SPI NOR Flash 存参数运行久了问题接二连三。第一个痛点是擦除机制。NOR Flash 写入前必须擦除到 0xFF而擦除的最小单位是扇区哪怕你只想改其中一个字节也要先把这个扇区整块读出来、擦掉、再写回去。参数区为了保证掉电安全通常还会做双备份和回滚一套流程下来一次参数保存动辄几十毫秒如果中途再被高优先级中断打断数据就可能不完整。第二个痛点是写寿命。参数虽然每次只写几十个字节但设备每做一次整定、每一次故障记录、每一次固件配置变化都会触发写入。按每天写几百次的频率算十万次寿命的 Flash 理论寿命只有一两年。做过工业产品的人都懂客户现场的写入频率你是不可控的最怕的就是设备还在用、存储先报废。第三个痛点是掉电写入可靠性。Flash 页编程时间在毫秒级掉电瞬间那几毫秒根本写不完一个扇区。如果只依赖外部 EEPROM又受容量和速度限制想存故障录波这种几百 KB 的数据基本没戏。1.2 MR25H40CDF 到底解决了什么不会磨损的“非易失 SRAM”MR25H40CDF 是 Everspin 的 4Mbit 串行 MRAM容量 512KBSPI 接口DIP 时代意义上的“一颗芯片替代一块小容量的 NOR Flash EEPROM”。它最核心的一点是数据存储在磁性隧道结上写入靠磁矩翻转掉电后状态保持不需要刷新也不需要擦除。你把它当成一片“非易失的 SRAM”来用就对了。这就带来了几个对工业场景特别有价值的特点写入不需要先擦除任何地址都可以直接覆盖写入没有页编程限制单字节、多字节都能写写寿命远超 Flash 和 EEPROM正常产品生命周期内基本不需要考虑磨损问题SPI 指令集和传统 SPI Flash 高度兼容读写函数的骨架可以直接从现有 Flash 驱动改过来。MR25H40CDF 的具体参数上工业级温度范围做到 -40 到 105°C供电 3.3V最高 SPI 时钟按 40MHz 设计实测稳定跑 20MHz 已经比绝大多数 EEPROM 快了一个数量级。封装是 DFN-8体积很小非常适合主控板空间紧凑的场景。当然它也有局限容量只有 512KB不能当大容量存储用价格比同容量的 NOR Flash 贵不少。它适合的是“数据频繁更新、不能丢、必须可靠”的那类场景而不是大文件存储。1.3 MKV44F256VLH16 和 MRAM 搭配的逻辑MKV44F256VLH16 是 NXP Kinetis KV44 系列的一颗 Cortex-M4F 主控主频 168MHzLQFP64 封装256KB Flash、32KB SRAM。这颗芯片在电机控制领域的定位很明确带浮点单元和 DSP 指令FlexTimer 输出互补 PWMADC 可以多通道同步采样非常适合伺服、变频器这类对实时性要求高的控制板。之所以把它和 MR25H40CDF 放在一起是因为这套组合解决了工业控制板的一个典型矛盾控制环路要求 MCU 算力强、中断多、实时任务重存储任务则要求接口简单、写入快速、掉电安全。KV44 的 DSPI 模块支持 DMA 搬运MRAM 写入不需要等待擦除两者配合之后存储操作可以做成“主循环里下命令DMA 后台搬数据”对控制环路的打扰降到最低。另外 KV44 内部有低压检测模块掉电瞬间可以触发中断这正好和 MRAM 的瞬时写特性组成一套掉电保护方案。Flash 在掉电瞬间写不完MRAM 可以。这个组合从硬件层面就解决了我在 1.1 里说的问题后面的电路和代码都是围绕这一点展开的。2. 硬件连接一个让 HOLD# 浮空的坑与完整电路参考2.1 SPI 端口复用与引脚分配MR25H40CDF 的接口非常传统SCK、MOSI、MISO、CS 四条信号线外加 /WP 和 /HOLD 两个控制引脚。和 SPI NOR Flash 唯一的差别是它没有状态忙引脚MISO 上也不会出现像 Flash 那样的 busy 电平所以软件逻辑更简单。在 KV44F256VLH16 上我建议把 MRAM 挂在 SPI0 上CS 用任意 GPIO。具体引脚号以你用的 SDK 管脚工具生成为准不同封装、不同管脚复用配置出来的编号不一样强行记某个引脚号反而容易出错。我画板时的接线关系是这样的MR25H40CDF 引脚功能接到 MKV44F256VLH16CS#片选任意 GPIO推挽输出SCK时钟SPI0 SCKMOSI主发从收SPI0 MOSIMISO主收从发SPI0 MISOVDD电源3.3V并联 0.1uF 4.7uF 电容GND地系统地/WP写保护经 10kΩ 电阻上拉到 3.3V/HOLD保持经 10kΩ 电阻上拉到 3.3V这里重点说一下 /HOLD。这颗引脚的作用是暂停串行通信低电平有效。芯片运行时如果 /HOLD 被拉低SPI 时钟和数据的当前传输会被冻结MISO 变高阻MCU 端看起来就像是读回的数据突然少了一拍、错了一位而且不是每次都复现非常难查。我曾经在一版板上为了让电路“简洁”把 /HOLD 直接悬空结果整机做 EMC 辐射测试的时候只要一开变频器负载偶发读回数据错误最后用示波器抓到 /HOLD 上出现窄毛刺才定位到是悬空引脚被耦合噪声拉低。所以 /HOLD 和 /WP 都必须上拉优先选择上拉到 VDD不要由 MCU GPIO 控制。你也许觉得 GPIO 能主动控制更灵活但在工业现场MCU 上电时序里任何一段 GPIO 默认状态不确定都可能让存储芯片进入异常状态。硬件上做到“默认就是安全的”比软件里绕来绕去靠谱得多。2.2 电源退耦与掉电保持电路MRAM 写入时内部电流瞬态变化比 EEPROM 明显电源必须退耦到位。我每片 MR25H40CDF 旁边都放了 0.1uF 陶瓷电容紧贴 VDD 引脚再远一点放一颗 4.7uF 钽电容或高质量陶瓷电容。注意不要只在 MCU 电源端放一大颗电容就觉得完事了高频去耦讲究的是“靠近负载”电容离芯片超过 5mm效果就大打折扣。掉电保护电路需要单独提一下。KV44 的 LVD 模块可以监测 VDD 跌落当电压低于设定阈值时触发中断。我在这里做了一件事在 MCU 的 VDD 输入端加了一颗 1000uF 的储能电容同时把 LVD 中断优先级调到最高。正常运行时系统电流几十毫安掉电瞬间 LVD 触发之后1000uF 电容上存的电还能让 MCU 稳定运行至少几毫秒而 MRAM 写入几 KB 数据只需要几十微秒余量非常充足。这个方案比外置掉电检测芯片成本低、响应快而且由于 MRAM 不需要像 Flash 那样等待擦除完成窗口压力小了很多。实际测试中断电瞬间触发中断到数据写完、CS 释放整个过程大约 400 微秒之后 MCU 即使马上失去供电也不影响数据完整性。2.3 PCB 布局与长线传输注意事项工业控制板难免要在 MCU 板和存储器件之间走一段线我的经验是MRAM 尽量靠近 MCUSPI 线总长控制在 20mm 以内减少线路电感带来的信号完整性问题SPI 三条信号线 SCK、MOSI、MISO 尽量等长避免时钟采样点偏移信号线上可以加 22Ω 串联电阻放在 MCU 端用来抑制振铃。如果板子空间紧张这个电阻可以省但走线超过 5cm 时建议保留如果 MRAM 和 MCU 分属两块板用排线连接那就把 SPI 时钟降到 10MHz 以内并且加地线隔离不要省那一根地线/CS、/WP、/HOLD 在 PCB 上走线不要和 PWM 输出、继电器驱动线平行长距离走MRAM 的这几个控制脚对毛刺敏感我在实际产品里就因为 /CS 走线贴着逆变桥驱动线导致上电瞬间 MRAM 偶发进入错误状态。3. 驱动实现用 Kinetis SDK 把 MR25H40 跑起来3.1 SPI 初始化模式、时序与速率选择MR25H40CDF 支持 SPI Mode 0 和 Mode 3我习惯用 Mode 0即 CPOL0、CPHA0空闲时 SCK 为低电平数据在上升沿采样。工业环境里只要不是超长走线Mode 0 最直观也好调试。基于 Kinetis SDK 的初始化代码大致是这个样子注意不同 SDK 版本的 API 签名略有差异但思路一致#include fsl_spi.h #include fsl_gpio.h #include fsl_clock.h #define MRAM_SPI SPI0 #define MRAM_SPI_CLK kCLOCK_CoreSysClk /* 实际时钟源按工程配置 */ void MRAM_InitSPI(void) { spi_master_config_t config; SPI_MasterGetDefaultConfig(config); config.baudRate_Bps 20000000UL; /* 工业板 20MHz实验室短跑线可 40MHz */ config.polarity kSPI_ClockPolarityLow; /* CPOL0 */ config.phase kSPI_ClockPhaseFirstEdge; /* CPHA0 */ config.direction kSPI_MsbFirst; SPI_MasterInit(MRAM_SPI, config, CLOCK_GetFreq(MRAM_SPI_CLK)); /* CS 引脚初始化为高电平禁止片选 */ gpio_pin_config_t csConfig {kGPIO_DigitalOutput, 1}; GPIO_PinInit(CS_GPIO, CS_PIN, csConfig); }速率选择这里多说一句。MR25H40CDF 标称最高 40MHz但工业现场的 EMC 环境不会给你展示这个上限的机会。我最后量产定在 20MHz短距离、同板布局时跑得又快又稳。如果板内走线很短、干扰源控制得好也可以上 40MHz但代价是画板要求高很多而且读回校验一旦出错排查成本远大于那点速度收益。3.2 核心指令集与读写函数MR25H40CDF 的指令集和 SPI NOR Flash 很接近我用到的指令就六条指令命令字节说明WREN0x06写使能WRDI0x04写禁止RDSR0x05读状态寄存器READ0x03读数据24 位地址FAST_READ0x0B快速读比 READ 多一个 dummy 周期WRITE0x02写数据24 位地址无需擦除和 Flash 驱动最不一样的地方是写完 WRITE 指令之后没有“等待页编程完成”的过程不需要查 busy 位也不需要读状态寄存器确认编程结束。数据手册里这个写入是立即完成的所以写完直接释放 CS 即可。驱动代码可以做到非常干净#define MRAM_WREN 0x06U #define MRAM_WRDI 0x04U #define MRAM_RDSR 0x05U #define MRAM_READ 0x03U #define MRAM_FAST_READ 0x0BU #define MRAM_WRITE 0x02U void MRAM_Select(void) { GPIO_PinWrite(CS_GPIO, CS_PIN, 0U); } void MRAM_Release(void) { GPIO_PinWrite(CS_GPIO, CS_PIN, 1U); } void MRAM_WriteEnable(void) { uint8_t cmd MRAM_WREN; MRAM_Select(); SPI_MasterWriteBlocking(MRAM_SPI, cmd, 1); MRAM_Release(); } uint8_t MRAM_ReadStatus(void) { uint8_t cmd MRAM_RDSR; uint8_t status 0; MRAM_Select(); SPI_MasterWriteBlocking(MRAM_SPI, cmd, 1); SPI_MasterReadBlocking(MRAM_SPI, status, 1); MRAM_Release(); return status; } status_t MRAM_Read(uint32_t addr, uint8_t *buf, uint32_t len) { if (addr len 0x80000UL) { return kStatus_InvalidArgument; } uint8_t hdr[4] { MRAM_READ, (uint8_t)(addr 16), (uint8_t)(addr 8), (uint8_t)(addr) }; MRAM_Select(); SPI_MasterWriteBlocking(MRAM_SPI, hdr, sizeof(hdr)); SPI_MasterReadBlocking(MRAM_SPI, buf, len); MRAM_Release(); return kStatus_Success; } status_t MRAM_Write(uint32_t addr, const uint8_t *buf, uint32_t len) { if (addr len 0x80000UL) { return kStatus_InvalidArgument; } uint8_t hdr[4] { MRAM_WRITE, (uint8_t)(addr 16), (uint8_t)(addr 8), (uint8_t)(addr) }; MRAM_WriteEnable(); /* 每次写前都发 WREN */ MRAM_Select(); SPI_MasterWriteBlocking(MRAM_SPI, hdr, sizeof(hdr)); SPI_MasterWriteBlocking(MRAM_SPI, (uint8_t *)buf, len); MRAM_Release(); return kStatus_Success; }几个细节值得记录MRAM 的地址是 24 位的4Mbit 容量对应最大地址 0x7FFFF所以写函数里做个边界检查防止上层传入越界地址把数据写到非线性空间去每次写数据之前都发一次 WREN。虽然理论上 WEL 位在成功写入之后会清零但你在代码里做到“写前必使能”会让逻辑更健壮尤其当 /WP 引脚异常时能立刻暴露问题读函数不需要写使能这是新手最容易搞混的地方。读数据、读状态寄存器永远不需要 WREN快速读指令 FAST_READ 比 READ 多一个 dummy 周期如果你的系统里 SPI 时钟跑得很高建议用 FAST_READ地址建立时间更充裕。我在 20MHz 下直接用 READ省一个字节开销。3.3 优化DMA 搬运与中断设计工业主控最宝贵的资源是 CPU 时间。KV44 的 DSPI 模块支持 DMA 请求把 MRAM 的读写做成 DMA 搬运之后CPU 只需要在开头配置好 DMA 描述符剩下的数据搬运全交给 DMA 控制器。我在工程里做了两层小数据量比如保存 32 字节参数直接用阻塞式 SPI 写耗时约 30 微秒级别控制环路的周期是 100 微秒级别只要不在中断临界区调用问题不大大数据量比如故障录波一次 4KB 波形数据走 DMA在主循环里触发传输完成后由 DMA 中断置标志位。DMA 配置代码较长核心思路是配置一个 DMA 通道源地址指向 MRAM 数据缓冲区目的地址指向 SPI0 的数据寄存器 PUSHR传输计数设为 len完成一次传输后触发 DMA 中断传输过程中 CS 保持低电平DMA 完成中断里做读回校验和释放 CS。用 DMA 之后实测连续写 4KB 数据的 CPU 占用从阻塞方式的近 100% 降到接近 0%。如果你的产品里除了存储还有实时控制、通信、显示这一步优化值得做。4. 数据可靠性设计掉电保存、环形日志与 CRC4.1 借助 KV44 的低压检测实现“最后一刻写入”MRAM 写得快但如果不配合掉电检测也只是“快”还不是“可靠”。掉电瞬间如果 MCU 认为电源正常继续跑主循环结果电压一路滑坡数据写到一半 SPI 时钟就开始乱那数据一样完蛋。我的做法是用 KV44 的 PMC 模块配置低压检测pmc_lvd_config_t lvdConfig; PMC_GetLvdDefaultConfig(lvdConfig); lvdConfig.enableLvd true; lvdConfig.requestInterrupt true; lvdConfig.thresholdVoltage kPMC_LvdLowTripPoint; /* 根据原理图定 */ PMC_ConfigureLvd(PMC, lvdConfig);LVD 中断触发之后立刻把 MCU 从“正常运行”切到“紧急保存模式”。在这个模式里关闭所有高功耗外设尤其是 PWM 输出和通信外设降低电源电流消耗把需要保存的关键数据运行计数值、当前参数版本、最近一次故障码打包成结构体调用 MRAM_Write 写入固定地址写完读回校验一致后清除 LVD 中断标志。这里要提一个容易忽略的问题LVD 中断里不要再调用任何可能阻塞的外设函数尤其不要发串口打印。掉电瞬间每一微秒都宝贵串口打印会把宝贵的窗口浪费掉而且外部电平也可能已经乱了。我见过同事在掉电中断里加串口调试信息结果一整块参数区偶发损坏查了好几天才找到原因。4.2 无磨损均衡的数据布局参数区 运行日志MRAM 最大的优势之一就是不需要磨损均衡。Flash 要做的“日志追加、块搬运、冷热数据分离”这些设计在 MRAM 上可以直接扔掉。我把整个 512KB 分成三块区域地址范围大小用途参数区 A0x00000 - 0x0FFFF64KB当前有效参数副本参数区 B0x10000 - 0x1FFFF64KB参数备份副本日志区0x20000 - 0x7FFFF384KB故障录波、运行日志环形覆盖参数区做双副本的考虑不是寿命而是防止中途掉电把唯一的副本写坏。即使 MRAM 写入不需要擦除也无法保证掉电瞬间刚好写到一半就被切断双副本加回滚是成本最低的保护方式每次保存参数时先写 B 区写完做 CRC 校验CRC 通过后把 A 区的旧参数覆盖为同样内容开机读取时优先读 A 区如果 A 区 CRC 失败就自动回滚到 B 区如果 A 和 B 都坏了基本属于其他硬件故障连带的极端情况加载出厂默认参数。日志区我用的是最简单的环形追加日志头部记录写指针位置每次新数据从指针处开始写写满后回到区域起点覆盖最老的数据。由于 MRAM 没有磨损问题这种“无均衡环形缓冲区”可以一直跑下去逻辑比 NAND Flash 上的 FTL 简单一个量级。4.3 校验与恢复策略我用的校验是 CRC32。工业数据完整性要求高CRC16 在某些碰撞场景下不够用CRC32 是底线。具体实现不贴了建议直接用编译工具链自带的 CRC 查表法在线计算量小。写流程的完整校验策略上层应用准备好数据缓冲区计算出 CRC32 附加在数据末尾调用 MRAM_Write 写入目标地址紧接着调用 MRAM_Read 把同一地址读回来重新计算 CRC 并比对不一致则重试一次重试仍然失败上报存储故障不再盲目重复写。有人觉得每次写完都读回来会降低一半以上的写性能但工业数据存储本来就是低频操作哪怕单次保存需要多花一两毫秒换来的却是数据「写没写对」的确定性。故障录波这种大数据量场景则不逐字节校验只校验头部和尾部的几个关键标记字段减少时间开销。5. 实测数据与现场踩坑记录5.1 读写性能实测我用逻辑分析仪抓了实际波形SPI 时钟 20MHz结果如下操作实测耗时说明单地址写 4 字节约 5.8 微秒含 WREN、命令头、数据发送、CS 释放单地址读 4 字节约 5.5 微秒含命令头、数据读取、CS 释放连续写 4KB约 1.7 毫秒纯 SPI 数据传输不含上层 CRC连续读 4KB约 1.6 毫秒纯 SPI 数据传输写满整个 512KB约 0.25 秒持续写不做擦除不需要等待这个性能对电机控制板来说绰绰有余。我印象很深的是第一次测试时主循环里每 100 微秒记录一组 16 字节波形数据MRAM 写操作平均耗时不到 10 微秒DMA 模式下对电流环几乎没有影响。5.2 长时间运行稳定性测试稳定性测试我做了三轮分别对应三个我最担心的维度写寿命测试用固定地址持续写入随机数据每写完一次立即读回比对跑满 500 万次零错误。这基本等效于产品 10 年以上的写操作总量掉电测试控制板上电后随机断电断电瞬间用 LVD 中断写 32 字节关键数据重复 1000 次断电。每次断电完成后重新上电读回数据全部正确高温测试85°C 环境箱里连续运行 500 小时期间每 10 分钟写一次日志测试结束后读回所有日志数据CRC 全部通过。MR25H40CDF 的磁性存储机理决定了它不会出现 Flash 那种“写坏一个 bit 后越来越多错误”的磨损老化特征测试结果也符合预期。需要说明的是500 万次单地址写只是我们项目验证过的量级不代表芯片上限Everspin 的 MRAM 数据手册标称写耐久通常都在 10^14 次以上这个指标在实际项目中根本用不完。5.3 几个容易翻车的细节最后把我自己踩过的、以及帮同事排过的坑集中列一下每一条都是实际代价换来的/HOLD 悬空导致偶发读回错位症状是开电机负载后偶尔读回数据中有一个字节变成 0xFF 或 0x00SS 线、时钟线全部正常最后定位到 /HOLD 被噪声拉低。解决办法就是上拉到 VDD10kΩ 足够。/WP 没上拉导致写不进去芯片的 /WP 低电平时即使发了 WREN写操作也会被拒绝。如果你发现 MRAM 写操作“看起来成功”但读回来全是旧数据先量 /WP 电平而不是怀疑代码。在 MRAM 上执行 Flash 扇区擦除指令这属于驱动移植时最容易犯的错。MR25H40 没有擦除指令不支持 0x20扇区擦除或 0xD8块擦除发了这些指令芯片不会响应也不会报错后续读写行为可能变得诡异。换芯片后一定要审查指令集别让旧 Flash 驱动里的擦除逻辑混进来。SPI Mode 不匹配导致读回错位MRAM 支持 Mode 0 和 Mode 3但你的 SPI 外设可能默认配置成 Mode 1 或者 Mode 2。如果读回来的字节看起来像“移了一位”先怀疑时钟极性和相位。CS 释放前最后一个时钟沿没走完Kinetis SDK 的阻塞式 SPI 函数返回时代表最后一个数据字节已经写入 FIFO 并完成发送但在某些配置下还可能存在一个时钟周期的尾巴。稳妥做法是在释放 CS 之前加一个极短延时或者用 SPI 的完成标志确认传输结束。我遇到过 40MHz 高频下 CS 释放太早导致最后一个字节丢位的问题降速到 20MHz 后消失但本质原因是时序余量不足。LVD 中断里做复杂操作导致保存失败掉电中断里做 CRC 计算没问题但千万别调用慢速外设。我建议关键数据的 CRC 用查表法计算 32 字节不到 2 微秒足够快。如果用一句话总结这次选型MR25H40CDF 加 MKV44F256VLH16 的组合最适合的场景就是“数据要频繁改、掉了电坚决不能丢、控制环路还特别忙”的工业产品。MRAM 把存储可靠性问题从“耗材管理”变成了“普通外设”KV44 又提供了足够的算力和便捷的 DMA/LVD 外设配合。等这套方案跑完一轮量产验证之后我下一个项目里凡是涉及故障录波、运行日志和参数备份的地方都会优先考虑同样的组合。存储这块能用 MRAM 的地方就不要再用 Flash 硬扛了。
返回列表