工业控制器分级存储方案:EEPROM+NOR Flash+SD卡实战拆解

发布时间:2026/11/6 8:24:09
工业控制器分级存储方案:EEPROM+NOR Flash+SD卡实战拆解

做工业控制器这些年,经常被别人问到一个问题,不是“你的控制算法怎么写的”,而是“你的数据到底怎么存的”。参数要存、日志要存、固件要存,实时性要求还高,动不动就是掉电不丢数据、几十万次擦写寿命、现场工程师抱着笔记本就要导数据。这些需求单独拎出来都不算复杂,但全部叠在一台 STM32+FPGA 架构的工业控制器里,就没那么轻松了。

这篇文章是硬件篇系列的第 12 篇,把我在几个实际项目里落地过的一套分级存储方案完整拆开讲:EEPROM 管小参数、NOR Flash 管固件与配置、SD 卡管大容量日志,配合 STM32 和 FPGA 各自的职责划分,从选型逻辑、电路设计、掉电一致性到现场踩坑,一次讲透。适合正在做工业控制器、边缘网关、运动控制或数据采集设备的硬件和嵌入式工程师参考,也适合想系统理解存储架构设计的同学。

先建立一个共识:没有一种存储介质能同时满足“容量大、速度快、寿命长、掉电不丢”。分级存储方案的本质,就是让每种介质干自己最擅长的事,再用架构设计规避各自的短板。下面我把这套方案的每个决策节点都拆开聊。

1. 分级存储的整体设计思路

1.1 先给数据分类,再谈存储方案

拿着需求清单直接选存储芯片,是新手最容易犯的错。我在定方案之前会先做一次数据分类,把控制器里所有需要非易失保存的数据,按“变更频率 × 单次数据量 × 可靠性要求”三个维度列成一张表。

以一台典型的运动控制器为例:设备序列号、校准参数、用户配置这类数据,每次写入量可能只有几十字节,但变更频繁,而且一旦丢失整个设备就得返厂;运行日志、报警记录、工艺曲线这类数据,单次写入量从几十字节到几 KB 不等,总量可能到几百 MB,丢失之后虽然不致命,但客户会非常在意;FPGA 配置文件、Bootloader、固件镜像这类数据,写入次数极少,可能一年就升级一次,但容量大,对完整性和安全校验要求极高。

三类数据对存储介质的诉求完全不同。参数类要求写入寿命长、掉电不丢、访问简单;日志类要求容量大、顺序写入且不心疼寿命;固件类要求支持大块擦写、读取速度快,最好还能做双备份。这么一分,思路就清晰了:小参数给 EEPROM,固件配置给 NOR Flash,日志给 SD 卡。

这里解释一下为什么不用更“省事”的方案。有人会问,能不能用一片大容量 NOR Flash 全搞定?不能。NOR Flash 虽然读取快、支持 XIP 执行,但写入前必须按扇区擦除,擦写寿命通常只有 10 万次。拿 4MB 的 NOR Flash 记日志,按每秒写一次日志的节奏,一个扇区很快磨穿,而且擦除块太大,写小参数会造成惊人的空间浪费。把这一切都交给 SD 卡呢?SD 卡内部有磨损均衡,容量和寿命也都够,但写入时间存在不确定性,最坏情况会有几十甚至数百毫秒的阻塞。控制器保存校准参数这种操作往往发生在关键时序附近,等不起。更不用说 SD 卡是可插拔的,跑现场的设备没法保证卡一直在,也不适合存放系统关键配置。

1.2 分级的本质是约束下的结构化取舍

EEPROM 寿命长(通常百万次)、字节级读写灵活,但容量天花板太低,主流型号到 1Mbit 就到顶了,存固件和日志完全不够。NOR Flash 读取快、可执行,但写入前要擦除、寿命偏短、容量中等。SD 卡容量大、有文件系统、使用方便,但写入时间抖动大、依赖卡本身质量、不适合存系统关键参数。

