1. 项目缘起与方案选型
1.1 为什么要在工业场景里折腾 MRAM 和 PIC24
做工业嵌入式这行十几年,最头疼的从来不是算法多复杂,而是数据存不住。你想想,一台装在配电柜里的数据采集终端,现场电网波动、夏季高温、冬季低温,偶尔还来个雷击浪涌,用 EEPROM 存参数吧,写入速度慢得让人抓狂,擦写寿命也就百万次级别,高频记录场景下两三年就写废了;用 SPI Flash 呢,写入前得先擦除整个扇区,掉电瞬间如果正好在擦除,数据直接丢一片,而且 Flash 的块擦除机制让频繁的小数据更新变得极其低效。
所以当项目要求“在工业和嵌入式应用中稳定存储和读取数据”时,我第一时间想到的就是MRAM——磁性随机存储器。这次选用的MR25H40CDF是 Everspin 家的 4Mbit SPI 接口 MRAM,它的特性用一句话概括就是:像 SRAM 一样快,像 Flash 一样非易失,像 EEPROM 一样耐写。没有擦除操作,字节级写入,写入次数近乎无限,掉电数据保持二十年以上。主控这边选了PIC24FV32KA301,Microchip 家的 16 位单片机,带 32KB Flash、2KB RAM,关键是它有独立的 SPI 模块,而且工作电压范围宽(2.0V~5.5V),在工业现场那种电源不太干净的环境里比较皮实。
这套组合的核心思路很明确:用 MRAM 替代传统 EEPROM 和 Flash 做数据存储介质,用 PIC24 的硬件 SPI 做高速数据搬运。适合谁看?如果你正在做工业数据记录仪、智能仪表、PLC 扩展模块、或者任何需要频繁写入且不能丢数据的嵌入式设备,这套方案可以直接抄作业。哪怕你用的是 STM32 或者别的 MCU,MRAM 的驱动逻辑和 SPI 操作思路也是通用的。
1.2 MR25H40CDF 到底强在哪:和 EEPROM、Flash 的硬核对比
先把这个器件的关键参数摆出来,后面所有设计都围绕这些数字展开。
| 特性 | MR25H40CDF (MRAM) | 典型 SPI Flash (W25Q64) | 典型 EEPROM (24C512) |
|---|---|---|---|
| 容量 | 4Mbit (512KB) | 64Mbit (8MB) | 512Kbit (64KB) |
| 接口 | SPI (最高 40MHz) | SPI (最高 104MHz) | I2C (最高 1MHz) |
| 写入方式 | 字节级,无需擦除 | 页编程,需扇区擦除 | 字节级 |
| 擦写寿命 | 10^14 次以上 | 10万次 | 100万次 |
| 写入周期时间 | 无延迟,直接写入 | 页编程 0.7ms,扇区擦除 45ms | 5ms |
| 数据保持 | 20年 | 20年 | 40年 |
| 工作温度 | -40~85°C | -40~85°C | -40~85°C |
| 掉电风险 | 写入即保存 | 擦除中掉电丢数据 | 写入中掉电丢数据 |
这张表里最要命的是写入周期时间那一行。Flash 写一个字节要先擦 4KB 扇区,耗时 45ms,这期间如果掉电,整个扇区的数据全没了。MRAM 写一个字节就是写一个字节,总线时钟跑完就存好了,没有内部擦除周期,没有等待轮询,CPU 发完数据就可以干别的事。我在一个电网监测项目里实测过,用 Flash 方案时每次记录电流电压要等 50ms 以上,换成 MR25H40CDF 后写入耗时降到微秒级,整个系统的响应速度提升了一个数量级。
还有一个容易被忽略的点:MR25H40CDF 的 SPI 接口支持最高 40MHz 时钟。PIC24FV32KA301 的 SPI 模块在 16MHz 系统时钟下可以跑到 8MHz,虽然跑不满 MRAM 的极限,但对于 512KB 容量的数据记录来说,连续读取速率能到 1MB/s 左右,完全够用。如果你用更快的 MCU,比如 STM32H7 系列,SPI 跑到 40MHz,那读取速度直接起飞。
1.3 硬件连接:SPI 总线的物理层设计要点
PIC24FV32KA301 和 MR25H40CDF 之间的连接不复杂,但工业环境下有几个细节必须注意。先看引脚映射:
| PIC24FV32KA301 引脚 | 功能 | MR25H40CDF 引脚 | 说明 |
|---|---|---|---|
| RP0 (Pin 4) | SDO1 | SI (Pin 5) | 主出从入 |
| RP1 (Pin 5) | SDI1 | SO (Pin 2) | 主入从出 |
| RP2 (Pin 6) | SCK1 | SCK (Pin 6) | 时钟 |
| RP3 (Pin 7) | SS1 | CS (Pin 1) | 片选,低有效 |
| VDD (Pin 13) | 3.3V | VDD (Pin 8) | 电源 |
| VSS (Pin 12) | GND | VSS (Pin 4) | 地 |
这里有个坑:MR25H40CDF 的 CS 片选信号必须用硬件片选,不能用软件 GPIO 模拟。为什么?因为 MRAM 在 CS 拉低后需要立即响应 SPI 时钟,如果 CS 由软件控制,中断延迟可能导致 CS 拉低和第一个时钟沿之间的时间不确定,MRAM 内部状态机可能误判指令。PIC24 的 SPI 模块支持硬件 SS 引脚自动控制,配置好 SSEN 位就行,省心又可靠。
PCB 布局上,SPI 的四根信号线尽量等长走线,SCK 时钟线远离模拟信号,CS 线加 10K 上拉电阻保证空闲时高电平。电源引脚旁边放 0.1uF 和 1uF 陶瓷电容各一个,MRAM 在写入瞬间电流会有小幅波动,去耦电容不能省。如果设备工作在强电磁干扰环境,SPI 线上可以串 22 欧姆电阻做阻抗匹配,实测能有效抑制振铃。
注意:MR25H40CDF 的 WP 引脚(写保护)和 HOLD 引脚在标准 SPI 模式下如果不使用,必须上拉到 VDD,不能悬空。悬空时引脚电平不确定,可能导致器件进入意外状态。
2. SPI 驱动层实现:从寄存器配置到字节收发
2.1 PIC24FV32KA301 的 SPI 模块初始化
PIC24 的 SPI 模块配置比 STM32 简单不少,没有那么多复用功能要开,但几个关键寄存器的位必须设对。我直接上代码,然后逐行解释为什么这么设。
// SPI1 初始化,主模式,8MHz 时钟,模式 0 void SPI1_Init(void) { // 1. 关闭 SPI1 再配置 SPI1STATbits.SPIEN = 0; // 2. 清空接收缓冲,防止残留数据干扰 volatile uint16_t dummy = SPI1BUF; (void)dummy; // 3. 配置控制寄存器 SPI1CON1bits.MSTEN = 1; // 主模式 SPI1CON1bits.CKP = 0; // 空闲时时钟低电平 SPI1CON1bits.CKE = 1; // 数据在时钟上升沿变化,下降沿采样 SPI1CON1bits.SMP = 0; // 中间采样 SPI1CON1bits.MODE16 = 0; // 8 位模式 SPI1CON1bits.SSEN = 1; // 硬件片选使能 SPI1CON1bits.SPRE = 0b110; // 次级预分频 2:1 SPI1CON1bits.PPRE = 0b10; // 初级预分频 4:1 // 4. 配置引脚方向 TRISBbits.TRISB0 = 0; // SDO1 输出 TRISBbits.TRISB1 = 1; // SDI1 输入 TRISBbits.TRISB2 = 0; // SCK1 输出 TRISBbits.TRISB3 = 0; // SS1 输出 // 5. 使能 SPI1 SPI1STATbits.SPIEN = 1; }时钟计算过程:PIC24FV32KA301 的系统时钟配置为 16MHz(内部 FRC 加 PLL),SPI 时钟源为 Fcy = 8MHz。初级预分频 PPRE=0b10 对应 4:1,次级预分频 SPRE=0b110 对应 2:1,最终 SPI 时钟 = 8MHz / 4 / 2 = 1MHz。等等,1MHz 是不是太慢了?MRAM 支持 40MHz 啊。
这里有个取舍:工业环境下 SPI 时钟不是越快越好。1MHz 时信号边沿平缓,过冲小,长线传输更稳。如果你板子走线短、电源干净,可以把 PPRE 设为 1:1,SPRE 设为 1:1,SPI 时钟直接到 8MHz。我一般先在 1MHz 下调通功能,再逐步提速测试稳定性,找到那个“既快又不丢数据”的平衡点。
模式 0(CPOL=0, CPHA=0)是 MRAM 的标准工作模式,CKP=0 和 CKE=1 的组合对应数据在上升沿变化、下降沿采样。这个别搞反,搞反了读出来的数据全是 0xFF 或者 0x00。
2.2 MRAM 指令集:读、写、状态寄存器操作
MR25H40CDF 的指令集很精简,常用的就四条:
| 指令名称 | 指令码 | 功能说明 |
|---|---|---|
| WREN | 0x06 | 写使能,写入前必须发 |
| WRDI | 0x04 | 写禁止,写入完成后发 |
| RDSR | 0x05 | 读状态寄存器 |
| READ | 0x03 | 读数据 |
| WRITE | 0x02 | 写数据 |
和 Flash 不同,MRAM没有扇区擦除指令,也不需要轮询忙状态。WREN 发完之后直接发 WRITE 指令加地址加数据就行,CS 拉高那一刻数据就存进去了。状态寄存器的 bit0 是 WEL(写使能锁存),发完 WREN 后读状态寄存器应该看到 WEL=1,发完 WRDI 或完成写入后 WEL=0。这个可以用来确认写使能是否生效。
地址是 24 位的,因为 512KB 容量需要 19 位地址线,但 MRAM 按字节寻址,所以用 3 个字节传输地址。高 5 位是无关位,填 0 就行。
2.3 底层字节收发函数:一个容易踩坑的地方
PIC24 的 SPI 收发是同一个缓冲寄存器,写进去启动发送,读出来是接收数据。但这里有个时序陷阱:写 SPI1BUF 之后不能立即读,必须等 SPIRBF 标志置位。
// SPI 单字节收发 uint8_t SPI1_ExchangeByte(uint8_t txData) { // 等待发送缓冲空 while (SPI1STATbits.SPITBF == 1); // 写入发送数据 SPI1BUF = txData; // 等待接收完成 while (SPI1STATbits.SPIRBF == 0); // 读取接收数据 return (uint8_t)SPI1BUF; }这段代码看着简单,但我在实际项目里见过有人写成“先读再等”,结果读出来的是上一次的残留数据。还有一个坑:SPI1BUF 是 16 位寄存器,在 8 位模式下读出来低 8 位有效,但如果你不小心读了高字节,可能触发接收溢出。所以强制转成 uint8_t 再返回。
连续读写时,每次 CS 拉低到拉高之间可以传输任意字节。MRAM 支持地址自动递增,发一个 READ 指令加起始地址后,可以连续读整个芯片的内容,地址到边界自动回卷。写入时也一样,连续写多个字节,地址自动加一,不需要每次重新发指令。
3. 数据存储与读取的完整实现
3.1 单字节写入与读取:最基础的原子操作
先实现最底层的单字节读写,这是所有上层功能的基础。
// 向 MRAM 指定地址写入一个字节 void MRAM_WriteByte(uint32_t addr, uint8_t data) { MRAM_CS_LOW(); // 片选拉低 SPI1_ExchangeByte(0x06); // WREN 写使能 MRAM_CS_HIGH(); // 拉高结束指令 MRAM_CS_LOW(); // 再次拉低准备写数据 SPI1_ExchangeByte(0x02); // WRITE 指令 SPI1_ExchangeByte((addr >> 16) & 0xFF); // 地址高字节 SPI1_ExchangeByte((addr >> 8) & 0xFF); // 地址中字节 SPI1_ExchangeByte(addr & 0xFF); // 地址低字节 SPI1_ExchangeByte(data); // 写入数据 MRAM_CS_HIGH(); // 拉高,数据存入 } // 从 MRAM 指定地址读取一个字节 uint8_t MRAM_ReadByte(uint32_t addr) { uint8_t data; MRAM_CS_LOW(); SPI1_ExchangeByte(0x03); // READ 指令 SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); data = SPI1_ExchangeByte(0x00); // 发送哑元,读取数据 MRAM_CS_HIGH(); return data; }注意 WREN 和 WRITE 之间必须拉高 CS 再拉低,这是 MRAM 的指令序列要求。WREN 是一个独立指令,执行后 WEL 位置 1,然后下一个 CS 下降沿开始的 WRITE 指令才会被接受。如果你在同一个 CS 低电平期间连续发 WREN 和 WRITE,MRAM 会把 WRITE 当成无效指令忽略掉。这个坑我踩过,当时调了半天以为硬件坏了,后来翻数据手册才发现这个细节。
3.2 页写入与连续读取:提升吞吐量的关键
单字节读写适合参数存储,但数据记录场景需要批量搬运。MRAM 没有页大小限制,理论上可以一次写入整个芯片,但实际使用中我建议按 256 字节分块,方便上层管理。
// 连续写入多个字节 void MRAM_WriteBuffer(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t i; MRAM_CS_LOW(); SPI1_ExchangeByte(0x06); // WREN MRAM_CS_HIGH(); MRAM_CS_LOW(); SPI1_ExchangeByte(0x02); // WRITE SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); for (i = 0; i < len; i++) { SPI1_ExchangeByte(buf[i]); } MRAM_CS_HIGH(); } // 连续读取多个字节 void MRAM_ReadBuffer(uint32_t addr, uint8_t *buf, uint16_t len) { uint16_t i; MRAM_CS_LOW(); SPI1_ExchangeByte(0x03); // READ SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); for (i = 0; i < len; i++) { buf[i] = SPI1_ExchangeByte(0x00); } MRAM_CS_HIGH(); }连续读取时,MRAM 内部地址指针自动递增,每读一个字节地址加一。如果读到芯片末尾,地址会回卷到 0x00000 继续读。这个特性可以用来做环形缓冲区,写指针和读指针都在软件里维护,硬件层面不需要特殊处理。
实测数据:在 SPI 时钟 8MHz 下,连续读取 512 字节耗时约 520 微秒,平均每个字节 1 微秒左右。写入 512 字节耗时约 540 微秒,因为多了 WREN 指令的开销。这个速度对于工业数据记录来说绰绰有余,1 秒钟能存上千条记录。
3.3 数据结构设计:如何组织 512KB 的存储空间
512KB 说大不大,说小不小。如果只是存几个配置参数,随便找个地址写就行。但工业设备往往需要存储历史数据、故障录波、事件日志,这就需要规划存储布局。
我的习惯是把 MRAM 分成几个区域:
| 地址范围 | 大小 | 用途 | 更新频率 |
|---|---|---|---|
| 0x00000 - 0x00FFF | 4KB | 系统配置参数 | 低 |
| 0x01000 - 0x01FFF | 4KB | 设备校准数据 | 低 |
| 0x02000 - 0x03FFF | 8KB | 事件日志(环形) | 中 |
| 0x04000 - 0x7FFFF | 496KB | 历史数据记录(环形) | 高 |
配置参数区存设备 ID、通信地址、量程设置这些,每次修改后写入,平时只读。事件日志区用环形缓冲,每条日志 32 字节,记录时间戳、事件类型、事件值,写满后从头覆盖。历史数据区存周期性采样值,每条记录 16 字节,包含时间戳和 3 个通道的采样值。
环形缓冲的写指针存在 MRAM 的固定位置,每次写入前先读指针,写完数据后更新指针。这里有个关键点:指针更新必须用原子操作。如果写完数据还没更新指针就掉电了,下次上电会覆盖掉有效数据。我的做法是先把数据写入目标地址,然后更新指针,指针更新是单字节写,MRAM 的写入是原子的,不会出现写一半的情况。
提示:环形缓冲的读指针不需要存在 MRAM 里,可以存在 RAM 中,上电后从写指针往前推固定条数来恢复。这样减少 MRAM 写入次数,虽然 MRAM 寿命近乎无限,但能省一点是一点。
4. 工业现场的数据可靠性设计
4.1 掉电保护:MRAM 的天然优势与软件配合
MRAM 最大的卖点就是掉电不丢数据,但前提是数据已经写进去了。如果掉电发生在 SPI 传输过程中,最后一个字节可能没写完。虽然 MRAM 的写入速度极快,但 SPI 时钟 1MHz 时传输一个字节需要 8 微秒,这 8 微秒内掉电,数据就丢了。
工业设备一般都有掉电检测电路,检测到电源电压下降到阈值时触发中断,MCU 在中断里完成紧急数据保存。用 Flash 方案时,这个中断处理时间要预留 50ms 以上,因为要等擦除完成。用 MRAM 方案,中断里只需要把关键数据通过 SPI 发出去,8 微秒一个字节,存 100 个字节也就 800 微秒,对掉电保持电容的要求低得多。
我通常会在掉电中断里做三件事:第一,停止所有数据采集任务;第二,把当前未保存的采样值写入 MRAM;第三,更新写指针。整个过程控制在 2ms 以内,用 100uF 电容就能撑住。
4.2 数据校验:CRC 还是校验和
工业环境电磁干扰强,SPI 总线上的数据可能被干扰导致位翻转。虽然 MRAM 本身很可靠,但传输过程不可靠。所以每条记录都要加校验。
我对比过 CRC16 和简单校验和。CRC16 检错能力强,能发现所有单比特、双比特错误和大多数突发错误,但计算需要查表或移位迭代,耗时约 20 微秒每字节。简单校验和就是一个字节累加取反,计算只要 1 微秒,但检错能力弱,两个字节同时出错可能检测不到。
对于历史数据记录,我推荐用 CRC16。每条记录 16 字节数据加 2 字节 CRC,存储开销 12.5%,可以接受。计算 CRC 的时间相对于 SPI 传输时间可以忽略。对于配置参数,用简单校验和就够了,因为配置参数不常写,写完会立即回读验证。
// CRC16-CCITT 计算 uint16_t CRC16_Calculate(uint8_t *data, uint16_t len) { uint16_t crc = 0xFFFF; uint16_t i, j; for (i = 0; i < len; i++) { crc ^= (uint16_t)data[i] << 8; for (j = 0; j < 8; j++) { if (crc & 0x8000) crc = (crc << 1) ^ 0x1021; else crc <<= 1; } } return crc; }4.3 写入均衡:MRAM 不需要但软件可以做
MRAM 的擦写寿命是 10^14 次以上,按每天写 100 万次算,能写 270 年。所以从寿命角度讲,完全不需要写入均衡。但工业项目里有个现实问题:如果每次都写同一个地址,虽然 MRAM 不会坏,但万一那个存储单元因为其他原因(比如制造缺陷)失效了,数据就全丢了。
我的做法是在配置参数区做简单的双备份:主参数存在 0x00000,备份参数存在 0x00800,每次写入时先写备份再写主,读取时先读主,CRC 校验失败再读备份。这样即使主存储区出问题,备份还能顶上。对于历史数据区,因为是环形缓冲,天然就有写入均衡的效果,不需要额外处理。
5. 常见问题与排查实录
5.1 读出来全是 0xFF 或 0x00
这是最典型的问题,原因通常有三个:SPI 模式不对、片选没拉低、或者 MRAM 没供电。排查步骤:
- 用示波器看 SCK 引脚,确认有波形输出。如果没有,检查 SPI 模块是否使能,引脚方向是否配置正确。
- 看 CS 引脚,发指令时应该拉低,指令结束拉高。如果一直高,检查 SSEN 位和 TRIS 方向。
- 看 SI 引脚,应该有数据输出。如果没有,检查 SDO 引脚映射是否正确。
- 用万用表量 MRAM 的 VDD 引脚,应该是 3.3V。如果电压偏低,检查电源走线和去耦电容。
我遇到过最隐蔽的一次是 CS 引脚被配置成了模拟功能,数字输出被禁用,示波器看 CS 一直是高阻态。后来查寄存器发现 ANSBbits.ANSB3 没清零,PIC24 的引脚默认是模拟输入,用之前必须设成数字模式。
5.2 写入后读出来数据不对
如果写入的数据和读出的数据不一致,先确认 WREN 指令是否生效。读状态寄存器的 WEL 位,发完 WREN 后应该是 1。如果 WEL=0,说明 WREN 没被接受,检查 CS 时序:WREN 指令期间 CS 必须拉低,指令发完拉高,然后再拉低发 WRITE。
另一个可能是地址算错了。MRAM 是 24 位地址,但 512KB 容量只需要 19 位,高 5 位填 0。如果你把地址左移了或者右移了,写进去的位置就不对。建议用宏定义地址边界,编译时检查越界。
5.3 SPI 高速通信时数据出错
把 SPI 时钟提到 8MHz 以上时,如果出现偶发性数据错误,通常是信号完整性问题。排查方法:
- 用示波器看 SCK 和 SI 的波形,有没有过冲、振铃、边沿变缓。
- 如果过冲严重,在信号线上串 22~100 欧姆电阻。
- 如果边沿变缓,检查走线是否太长、负载电容是否太大。
- 降低 SPI 时钟到出错率可接受的范围,工业产品稳定第一,速度第二。
我在一个项目里把 SPI 从 8MHz 降到 4MHz,误码率从每天几次降到零。客户问为什么不用高速,我说工业现场电磁环境复杂,4MHz 够用就行,稳定比快重要。
5.4 常见问题速查表
| 现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| 读全 0xFF | CS 未拉低 | 示波器看 CS 引脚 | 检查 SSEN 和 TRIS 配置 |
| 读全 0x00 | MISO 未连接 | 万用表测通断 | 检查 SDI 引脚焊接 |
| 写入无效 | WREN 未生效 | 读状态寄存器 WEL 位 | 确保 WREN 和 WRITE 之间 CS 拉高 |
| 数据偶发错误 | SPI 时钟太快 | 降低时钟测试 | 降速或加匹配电阻 |
| 地址错乱 | 地址计算错误 | 打印地址值 | 检查地址移位和掩码 |
| 掉电丢数据 | 写入未完成 | 掉电中断加延时 | 用示波器看掉电时序 |
6. 实操心得与扩展思路
6.1 从 1MHz 到 8MHz 的提速过程
我调 SPI 的习惯是“先通后快”。第一步,SPI 时钟设 1MHz,确保读写功能正常。第二步,写一个测试模式,往 MRAM 里写 0x55 和 0xAA 交替的数据,读出来比对。第三步,逐步提高时钟到 2MHz、4MHz、8MHz,每提一档跑 24 小时压力测试,记录误码率。第四步,找到误码率突增的临界点,然后降一档使用。
这个方法帮我省了很多调试时间。有一次提到 12MHz 时误码率突然从零跳到千分之一,降到 8MHz 就完全正常。后来分析是 PCB 走线太长导致信号反射,8MHz 时反射波在采样点之前已经稳定,12MHz 时反射波正好落在采样窗口里。
6.2 用 MRAM 做数据记录仪的完整思路
如果你要做一个工业数据记录仪,我的建议是:主控用 PIC24FV32KA301,存储用 MR25H40CDF,加一个 RTC 芯片打时间戳,加一个 RS485 收发器做通信。数据采集用定时器中断触发,每 100ms 采一次,数据先存 RAM 缓冲,攒够 16 条记录后一次性写入 MRAM。这样减少 SPI 操作次数,也降低掉电丢数据的风险。
记录格式可以这样设计:每条记录 16 字节,前 4 字节是 Unix 时间戳,中间 8 字节是 4 个通道的 16 位采样值,后 2 字节是 CRC16,剩下 2 字节保留。512KB 能存 32768 条记录,按 100ms 一条算,能存 54 分钟。如果采样间隔放宽到 1 秒,能存 9 小时。需要更长时间就换更大容量的 MRAM,MR25H40CDF 是 4Mbit,还有 16Mbit 的型号可选。
6.3 后续可以扩展的方向
这套方案跑通之后,可以往几个方向扩展。一是加文件系统,把 MRAM 模拟成 FAT 格式的磁盘,方便通过 USB 导出数据。二是加无线模块,用 SPI 接口的 LoRa 或 WiFi 模块把数据传到云端。三是做双 MRAM 冗余,两个 MRAM 存同样的数据,读取时对比校验,可靠性再上一个台阶。
我个人最看好的是把 MRAM 和铁电存储器结合使用:MRAM 存历史数据,铁电存频繁更新的配置参数,各取所长。不过铁电容量小,最大也就几兆位,适合存参数不适合存数据。
最后分享一个小技巧:MRAM 的 CS 引脚在空闲时一定要拉高,不要悬空。我见过一个案例,CS 悬空时受干扰偶尔拉低,MRAM 误以为收到指令,把内部状态机搞乱了,重新上电才恢复。加一个 10K 上拉电阻就能解决,成本几分钱,省心很多。