☰
MRAM+STM32F765ZI工业存储方案:从SPI驱动到掉电保护实践
2026/10/4 1:25:12 网站建设 项目流程

前阵子在给一套工业数据记录终端做存储方案,第一版用的是 SPI NOR Flash,结果连续写日志时经常遇到擦除延迟,掉电瞬间还会丢最后一帧数据。后来把存储介质换成了 Everspin 的 MR25H40CDF,主控用 STM32F765ZI,整个“存储和读取数据”的链路一下子干净了很多。这颗 MRAM 是 4Mbit 的 SPI 非易失存储器,写入寿命极高,不需要先擦除再写,写进去立刻掉电也不会丢;配合 Cortex-M7 级别的 STM32F765ZI,跑数据采集、参数保存、故障日志这类嵌入式应用非常顺手。这篇就把我在这个项目里的硬件接线、驱动实现、应用层分区和踩坑记录完整写一遍。

内容适合正在做工业控制器、仪器仪表、边缘计算节点或者运动控制系统的嵌入式工程师参考。如果你只是想把数据存进 Flash,那 NOR Flash 更划算;但如果你需要频繁写入、掉电不丢、现场维护要省心,MRAM 这套思路值得看完。

1. 为什么选择 MR25H40CDF + STM32F765ZI 做嵌入式存储

1.1 现场需求:频繁写、掉电不丢、不能有擦除等待

这个项目的实际场景是给一条产线上的压力、温度和振动传感器做数据记录终端。设备每 100ms 采集一组数据,每组约 64 字节,除了实时上传,还要在本地保留最近 48 小时的原始数据,用于故障回溯。这类需求有几个特点:

  • 写入频率高。一天下来要写几十万条记录,如果用普通的 SPI NOR Flash,每页写满后要执行块擦除,块擦除时间是几十到几百毫秒,系统很容易在上层出现“写不进去”的假卡顿。
  • 掉电时机不可控。工业现场经常直接拉闸,Flash 在写入过程中掉电,轻则丢一页数据,重则出现 ECC 都救不回来的坏块。
  • 数据要长期可靠。现场设备可能几年不拆下来维护,存储介质不能因为写次数耗尽而整体报废。
  • 读取速度也不能太差,故障分析时要把历史曲线快速拉出来。

MR25H40CDF 正好把这些痛点全部避开。它是 MRAM,全称是磁阻式随机存取存储器,核心存储单元是磁性隧道结,写入靠磁化方向翻转实现,不需要电荷泵、不需要擦除块。读和写都是字节级的,写入后数据立即稳定,不存在 Flash 那样的“编程后再校验”机制。加上 STM32F765ZI 的高主频和丰富外设,这套组合做工业数据记录终端很合理。

1.2 MRAM 与 NOR Flash、FRAM、EEPROM 的差异对比

很多工程师一听到非易失存储,第一反应就是 NOR Flash 或者 EEPROM。但在频繁写入的场景里,它们和 MRAM 的差距非常明显,我直接列一张对比表:

参数MR25H40CDF(MRAM)常见 SPI NOR Flash常见 SPI EEPROM(如 M95xxx)
写入前是否需要擦除不需要需要,按扇区/块擦除不需要
最小写入单位1 字节通常 1 字节,但页编程前必须先擦除1 字节
写寿命大于 1e14 次典型 1e5 ~ 1e6 次典型 1e6 次
写入等待核心写入约 35ns,命令开销为主页编程几 ms,块擦除几十到几百 ms写一字节几 ms
掉电数据可靠性写入瞬时稳定可能在掉电时出现半编程页相对可靠但容量小
容量选择常见到几十 Mbit可达 Gbit 级通常几百 Kbit~Mbit
单位成本高低中

这张表让我决定:日志和关键参数这类“小容量、高频写、不能丢”的数据放在 MRAM 里;大文件、固件升级包这类“低频写、大容量”的数据放到另一颗大容量 NOR Flash 里。MRAM 不是万能的,它容量不大、价格偏贵,但把它用在最需要可靠性的一小块关键数据上,性价比反而很高。

FRAM 也要提一下。FRAM 的写寿命同样非常高,容量也合适,但它的 SPI 接口器件在市场中相对少一些,而且不同厂家的 FRAM 在高温、抗辐射等指标上有差异。MRAM 在数据保持、耐温和位翻转方面表现更稳定,尤其是设备工作温度可能到 85℃ 以上的场景,我最后选了 MRAM。

