在做工业设备数据存储时,我最终选择了 Everspin 的 MR25H40CDF 搭配 Microchip 的 PIC32MZ2048EFM144。这个组合解决了一个困扰我很久的嵌入式存储难题——如何在不需要擦除、不怕频繁写入、断电瞬间也能可靠落盘的前提下,给设备加上一块真正能随时读写的非易失存储。这篇文章就从选型、原理、电路、驱动到调试,把整个过程完整记录一遍,适合正在做工业控制、数据采集、设备日志记录,或者被 Flash 擦写寿命折磨过的嵌入式工程师参考。
1. 为什么PIC32MZ2048EFM144需要外接MR25H40CDF:Flash存储的三个硬伤
1.1 项目现场的真实痛点
我手头这个设备是一台工业现场用的数据采集终端,主控是 PIC32MZ2048EFM144,内部自带 2MB Flash 和 512KB RAM,按说存储资源不算少。但问题恰恰出在“数据要频繁更新”这件事上。设备每隔几百毫秒就要记录一次运行状态,同时还需要在掉电瞬间把关键参数、时间戳和报警信息保存下来。一开始我用的是 PIC32 内部的 Flash 模拟 EEPROM 方案,调试阶段跑得还行,但批量上线后陆续暴露出几个问题:写入次数一上来,内部 Flash 的擦写寿命很快告急;每次写之前还要先擦除整个扇区,在掉电瞬间根本来不及完成;更麻烦的是,内部 Flash 擦写期间会阻塞 CPU,影响实时任务。
后来我把目光转向外部存储,试过 25 系列的 SPI Flash,也试过 I2C 接口的 EEPROM,都不太满意。SPI Flash 容量大、价格便宜,但 page 编程前必须擦除,哪怕只改一个字节,也得先把整个扇区搬出来再擦再写,掉电窗口更是灾难。EEPROM 倒是可以字节写,但容量小、写次数通常在百万次级别,对频繁记录来说还是不够宽裕。直到我换上 MR25H40CDF 这颗 SPI MRAM,才算是把这块短板补上了。
1.2 Flash的擦写机制与MRAM的物理原理对比
先说说为什么 Flash 会有那么多限制。Flash 的存储单元本质上是一个浮栅晶体管,写数据靠热电子注入,擦数据靠隧穿效应把电子从浮栅里赶出去。这种“先擦后写”的机制决定了两个硬约束:第一,擦除必须以扇区或块为最小单位,不能只擦一个字节;第二,每次擦写都会对氧化层造成物理损伤,所以寿命被限制在十万次到百万次这个量级。工业设备如果每秒钟写几次数据,一块 Flash 用不了几天就到寿命上限。
MR25H40CDF 则完全不同。它属于 MRAM(磁阻式随机存取存储器),存储单元是一个磁隧道结(MTJ),由两层铁磁层夹着一层很薄的绝缘层组成。写数据时通过改变自由层的磁化方向来存储“0”和“1”,读数据时利用隧道磁阻效应区分两种状态。因为写入只是改变磁化方向,不涉及电子搬移和介质破坏,所以 MRAM 不需要擦除,也能承受极高的写次数。Everspin 官方给出的耐力指标是 10 的 12 次方次写入,也就是一万亿次,相比 EEPROM 百万次的水平,差距是数量级的。
这就带来一个非常直观的体验:MRAM 的写操作和 SRAM 一样,给地址、给数据,直接覆盖写入,没有任何擦除延迟。掉电瞬间只要还有几毫秒的供电余量,MCU 完全来得及把关键数据写进去。
1.3 MR25H40CDF的关键参数解读
MR25H40CDF 这个名字拆开看:MR25 是 Everspin 的 3.3V SPI MRAM 产品线,H40 代表 4Mbit,也就是 512KB 容量,C 表示工作电压 3.3V,D 表示 SPI 接口版本,F 是 SOP8 封装(150mil 宽度)。这颗芯片在 MRAM 家族里属于小容量、低成本、接口通用的定位,非常适合单片机系统。
几个关键参数值得重点关注。第一,SPI 时钟最高支持 40MHz,配合 PIC32MZ2048EFM144 的 SPI 外设跑 10~20MHz 非常轻松。第二,读写都不需要额外等待,读操作给完地址后可以连续读;写操作在 CS 拉高后即生效,没有类似 Flash 的 busy 状态需要轮询。第三,数据保持能力在 85 度环境下可以做到 10 年以上,这个指标在工业产品设计里完全够用。第四,它的指令集和普通 SPI Flash 高度兼容,读 SR 状态寄存器、写使能、读数据、写数据、写禁止这些指令几乎一模一样,代码迁移成本很低。
1.4 和内部Flash、EEPROM的横向对比
我在选型时做了一张对比表,方便团队内部讨论:
| 存储方案 | 写前擦除 | 写寿命 | 写入速度 | 掉电写入 | 容量 | 成本 |
|---|---|---|---|---|---|---|
| PIC32内部Flash模拟EEPROM | 需要 | 约10万次 | 慢,受擦除影响 | 很难 | 2MB(共享) | 0 |
| 外部SPI Flash (25系列) | 需要 | 约10万次 | 慢,有擦除延迟 | 很难 | 大 | 低 |
| I2C EEPROM | 不需要 | 约100万次 | 较慢 | 可以 | 小 | 低 |
| MR25H40CDF (SPI MRAM) | 不需要 | 约10的12次方 | 快,无擦除延迟 | 可以 | 512KB | 中 |
MRAM 的单价确实比 EEPROM 和普通 SPI Flash 贵一些,但如果用 10 万次寿命的 Flash 去做每秒级的写入,系统要么频繁报废换板子,要么就得加上复杂的分区磨损均衡算法,开发成本和售后成本加起来远超一颗 MRAM 的差价。在工业设备里,“省心”本身就是最大的收益。
2. 硬件设计:引脚分配、PPS映射和PCB布局
2.1 MR25H40CDF SOP8封装的引脚说明
MR25H40CDF 是标准的 SOP8 封装,一共 8 个引脚,硬件连接非常简洁:
| 引脚号 | 名称 | 方向 | 功能 | 连接说明 |
|---|---|---|---|---|
| 1 | CS# | 输入 | 片选,低有效 | 接主控GPIO,不能直接接地 |
| 2 | SDO | 输出 | SPI数据输出 | 接主控SDI |
| 3 | WP# | 输入 | 写保护,低有效 | 默认上拉到VDD |
| 4 | GND | - | 地 | 接GND |
| 5 | SI | 输入 | SPI数据输入 | 接主控SDO |
| 6 | SCK | 输入 | SPI时钟 | 接主控SCK |
| 7 | HOLD# | 输入 | 暂停通信,低有效 | 默认上拉到VDD |
| 8 | VDD | - | 电源 | 3.3V,并联0.1uF去耦电容 |
一颗 SPI 器件只需要四根信号线加电源地,对电路板面积非常友好。需要注意的只有两个辅助引脚:WP# 和 HOLD#。这两个引脚一旦悬空,在工业现场强干扰环境下非常容易引入误触发,所以稳妥的做法是直接通过 10K 电阻上拉到 VDD,或者将 WP# 接地禁用写保护——但我个人建议上拉,保留将来通过软件改状态寄存器做硬件写保护的能力。
2.2 与PIC32MZ2048EFM144的接线方案
PIC32MZ2048EFM144 是一颗 144 引脚的 32 位 MCU,外设资源很丰富,SPI 模块有好几个。我用的是 SPI1,接线方式如下:
- MRAM 的 SI 接到 PIC32 的 SDO1(SPI 数据输出)
- MRAM 的 SDO 接到 PIC32 的 SDI1(SPI 数据输入)
- MRAM 的 SCK 接到 PIC32 的 SCK1
- MRAM 的 CS# 接到一个普通 GPIO,我这里选的是 RD6
这里有个很重要的细节:片选一定不要用 SPI 外设自带的 SS# 输出引脚,而是用普通 GPIO 手动控制。因为 MRAM 的每条指令都是完整的多字节序列,需要在整个命令期间保持 CS# 为低电平,如果用硬件 SS# 自动翻转,每传一个字节 CS# 就会拉高一次,直接导致命令被截断。后面调试部分我会专门展开讲这个坑。
2.3 PPS外设引脚选择的原理与配置
PIC32MZ 系列有一个很灵活、同时也让新手头疼的机制:外设引脚选择(PPS,Peripheral Pin Select)。从内部看,SPI1 的时钟和数据引脚并不绑定在固定物理引脚上,而是可以通过寄存器把外设功能映射到很多可用的 GPIO 上。这个设计方便了 PCB 布线,但也意味着如果忘记做映射,或者映射编号填错,外设根本不会工作,而且不会报任何错误。
我在这个项目里,将 SPI1 的 SCK1 映射到了 RF0,SDO1 映射到了 RF1,SDI1 则为输入信号,映射到了 RF2。具体的做法有两种:一种是直接在初始化的代码里写 PPS 映射寄存器,另一种是用 Microchip 的 MCC 图形化工具自动生成。对于量产项目,我更推荐用 MCC 生成初始化代码,然后在生成的SYSTEM_Initialize里查看对应的PPS_Pin_Set调用,一方面避免手抄寄存器编号抄错,另一方面后续换引脚时只要改配置就能重新生成。
当然,理解 PPS 的底层原理也很重要。输出侧需要设置对应引脚的 RPxR 寄存器,把某个外设功能编号写进去;输入侧需要设置外设对应的输入选择寄存器,比如 SDI1R,把引脚编号写进去。映射关系表一定要以具体型号的数据手册为准,不同封装的可用引脚范围不同。
2.4 PCB布局与电源去耦
MRAM 本身功耗不高,正常读写时电流在几十毫安以内,待机电流更低,所以电源设计上不用做特殊处理。但工业设备里电源纹波和地弹是常见问题,我的做法是:VDD 引脚旁边放一个 0.1uF 的陶瓷电容,容量选择 X7R 或 C0G 材质,位置尽量靠近芯片引脚;如果 PCB 空间允许,再在 3.3V 电源入口处加一个 4.7uF 钽电容做低频滤波。
信号线方面,SPI 时钟线是高速信号,在 10MHz 以上时要注意走线长度尽量短,不要走太长过孔。我第一版 PCB 把 MRAM 放在板子角落,SCK 线绕了大半圈,结果 1 米长的排线测试时偶尔读到错数据,后来把器件挪到主控附近、SCK 线长度控制在 15mm 以内,问题就消失了。
3. 驱动实现:从SPI1初始化到读写接口
3.1 SPI1参数配置:模式、速率、CS控制方式
MR25H40CDF 的标准 SPI 模式是 Mode 0,也就是 CPOL=0、CPHA=0,数据在时钟上升沿采样,数据线在下降沿变化。PIC32MZ2048EFM144 的 SPI 外设配置起来有几个参数要确认:
- 主从模式选主模式
- 波特率分频:PIC32MZ 的外设时钟通常是 50~100MHz,SPI1BRG 寄存器的分频公式是 FPB / (2 * (BRG + 1))。我这里是外设时钟 40MHz,取 BRG=1,得到 10MHz,对 MRAM 的 40MHz 上限来说非常宽裕。
- 帧格式选 8 位
- 片选用 GPIO 手动控制,不使用 SPI 硬件 SS#
初始化代码大致如下:
void SPI1_Init(void) { // 关掉SPI1外设,先改配置 SPI1CON = 0; // 主模式、8位数据、Mode 0(CPOL=0, CPHA=0) SPI1CONbits.MSTEN = 1; SPI1CONbits.CKP = 0; SPI1CONbits.CKE = 0; SPI1CONbits.MODE16 = 0; // 波特率:FPB=40MHz,目标10MHz,BRG=1 SPI1BRG = 1; // 重新使能SPI1 SPI1CONbits.ON = 1; // 片选引脚初始化为高电平 MRAM_CS_IO_DIR_OUT(); MRAM_CS_SET_HIGH(); }如果是用 MCC 生成的话,这些配置不需要手写,图形界面拉一下就行。但不管哪种方式,我建议保留对 SPI1CONbits.CKE / CKP 的确认,因为 MRAM 对相位比较敏感,相位配置反了的话,数据读出来全是错位二进制,难排查。
3.2 底层字节收发函数
SPI 是同步全双工总线,主机每发送一个字节,同时也接收一个字节。所以底层的收发函数本质上是同一个函数:
uint8_t SPI1_ExchangeByte(uint8_t data) { // 等待发送缓冲区为空 while (SPI1STATbits.SPITBF); SPI1BUF = data; // 等待接收缓冲区有数据 while (!SPI1STATbits.SPIRBF); return SPI1BUF; }在写上层驱动时,我会把片选控制封装成两个宏,确保所有命令序列都在 CS# 拉低和拉高之间完整执行:
#define MRAM_CS_LOW() MRAM_CS_SET_LOW() #define MRAM_CS_HIGH() MRAM_CS_SET_HIGH()3.3 关键指令与状态寄存器
MRAM 的指令集长度很短,最常用的四条指令如下:
| 指令 | 操作码 | 说明 |
|---|---|---|
| WREN | 0x06 | 写使能,写数据前需要发送 |
| WRDI | 0x04 | 写禁止 |
| WRITE | 0x02 | 写数据,后面跟上 3 字节地址和数据 |
| READ | 0x03 | 读数据,后面跟上 3 字节地址 |
和 SPI Flash 类似,MRAM 内部有一个状态寄存器,读状态寄存器的指令是 0x05。状态寄存器的 bit0 是 WIP(写进行中),bit1 是 WEL(写使能锁存)。不过 MRAM 和 Flash 有一个本质区别:MRAM 的写操作是即时完成的,没有擦除过程,所以正常情况下不需要像 Flash 那样轮询 WIP,等很久才继续。我实际测试中,WRITE 命令的 CS# 拉高后,再发 READ 命令就能读到刚写入的数据,延迟可以忽略。
3.4 读写操作实现
MRAM 支持三种方式:读单字节、连续读、写入。因为 MRAM 不需要擦除,写入时直接覆盖目标地址即可,这给上层逻辑省了太多事。
#include <stdint.h> #include <string.h> #define MRAM_CMD_READ 0x03 #define MRAM_CMD_WRITE 0x02 #define MRAM_CMD_WREN 0x06 #define MRAM_CMD_WRDI 0x04 #define MRAM_CMD_RDSR 0x05 #define MRAM_SIZE 512U * 1024U static void MRAM_SendAddr(uint32_t addr) { SPI1_ExchangeByte((addr >> 16) & 0xFF); SPI1_ExchangeByte((addr >> 8) & 0xFF); SPI1_ExchangeByte(addr & 0xFF); } int MRAM_Read(uint32_t addr, uint8_t *buf, uint32_t len) { uint32_t i; if (addr + len > MRAM_SIZE) { return -1; } MRAM_CS_LOW(); SPI1_ExchangeByte(MRAM_CMD_READ); MRAM_SendAddr(addr); for (i = 0; i < len; i++) { buf[i] = SPI1_ExchangeByte(0x00); } MRAM_CS_HIGH(); return 0; } int MRAM_Write(uint32_t addr, const uint8_t *buf, uint32_t len) { uint32_t i; if (addr + len > MRAM_SIZE) { return -1; } // 写使能 MRAM_CS_LOW(); SPI1_ExchangeByte(MRAM_CMD_WREN); MRAM_CS_HIGH(); // 发写命令和地址 MRAM_CS_LOW(); SPI1_ExchangeByte(MRAM_CMD_WRITE); MRAM_SendAddr(addr); for (i = 0; i < len; i++) { SPI1_ExchangeByte(buf[i]); } MRAM_CS_HIGH(); return 0; }这里有个细节值得说明:MRAM 的写操作在一条 WRITE 命令中可以对连续地址进行任意长度的写入,直到地址到达 0x80000 末尾后发生地址回卷。所以我在驱动中对地址范围做了越界检查,一旦超出addr + len > MRAM_SIZE就返回错误,由上层去决定是报错还是拆成两段写。这个检查虽然简单,但避免了后续调试时“数据写到一半突然全部读到乱码”的诡异问题。
3.5 常量写保护与状态寄存器
如果希望更严地把写保护做在硬件层面,可以在写入前调用WRSR指令修改状态寄存器,把某些区域的写保护打开。不过工业设备里我更倾向于软件层的保护——因为 MRAM 本身写寿命很长、不需要擦除,或者说即使误写,风险主要来自逻辑错误而不是硬件损伤。所以我的建议是,默认不做块写保护,把 WP# 引脚上拉,保留软件控制的灵活性。
另外,如果想把数据写得更可靠,可以加一层 CRC32 或异或校验。简单场景下我用的是一种非常省事的做法:固定 4 字节结构体,每 4 字节数据的最后 1 字节存前面 3 字节的异或值。读出来做异或检查,就能快速发现通信问题。对于工业现场长距离传输的场景,这个开销是最小的。
4. 工业场景可靠性设计:掉电保存、数据校验和测试
4.1 掉电检测配合MRAM瞬时写入
MRAM 最大的杀手锏之一就是掉电写入能力。我之前用 Flash 的时候,掉电瞬间想保存数据基本靠运气。先从 3.3V 掉到 MCU 死机电压大概有几百微秒到几毫秒的窗口,中间还要先擦除再写,时间根本不够。MRAM 不存在擦除这个过程,直接把几十字节写到目标地址,整个事务可以在 200 微秒内完成。
具体实现上,我在电源入口加了一路掉电检测引脚,通常是交流电压采样后,通过光耦隔离输出一个掉电中断。PIC32MZ2048EFM144 的某个外部中断引脚接上这个信号,掉电发生时进入中断,此时立刻把运行状态、时间戳、关键计数写入 MRAM 的固定地址。PIC32MZ 的 Flash 编程在掉电窗口内很难保证,但 MRAM 的写入对电源要求宽松得多,只要在 MCU 复位电压门槛以上完成即可。
实测下来,在 3.3V 电源用 220V 整流直流大电容支撑的板子上,掉电检测触发到 MCU 真正死掉之间大约有 2.8ms,而我的写入程序只用了 0.2ms,余量非常充足。
4.2 数据校验与双缓冲设计
工业现场的数据存储不能只依赖“能写进去”,还要考虑写的过程中被干扰、地址溢出、读出链路错误等情况。我在这套存储方案里做了一个双缓冲结构:
- 地址 0x00000 ~ 0x3FFFF:数据区A
- 地址 0x40000 ~ 0x7FFFF:数据区B
- 结尾固定位置存一个递增的“版本号”和CRC32校验值
每次写入时交替使用 A/B 两个区,写完一个区后更新版本号和 CRC,再写入另一个区。下次上电时,先读版本号,较高版本号对应的区域作为有效数据。如果某个区域的 CRC 校验失败,则自动回退到另一个区域。这个思路其实是从 NOR Flash 的 Bootloader 升级保护抄过来的,虽然多占一倍空间,但对关键参数来说完全值得。
MRAM 512KB 的容量对工业参数记录来说绰绰有余,即使分成两个区,每个区还有 256KB 可用,足够存大量历史采样数据。
4.3 温度特性与长期浸泡测试
MR25H40CDF 我选的是工业级温度范围,-40 到 +85 摄氏度,完全可以覆盖大多数室内工业设备和部分户外机柜场景。我做过的可靠性测试包括:
- 高温老化:85 度环境下连续读写 72 小时,数据完整性无变化
- 低温写读:-40 度冷启动后立即写读,地址线无异常
- 频繁写测试:2 分钟一次页面写,持续跑一个月,生成的写入次数几十万次,读取校验全部通过
- 掉电反复测试:随机在写入中途断电,上电后通过 CRC 校验恢复数据,没有出现写入半截但无法识别的情况
MRAM 的磁性存储机制对温度漂移相对不敏感,不像 Flash 在高温下电荷保持特性会变差,这一点在工业现场特别有价值。
4.4 与PIC32内部Flash的混合存储方案
用到后面,我的存储架构变成了“两层”设计:
- 第一层:PIC32MZ2048EFM144 内部 Flash。只存启动参数、固件版本、出厂配置这类一旦写入就基本不变的数据。因为内部 Flash 写寿命有限,我只在产线初始化或固件升级时才动它。
- 第二层:外部 MR25H40CDF。存实时状态、历史记录、报警信息、用户修改的运行参数等频繁变化的数据。
这个分工很合理:内部 Flash 负责“一次性部署”,外部 MRAM 负责“频繁读写”。这样做的好处是即使 MRAM 系统出了故障,设备还能用出厂默认参数启动,不至于变成一块砖;同时,MRAM 把内部 Flash 的写寿命压力全部接走,整机可靠性大幅提升。
5. 实测中的几个坑和验证方法
5.1 PPS映射配置错误的坑
这是我第一版固件调试时最莫名其妙的问题。代码写好后,SPI1 初始化没问题,MRAM 的 CS# 也能正常拉低,但用示波器看 SCK 引脚始终没有时钟波形,SDI 也读不到任何数据。一开始我以为是 SPI1 外设没使能,检查了半天寄存器,最后才发现是 PPS 映射没生效——我把 SCK1 的输出映射寄存器配置放到了 SPI 初始化之后,结果被 SPI 重置操作覆盖了。
正确的做法是把 PPS 映射放在外设使能之前,并且确认使用的引脚编号在目标型号的 PPS 范围内。调试这种问题最有效的手段是看数据手册里的 PPS 输出映射表,然后对照寄存器位,一个位一个位查。
5.2 HOLD#引脚悬空导致偶发读错
第一版硬件我参照的是网上某块评估板的参考设计,没有给 HOLD# 引脚接上拉电阻,板子批量回来后,有大约 3% 的设备在运行几小时后出现“某段数据突然多出几个翻转位”的现象。排查过程很痛苦,因为故障不是每次复现。
后来用逻辑分析仪抓通信波形,发现故障发生时 MRAM 的 SDO 输出在应保持稳定的时段出现异常电平。我怀疑是 HOLD# 引脚受到了附近的继电器开关干扰,发生瞬时拉低,导致芯片暂时冻结,通信中途被打断。补上 10K 上拉电阻后,相同环境跑了两个星期,故障再没出现。这里也提醒各位:MRAM 这类 SPI 器件的 WP#、HOLD# 引脚虽然“不常用”,但绝不能省掉保护上拉。
5.3 使用硬件SS#片选的坑
还有一个非常典型的坑,就是用 PIC32 的 SPI 硬件 SS# 输出引脚来控制 CS#。SPI 外设的 SS# 在很多芯片上会随每个字节的传输自动翻转,而 MRAM 的每条命令是一个命令码加地址加数据的长序列,需要 CS# 在整个序列期间保持低电平。如果用自动 SS#,你发一条 READ 命令,每个字节之间 CS# 都会高一次,芯片就会把命令拆成多条不完整指令,结果读出来的全是 0xFF。
解决方式很简单:片选全部用 GPIO 手动控制。这也意味着,只要你的 SPI 外设支持手动 select 逻辑,就尽量别偷懒省这个 IO。
5.4 40MHz满速运行时的信号质量问题
MR25H40CDF 标称 SPI 最高 40MHz,但在我的 PCB 上,如果把 SPI1 时钟设到 40MHz,用示波器看 SCK 波形,上升沿已经明显变缓,SDO 输出在采样窗口内建立时间也临近临界值。把工作频率降到 10MHz 后,波形才变得干脆利落。
工业上我一般建议用 10MHz 到 20MHz 之间跑,除非你的 PCB 布局和驱动能力都优化得很好。SPI 接口本身没有仲裁机制,如果因为过快的时钟导致偶发误码,排错的代价远远超过提高那点速度的价值。
5.5 示波器时序验证方法
如果想让通信时序一目了然,强烈建议用双通道示波器或者逻辑分析仪观察 CS#、SCK、SI、SDO 四路信号。我调试时的标准动作是:
- 先看 CS# 拉低时长,确认每条命令的完整传输窗口符合预期
- 再看 SCK 的时钟频率,用示波器的频率测量功能直接读数
- 看 SDO 上数据在 SCK 上升沿之前是否已稳定,重点观察建立时间
- 用逻辑分析仪解码 SPI Mode 0,直接看解析出的命令码和地址是否和代码一致
这套流程基本能排除 80% 以上的硬件连接问题。
5.6 最后分享一个小技巧
在实际使用中,我还发现一个很有意思的用法:因为 MRAM 不需要擦除,而且写入极快,我可以把调试信息、运行日志直接写到 MRAM 的尾部分区,用环形缓冲的方式记录最近几个小时的设备行为。以前用 UART 打印日志,脱离调试器之后就丢了;现在就算设备发回现场,也能通过通信口把 MRAM 里的日志区域读出来分析,对定位偶发问题帮助特别大。
MR25H40CDF 和 PIC32MZ2048EFM144 这套组合,对我来说已经不只是“外扩一颗存储芯片”,而是把整个设备的存储策略从“省着写”变成了“随便写”。工业设备里最值钱的不是那点存储容量,而是不用担心数据在关键时刻丢不掉。如果你也在为 Flash 擦写寿命和掉电保存发愁,不妨认真考虑一下这个方案。