成本、寿命、性能三个维度不可能在一种介质上同时拉满。硬件设计的本质就是在约束条件下做取舍,而分级存储就是把这个取舍变成可复用的结构。每一级存储都是另一级的补充,而不是竞争关系。想通这一点,后面所有设计决策就都有了一个统一判断标准:某一类数据放在某一级介质上,是否充分利用了它的优势,是否有效规避了它的劣势。

2. 存储介质选型:EEPROM、NOR Flash 与 SD 卡

2.1 EEPROM:小参数的高频变更主力

EEPROM 在方案里的角色是“参数级存储”。我常用的型号是 Microchip 的 AT24C 系列,工业级温度范围 -40°C 到 +85°C 甚至更宽的 -40°C 到 +105°C 版本都有,容量从 2Kbit 到 1Mbit 可选,I2C 接口,手册上的擦写寿命典型值是 100 万次。实际工程中我一般按 50 万次做设计余量,因为温度升高、电压波动都会影响实际寿命。

选 EEPROM 有个容易忽略的点:写入时间。AT24C256 在 I2C 时钟 400kHz 下,页写入 64 字节所需的时间典型是 5ms,而且是器件内部的写循环,期间主控发任何指令都不会被响应。这意味着写参数不能简单地在中断服务函数里发起,必须放到任务上下文,而且要处理好“写完等待”这个过程。具体的错误做法和正确流程,我放在第 6 节里展开讲。

另外一个设计细节是页边界问题。AT24C256 页大小是 64 字节,如果写入的数据跨越页边界,EEPROM 会把地址自动回卷到页首,覆盖原来的数据。驱动程序里要做一次长度判断,先填满当前页剩余空间,再换页继续写,否则会出现数据错乱。

2.2 NOR Flash:固件配置的中转站与基石

方案里 NOR Flash 的角色是“固件与配置存储”,挂在 FPGA 侧,主要承担两件事:保存 FPGA 配置文件,以及作为应用升级时的中间缓存区。我常用华邦 W25Q128JV,SPI 接口,容量 128Mbit。除了标准 SPI,它还支持 Dual/Quad SPI 模式,FPGA 侧一般会用 Quad SPI 提高读写吞吐。

NOR Flash 的写操作要特别注意“先擦后写”的规则。擦除最小单位是扇区,W25Q128 的扇区是 4KB,128 个扇区一组组成 64KB 的块。擦除一个扇区的时间典型在 45ms 到 400ms 之间,编程一页(256 字节)的时间在 0.7ms 到 3ms 左右。如果 FPGA 侧的读写控制状态机没有正确等待 BUSY 状态位清零就发下一条命令,就会出现写失败或数据错乱。

NOR Flash 的寿命按擦除次数计算,10 万次是典型值。对固件缓存用途来说完全够用,但如果拿它存频繁变更的参数,需要设计磨损均衡策略,这和 EEPROM 的做法完全不同,后面第 5 节会详细讨论。

2.3 SD 卡:大容量日志文件的承载者

SD 卡在本方案里承担日志和历史数据的最终落盘。控制器一年可能要产生几百 MB 到几个 GB 的日志,SDHC 卡(4GB 到 32GB)和 SDXC 卡(64GB 以上)在实际项目里都见过使用。

工程上 SD 卡通常走两条路:一条是 SDIO 接口,STM32 全速四线模式,吞吐高,驱动复杂度也高;另一条是 SPI 模式,兼容性好,但速度一般,适合数据量不大的场景。日志数据量大、要求持续写入,所以我的方案里 STM32 的 SDIO 四线模式是首选,底层用 FatFS 文件系统管理文件,中断和 DMA 配合做数据搬运。

SD 卡最需要工程师警惕的是写入时间的抖动。标称写入速度 10MB/s 的卡,在随机碎片环境下单次 4KB 写入可能需要几十毫秒,有些劣质卡甚至会到数百毫秒。这是 SD 卡内部固件做磨损均衡和坏块管理导致的,属于正常现象。产品级设计必须允许这种时间抖动,不能把 SD 卡写入放在硬实时任务里,否则运动控制周期会被拉爆。

2.4 三种介质的横向对比