1.3 整体数据流:采集、缓存、落盘、上报

整个系统的数据流可以概括成四层:

  1. 传感器采集层:ADC 或外部总线读取温度、压力、振动波形。
  2. STM32F765ZI 处理层:滤波、编码、加时间戳,生成一条条定长记录。
  3. MRAM 存储层:通过 SPI 将记录写入预设的数据区,同时维护写指针和统计信息。
  4. 上报与导出层:通过以太网或 USB 读取 MRAM 内容,生成 CSV 或二进制波形文件。

这种分层让“存储和读取数据”不再只是几个 SPI 读写函数,而是作为整个嵌入式项目的独立模块。驱动层只负责字节读写,应用层负责分区分配、写指针管理和掉电恢复,两层解耦后,后面换主控或者换存储芯片,改动面都很小。

2. 硬件接线与最小系统:别在 PCB 上省这几件事

2.1 MR25H40CDF 引脚和外围电路怎么接

MR25H40CDF 是标准的 SPI 接口芯片,引脚不多,但每个引脚都有讲究。它主要引脚如下:

  • VCC、VSS:电源和地。供电范围 2.7V ~ 3.6V,和 STM32F765ZI 的 3.3V I/O 完全兼容。
  • CS#:片选,低电平有效。直接用 STM32 的一个普通 GPIO 控制,不要和别的外设共享片选。
  • SCK:SPI 时钟输入。
  • SI(MOSI):主发从收数据线。
  • SO(MISO):从发主收数据线。
  • WP#:写保护引脚,低电平有效。这个引脚悬空会导致写入指令被忽略,必须接上拉电阻到 VCC。
  • HOLD#:暂停引脚,低电平有效。当它拉低时,SPI 时钟被暂停且数据线保持状态,正常使用也一定要上拉到 VCC。

我刚开始画板子时把 WP# 和 HOLD# 都漏接了,结果芯片能读不能写,查了半天才发现是引脚电平问题。正确的接法是:

  • WP# 接 10kΩ 上拉到 VCC。
  • HOLD# 接 10kΩ 上拉到 VCC。
  • CS# 接 STM32 GPIO,平时默认高电平。
  • VCC 旁放一颗 0.1μF 陶瓷电容,尽量贴近芯片电源引脚;如果电源线路较长,再加一颗 4.7μF 电容。

注意 MR25H40CDF 虽然有睡眠模式指令,但正常运行时不需要额外控制引脚,所以硬件上保持 WP# 和 HOLD# 都主动拉高是最省心的做法。

2.2 STM32F765ZI 的 SPI 引脚分配与冲突排查

STM32F765ZI 的 SPI 资源非常多,有 6 个 SPI 控制器。我在项目里选了 SPI1,因为它挂在 APB2 总线上,外设时钟更高,能跑更高的 SPI 速率。具体引脚分配如下:

信号引脚复用功能说明
SCKPA5AF5SPI1 时钟
MISOPA6AF5SPI1 主入从出
MOSIPA7AF5SPI1 主出从入
CS#PA4GPIO 输出软件控制片选

选择 PA5/PA6/PA7 主要是因为它们不占用调试下载接口,不像 PB3/PB4 在某些 Boot 配置下会被 JTAG 功能占用。PA4 作为片选也不影响其他外设。如果你的板子上 SPI1 引脚被 LCD 或者 ADC 占用了,也可以换成 SPI2 或 SPI3,只要按数据手册里 AF 复用表重新确认引脚即可。

实际焊接时还要检查 PA4、PA5、PA6、PA7 这几个引脚有没有被板卡的跳线或者板载器件占用。比如某些开发板为了兼容 Arduino,把 PA6 引出后可能带有下拉电阻,这会破坏 SPI 信号电平。定制硬件时最好让 SCK、MOSI 线上不要并接大电容,不然 SPI 频率一高就容易边沿失真。

2.3 电源、去耦和强磁场防护

MRAM 对电源质量不算苛刻,但也不是完全无所谓。芯片内部写操作是电流驱动磁性翻转,瞬间电流变化比普通 EEPROM 更陡,所以电源线上的去耦电容绝对不能省。

我实际的电源设计是:

  • VCC 用 3.3V 电源轨,主控和 MRAM 共用,但电源走线宽度不低于 0.5mm。
  • MRAM 电源脚旁边并接 0.1μF 陶瓷电容 + 4.7μF 钽电容。
  • 在 PCB 布局上,MRAM 尽量靠近 STM32,SCK、MOSI、MISO 这三根线等长,走线长度控制在 30mm 以内,减少反射。

