做工业设备最怕的不是算力不够,而是现场断电那一瞬间,正在写的数据到底落没落盘。这几年我在几个需要频繁改写参数、又要严格保证掉电不丢数据的项目里,最后都选了 Everspin 的 MR25H40CDF 这颗 4Mbit 串行 SPI MRAM,配合 STM32L496AG 来搭存储链路。MR25H40CDF 是磁阻随机存储器,不需要先擦除、写入寿命几乎无上限;STM32L496AG 属于 STM32L4 系列,Cortex-M4F 内核,外设丰富,低功耗模式也多,两者在产品里配合起来很顺手。下面我把这套方案的选型逻辑、电路设计、SPI 读写实现、工业可靠性处理和实测踩坑完整梳理一遍,希望帮你少走几条弯路。
1. 方案选型:为什么工业频繁改写场景我选了 MRAM + STM32L496AG
1.1 工业现场存储需求的真实痛点
工业嵌入式设备里的数据存储,跟消费电子有很大区别。消费级产品里放张 TF 卡或者 SPI NAND,掉电丢几个文件一般无感。但工业现场,比如伺服驱动器、电力监测终端、光伏逆变器、充电桩计费模块,这些设备几乎都有一个共同要求:配置参数必须频繁修改,运行数据必须随时记录,而且外部电源可能随时被拉断。
这里面最典型的“高致死率”场景,我遇到过两类:
第一类是多参数频繁改写。设备调试时工程师会反复修改 PID 参数、通信地址、校准系数,一天可能写几百次。如果存储介质是 SPI NOR Flash,那么每改一次参数,都要经历“擦除-写入”循环,NOR Flash 的擦写寿命通常标称 10 万次,表面上够用,但体量小的 4Mbit/8Mbit 器件在高温环境下寿命会打折,我见过现场半年就出现整片校验失败的案例。
第二类是高速连续记录。设备需要把电流、电压、温度等采样数据按分钟或秒级落盘,这种场景写频次轻松过百万。NE5532 时代大家用 EEPROM 存参数还行,但存储高频过程数据就必须换思路了。EEPROM 写入一个字节要等几毫秒到十几毫秒,SPI 上连续记录根本吃不消;NOR 又扛不住寿命;FRAM 在小容量并口场景不错,但大一点的串口产品选择很有限。
MR25H40CDF 这个 4Mbit 串行 MRAM 基本上就是冲着这些痛点来的。它是磁阻式随机存储,写入是真正意义上的“直接覆盖写”,不需要擦除。写入寿命标称可以做到无限级别,至少是远超 Flash 和 EEPROM 几个数量级。所以我在设备频繁改参数、掉电不能丢数据、还要快速连续的场景里,最终确定以它作为主存储介质。
1.2 为什么选 MRAM 而不是 NOR Flash、EEPROM 或 FRAM
我做过一张很直观的对比表,在项目立项阶段给同事评审用,这里也放出来,你会对选型差异更清晰:
| 指标项 | MR25H40CDF(MRAM) | SPI NOR Flash | SPI EEPROM | FRAM |
|---|---|---|---|---|
| 写入前是否需要擦除 | 不需要,直接覆盖写 | 需要按扇区擦除 | 不需要,按字节写 | 不需要 |
| 典型写入寿命 | 近乎无限,远超 Flash | 10 万次级 | 100 万次级 | 100 万亿次级 |
| 单字节写入速度 | SPI 时钟决定,极快 | 需要擦除+编程,慢 | 较慢,需要 tWR | SPI 时钟决定,快 |
| 掉电数据保持 | 20 年以上典型值 | 通常 20 年 | 通常 20 年 | 10 年以上 |
| 抗辐射/抗干扰 | 较好,适合工业电磁环境 | 一般 | 一般 | 一般 |
| 容量覆盖 | 小容量到几十 Mbit 均有 | 大容量便宜 | 小容量为主 | 小容量为主 |
其实这套逻辑在企业里有句话说得通俗:Flash 适合“偶尔写一次、长期读”,MRAM 适合“经常写、随时断、必须准”。我为什么没全上 FRAM?不是不好,而是在串行接口、容量覆盖、供货稳定性和工业温度范围这些维度上,MRAM 的可选项更多,尤其是像 MR25H40CDF 这种 SPI 接口的贴片封装,几乎可以当做一个“非易失 SRAM”来用,软件成本也低。
1.3 为什么主控选了 STM32L496AG
STM32L496AG 并不是我拍脑袋选的。L4 系列主频 80MHz,Cortex-M4F 带 FPU,处理工业控制算法完全够,同时低功耗特性又很出色。具体到我这个存储应用,它有两个特别实用的优势:第一,它有多个 SPI 接口,我一个 SPI 挂在 MRAM 上,另一个 SPI 还可以接传感器、LCD、加密芯片,外设互不占用;第二,STM32 L4 系列自带硬件 CRC 计算单元,做数据完整性校验的时候不用靠软件去循环移位,省下的 CPU 时间很可观。
对于工业现场,STM32L496AG 的工作电压和 MR25H40CDF 正好都是 3.3V 逻辑,可以直接接,不需要电平转换。而且 L4 的 GPIO 可以配置为开漏输出带上拉,跟 MRAM 的 SPI 信号对接很轻松。库函数层面,官方 HAL 和 LL 库都支持得很成熟,工程实现周期短。所以这个主控加存储的组合,在成本和开发效率上都很平衡。
2. 硬件连接与电路设计:把 8 脚 MRAM 接进 STM32L496AG
2.1 认识 MR25H40CDF 的引脚与 SPI 接口
MR25H40CDF 是 8 脚封装的小芯片,常见的是 DFN 封装,体积很小,适合空间受限的工业板卡。它的接口实际上就是标准的四线 SPI:SCK 时钟、SI 数据输入、SO 数据输出、CS 片选,另外还有两个控制引脚 HOLD 和 WP,这在传统 SPI NOR Flash 里也有,但很多工程师第一次用 MRAM 时会忽略它们,埋下大坑。
HOLD 引脚叫保持脚,当它被拉低时,芯片会暂停与主机之间的数据通信,但片选和内部地址状态保持不变。这个功能用在多主机竞争总线的场景中,但工业单片机上基本用不到。如果该引脚悬空,芯片可能受到周围噪声干扰而进入保持状态,造成 SPI 通信假死,表现就是数据偶发错乱或者卡住。所以我强烈建议在硬件设计上把这个引脚直接通过电阻上拉到 VCC。
WP 引脚是写保护脚,它配合状态寄存器里的块保护位来使用。在标准写入流程中,如果 WP 一直为低,可能会禁止写入操作,导致你在软件里怎么发 WREN 都没反应。常规做法是把它也上拉到 VCC,保持写功能可用后再靠软件去控制状态寄存器做保护策略。
HOLD 和 WP 这两个引脚在标准 SPI 主从连接中不参与数据交互,但两颗电阻加起来不到 2 分钱成本,能省掉后续大量的排查时间,这笔账一定要算清楚。
2.2 和 STM32L496AG 的实际接线参考
我以 STM32L496AG 的 SPI1 为例,给出我项目里实际使用的连接方式。SCK、MOSI、MISO、CS 分别接主控的 SPI1_SCK、SPI1_MOSI、SPI1_MISO 和任意一个普通 GPIO。下面这个表格是一份可以直接抄的接线方案:
| MR25H40CDF 引脚 | 信号方向 | STM32L496AG 引脚 | 备注 |
|---|---|---|---|
| CS | 输入,主机控制 | PB6(任意 GPIO,推挽输出) | 片选信号,低电平有效 |
| SCK | 输入,SPI 时钟 | PA5 或 PB3(SPI1_SCK) | 与 SPI 外设时钟对齐 |
| SI | 输入,主机输出 | PA7 或 PB5(SPI1_MOSI) | 数据输入 |
| SO | 输出,从机输出 | PA6 或 PB4(SPI1_MISO) | 数据输出,配置为输入模式 |
| VCC | 电源 | 3.3V | 并联去耦电容 |
| GND | 地 | GND | 单点就近连接 |
| HOLD | 控制引脚 | 3.3V 上拉 | 10k 电阻上拉 |
| WP | 控制引脚 | 3.3V 上拉 | 10k 电阻上拉 |
实际画 PCB 时还要注意,SPI 信号线尽量短,不要绕远走线;如果板上有电机驱动、继电器这类干扰源,信号线上可以串联 33Ω 到 47Ω 的小电阻,抑制过冲。MRAM 的供电一定要干净,我习惯在 VCC 引脚旁边放一个 0.1uF 陶瓷电容,再放一个 1uF 到 4.7uF 的钽电容或陶瓷电容,这样能显著提升上电瞬间和突发写入时的电源稳定性。
2.3 电源、去耦和上电时序的注意事项
说到上电时序,这是工业产品最关键也最容易被忽略的一环。MR25H40CDF 是存储芯片,上电瞬间如果 CS 意外出现低电平毛刺,再加上 SPI 总线上有杂波,芯片是有可能接收到非法指令的。我之前用逻辑分析仪抓上电瞬间的信号,就看到过在 VCC 爬升过程中 CS 被拉到了低电平,持续了十几微秒。这会造成什么后果?如果写入使能位恰好被置位,又收到部分时钟,可能改写某些地址内容。
解决办法有三个层次:第一,在 CS 引脚上加一个 10k 到 100k 的上拉电阻,保证主控复位完成前 CS 保持高电平;第二,主控侧 GPIO 在初始化之前先确保默认输出高,或者把片选引脚配置为带上拉的输入,等 SPI 外设初始化完成后再切换为推挽输出并拉高;第三,在系统软件里,主控启动后不要立即访问 MRAM,先等电源稳定,最好延时几十毫秒,再执行首次读状态寄存器操作。
在电源管理上,如果系统有外部看门狗或者电压监测芯片,可以把 MRAM 的写保护引脚 WP 接到一个由电压监测控制的 GPIO 上,当检测到电源掉电时立即把 WP 拉低,从硬件上禁止一切写入动作,这种设计在掉电保存场景中会比较稳妥。不过要注意,WP 引脚在 MRAM 里是否真正禁止写入还取决于状态寄存器的块保护位的配置,所以软件策略上仍需双重保障。
3. 固件实现:SPI 配置、读写驱动与状态寄存器控制
3.1 STM32CubeMX 里的 SPI 配置要点
用 STM32CubeMX 配置 STM32L496AG 的 SPI1 时,有几个参数不要凭默认值直接生成。MR25H40CDF 同时支持 SPI Mode 0(CPOL=0, CPHA=0)和 Mode 3(CPOL=1, CPHA=1),我建议固定使用 Mode 0,因为绝大多数主机在 Mode 0 下时序边缘更清晰,逻辑分析仪看起来也更直观。
CubeMX 里的关键配置项我建议这样设置:
- Mode:Full-Duplex Master;
- Hardware NSS 设为 Disable,使用 Software NSS;
- Prescaler:如果主频 80MHz,先选 8 分频,SPI 时钟就是 10MHz,对 MRAM 来说是非常宽松的速度;
- Data Size 选 8 Bits;
- First Bit 选 MSB First;
- CPOL=Low,CPHA=1Edge(即 Mode 0)。
为什么不让 CubeMX 自动使用硬件 NSS?因为硬件 NSS 在从机模式下很智能,但在主机模式下你希望完全掌控片选拉低和拉高的时机,特别是写 MRAM 时需要先发一条 WREN 使能命令,再拉高 CS,然后再拉低 CS 发写指令。这个过程中间 CS 必须有一个高电平脉冲。如果由硬件 NSS 自动控制,时间点很难掐准。所以项目里我永远只用软件 NSS,把 CS 当成一个普通 GPIO 来操作。
3.2 核心读写驱动代码实现
先看底层操作。我用 STM32 HAL 库,CS 引脚操作直接用 GPIO,SPI 收发用 HAL 库的阻塞接口。为了给频繁写入场景留余量,后面还可以换 DMA,但阻塞方式逻辑最简单,适合作为基础版本。
#define MRAM_CS_LOW() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_RESET) #define MRAM_CS_HIGH() HAL_GPIO_WritePin(MRAM_CS_GPIO_Port, MRAM_CS_Pin, GPIO_PIN_SET) static void MRAM_SendByte(uint8_t data) { HAL_SPI_Transmit(&hspi1, &data, 1, 100); } static uint8_t MRAM_ReceiveByte(void) { uint8_t data = 0; HAL_SPI_Receive(&hspi1, &data, 1, 100); return data; }写一个字节的完整流程是:拉低 CS,发送指令和数据字节,最后拉高 CS。这里有一个容易犯的错误:如果 CS 拉高太早,芯片可能还没完成当前字节的采样;如果拉高太晚,则可能多收一个无效时钟。SPI 是同步协议,每个字节完成后 CS 必须有一个确定的释放动作,所以我在函数里严格保持“指令阶段”和“数据阶段”的 CS 时序。
下面是写使能和读状态寄存器的函数:
void MRAM_WriteEnable(void) { MRAM_CS_LOW(); MRAM_SendByte(0x06); // WREN MRAM_CS_HIGH(); } uint8_t MRAM_ReadStatus(void) { uint8_t status = 0; MRAM_CS_LOW(); MRAM_SendByte(0x05); // RDSR status = MRAM_ReceiveByte(); MRAM_CS_HIGH(); return status; }写完数据之后,可以通过状态寄存器的 WEL 位确认芯片已经正确接收写使能指令。实际项目里,我不会每次写完数据都去读状态寄存器,那样太耗时间;但在上电自检和写入异常重试流程中,这个函数是必备的。
再来看单次写入和读取缓存的核心函数。MR25H40CDF 是 4Mbit,换算成字节是 512KB,寻址需要 19 位地址,SPI 指令里仍然统一用 3 字节地址来发送,高字节在前,多余的高位补零。
void MRAM_WriteBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_WriteEnable(); MRAM_CS_LOW(); MRAM_SendByte(0x02); // WRITE MRAM_SendByte((addr >> 16) & 0xFF); MRAM_SendByte((addr >> 8) & 0xFF); MRAM_SendByte(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { MRAM_SendByte(buf[i]); } MRAM_CS_HIGH(); } void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint32_t len) { MRAM_CS_LOW(); MRAM_SendByte(0x03); // READ MRAM_SendByte((addr >> 16) & 0xFF); MRAM_SendByte((addr >> 8) & 0xFF); MRAM_SendByte(addr & 0xFF); for (uint32_t i = 0; i < len; i++) { buf[i] = MRAM_ReceiveByte(); } MRAM_CS_HIGH(); }这里有个和 NOR Flash 完全不一样的体验:MRAM 在连续写模式下,只要 CS 一直保持低电平,地址会在内部自动递增,直接把整块数据连续写进去就行,不用考虑“页缓冲写满要停”的问题。我在调试时经常让设备连续写 512KB 的满容量数据做压力测试,速度完全由 SPI 时钟决定,这套代码在 10MHz 时钟下写 64 字节缓存只需要几十微秒,工业现场轮询记录绰绰有余。
3.3 状态寄存器与写保护控制
MR25H40CDF 的状态寄存器功能跟 SPI NOR Flash 类似,但使用上要格外注意。写入状态寄存器之前同样要先发 WREN 命令,写完状态寄存器之后,芯片会自动把 WEL 位清零,这个行为确保你不会在误操作情况下连续修改保护位。
我一般在项目里这样设计写保护策略:出厂时把状态寄存器配置为“全局可写”模式,也就是关闭块保护;批量生产完成后,如果希望防止现场被异常改写关键参数,可以把状态寄存器里的保护位配置为只读区域。具体位配置要对应芯片手册,这里我给个常用伪代码思路:
void MRAM_WriteStatus(uint8_t status) { MRAM_WriteEnable(); MRAM_CS_LOW(); MRAM_SendByte(0x01); // WRSR MRAM_SendByte(status); MRAM_CS_HIGH(); }在调试过程中,我最常犯的错是:写完保护状态后忘记重新发 WREN 就试图写数据。如果状态寄存器里保护位已经打开,后续 WRITE 都会失败。建议新项目阶段先保持状态寄存器为 0x00,等所有读写逻辑验证通过后再加保护策略。
3.4 连续写入与掉电保存策略
MRAM 虽然不存在擦写次数上限,但工业程序里仍不能把“无线寿命”当成“无限可随意丢数据”的借口。我建议把数据在 MRAM 中的布局分成若干逻辑块,比如参数区、历史记录区、运行日志区,每个区域设置一个版本号和 CRC 校验。这样即使遇到极端掉电,也只影响当前正在写的某个逻辑块,其他区域的数据仍然完好。
掉电保护场景下还有一个细节:MRAM 写一个字节是一个完整的原子操作,如果电源在写入过程中断掉,可能出现的情况是当前字节写了一半,内部数据处于不确定状态,但它不会像 NOR Flash 那样连带破坏整个扇区。这是我选择 MRAM 的另一个重要原因。配合写序列号,程序上电后可以快速判断最新有效记录,跳过损坏记录,这种机制在数据采集终端里极其管用。
4. 可靠性设计:掉电保存、CRC 校验与抗干扰策略
4.1 掉电毛刺与 CS 误动作的防护
前面提到过上电毛刺,这里详细讲掉电。工业现场最恶劣的情况是:设备正常运行,外部电源被直接断开,期间主控可能还没来得及通知存储系统“我要掉电了”,于是 SPI 总线上发生任何不可预知的信号都算正常。MRAM 的 CS 一旦被噪声拉低,芯片就会进入接收状态,此时时钟线再抖动几个周期,它就可能把一根无用数据写到某个地址。
我曾在一台 24V 供电的电机控制器上遇到过这样的问题:每次大功率电机停转瞬间,静态存储区的数据总会有偶尔的跳变。后来用示波器一抓,发现是 24V 到 3.3V 的 DC-DC 在负载突变时输出有跌落,MRAM 的 CS 线和地平面之间出现了一个 2V 左右的负向毛刺,直接触发了片选信号。解决方法是把 CS 上拉电阻从 10k 改到 4.7k,同时在 CS 引脚上并联一个 1nF 到 10nF 的电容,增加抗噪容限,问题才消失。
所以别小看 CS 上拉和滤波,这是磁存储器件在现场最需要的一道防线。
4.2 数据校验:从单字节到 CRC32
MRAM 本身不会因为写入寿命失效,但 SPI 通信链路可能受干扰,板上电源也可能导致瞬时错误。为了数据可靠,我采用的是“帧头 + 序列号 + 长度 + 数据 + CRC”的结构。STM32L496AG 自带硬件 CRC 外设,用起来相当方便。不过要注意,硬件 CRC 默认的多项式和算法跟很多上位机工具不一致,如果你既要在 MCU 里生成 CRC 又要配合上位机校验,建议统一约定初始值和输入输出格式,或者干脆用软件实现 CRC32,代码也就几十行。
存储帧结构示例:
| 偏移 | 内容 | 长度 | 说明 |
|---|---|---|---|
| 0 | Frame Header | 2 字节 | 固定 0xAA55,用于快速定位起始 |
| 2 | 序列号 | 4 字节 | 每次写入递增,用于判断新旧 |
| 6 | 数据长度 | 2 字节 | 变长记录使用,固定记录可省 |
| 8 | 业务数据 | N 字节 | 实际负荷 |
| 8+N | CRC32 | 4 字节 | 对前面整帧计算 |
读取时可以先用帧头扫描有效区域,再校验序列号和 CRC。如果发现 CRC 错误但序列号更高,说明这是一条在掉电瞬间产生的半截记录,直接丢弃回退到上一条有效记录即可。这套策略我在充电桩计费模块上用了一年多,没有出现重复计费或者掉电丢记录的问题。
4.3 抗电磁干扰:走线、滤波和地平面
MRAM 本身对电磁干扰不敏感,但 SPI 是高速数字接口,干扰会从线上进来。PCB 设计时,SCK 和 MOSI 尽量短,并且不要和继电器控制线、PWM 驱动线并行走线。如果板子空间紧凑,至少要用一层完整地平面给 MRAM 区域做屏蔽。MISO 线上可以预留一个 33Ω 电阻位,实际测试中如果发现读回数据偶尔为 0xFF,可以考虑用这个电阻限制边缘速率。
另外,工业现场如果存在强静电环境,I/O 口对地要加 TVS 管,SCK 和 MOSI 优先选择主控芯片自带的 GPIO 保护;MRAM 的 HOLD 和 WP 引脚上拉之后再加一个小电容,能进一步过滤噪声导致的保持状态误触发。
4.4 低功耗模式下的 MRAM 行为
STM32L496AG 的强项是低功耗,很多工业设备要求待机电流做到微安级别。在这种模式下,SPI 外设时钟会被关闭,GPIO 状态需要程序显式维持。如果你的 MRAM 的 CS 引脚在进入 STOP 模式之前没有被拉高,芯片会一直处于待机通信状态,虽然 MRAM 静态电流本身不大,但总线上浮空可能会造成额外的漏电。
更稳妥的做法是:在进入 STOP 模式前,先把 CS 拉高,并确保 SCK、MOSI 为确定电平,避免浮空输入导致 CMOS 门电路反复翻转。如果追求极限功耗,可以使用 MRAM 的睡眠指令,但进入睡眠后必须通过正常的唤醒时序恢复,否则后续读写都会没有响应。这部分功能建议在硬件自检程序里做完整验证,不要在产品量产后再去补。
5. 现场排查:5 个常见坑与调试心得
5.1 SPI 通信无响应,读回全是 0xFF
这是我和同事刚接触 MRAM 时最常遇到的问题。第一反应是查焊接,DFN 封装引脚间距小,手工焊接很容易连锡或者虚焊,特别是 SO 引脚如果虚焊,主机读到的就是一直为高电平 0xFF。第二个怀疑对象是 SPI Mode 不对,如果 CubeMX 里配置成 Mode 1 或者 Mode 2,MRAM 识别不了指令,也会表现为全 0xFF。第三个,检查 HOLD 引脚是否被拉低,一旦 HOLD 拉低,芯片相当于被冻结,不响应任何命令。
排查顺序建议:先用万用表量 HOLD 和 WP 的电压,确认它们都是高电平;再用逻辑分析仪抓 CS、SCK、MOSI,确认主机确实发出了 0x03 READ 指令;最后检查 SO 线上有没有波形。这样基本十分钟能定位问题方向。
5.2 写入不生效,数据读出来还是旧值
写入不生效大多数情况是没有正确执行写使能。MRAM 入坑第一课就是:必须严格在每次写操作前发 WREN,写完会清除 WEL 位。如果你用 NOR Flash 的驱动直接套 MRAM,而 NOR 驱动里有时会偷懒只在初始化时发一次 WREN,那么第二次写就会失败。这是移植代码时最隐蔽的坑。
如果软件确认 WREN 已经发送,仍然写不进去,请检查 WP 引脚的硬件连接。WP 拉低时,配合状态寄存器块保护位,可以完全禁止写入。我在测试板上曾经因为飞线接触不良,导致 WP 悬空,数据写入一会儿好一会儿坏,最后重新焊接后才稳定。
5.3 数据错位,读取内容整块偏移
地址发送顺序是另一个高频问题。SPI 指令格式里地址是 3 字节,必须是高字节在前,比如访问地址 0x000100,你要依次发送 0x00、0x01、0x00。如果习惯性按低字节在前发,那读回的数据就全乱套。此外,连续读模式下,读完一个缓冲区后 CS 必须拉高结束本次命令,否则地址会继续递增,下一个缓冲区读取就会从错误位置开始。
我建议在驱动函数里加一个入参检查断言,确保用户传入的地址不超过芯片容量,避免地址溢出后访问到不存在的区域。MR25H40CDF 的 512KB 空间,地址超过 0x7FFFF 后高位无效,硬件上会绕回,但是这个错误逻辑会让排查变得非常困难。
5.4 偶发卡死,程序长时间无响应
偶发卡死优先怀疑 HOLD 引脚被干扰拉低。我在一个逆变器项目上遇到过运行几天后读数据卡死,最后把 HOLD 引脚的 10k 上拉改成了 1k,并且并联了 100nF 电容,问题再也没有出现。另外,如果代码中 CS 拉低后发生中断,而中断服务函数恰好也操作了同一个 SPI,那么 SPI 状态会被打乱。这种低概率问题在高实时性系统里要特别注意,建议在访问 MRAM 的代码段关闭可重入中断,或者为 SPI 操作加一个互斥锁。
5.5 调试利器:逻辑分析仪和内存测试模式
最后分享一个趁手的调试方法:新板子第一次调 MRAM 时,不要直接跑整个业务固件,先写一个最小自检程序。上电后读状态寄存器,随后写满一个 64 字节缓冲区,内容依次为 0x00、0x11、0x22 到 0x3F,再读回比对。如果这一条链路通过,硬件基本没问题。
如果还是找不到问题,就把逻辑分析仪的三个通道分别接在 CS、SCK、MOSI 上,触发条件设为 CS 下降沿,抓一段波形后跟芯片手册的时序图一对就知道问题出在哪个阶段。工业项目调试时间非常宝贵,设备越早通过自检,后面留给整机联调的时间就越充裕。
我在实际项目中最大的体会是,MR25H40CDF 看似是一颗不起眼的小芯片,但它把“频繁写、快速存、掉电不丢”这三个原本在 Flash 体系中需要各种复杂策略才能解决的问题,直接降维成了普通 SPI 读写,真真切切减少了嵌入式存储这块的开发成本。如果你正在为一个需要频繁改写、还要保证掉电可靠性的产品选存储方案,建议也拿这颗料和 STM32L496AG 搭一套最小系统,先跑压力测试,再决定是否大规模用起来。