维度EEPROM(AT24C 系列)NOR Flash(W25Q 系列)SD 卡(SDHC/SDXC)
典型容量2Kbit ~ 1Mbit1MB ~ 64MB4GB ~ 2TB
接口I2CSPI / Dual / Quad SPISDIO / SPI
最小写入粒度字节(页写最优)页(256B),擦除最小扇区 4KB块(512B),由文件系统管理
写寿命约 100 万次约 10 万次擦除视主控内部磨损均衡,寿命差异大
写入时间典型值页写 5ms页编程 1~3ms,扇区擦除 45~400ms块写入,存在明显抖动
掉电数据保持10 年以上20 年以上依赖卡素质和文件系统完整性
适合存什么参数、校准值、运行计数固件、配置、FPGA bitstream日志、历史数据、升级包

这张表是我做选型时的速查依据。边界一旦清晰,存储分配的争议就少了。

3. STM32 与 FPGA 的任务切分

3.1 STM32 管参数与日志,贴近实时计算和应用层

STM32 在方案里跑着实时控制和通信,它管理的存储对象是两类:EEPROM(通过 I2C1,400kHz)和 SD 卡(通过 SDIO,四线 DMA 模式)。让 STM32 管 EEPROM 的原因很简单:校准参数和用户配置往往与系统运行逻辑强耦合,这些数据的读写在应用层管理最方便,不需要绕过 FPGA 做协议转发。

STM32 管 SD 卡是自然分工:FatFS 文件系统栈跑在应用处理器上最方便,ST 官方 STM32Cube 软件包里就有 SD 卡加 FatFS 的 BSP 示例代码,省掉大量底层移植工作。但有一个提前规划要注意:FatFS 的底层磁盘 I/O 函数和 SD 卡 DMA 传输需要共享内存缓冲区,缓冲区放 DTCM 还是 AXI SRAM,直接影响 DMA 访问效率。我建议把缓冲区放在支持 DMA 访问的 SRAM 区域,不要放在只被 CPU 访问的 DTCM。这个细节在日志频繁写入、CPU 负荷高的时候差别非常大,放错位置带来的性能损失可能是 30% 以上。

3.2 FPGA 管 NOR Flash,发挥并行时序控制的优势

FPGA 侧的任务是管理 NOR Flash,主要是为了两件事:上电时快速加载配置,以及配合 STM32 做固件升级的中转缓存。FPGA 的读时序控制天然适合 SPI 外设——可以用纯状态机实现精确到时钟周期的读写时序,不必担心 CPU 中断、DMA 竞争带来的时间不确定性。

这里有个工程细节。W25Q128 的 Quad SPI 读取地址和数据都按 4 位传输,FPGA 的 Flash 控制器状态机如果没按手册时序图里的“等待周期(dummy cycles)”设计,在低温或高速时钟下容易读到错数据。我一般把 dummy cycle 参数做成寄存器可配,调试时先用宽松值验证逻辑,再逐步收紧以满足带宽要求。后面第 6 节的案例里会详细展开。

3.3 为什么把 NOR Flash 挂在 FPGA 而不是 STM32

有人会质疑:STM32 的 SPI 也能访问 NOR Flash,何必挂在 FPGA 上?我在实践中总结出三条理由。

第一,上电时序。FPGA 配置文件必须在系统初始化早期加载,通常是在电源稳定后几百毫秒内完成。如果配置文件存放在 STM32 管理的 NOR Flash 里,那 FPGA 就必须等 STM32 先完成启动,再通过通信链路把配置数据推过来,整个启动链路又长又脆弱。而让 FPGA 直接挂一片 NOR Flash,上电后 FPGA 从 Flash 自加载,独立完成配置,STM32 可以并行做自己的初始化,系统启动时间能缩短到原来的三分之一。

第二,带宽。日志量大的场景下,STM32 的 SDIO 接口已经被日志写入占用了大量带宽,如果再通过 STM32 中转 NOR Flash 的数据,会造成明显瓶颈。FPGA 直接读 Flash,配置加载带宽可以做到几十 MB/s 级别,完全不受 STM32 侧的负载影响。