还有一点很多人会忽略:MRAM 内部存储单元是磁性材料,虽然芯片封装已经做了磁场屏蔽设计,但不要让芯片贴近大功率电感、变压器或者强磁铁。我们在后期测试时发现,把 MRAM 放在一个额定电流 20A 的功率电感上方 5mm 以内,长时间读取会出现偶发数据翻转。后来在布板时把存储芯片和功率部分隔开 2cm 以上,再把电源地的环路面积缩小,问题就消失了。工业设备里电机、变频器到处都是,这一点值得提前注意。

3. SPI 驱动实现:从 CubeMX 配置到读写函数落地

3.1 SPI 参数怎么配:模式、时钟、NSS

MR25H40CDF 支持 SPI Mode 0 和 Mode 3,也就是 CPOL=0/CPHA=0 和 CPOL=1/CPHA=1。两种模式在数据手册里都允许,我习惯用 Mode 0,因为和大部分 SPI 传感器保持一致。

在 STM32CubeMX 里,把 SPI1 配置成:

  • Mode:Full-Duplex Master
  • Data Size:8 Bit
  • First Bit:MSB First
  • Clock Polarity:Low
  • Clock Phase:1 Edge
  • NSS:Software
  • BaudRate Prescaler:SPI1 使用 APB2 时钟,我这边 APB2 是 108MHz,预分频选 4,得到 27MHz 的 SPI 时钟。

MR25H40CDF 的 SPI 时钟上限与具体后缀有关,常见规格支持到 40MHz 左右,27MHz 留了不少余量。如果你用的是低功耗模式或者板卡走线比较长,分频 8 拿到 13.5MHz 也更稳妥。SPI 速率不是越高越好,稳定性优先。

NSS 一定要选 Software。如果选 Hardware NSS,STM32 会自动控制片选,在某些封装里 CS 时序会和 MRAM 的指令序列不匹配,尤其是连续收到 WREN 后再发 Write 指令时,经常出现片选提前释放的问题。

3.2 指令集封装:WREN、RDSR、READ、WRITE

MR25H40CDF 的指令集和 SPI NOR Flash 比较接近,核心就这几个指令:

指令操作码说明
WREN0x06写使能
WRDI0x04写禁止
RDSR0x05读状态寄存器
WRSR0x01写状态寄存器
READ0x03读数据
WRITE0x02写数据

我封装驱动时没有用 HAL 的高层扩展,而是直接操作片选 GPIO 加 HAL_SPI_Transmit/Receive,代码清晰也容易查问题。

先看写使能函数:

#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) #define MRAM_CMD_WREN 0x06 #define MRAM_CMD_WRDI 0x04 #define MRAM_CMD_RDSR 0x05 #define MRAM_CMD_WRSR 0x01 #define MRAM_CMD_READ 0x03 #define MRAM_CMD_WRITE 0x02 static void mram_write_enable(void) { uint8_t cmd = MRAM_CMD_WREN; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); MRAM_CS_HIGH(); }

注意 WREN 是一个独立命令。发送完 0x06 之后,CS# 必须拉高至少一个 SPI 时钟周期,让芯片记录这个写使能状态,然后再发起后续 Write 指令。很多人第一次移植时只发 WREN 不拉高 CS,结果后面写入全部失败。

读状态寄存器:

static uint8_t mram_read_status(void) { uint8_t cmd = MRAM_CMD_RDSR; uint8_t status = 0; MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi1, &cmd, 1, HAL_MAX_DELAY); HAL_SPI_Receive(&hspi1, &status, 1, HAL_MAX_DELAY); MRAM_CS_HIGH(); return status; }

状态寄存器 bit0 是 WIP,表示是否正在写入;bit1 是 WEL,表示写使能锁存状态。正常情况下 MRAM 写入很快,但还是建议在批量写之后检查一次 WIP,兼容极端情况。

批量写函数:

void mram_write_buf(uint32_t addr, const uint8_t *buf, uint32_t len) { uint8_t hdr[4]; if ((addr + len) > MRAM_CAPACITY_BYTES) { return; } mram_write_enable(); hdr[0] = MRAM_CMD_WRITE; hdr[1] = (uint8_t)(addr >> 16); hdr[2] = (uint8_t)(addr >> 8); hdr[3] = (uint8_t)(addr); MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi1, hdr, 4, HAL_MAX_DELAY); HAL_SPI_Transmit(&hspi1, (uint8_t *)buf, len, HAL_MAX_DELAY); MRAM_CS_HIGH(); while (mram_read_status() & 0x01) { /* 等待 WIP 清零 */ } }

