ARTICLE DETAIL

资讯详情

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

STM32F401RB外扩MRAM替代Flash:工业数据存储与掉电保护实战

STM32F401RB外扩MRAM替代Flash:工业数据存储与掉电保护实战 做嵌入式做久了你会发现很多故障其实不是 CPU 算得慢也不是外设逻辑写错而是数据存不住、存不对。我这里有一台现场仪表的存储模块升级项目MCU 用的是 STM32F401RB原来想把运行参数和最近一段采样数据放在内部 Flash结果一算日志写入频率再一算擦写寿命心里就发毛。后来换成 MR25H40CDF 这颗 SPI 接口的 MRAM也就是磁阻随机存取存储器才把掉电存储和频繁记录的问题一次解决。这篇就把选型、接线、驱动、存储格式和调试过程中踩过的坑都列出来给同样在工业设备和嵌入式应用里折腾数据存储的朋友做个参考。内容不挑 IDEHAL 库为主寄存器思路也顺带说。1. 为什么工业存储选了 MRAM而不是 Flash/EEPROM1.1 常见非易失存储方案的对比嵌入式系统里做数据存储大家第一时间想到的往往是 EEPROM 和 SPI NOR Flash毕竟便宜、常见、资料多。但真正到工业场景里这两个方案都有让人头疼的地方。EEPROM 容量普遍不大几 Kbit 到几十 Kbit 是主流写寿命一般在百万次量级。对于只存几个配置参数的应用EEPROM 完全够用可一旦要高频写日志、记录曲线数据、保存最近一段时间的原始采样值容量最先不够寿命也是大问题。NOR Flash 容量倒是够几 MB 到几十 MB 都很容易买到。但 NOR Flash 有一个根深蒂固的毛病写之前必须擦除而且擦除是按扇区来的。这意味着你要修改一个小数据底层也得先把整个扇区读出来、改完、再擦掉、再写回去。平时这么干没问题可一旦掉电发生在“擦除一半”或者“写入一半”的窗口期数据就可能整片损坏。工业现场哪能接受这种事。MRAM 的存储原理和这两种完全不同。它内部用的是磁隧道结靠磁化方向来存储数据而不是靠电荷。电荷会漏、会漂磁化方向不会。所以 MRAM 天然具备非易失、不需要刷新、写入不需要擦除的特点寿命更是长到可以忽略磨损。我做一个比较粗糙的对照表看起来更直观方案写前擦除擦写寿命单字节随机写写入等待掉电写窗口典型容量EEPROM不需要约 10^6 次支持有一般几十 KbitNOR Flash必须整扇区擦约 10^5 次不支持按页写有编程时间风险较大几 MBMRAM不需要10^14 次以上支持无写即生效极短风险低1~16 Mbit从这个表能直接看出MRAM 在“频繁写 随机改 掉电不能丢”这个组合场景里几乎是唯一不用做复杂软件补偿的方案。1.2 MR25H40CDF 的指标到底意味着什么MR25H40CDF 是 Everspin 的 4Mbit SPI MRAM也就是 512KB组织方式为 512K x 8bit。为什么这个容量在工业设备里很舒服因为它能装下不少东西设备参数、校准数据、运行日志、最近一段时间的传感器原始值都能放得下。而 512KB 对于 STM32F401RB 这种 128KB Flash、20KB SRAM 的中小容量 MCU 来说刚好是“本地内存之外的一条大仓库”。数据手册上几个关键指标我实际用下来是这样理解的读写对称。SPI 时钟范围内读多快写就多快没有“编程时间”的概念。无需擦除。操作 MRAM 就能像操作 SRAM 一样直接对任意地址写新值不需要提前做扇区擦除。非易失。掉电后数据保留而且工业级温度范围基本覆盖 -40℃ 到 85℃。耐久性极高。测试环境里连续刷一整天数据连状态寄存器都不用操心。对经常做工业设备的工程师来说这几条意味着软件架构可以大幅简化。你不用维护 Flash 的磨损均衡算法不用考虑页对齐更不用为“擦除中断”写复杂的恢复逻辑。数据存储就变成单纯的“写地址、写数据、读数据”。1.3 与 STM32F401RB 搭配时的资源成本STM32F401RB 是一颗 Cortex-M4 内核的中小容量 MCU主频可以跑到 84MHz片上资源在低成本设备里算均衡。它有 3 个 SPI 外设其中 SPI1 挂在 APB2 总线上时钟可以到 84MHz非常适合接 MRAM另外还有 DMA 控制器做批量读写不用占 CPU。实际选型时还有一个很现实的因素F401RB 的内部 Flash 只有 128KB固件、协议栈、字库一放就紧张再拿它存数据不现实。而外扩一片 MRAM 之后代码照常从内部 Flash 启动数据全部走 SPI 接口两边互不干扰。MRAM 也不是启动介质它老老实实当数据仓库用就行。成本方面MRAM 单价比普通 SPI NOR Flash 高这是事实。但要做整体成本评估的话MRAM 能省掉掉电保护电路、省掉复杂的软件均衡逻辑、省掉售后数据丢失的维护成本。工业设备里一次现场故障可能就抵掉几十片 MRAM 的差价。这个账要这样算。2. 硬件接线引脚不多但细节不少2.1 MR25H40CDF 引脚与 F401RB 的对应MR25H40CDF 是 8 脚 DFN 封装典型 SPI 接口主要引脚就 6 个CS#、SCK、SI、SO、VDD、VSS。另外还有 WP# 和 HOLD# 两个控制脚下面单独说。在 STM32F401RB 上我推荐把 MRAM 接到 SPI1因为 SPI1 时钟频率最高而且引脚正好是 PA4~PA7在 LQFP64 封装里很容易走线。接线关系如下MR25H40CDF 引脚信号方向接到 STM32F401RB说明CS#输入PA4软件控制片选GPIO 输出SCK输入PA5SPI1_SCKSI输入PA7SPI1_MOSI主发从收SO输出PA6SPI1_MISO主收从发VDD电源3.3V接 0.1uF 去耦电容VSS地GND散热焊盘也建议接 GNDWP#输入接 3.3V 上拉低电平禁止写状态寄存器HOLD#输入接 3.3V 上拉低电平暂停通信不能悬空片选我强烈建议用软件 GPIO 控制而不是使用 SPI 外设的硬件 NSS。原因有两个一是软件片选能随便调整时序方便调试二是如果以后你要在同一 SPI 总线上挂第二个设备软件片选可以非常自然地进行分时切换。硬件 NSS 在半双工和切换时间上有时候会给你添乱。2.2 WP、HOLD 两个控制引脚的接法WP# 和 HOLD# 是很多人忽略的地方。WP# 拉低会让状态寄存器进入写保护状态如果此时你想通过 WRSR 命令修改块保护位芯片会直接忽略。所以常规用法是把 WP# 上拉到 3.3V保证状态寄存器可写。HOLD# 的问题更隐蔽。这个脚一旦拉低SPI 通信会暂停时钟信号等于被挂起。如果这个引脚悬空附近有稍微强一点的干扰信号就可能误触发 HOLD导致整个 SPI 传输卡住或者数据错乱。我在实验室里遇到过类似现象症状就是“偶尔读出来一个字节不对”查到最后发现 HOLD# 没接固定电平。这两个脚的处理方式很简单各自用一个 10K 电阻上拉到 3.3V一劳永逸。不要直接短接 3.3V 也行但要有明确的电平状态绝不能悬空。2.3 电源、去耦和 DFN 封装注意事项DFN 封装本身很小引脚间距小手工焊接对温度和时间要求比较高。如果焊接时间过长或者助焊剂残留没清理干净容易出现虚焊。我在实际调试时遇到过“写进去读出来是好的过几天又不对”的情况最后发现是 MISO 引脚虚焊温度一变化就接触不良。电源部分VDD 引脚旁边放一个 0.1uF 陶瓷电容最好再并联一个 1uF 电容。MRAM 在 SPI 时钟翻转时会有瞬态电流电源纹波过大可能引起误写。STM32F401RB 的 3.3V 电源轨如果同时给其他数字电路供电建议给 MRAM 单独加一个磁珠或者小电阻隔离避免高频噪声串进来。SPI 走线尽量短特别是 SCK 和 MOSI如果 PCB 空间允许控制在 5cm 以内。长线带来的电容和反射会让信号边沿变差数据传输在高频下容易出错。工业设备里如果非要飞线连接建议把 SPI 速率降低后面我会专门说这个事。3. 软件驱动从 SPI 初始化到读写函数3.1 把 SPI1 配置成模式 0 主模式MR25H40CDF 支持标准 SPI 模式常用的有模式 0CPOL0CPHA0。我一般固定用模式 0和大多数 SPI 器件保持兼容。初始化 GPIO 和 SPI 外设时注意 PA4 要配置成普通输出PA5、PA6、PA7 配置成 SPI1 的复用功能。static SPI_HandleTypeDef hspi1; static void MX_GPIO_Init(void) { GPIO_InitTypeDef gpio {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_SPI1_CLK_ENABLE(); /* PA5: SCK, PA7: MOSI */ gpio.Pin GPIO_PIN_5 | GPIO_PIN_7; gpio.Mode GPIO_MODE_AF_PP; gpio.Pull GPIO_PULLUP; gpio.Speed GPIO_SPEED_FREQ_HIGH; gpio.Alternate GPIO_AF5_SPI1; HAL_GPIO_Init(GPIOA, gpio); /* PA6: MISO */ gpio.Pin GPIO_PIN_6; gpio.Mode GPIO_MODE_AF_PP; gpio.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, gpio); /* PA4: CS */ gpio.Pin GPIO_PIN_4; gpio.Mode GPIO_MODE_OUTPUT_PP; gpio.Pull GPIO_PULLUP; gpio.Speed GPIO_SPEED_FREQ_HIGH; HAL_GPIO_Init(GPIOA, gpio); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } static void MX_SPI1_Init(void) { hspi1.Instance SPI1; hspi1.Init.Mode SPI_MODE_MASTER; hspi1.Init.Direction SPI_DIRECTION_2LINES; hspi1.Init.DataSize SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity SPI_POLARITY_LOW; hspi1.Init.CLKPhase SPI_PHASE_1EDGE; hspi1.Init.NSS SPI_NSS_SOFT; hspi1.Init.BaudRatePrescaler SPI_BAUDRATEPRESCALER_16; hspi1.Init.FirstBit SPI_FIRSTBIT_MSB; hspi1.Init.TIMode SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial 7; HAL_SPI_Init(hspi1); }这里 SPI1 挂载在 APB2 上F401RB 的 APB2 最高 84MHz预分频 16 之后就是 5.25MHz。这个速率对 MRAM 来说非常安全线长一点也没关系。如果 PCB 走线很短、环境干扰也小可以尝试预分频 8跑 10.5MHz但没必要为了快那么一点牺牲稳定性。3.2 MRAM 命令集和基础读写驱动MR25H40C 的命令集和普通 SPI NOR Flash 很像主要有下面几个命令操作码说明WREN0x06写使能WRDI0x04写禁用RDSR0x05读状态寄存器WRSR0x01写状态寄存器READ0x03读数据WRITE0x02写数据标准 SPI MRAM 的 24 位地址可以直接覆盖整个 512KB 空间。读写的时候先拉低 CS发送命令字和地址再传输数据最后拉高 CS。下面是一个简单的读写驱动HAL 库方式实现static void MRAM_CS_Low(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET); } static void MRAM_CS_High(void) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET); } static void MRAM_WREN(void) { uint8_t cmd 0x06; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 1, 10); MRAM_CS_High(); } uint8_t MRAM_ReadStatusReg(void) { uint8_t cmd 0x05; uint8_t val 0; MRAM_CS_Low(); HAL_SPI_TransmitReceive(hspi1, cmd, val, 1, 10); MRAM_CS_High(); return val; } void MRAM_ReadBuf(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t hdr[4]; hdr[0] 0x03; hdr[1] (addr 16) 0xFF; hdr[2] (addr 8) 0xFF; hdr[3] addr 0xFF; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, hdr, 4, 100); HAL_SPI_Receive(hspi1, buf, len, 1000); MRAM_CS_High(); } void MRAM_WriteBuf(uint32_t addr, const uint8_t *buf, uint32_t len) { uint8_t hdr[4]; hdr[0] 0x02; hdr[1] (addr 16) 0xFF; hdr[2] (addr 8) 0xFF; hdr[3] addr 0xFF; MRAM_WREN(); MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, hdr, 4, 100); HAL_SPI_Transmit(hspi1, (uint8_t *)buf, len, 1000); MRAM_CS_High(); }注意写操作之前必须先发 WREN 命令。MRAM 内部有一个写使能锁存器只有 WREN 之后的下一次写操作才有效。这个机制和 Flash 类似是为了防止电气干扰造成误写。实际项目里如果数据量比较大比如一次写几百字节日志我建议用 DMA。HAL_SPI_Transmit 是阻塞式会占用 CPU。DMA 发送时CS 拉低的动作要在 DMA 开始前完成DMA 传输完成中断里再把 CS 拉高中间不能有别的线程干扰。3.3 状态寄存器里的块保护位是个隐形开关MRAM 的状态寄存器里有一个很容易忽略的位域就是块保护 BP0、BP1以及 WPEN。如果这些位被置位MRAM 会把一部分地址空间变成只读区域这时候你写数据会失败但读数据完全正常。这不是危言耸听。我在调试一块新板子时初始化的代码没问题逻辑也没问题但写进去的数据一断电就变回原来的值读出来的永远是上电时的初始状态。折腾了半天发现状态寄存器里 BP 位居然有值把块保护打开了。解决办法很简单上电初始化时主动把状态寄存器清掉。先发 WREN再发 WRSR数据写 0x00把 BP 位全部清除void MRAM_ClearBlockProtect(void) { uint8_t cmd[2]; MRAM_WREN(); cmd[0] 0x01; cmd[1] 0x00; MRAM_CS_Low(); HAL_SPI_Transmit(hspi1, cmd, 2, 10); MRAM_CS_High(); }执行完这一步之后检查一下 MRAM_ReadStatusReg() 的返回值确认 BP 位已经清掉。正常情况返回 0x00但不同批次芯片可能有细微差异以数据手册位定义为准。我还要多说一句如果你在产品量产阶段希望保护某些关键参数区不被误写那可以反过来利用块保护。把 BP 位设成保护一段区域平时软件怎么跑都不会覆盖到这段地址需要更新时再临时解除。这是一种可选的加固手段但绝大多数场景还是建议全部放开。4. 数据存储格式与量化数据存储4.1 设计一个带 CRC 的存储头有了读写驱动之后接下来最关键的是数据怎么组织。如果只是随便在固定地址写几个字节后面维护起来会很痛苦。我习惯在 MRAM 里做一个统一的“存储区管理”层每个区域开头放一个固定结构的头。#pragma pack(push, 1) typedef struct { uint32_t magic; /* 魔数用于识别初始化状态 */ uint16_t version; /* 结构版本号 */ uint16_t item_count; /* 数据块数量 */ uint32_t data_len; /* 实际数据长度 */ uint32_t crc32; /* 数据区 CRC32 校验 */ uint32_t last_write_time; /* 最后写入时间戳 */ } StorageHeader_t; #pragma pack(pop)魔数用来判断这块区域是否已经初始化过。如果读出不是约定魔数说明芯片可能是全新状态或者数据被破坏这时就执行初始化流程。版本号用于以后升级数据格式不至于因为结构体多了一个字段导致老设备读不了。CRC32 是必须的。工业环境里哪怕 MRAM 自身可靠性极高SPI 传输过程中仍然可能受到干扰。每次写入数据后计算一次 CRC每次读取时校验一遍一旦不对就报错或者启用备份。成本很低但能避免很多现场疑难杂症。在 512KB 空间里我会把地址规划做一个固定分区比如起始地址大小用途0x000004KB设备参数区和校准数据0x010008KB用户配置、配方数据0x0300032KB运行日志环形区0x0B000240KB采样数据区0x3B000剩余扩展区这个分区不是死的但一旦生产发布就不要随意改。分区的好处是不同数据类型互不干扰某个区域写坏了不至于拖累其他区域。4.2 量化数据怎么组织和写入这里结合最近常被提到的“量化数据存储”概念展开说。所谓量化数据在工业采集场景里就是把连续的模拟量变成离散的数字样本。比如 PLC 采集温度、压力、电流ADC 每毫秒采一个点这些样本值就需要稳定落盘。MRAM 天然适合做这种数据缓冲。以前用 Flash 存采样数据最大的限制是页对齐和擦除。我见过很多用 SPI Flash 做数据记录的项目代码里不得不维护一个“当前页指针”到页边界就换页写满一个扇区就要擦除掉电还得考虑当前页是否有效。用 MRAM 之后这些逻辑全部不需要了。实际项目里我通常把 ADC 采样值先组织成一个块批量写入#pragma pack(push, 1) typedef struct { uint32_t block_index; /* 块序号用于乱序检测 */ uint32_t timestamp; /* 采样起始时间 */ uint16_t sample_count; /* 本次块内样本数量 */ uint16_t sample_rate; /* 采样率单位 Hz */ int16_t raw_samples[128]; /* 原始采样值或量化后的数值 */ uint16_t crc16; /* 当前块校验 */ } SampleBlock_t; #pragma pack(pop)写的时候MCU 的 DMA 把一整块 SampleBlock_t 从 SRAM 搬到 MRAM。由于 MRAM 不需要等待擦除整个写入时间就是 SPI 传输时间。比如 128 个样本加上头部一共约 270 字节在 5.25MHz 下传输不到 1 毫秒。这个速度在工业采集里非常可观。如果还要做数据压缩可以在软件层先把原始样本做差分、编码再塞进 raw_samples 字段。MRAM 本身不管数据内容它只保证你写进去的字节是什么读出来还是什么。存储格式完全由应用层决定。4.3 掉电保护的关键实践MRAM 写入不需要额外等待但 CS 拉高的瞬间最后一位数据才真正被锁存到存储单元。所以掉电保护的核心原则是保证一次完整的 SPI 写操作在电源跌落到危险值之前结束。STM32F401RB 内置了可编程电压检测器 PVD可以监控 VDD。当电压跌到阈值以下会触发中断。我通常把这个中断设为最高优先级在中断里执行紧急数据保存。由于 MRAM 写入速度快在电压尚未彻底掉下来之前把当前的关键状态写进 MRAM 是可行的。实际做法是准备两块区域交替写入。第一次写 A 区第二次写 B 区再次写 A 区永远保持至少一份完整数据。读写流程如下写入前先更新存储头的 version 和 last_write_time。写入数据区。最后写入 CRC 和 magic。如果某一半写入过程中掉电下次上电读到 magic 或 CRC 不匹配就自动回退到另一半。这个“双缓冲”是老生常谈但真的管用。MRAM 让这种策略变得很轻量因为不需要擦除写一份完整数据只需要几毫秒甚至几百微秒。Flash 上做同样的策略光是擦除一个扇区可能就要几十毫秒掉电窗口太大。还有一个细节不要在掉电中断里做复杂计算更不要在中断里调用阻塞式延时。掉电中断里只做最必要的事把采集到的数据锁存到 SRAM然后触发 MRAM 写入如果能用 DMA 就用 DMA。平时正常工作时数据提前整理好放在 SRAM 里掉电只把指针和头部信息更新一下速度会快得多。5. 调试排查现场遇到的那些坑5.1 读出来全是 0xFF 或 0x00这是最常见的现象。第一次把 MRAM 焊上跑一个“读全片”的测试函数结果一片空白心里咯噔一下。排查思路从硬件到软件一步一步来。先用示波器或者逻辑分析仪看 CS、SCK、MOSI 三个信号。CS 拉低后发 READ 命令和地址观察 MOSI 上有没有对应的波形。如果 MOSI 没有波形检查 GPIO 复用功能配置PA7 是不是被别的初始化代码抢走了。如果 MOSI 正常但 MISO 一直无输出重点检查 MISO 线路和焊接。还有一种可能是 SPI 模式不对。MRAM 支持模式 0 和模式 3但如果你初始化成了模式 1相位不对读回来的数据就会错位。把 CLKPolarity 和 CLKPhase 改成模式 0 的配置一般就能解决。我调试时有个习惯先读状态寄存器如果状态寄存器能读出来说明 SPI 通路基本通了。状态寄存器值不对再回头看时序和接线。5.2 写入后数据莫名丢失写入后读出来是对的但一断电或者一段时间后再读就变回去了。这个症状十有八九和写保护有关。第一检查点MRAM_WriteBuf 之前有没有发 WREN。SPI MRAM 的命令流程里WREN 是必须的。如果漏发芯片会认为写操作非法数据根本进不去。第二检查点状态寄存器的 BP 位。前面说的块保护位可能把目标地址设成只读。你去写只读区域芯片不会报错只是数据落不进去。上电初始化时主动清一次块保护能规避大部分问题。第三检查点WP# 引脚有没有被拉低。如果 WP# 为低状态寄存器也被保护即使你发 WRSR 想清块保护也没用。这个引脚必须上拉。最后检查电源。VDD 纹波太大或者电压在写入瞬间出现跌落会导致内部逻辑判断异常。可以在 CS 上升沿附近用示波器细看电源波形。5.3 误把 MRAM 当 Flash 按页操作如果你之前一直在写 SPI NOR Flash换到 MRAM 时最容易犯的错就是保持“页操作思维”。NOR Flash 写数据有页边界限制超过 256 字节会自动回绕所以代码里到处都是“如果跨越页边界就分两次写”的逻辑。MRAM 没有页缓冲也没有页边界。你在一次 CS 拉低期间可以连续写任意长度地址不会回绕也不会被内部机制打断。我见过有人把 MRAM 驱动里强行加了一个 256 字节截断逻辑结果写超过 256 字节的数据时反而出问题。改成直接连续写之后就正常了。另外有些人会把 NOR Flash 的擦除习惯带到 MRAM写入前先发一个“扇区擦除”命令。MR25H40C 的数据手册里根本没有擦除命令你发的 0x20 之类的操作码会被芯片当成未知命令行为不可预期。正确做法就是直接覆盖写MRAM 的新值会替换掉旧值不需要中间擦除步骤。5.4 高频 SPI 和长线的稳定性SPI 速率不是越高越好。MRAM 理论上支持很高的 SPI 时钟但实际系统里还有 PCB 走线、连接器、干扰源等因素。我遇到过一次现场设备周期性数据错一个字节查到最后是设备内部一个电机驱动器在启动瞬间产生强干扰而 MRAM 的 SCK 线正好从附近走过。这种问题比较难复现因为不是每次都错。建议先从降速开始排查把预分频从 16 改成 32也就是 84MHz/32 2.625MHz。如果降速后问题消失基本就是信号完整性问题。要么优化走线、加滤波要么让 SPI 保持一个保守的速度。工业设备宁愿慢一点也要稳。长线场景下比如调试工装用杜邦线连接 MRAM 和 MCU我建议直接把速率降到 1MHz 以下。杜邦线本身有电感信号边沿会变得很糟糕高速必出问题。还有一个容易忽略的点CS 线的毛刺。如果 CS 在空闲时间被干扰拉低了一小段芯片可能误以为进来了一个命令。解决办法是 CS 引脚设置内部上拉同时 PCB 上对地加一个小电容把高频毛刺滤掉。6. 最后再啰嗦一句存储设计这个项目做下来我最深的体会是存储方案的选择一定要在软件架构定型之前做。很多人先把代码写完了最后才发现 Flash 的擦写模型撑不住业务逻辑然后回过头来堆补丁那种痛苦我经历过太多次。MRAM 不是万能的它有价格问题也有容量上限。但在工业数据采集、设备参数保存、掉电日志这些场景里它的特性简直是为嵌入式应用量身定做的。MR25H40CDF 加上 STM32F401RB一套组合下来硬件简单、软件清晰、掉电可靠我从调试到量产几乎没有再为存储的事头疼过。如果你正在做类似项目选型阶段不妨直接画一张生命周期表把设备每天写多少次、每次写多少字节、要保存几年算清楚再对照着看 Flash、EEPROM、MRAM 的极限。算完之后你会发现MRAM 显得贵但算完维护成本和故障风险它往往是最划算的那个。最后分享一个小技巧新板子回来后先别急着跑业务逻辑写一个全片读写自检程序对每个地址区域写入递增 pattern读回来比对连续跑几百轮。这一步能帮你快速确认焊接、电源、SPI 通路是不是真的没问题。后面再跑应用心里就有底了。
返回列表