第三,故障隔离。如果 STM32 侧软件崩溃或者 SD 卡长时间写入阻塞,FPGA 侧的 Flash 访问不依赖 STM32,系统级可靠性高不少。这在工业设备上是一个很实际的优势。我不止一次见过因为 SD 卡卡死导致 CPU 忙等,连带 FPGA 配置都无法加载的现场问题,分级之后这个问题从根源上消失了。

4. 硬件电路设计要点:原理图与 PCB 层面的工程细节

4.1 EEPROM 电路:时序、上拉与保护引脚

EEPROM 的电路看似简单,但几个细节直接影响可靠性。I2C 总线的 SCL、SDA 都需要上拉电阻,我习惯用 4.7kΩ 上拉到 3.3V。如果总线上还有其他 I2C 器件,电阻值需要根据总线总电容和速率综合计算:400kHz 下总线电容一般控制在 400pF 以内,上拉电阻在 2.2kΩ 到 4.7kΩ 之间都可以。总线过长或者挂载多器件时,需要算一下 RC 时间常数是否满足 I2C 规范的最短上升沿要求,这个不确认好,高速通信可能出现偶发 NACK。

AT24C 的写保护引脚 WP 千万不要悬空。WP 拉高时器件只读,拉低时允许写入。工程上我习惯把 WP 引到一个 GPIO 或者跳线。前期调试时通过硬件随时保护数据,量产时直接接 GND(允许写入)。如果 WP 悬空,引脚电平可能漂移,导致器件偶尔进入保护状态,现场改参数改不进去,排查起来非常痛苦。

还有 A0/A1/A2 地址引脚。如果板上只有一颗 EEPROM,这三个引脚全部接地即可,I2C 地址固定为 0x50。但如果板上还有温度传感器、I/O 扩展芯片等 I2C 器件,要确认地址没有冲突。我吃过一次亏:EEPROM 和一片 RTC 的 I2C 地址撞在一起,每次往 EEPROM 写参数,RTC 也收到相同的地址数据,最后两块芯片数据全乱。这个错误查出真相之前,我们甚至怀疑是电源干扰、代码错误,浪费了整整两天。

4.2 NOR Flash 电路:SPI 信号质量与电源去耦

NOR Flash 走 Quad SPI 时,时钟频率一般做到 50MHz 以上,这时信号边沿质量和参考地完整性必须认真对待。PCB 布线时,四根数据线 IO0~IO3、时钟 CLK、片选 CS# 要做等长,长度差控制在 10mil 以内比较稳妥。走线尽量短,优先走内层,避免跨分割区。如果走线较长,可以在 CLK 上加一个 33Ω 串阻抑制反射。

W25Q 系列对电源比较敏感,VCC 引脚旁的去耦电容我习惯放一个 0.1μF 陶瓷电容,再加一个 1μF 到 10μF 的钽电容或者多层陶瓷电容做低频缓冲。特别需要注意的是,如果 FPGA 的 IO Bank 电压和 Flash 的 VCC 不是同一个电源域,比如 FPGA 的 Bank 用的是 2.5V、Flash 用的是 3.3V,那么必须加电平转换芯片,不能直接连接。Quad SPI 模式中 IO2/IO3 方向会变化(既是输入又是输出),普通的单向电平转换芯片不能用,需要支持双向的器件,我用过 TI 的 TXS0108E 和电平方向可控的双向缓冲器。选型时不要把方向问题漏掉,否则项目后期会非常被动。

4.3 SD 卡电路:电平匹配、供电与走线

SD 卡接口的常见坑在电平。传统 SD 卡是 3.3V 电平,但 UHS-I 以上的卡在高速模式下可能切换到 1.8V 信号电平(SD 卡规范里的 Voltage Switch 过程)。如果 STM32 和 SD 卡座直连,使用 3.3V IO,对 UHS 模式的卡来说电平其实不匹配。好在多数工业控制器不追求极限速度,可以在 SD 卡初始化时用 CMD8/ACMD41 协商锁死在 3.3V 非 UHS 模式,电路上只需要保证 STM32 与卡座之间电平一致即可。