最大的优势在这里体现出来:写数据之前不需要发 Sector Erase,也不用担心跨页问题。MRAM 没有页边界概念,你从任意地址连续写多少字节都可以,芯片自己处理。这在应用层省了非常多事。

批量读函数:

void mram_read_buf(uint32_t addr, uint8_t *buf, uint32_t len) { uint8_t hdr[4]; if ((addr + len) > MRAM_CAPACITY_BYTES) { return; } hdr[0] = MRAM_CMD_READ; hdr[1] = (uint8_t)(addr >> 16); hdr[2] = (uint8_t)(addr >> 8); hdr[3] = (uint8_t)(addr); MRAM_CS_LOW(); HAL_SPI_Transmit(&hspi1, hdr, 4, HAL_MAX_DELAY); HAL_SPI_Receive(&hspi1, buf, len, HAL_MAX_DELAY); MRAM_CS_HIGH(); }

读操作不需要写使能,CS# 拉低后先发指令码和 24 位地址,再连续读数据即可。接收数据时 HAL_SPI_Receive 内部会自动发送 0xFF 作为时钟,所以 MOSI 上不需要额外灌数据。

3.3 时序里的关键点:WREN 之后必须做一次 CS 高电平

我用示波器抓时序时发现,这套芯片最容易出问题的地方不是通信速率,而是片选和 WREN 的配合。

正确的写时序是:

  1. CS# 从高拉到低。
  2. 发送 0x06(WREN)。
  3. CS# 从低拉到高。
  4. CS# 再次从高拉到低。
  5. 发送 0x02(WRITE)+ 3 字节地址 + 数据。
  6. CS# 从低拉到高。

如果你在步骤 3 之后没有把 CS# 拉高,紧接着就发 Write,芯片内部可能认为整个序列是一条非法指令,写入不生效。在软件里一定要保证每个命令都有独立的 CS# 高电平间隔,哪怕间隔只是几微秒,那也是必要的。

还有一点:CS# 低电平期间,SCK 的下降沿和上升沿必须干净,不要有毛刺。MRAM 对边沿比较敏感,如果 SCK 信号经过长排线传输,可以在靠近芯片端加一个 22Ω 到 33Ω 的串联电阻,能有效抑制振铃。

4. 应用层设计:分区规划、掉电保存与数据校验

4.1 512KB 空间怎么分区才够用

MR25H40CDF 是 4Mbit,也就是 512KB。这个容量不大,要提前规划好。我这边分成三个区域:

区域地址范围说明
产品信息区0x00000 ~ 0x0FFFF(64KB)设备序列号、硬件版本、校准参数
关键状态区0x10000 ~ 0x1FFFF(64KB)当前工作模式、写指针、告警标志
日志数据区0x20000 ~ 0x7FFFF(384KB)循环写入的传感器日志

产品信息区只有在出厂校准或者现场升级时才写入,平时只读。关键状态区每次开关机或者掉电保存时写入,数据量很小。日志数据区是核心,每条记录固定 128 字节,包含时间戳、通道号、数据值和 CRC,按顺序循环写入。

日志区用循环队列的思路管理。维护一个 32 位写指针,指针本身存放在关键状态区。每次写入时先更新指针,再写数据,避免中途掉电后找不到有效记录。由于 MRAM 不需要擦除,循环覆盖非常容易:写满后直接把指针拨回区域起点,新数据直接覆盖旧数据,不需要像 Flash 那样专门管理“哪些块需要先擦除”。

4.2 掉电瞬间抢救关键数据的实现思路

工业环境最常出现的致命操作就是突然断电。如果掉电瞬间正好在写日志,丢几条日志可接受,但关键状态信息不能乱。

我在 STM32F765ZI 上用了内部的 PVD 掉电检测,把阈值设置到 3.0V 左右,当 VDD 降到阈值以下时,触发 PVD 中断。中断里只做两件事:

  • 把关键状态区里的“当前写入地址”和“上一条记录索引”更新到 MRAM 的固定位置。
  • 设置一个标志位,下次上电时通过这个标志判断是否发生异常掉电。

PVD 中断处理要精简,不能在中断里做复杂的浮点运算或者整页拷贝,只写几十个字节到 MRAM,这个操作很快,SPI 时钟 27MHz 时不足 0.2ms,系统靠电源电容维持这段时间完全够用。

还要注意:掉电时如果主控有外部看门狗,它可能也在复位。所以掉电保存代码不要在中断里等待太长时间,写完后直接进入低功耗循环,避免因为看门狗复位导致写了一半又重启。

4.3 CRC 校验与日志自愈策略

MRAM 虽然可靠,但工业现场的外部干扰谁也说不准。我每条日志都带 16 位 CRC,结构体如下:

typedef struct { uint32_t timestamp; uint16_t ch1_raw; uint16_t ch2_raw; uint16_t ch3_raw; uint16_t crc16; } log_entry_t __attribute__((packed));

每次读取历史日志时,逐条校验 CRC。如果某一条 CRC 错误,先标记这条记录为“损坏”,然后继续向下读,而不是直接停止。读取结束后,把损坏记录所在的扇区地址记下来,在下次写入新日志时优先覆盖掉这些位置。

这种“带损坏标记的循环日志”策略比整区格式化实用得多。因为 MRAM 写寿命极高,即使某天真的出现了一两条坏数据,也不会扩散到整个日志区,现场维护只需要把损坏记录挑出来,整个存储系统依然可用。

我还做了一版双备份:产品信息区同时写两个副本,一个在主地址,一个在偏移 +4KB 的位置。读取时比较两个副本的 CRC,选正确的那份用。这样即使出现极端情况下的整页写坏,设备也能恢复正常参数,不需要返厂。

5. 调试实录与避坑速查

5.1 常见故障速查表

这里把我在项目中遇到的和帮别人排查过的问题整理成一张表,基本都是可以照着查的:

现象可能原因排查方向
写使能后写入无效WREN 和 WRITE 之间 CS 没有拉高用逻辑分析仪看 CS 电平时序
读出来全是 0xFFWP# 或者 HOLD# 悬空检查两个引脚是否有上拉
通信错位,第一字节丢失SPI 模式不对确认 CPOL/CPHA 是 Mode0 或 Mode3
连续写入后偶尔丢数据SPI 时钟过快或者供电不稳降低 SPI 分频,检查去耦电容
上电后首读正常,随后紊乱CS 被硬件 NSS 自动控制把 NSS 配置改成 Software
靠近功率器件时数据翻转强磁场干扰增加芯片与磁性元件距离,改善地环路
写入后立即读回全零地址超出了容量范围检查地址拼接,超过 512KB 被截断

第一行的问题我印象最深。用 STM32CubeMX 生成的代码,每个 SPI 命令都是独立的 HAL_SPI_Transmit,看起来没问题,但实际 WREN 函数返回后 CS 是拉高的,紧接着 Write 函数又把 CS 拉低,中间确实有一个高电平脉冲。问题出在有些工程师在实现时把 WREN 和 WRITE 放在同一次 CS 低电平里连续发送,芯片完全不认。记住:命令独立成帧,CS 必须分隔。

5.2 我踩过的三个隐蔽坑

除了表里的问题,还有三个坑比较隐蔽,单独拿出来说。

第一个坑是地址位宽。MR25H40CDF 虽然是 4Mbit,但地址是 24 位,只用低 19 位就足够覆盖全部 512KB。然而有些数据手册里的地址格式仍然占用 3 字节,高位是无关位。我一开始想把高字节清零省掉,直接发两个字节地址,结果读到 64KB 之后全是 0,因为芯片内部寄存器不接受缩短地址。正确做法是永远把 3 字节地址发全,高字节可以填 0。

第二个坑是 HAL_SPI_Transmit 接收超时参数。HAL_MAX_DELAY 在裸机环境下没问题,但如果你的系统带了 FreeRTOS,HAL_SPI_Transmit 内部会释放互斥锁并等待信号量,如果不小心在中断里调用,会直接卡死。我把驱动里的超时统一改成 100ms,并且在写日志任务里绝对不在中断上下文调用 SPI 函数。

第三个坑是 HOLD# 引脚在 PCB 上被我不小心连到了 MCU 的复位引脚。看起来像芯片偶发失效,实际上是因为系统复位瞬间 HOLD# 被拉低,MRAM 进入了暂停状态,之后 SPI 通信全部无效。这种问题只能靠查网表发现,所以画板的时候一定要把 WP#、HOLD# 这种“平时不该动”的引脚单独拉出来检查。