供电方面,SD 卡座 VDD 引脚要可靠去耦,并一个 10μF 和一个 0.1μF 电容。卡的电源尽量从单独的 LDO 出来,避免与逻辑电路共用噪声较大的 DCDC 电源。卡座的 CD(Card Detect)和 WP(Write Protect)引脚也不要忽略。CD 引脚在卡未插入时会呈现某个固定状态,插入后翻转,可以接到 STM32 的 GPIO 做卡检测中断。WP 引脚是机械开关,按规范低有效,但不同厂家卡座的极性常常标反,最好用一个 GPIO 检测并在软件里做极性配置。

SD 卡时钟线在 25MHz 以上时建议做阻抗控制,走线尽量短,靠近主控侧加 22Ω 到 33Ω 串阻。实测中,如果 SD 卡走线过长且没有串阻,在四线 SDIO 模式下很容易出现 CRC7 校验错误,表现为文件系统随机性损坏。这类问题在量产现场排查起来极难,出厂测试还不一定复现。

4.4 掉电保护电路:最后一道保险

工业控制器最怕的不是断电,而是断电瞬间正在写存储。EEPROM 和 NOR Flash 内部都有写循环,如果在写循环中途掉电,数据会处于不确定状态。应对方案是加掉电检测,通常用 STM32 的 PVD(可编程电压检测)或其他 MCU 的 Brown-Out Detector,配置在 2.9V 或 3.0V 触发,在电压进一步跌落前进入紧急处理流程:先停止新的写入请求,把关键状态和中间数据快速搬运到备用存储,然后等待系统完全断电。

NOR Flash 侧的掉电保护有一个更实用的办法:在 FPGA 里加一个“正在写保护”寄存器。FPGA 内部做一个 write-in-progress 标记位,掉电检测触发时将这个标记写入保留寄存器,同时禁止 Flash 擦写命令。上电后先检查这个标记,如果上次异常掉电时确实在做擦写,就在加载配置时做一次完整 CRC 校验,发现损坏自动从备份区恢复。这个方案是纯逻辑实现,不占用额外芯片,但可靠性提升非常明显。

5. 数据管理策略与掉电一致性

5.1 按数据生命周期设计写入模式

数据有了存放介质,还要有配套的写入生命周期管理。参数类的数据,EEPROM 的写入点应该集中在“修改确认”和“异常归档”两个场景,不要在每次控制运算里都触发写入。比如用户修改了 PID 参数,界面点“保存”时一次性写入;设备关机前把当前运行计数写到 EEPROM 的独立地址。

固件类的数据,NOR Flash 写入频次极低,但数据完整性无比重要。我一般规划两个存储分区:A 区和 B 区(双备份),加上一个标志位表示“当前有效分区”。升级时先写 B 区,写完做完整校验,校验通过后翻转标志再写 A 区。整个过程确保任何时刻都有一个有效配置可供加载。这个思路在工业设备领域至关重要,现场升级失败后系统起不来的案例,一半以上都可以用双备份结构规避。

日志类的数据是“追加写入”模式。日志文件按日期切分,单个文件大小控制在 32MB 以内,一方面便于现场直接拷贝分析,另一方面避免 FAT32 大文件出现碎片问题。FatFS 里用FA_OPEN_ALWAYS | FA_WRITE模式打开,每次写日志前f_lseek到文件末尾再追加。真正要做到可长期写入,还要定期用f_getfree检查剩余空间,低于阈值时滚动删除旧日志。

5.2 磨损均衡:参数频繁写要算寿命细账

EEPROM 的 100 万次寿命看起来很大,但对某些参数来说仍然不够。比如运行计数器,如果每次断电都加 1 并写入,设备一天开关 10 次,一年就是 3650 次,看起来离百万次很远。但如果是电机运行累计时间这种高频更新数据,每秒更新一次,一年就有 3100 万次写入,远超寿命。