5.3 可靠性与性能测试怎么做

做完整套驱动后,我跑了几组测试:

  • 写入速度:SPI 时钟 27MHz,单条 128 字节日志,包含命令开销,实测约 1.8MB/s 的有效写入速度。对 100ms 一条记录来说,占用不到 1% 时间。
  • 连续写入:用脚本向日志区写了超过 100 万条记录,验证循环覆盖后指针正确、带 CRC 的记录 100% 可读。
  • 掉电测试:用继电器控制板卡上电和断电,每次掉电前随机时刻写入一条日志,测了 200 次,没有出现指针错乱或关键状态丢失。
  • 高温老化:把整块板子放进 85℃ 恒温箱,持续写入 72 小时,读取全部数据比对一致。

性能方面 MRAM 和 NOR Flash 差距最明显的是小数据随机写。Flash 在随机写 64 字节时,可能先要擦除 4KB 的块,时间一下到了几毫秒;MRAM 则稳定在几十微秒级别。这个差距在日志记录场景中直接影响任务调度,用了 MRAM 后软件里再也不需要单独做“写 Flash 队列”。

6. 这样还能怎么扩展:容量翻倍与双存储架构

6.1 多片 MR25H40 级联扩展容量

一片 MR25H40CDF 是 512KB,如果日志容量不够,最简单的办法是两片并联在同一个 SPI 总线上,用两个 CS 引脚区分片选。驱动层加一个“目标芯片号”参数,读写前选择对应 CS 即可。

硬件上注意两条:MISO 是开漏还是推挽,MR25H40 的正常 MISO 是三态推挽,但仍然建议在每个 MISO 线上加 4.7kΩ 上拉到 VCC,避免片选切换瞬间总线悬空。软件上把地址空间从 512KB 扩成 1MB,高地址的 CS 选择映射到第二颗芯片,上层逻辑基本不用改。

如果还想更快,可以找支持 Dual SPI 或 Quad SPI 的 MRAM 型号,用 STM32F765ZI 的 QSPI 接口做 memory-mapped 模式,把 MRAM 映射到 MCU 的地址空间里,直接像读 SRAM 一样读写。不过 QSPI 版本的指令时序和 MR25H40 不太一样,需要重新适配驱动,不是完全 pin-to-pin 兼容,选型时要看算力、成本和软件投入。

6.2 MRAM + 大容量 NOR Flash 的混合方案

这个项目到了后期,我做了比较务实的容量分配:MRAM 负责“必须可靠且频繁更新”的数据,NOR Flash 负责“大块但低频”的数据。

具体来说:

  • 系统参数、运行累计量、当前告警状态放在 MRAM。
  • 历史告警文件、固件升级包、大段波形文件放在 16MB 的 SPI NOR Flash 里。
  • 读取时 MRAM 先把文件索引和地址表加载到 SRAM,再通过 NOR Flash 拉取大文件。

这种混合方案兼顾了成本、速度和可靠性。MRAM 很贵,但只需要小容量;NOR Flash 便宜,但扛不住高频随机写。把两者组合起来,系统既保留了 MRAM 的快速掉电保存能力,又有足够空间装固件和日志导出文件。我强烈建议做工业设备时不要指望一颗 MRAM 取代所有存储介质,明确数据分类才是正路。

6.3 继续深入的几个方向

如果这个方案要继续演进,我觉得至少有几个方向值得试:

  1. 把 MRAM 的读写封装成一个统一的 KV 存储接口,上层不要直接操作原始地址,而是通过 key 来读写,方便以后迁移到不同的非易失存储芯片。
  2. 在 STM32F765ZI 上把 SPI 驱动改成 DMA 模式,进一步提高写入吞吐,给更多采集通道留余量。
  3. 增加状态检查任务,定期读取 MRAM 的状态寄存器,判断芯片健康度并上报。
  4. 对日志区做简单的磨损感知,虽然 MRAM 寿命极高,但通过记录每个区域的擦写次数可以更早发现异常,适合要求严苛的无人值守设备。

最后再分享一个小技巧:我在应用层给每条日志都加了 2 字节同步头 0xA5 0x5A。读取时先找同步头,再根据记录长度跳转。就算哪次记录被意外破坏,也不会导致整个日志区解析失败。做工业存储,最怕的不是数据坏一块,而是坏一块之后整个文件系统都打不开。把日志设计成“条记录独立可解析”,比事后恢复数据要省心得多。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询