解决办法是“分块轮询写入 + 索引机制”。把 EEPROM 划出 64 个字的特定区域做计数器存储区,每次写操作不在固定地址写,而是按“上一次写入地址加 1(模 64)”的规律写到下一块。读取时扫描所有块,取“有效魔数最新且序号最大”的那个块作为当前值。这样 100 万次寿命直接乘以 64,变成 6400 万次。这个思路同样适用于 NOR Flash,只是 NOR 的最小擦除单位太大,需要配合“先擦后写”的缓冲策略。

NOR Flash 的磨损均衡略有不同。W25Q 擦除最小单位是 4KB 扇区,频繁更新的配置如果每次改几个字节都擦整个扇区,寿命消耗极快。实践中常用“固定基扇区 + 填充扇区”结构:数据写入时只写当前扇区的空白页,写满后才执行一次擦除操作。这样每次擦除能够承载多次逻辑更新,4KB 扇区的容量优势得到利用。

5.3 双备份与 CRC:数据可信的底线

无论介质选择多合理,工业现场的电磁环境和电源波动总会造成意想不到的数据损坏。我给自己定的规矩是:任何跨掉电周期的数据,至少带一层 CRC 校验;关键配置(校准参数、网络配置)必须双备份。

EEPROM 里的参数区,我习惯在 64 字节页内放“数据 + CRC16 + 序列号”。双备份就是同一份参数放在两个独立区域,读取时先读 A 区,CRC 错误就尝试 B 区,两个都错才判定参数丢失,并在上电日志里记录。NOR Flash 的双备份刚才提过,A/B 分区保存两份配置,加载时带完整校验。SD 卡日志文件虽然不需要双备份,但每条日志记录前要写固定长度的记录头,包含时间戳、数据长度和数据 CRC。这样即使文件尾部因为断电截断,也能通过扫描记录头恢复大部分数据。

这套组合拳打下来,除了物理硬件本身损坏,正常使用中几乎没有再遇到因掉电或干扰导致的彻底数据丢失。

6. 实测中的坑与排查经验

6.1 EEPROM 偶发写入失败,先查时序流程

第一个高频坑是 I2C 写 EEPROM 的时序顺序错误。正确流程是:写地址和数据字节,然后等待 EEPROM 内部写循环完成。很多同学写完数据后立刻发下一个写命令,此时最快的方式是“轮询应答”——向器件发一个写命令,等 ACK 返回,没有 ACK 说明器件还在忙。如果代码里写完立刻调用固定延时再发命令,延时不够就会出现偶发失败。

正确实现如下:

// I2C 写 EEPROM 并等待内部写循环完成 HAL_StatusTypeDef I2C_WriteByte(I2C_HandleTypeDef *hi2c, uint16_t dev_addr, uint16_t mem_addr, uint8_t *data, uint16_t len) { HAL_StatusTypeDef status; status = HAL_I2C_Mem_Write(hi2c, dev_addr, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, 100); if (status != HAL_OK) return status; // 关键:轮询 ACK,等待内部写循环结束 while (HAL_I2C_IsDeviceReady(hi2c, dev_addr, 5, 10) != HAL_OK) { // 手册规定最长 5ms,但实际环境温度不同,轮询最可靠 } return HAL_OK; }

另一个坑是页写跨页问题。AT24C256 每页 64 字节,写入数据跨越页边界时,EEPROM 会把地址回卷到页首,覆盖页首原有数据。驱动层必须做一个长度判断,计算当前地址到页末的剩余空间,超过部分拆成两次写操作。这个逻辑不能依赖应用层处理,否则调用方一多很容易漏。

6.2 NOR Flash 超时与误擦除,状态机盯紧 BUSY

FPGA 读 NOR Flash 超时,排起来往往比较玄学。我遇到过一次:常温下一切正常,低温 -20°C 下 Flash 读配置偶尔失败,系统偶尔起不来。排查后发现是 FPGA 的 Flash 控制器发出读命令后没有做足够长的 dummy cycle,低温下器件的输出建立时间变长,时序裕量不足。解决办法是把 dummy cycle 参数做成寄存器可配,低温环境调大 2 到 3 个周期。

如果使用 Quad SPI 模式,HOLD#(IO3)和 WP#(IO2)引脚的接线也要确认。许多工程师会把这两个引脚上拉到 VCC,防止误触发。我看到过因为 HOLD# 被意外拉低导致 Flash 进入 hold 状态、整个 Flash 看起来“卡死”的案例,连擦除都无法完成。这是单引脚问题导致的整芯片故障,排查难度极高。

NOR Flash 误擦除的另一个原因是上电顺序。如果 Flash 的 VCC 和片选信号在 Power-On Reset 过程中出现竞争,Flash 可能把随机的总线数据当作命令。稳妥做法是在片选引脚加 10kΩ 上拉电阻到 VCC,并在 FPGA 内部配置完成后才释放片选控制,确保上电时不出现不确定状态。

6.3 SD 卡初始化失败,多数是时序和电平

SD 卡初始化失败的排查,大概率是电平或时序协议问题。四线 SDIO 模式下,卡在初始化阶段要用较慢的时钟(400kHz 以下)发送 CMD0/CMD8/ACMD41 等命令,等卡进入就绪状态后再切到高速时钟。很多工程师为了省事,上来就用高频时钟发命令,很多卡直接不响应或超时。

卡进程卡死在 ACMD41 循环里时,另一个常见原因是卡检测(CD)信号和供电不同步。卡座带电源控制的话,插入检测后应先给卡供电,延时几十毫秒让卡完成上电,再开始初始化。STM32 的 SDMMC 外设有些还需要配置时钟使能,如果忘记打开时钟,直接读写寄存器会得到全零结果。

还有一点容易被忽略:FatFS 挂载失败不等于 SD 卡坏了。量产现场出现过一种情况:工程师用读卡器把 SD 卡格式化成 NTFS,设备上 FatFS 挂载失败,日志写不进去。我在代码里加了一个处理流程:挂载失败时尝试重新格式化(通过底层disk_ioctl的 CTRL_FORMAT),格式化前用魔数和文件系统类型做检测,避免把不该格式化的盘也格式化掉。这个功能代码量不大,但对现场运行帮助极大。

6.4 现场排查顺序清单

做硬件和嵌入式调试,最忌讳一上来就怀疑芯片坏了。我自己的排查顺序基本固定:先查供电、再查时钟与复位、然后查接口时序、最后查芯片配置和寄存器。对本文提到的三路存储,逐项排查的点如下表:

现象优先排查方向重点关注
EEPROM 读写偶尔丢失I2C 时序、页写跨页、上拉阻值写后轮询 ACK、拆分跨页写
EEPROM 完全无法写入WP 引脚状态、地址冲突WP 是否悬空、A0-A2 地址是否冲突
NOR Flash 读取数据不对Dummy cycle、Quad SPI 接线、电平匹配HOLD#/WP# 是否上拉、时序裕量
NOR Flash 擦写超时写状态机、电源去耦是否等待 BUSY 释放、芯片供电
SD 卡初始化失败初始化时钟、CD 信号、供电时序初始化时钟是否降至 400kHz、是否先供电后初始化
SD 卡文件系统损坏掉电保护、缓冲区、DMA掉电时是否在写、缓冲区是否 DMA 可达

这个表贴在我自己的调试笔记里,每次项目里有人反馈存储异常,先按这个清单筛查,大概率能找到问题。大部分“随机性故障”其实都有规律,只是需要从时序、电源、配置三个角度耐心排查。


这套分级存储在多个工业控制器项目里跑稳定之后,我最大的体会是:存储设计不是选芯片那么简单,而是要建立完整的数据生命周期管理思维。每种介质有它的脾气,工程师的工作不是让所有数据都跑在最快的介质上,而是让每类数据待在自己最合适的地方,并在硬件和软件两个层面为最坏情况做好预案。

如果你正在做类似的产品,我的建议是先把数据分类表画出来,再选介质,再画电路,最后才写驱动。顺序反了,后面大概率要返工。另外,双备份和 CRC 不是成本,是工业设备的底线,省这一块,将来一定会在现场加倍还回来。