一台工业控制器在现场运行,最怕的不是某个传感器读数异常,而是断电重启之后,参数配置全部归零、运行日志乱成一团。我做过好几个基于STM32+FPGA架构的控制器和边缘网关项目,早期一直有种错觉:数据存储嘛,无非就是选一颗好一点的存储芯片,把该写的东西写进去。直到有一套设备在现场因为日志区写入频繁,三个月就把NOR Flash的一个扇区写穿了;还有一次,工人拔SD卡的时候正好在写文件,插回来FAT表直接损坏。这些事让我彻底想明白一个道理——工业控制器的数据存储必须分级,而且每一级存储的选择和策略,都要在画原理图之前就定清楚。
这套方案里我最终采用的是EEPROM、NOR Flash、SD卡三级存储架构。EEPROM存参数配置,NOR Flash存运行日志,SD卡存采集数据和大文件。三者各司其职,配合STM32和FPGA的分工协作,才能把"数据怎么存"这个问题真正解决。这篇文章我会把存储选型逻辑、硬件连接、读写时序、掉电保护、磨损均衡这些细节从头到尾梳理一遍,适合正在做工业控制器、数据采集终端或者边缘网关的硬件工程师参考,也适合刚入门想搞懂存储系统设计的同学。
1. 工业控制器的数据不能一锅炖——先分清哪些数据需要落地
1.1 先列一张"数据清单",再谈存储选型
在设计存储方案之前,我习惯先把系统里所有需要非易失保存的数据列出来,而不是直接打开选型手册找芯片。很多新手一上来就纠结"用SPI Flash还是用SD卡",但如果不清楚要存的数据长什么样,选型就是拍脑袋。
拿一套典型的工业控制器来说,至少要面对这几类数据:
- 设备身份信息:序列号、硬件版本、生产日期,出厂时写入一次,之后基本不再变化,量非常小,几十字节就够。
- 标定参数与校准系数:ADC的零点偏移、温漂系数、传感器标定值,这类数据偶尔会因为重新标定而更新,典型量级是几百字节。
- 运行参数与配置:PID控制参数、通信地址、波特率、报警阈值、用户自定义配置,现场调试阶段会频繁改动,量级同样在几百字节到几KB。
- 系统运行日志:上电记录、故障告警、操作记录、状态变更事件,数据持续累积,随时间增长,少则几KB,多则几十MB。
- 采集数据与波形记录:如果控制器带高速ADC采样功能(比如振动监测、电能质量分析),FPGA侧持续产生大量数据,需要记录波形或者统计特征,数据量从几十MB到数GB都有可能。
- 固件升级包:现场升级时临时存放的新固件,通常几MB到几十MB,升级完成校验通过后可以删除。
把这六类数据摆在一起,你会发现它们的共性其实很少。有的要频繁改,有的几乎不碰;有的几个字节,有的几个GB;有的丢了能忍,有的丢一次就是事故。用一块芯片通吃所有需求,要么容量浪费,要么寿命耗尽,要么掉电保护做不过来。
1.2 按"变化频率+数据量+可靠性"三个维度给数据分桶
我对数据分桶的标准有三个维度:变化频率、数据量、可靠性要求。三个维度组合起来,正好对应三种不同的存储介质。
| 数据类型 | 典型容量 | 变化频率 | 掉电要求 | 可靠性要求 |
|---|---|---|---|---|
| 序列号/校准系数 | 几十~几百字节 | 极低 | 必须保留 | 极高 |
| 运行参数/配置 | 几百字节~几KB | 低 | 必须保留 | 高 |
| 系统日志 | 几KB~几十MB | 中 | 尽量保留 | 中高 |
| 高速采集数据 | 几十MB~GB | 高 | 尽量保留 | 中 |
| 固件升级包 | 几MB~几十MB | 极低 | 尽量保留 | 高(写坏则无法升级) |
变化频率直接决定了介质的擦写寿命是否够用。EEPROM擦写寿命在100万次左右,NOR Flash普遍是10万次,SD卡内部的NAND Flash也是10万次量级。如果有一个参数每次上电都要记录,频率很高,你把它放在NOR Flash里,按10万次寿命算,设备只要上电3万次就可能出问题——实际使用中很多控制器一天开关机十几次。数据量则决定了介质容量和文件系统的复杂度。可靠性要求决定了你要不要做双份备份、CRC校验、掉电保护这些策略。
打个比方,数据存储就像收拾实验室工位。重要的、经常摸的小工具放在手边的抽屉里(EEPROM),日常要用的文档放文件柜中层(NOR Flash),海量的历史记录和测试数据直接归档到仓库档案架上(SD卡)。不分级,什么东西都往同一个抽屉里塞,既放不下也找不着。数据分级之后,每级存储的任务就清晰了:EEPROM负责"必须存、很小、常改"的参数,NOR Flash负责"持续写、中等量、要可靠"的日志,SD卡负责"大量、按文件管理、可容忍偶发丢失"的采集数据。
2. 硬件分工怎么定:STM32管参数、FPGA管高速数据流的存储协作
2.1 为什么是双芯架构,而不是单芯片一把梭
工业控制器选STM32+FPGA的组合,核心原因在于控制面任务和数据面任务互相撕扯。STM32擅长系统管理、通信协议、参数处理这些逻辑复杂的事情,FPGA擅长并行采样、高速数据搬移、实时信号处理。存储任务也一样分裂:低速小数据量的存储管理适合STM32,高速大流量的数据搬运和缓冲必须交给FPGA。
如果只用一颗STM32,高速ADC采样过程会频繁打断CPU。要知道一个5MSPS、16位的ADC,一秒钟就是10MB数据,STM32要处理数据还要存卡,中断压力大到没法做其他任何事情。如果只用FPGA,文件系统、协议解析、参数管理这些逻辑实现起来代码量巨大,调试难度也高。双芯架构等于把"管数据的人"和"搬数据的人"分开,各干各的强项。
在存储领域的分工是这样的:
- STM32负责管理低速存储介质:EEPROM通过I2C接口、NOR Flash通过SPI接口,这两种介质的访问速率都在几十MHz以下,STM32完全吃得消。参数读写、日志管理、文件系统逻辑都由STM32完成。
- FPGA负责高速数据的缓冲和前置处理:ADC数据先进入FPGA的FIFO或DDR缓存,按批次吐给STM32,或者由FPGA直接控制DDR做多通道数据汇聚。FPGA不直接管文件系统,它管的是"把原始数据按批次稳妥地送到下一个存储环节"。
- STM32和FPGA之间通过自定义协议通信:可以是一对SPI接口、UART、或者并行FIFO,具体看数据量和实时性要求。数据量不大用UART或SPI就够,动不动几MB的突发数据建议上并行FIFO或者DDR共享总线。
2.2 STM32存储域:慢数据的归宿很清晰
STM32这边的存储任务相对集中,主要是三类:EEPROM的参数读写、NOR Flash的日志记录、SD卡的文件系统管理。为什么把这三块都放在STM32这边?因为它们的共同特点是操作节奏慢,每次操作间隔在几十毫秒到几百毫秒量级,STM32的阻塞式访问完全能接受,代码也好写、好调试。
EEPROM用I2C接口,硬件电路简单,两根线加上拉电阻就完事。参数变更时,上位机下发命令给STM32,STM32更新内存副本,然后按页拆分写入EEPROM。NOR Flash用SPI接口,日志事件发生时,STM32把事件结构体写入当前日志块,顺便更新块头信息。SD卡用SDIO接口,在数据归档或者文件导出时才会访问。
这个域的调试经验是:STM32代码里最好做一个统一的存储抽象层,所有读写EEPROM、NOR Flash、SD卡的函数都走同一套接口,比如store_param_write()、log_event_write()。这样即使后面换了Flash型号或者改了介质分配,应用层代码不用大改。
2.3 FPGA存储域:快数据需要缓冲和搬运的中转
FPGA侧的存储核心是数据中转。高速ADC的数据是连续不断的,你不能让STM32随叫随到,也不能让SD卡跟上ADC的实时速度。所以FPGA要做的是在数据源头和存储介质之间加一个大缓冲。
最简单的路径是:ADC数据流入FPGA内部FIFO,FIFO快满时触发一次搬运,把数据写到外部DDR缓存。DDR里攒够一批(比如512KB或者1MB),FPGA拉高"数据就绪"信号,STM32闻到信号后用DMA把这一批数据搬走,再写入SD卡。为什么FPGA要先过一遍DDR?因为FPGA内部RAM资源有限,一个中等规模的FPGA内部块RAM也就几百KB,撑不住连续高速采样的缓冲需求。外挂一颗DDR3或者DDR3L,成本不高,但缓冲能力完全不一样。
如果项目涉及多通道ADC同步采样,DDR缓存几乎是必需品。多路数据同时进来,FPGA内部需要一个多端口仲裁逻辑来管理DDR读写,这正好是FPGA擅长的事情。STM32只负责"在合适的时候取走已经就绪的数据块",完全不会因为数据流量大而被拖死。热词里提到的"基于FPGA的多端口DDR读写程序",说的就是这类场景的开发。
3. EEPROM里存参数:I2C读写看似简单,页写限制和掉电写坏才是真坑
3.1 器件选型与I2C总线设计要点
EEPROM这一级,我的选型逻辑是:容量根据参数总量来决定,2Kbit到64Kbit基本覆盖绝大多数场景。型号上最常用的是AT24C02/04/08系列,Microchip的AT24系列、ST的M24系列都可靠,而且引脚兼容,供应链有保障。
I2C总线设计有几个细节值得注意:
- 上拉电阻:典型值是2.2kΩ到4.7kΩ,取决于总线上挂了多少设备和走线长度。电阻太小灌电流大,电阻太大上升沿变缓,影响通信稳定性。
- 器件地址:EEPROM的A0/A1/A2引脚决定I2C地址,多颗EEPROM或者和其他I2C设备共总线时要注意区分。
- 通信速率:EEPROM支持100kHz和400kHz模式,控制器里通常选400kHz,但要留意总线上最慢设备的限制。
STM32和EEPROM的接口有两种做法:硬件I2C外设和GPIO模拟I2C。我的经验是:如果EEPROM只在设备启动、参数变更时才读写,GPIO模拟更加简单可控。硬件I2C外设在某些STM32系列上遇到总线错误后状态机会卡住,需要重新初始化才能恢复,排查起来比较烦。模拟I2C时序完全由自己掌握,出了问题也能直观定位。
3.2 分页写入和边界跨越问题
EEPROM写入有页写限制,这是最容易踩的坑。以AT24C02为例,一个页是8字节,一次页写最多只能写一个页。如果写入长度跨越页边界,芯片会自动回卷到本页开头覆盖数据,结果就是你看到的前8字节对、后面的字节全乱。
处理办法很简单:把要写入的数据按页边界对齐,逐页拆分写入。我后来一直沿用的代码逻辑是这样的:
#define EEPROM_PAGE_SIZE 8 // AT24C02 为8字节,AT24C64为32字节 #define EEPROM_WRITE_DELAY_MS 5 // 内部写周期时间 uint8_t eeprom_write_bytes(uint16_t addr, uint8_t *buf, uint16_t len) { while (len > 0) { uint16_t page_remaining = EEPROM_PAGE_SIZE - (addr % EEPROM_PAGE_SIZE); uint16_t chunk = (len < page_remaining) ? len : page_remaining; eeprom_page_write(addr, buf, chunk); delay_ms(EEPROM_WRITE_DELAY_MS); // 等待写周期完成 addr += chunk; buf += chunk; len -= chunk; } return 0; }读操作没有页限制,可以连续读多个字节,但要注意I2C发送读命令时的地址格式,连续读模式下每收到一个字节后要回复ACK,读完最后一个字节要回NACK再发STOP,否则从机不知道什么时候结束。
3.3 写入掉电保护与磨损均衡
EEPROM的擦写寿命是100万次,听起来很多,但工业现场的设备往往7x24小时运行,某些计数器、上电次数、运行时间这类参数会频繁更新。一旦写入过程中掉电,EEPROM的存储单元可能处于未定义状态,参数就会损坏。
我常用的保护策略是双份存储+标志位:
- 参数区分为A区和B区,两份镜像存储。
- 每个参数带CRC校验,CRC值随数据一起存储。
- 写入时先把目标的标志字节改成"无效",写完数据和CRC后,再把标志改成"有效"。
- 上电读取时先检查A区,如果标志“有效”且CRC正确就用A区;否则检查B区,两个区都损坏才恢复出厂值。
磨损均衡方面,如果某个参数确实高频更新,可以把地址分散到多个单元轮换。比如上电次数计数器,在EEPROM里划出32个地址,每次写入地址加1,写满32个地址后再回到第一个地址。这样同样数量的写入次数被摊到32个单元上,单点寿命压力小了很多。
4. NOR Flash里存日志:擦除周期、磨损均衡和掉电中断的应对思路
4.1 为什么选NOR Flash而不是NAND
工业控制器里的运行日志、事件记录,我优先选NOR Flash而不是NAND。原因有几个:
- 随机字节读取:NOR Flash支持按字节随机读取,甚至可以XIP(就地执行),做代码存储也没问题。
- 擦除粒度小:NOR Flash的最小擦除单位是扇区(通常是4KB),比NAND的最小擦除块(通常128KB以上)小得多,日志这类小块数据存储起来更方便。
- 可靠性好:NOR Flash的坏块管理简单,很多型号完全不需要处理坏块问题,NAND就必须要维护坏块表和ECC。工业级NAND Flash虽然也有,但管理复杂度显著上升,对日志这种场景来说性价比不高。
- 缺点是容量和写入速度不如NAND:但对几MB到十几MB的日志来说完全够用。
选型上常见的是W25Q16/32/64/128、GD25Q系列、MX25L系列,SOIC-8或WSON-8封装,电路设计简单。
4.2 SPI时序和"先擦后写"的底层逻辑
NOR Flash通过SPI接口访问,连线就是CS/CLK/MOSI/MISO四根线。电源去耦电容不可省,WP引脚(写保护)和HOLD引脚要注意处理,有些设计直接悬空会出诡异问题,最好明确上拉或者由GPIO控制。
写入流程要记住一个核心规律:Flash编程只能把1写成0,要把0恢复成1只能靠擦除。所以"修改一个字节"这个概念在NOR Flash里是不存在的,你只能先擦除整个扇区,再把需要保留的数据连同修改后的数据一起写回去,这个操作叫读-改-写。
一次完整的写入流程包括:
- 写使能(WREN,命令0x06)
- 页编程(命令0x02),每次最多写入256字节
- 轮询状态寄存器的WIP位(读命令0x05),等待内部编程完成
- 如果目标区域已有数据,需要先执行扇区擦除(命令0x20)或块擦除(命令0xD8)
SPI时钟速率方面,W25Q系列支持到104MHz甚至更高,但在STM32上我一般配置到几十MHz就够了,因为日志写入的频率不高,瓶颈不会在SPI时钟上。
4.3 环形日志与磨损均衡策略
日志是持续累积的,不能写满了再停机处理。我常用的方案是环形日志:
- 固定划分一段空间,比如4MB的NOR Flash,分成128个32KB的日志块。
- 维护两个指针:当前写指针和日志起始指针。写满一圈后覆盖最旧的日志块。日志块头存放序号、时间戳、数据长度、CRC校验值。
- 设备启动时扫描日志块头,按照序号重建日志索引。
磨损均衡在环形日志里天然实现了——所有日志块轮流被写入,不会出现某一个扇区被频繁擦除而其他扇区闲着的情况。虽然NOR Flash有10万次擦写寿命,128个块轮流用,实际寿命大大延长。如果日志写入频率很高,还可以在块头增加一个擦写次数统计,每次擦除时递增,方便做寿命预警。
4.4 擦除断电后的恢复方案
NOR Flash最怕擦除过程中掉电。擦除操作是要把整个扇区所有字节都写成0xFF,如果中途断电,扇区内容是没有定义的——可能是半擦除状态,也可能是随机数据。下一次上电读日志,就很有可能读到乱码,甚至因为块头信息损坏导致整个日志区不可用。
A.双标志位方案:
每个日志块头部放两个标志:BLOCK_DIRTY和BLOCK_IN_USE。写入日志前先把目标块标记为DIRTY,写完日志数据并校验通过后,再标记为IN_USE。上电扫描时,如果发现有块处于DIRTY状态,说明上次写入未完成,直接擦除重写或者丢弃该块。
B.双缓冲交替方案:
把日志区分为两个大区,交替写入。A区写完后写B区,B区写完后回头擦除A区重新写。这样不管什么时候掉电,总有一个区保存着完整的日志。代价是存储利用率减半,但可靠性大幅提升。
结合STM32的PVD(可编程电压检测)功能,可以在检测到电源跌落时进入紧急处理流程:停止日志写入、把缓存刷到NOR Flash、更新标志位。虽然掉电瞬间的操作窗口只有几毫秒到十几毫秒,但足够做最关键的保护动作。
5. SD卡里存采集数据:文件系统、掉电保护与FPGA高速吞吐路径
5.1 SDIO和SPI模式:速度、引脚与代码量的权衡
SD卡在嵌入式里有两种接法,SDIO模式和SPI模式。
- SDIO模式:4位数据线并行传输,理论上速度能到几十MB/s,适合持续大流量写入,STM32自带SDIO外设,配合DMA效率很高。
- SPI模式:引脚少只有CS/CLK/MOSI/MISO四根,但速度上限大概在25MHz时钟,实际吞吐量受到协议开销影响会更低,适合只存配置文件、不涉及大流量数据的场景。
在STM32+FPGA这套架构里,如果采样数据要落到SD卡,我建议直接上SDIO模式。工业控制器的PCB上多几根线不是问题,数据吞吐能力却差了一个数量级。另外要注意SD卡初始化有固定时序:上电后要给至少74个时钟周期的空闲脉冲,然后发CMD0进入初始化流程。很多新手遇到的"SD卡识别不了"问题,多半是初始化时序没按规范走。
5.2 FatFS在嵌入式里的配置细节
文件系统方面,FatFS是嵌入式的事实标准,配置好之后可以像操作普通文件一样管理SD卡数据。我一般这样配置:
- 打开长文件名支持(
FF_USE_LFN=2,带动态内存分配),方便生成带时间戳的文件名。 - 设置
FF_FS_RPATH=1,支持相对路径。 - 每个挂载的卷分配一个工作区,
f_mount之后才能访问。 - 打开文件写入后,一定要及时调用
f_sync()或者f_close(),否则数据只存在FATFS的缓存里,掉电就丢了。
还有一个容易被忽略的点:SD卡写入速度不是恒定的,旧卡、碎片化的卡写起来会明显变慢。如果系统对写入实时性有要求,应该采用先缓存再批量落盘的策略——FPGA先把数据攒到DDR里,攒到一定程度(比如512KB)再通知STM32一次性写入SD卡。这样减少了文件系统的寻道和碎片开销,吞吐量明显更稳定。
5.3 掉电损坏和拔卡问题的防护
SD卡直接掉电很容易损坏FAT表。现场调试这种问题遇到过太多次:工人直接拔卡拷数据,卡再插回去就挂载失败。从硬件到软件要做几道防护:
- 卡检测引脚:SD卡座的CD引脚接STM32的外部中断,检测到卡拔出就立刻停止写入任务,把已经缓存的数据先落盘。
- 文件轮换策略:日志类文件不要一个文件写几个月,单个文件过大一旦损坏损失惨重。建议按时间戳分文件,比如每小时或每天生成一个新文件。
- 双文件区轮换:如果数据非常重要,可以设计两个文件,A文件写完后写B文件,B文件写完后回头覆盖A文件。启动时校验哪个完整就用哪个。
- 定期卸载和挂载:STM32检测到文件系统异常时,可以提示用户重新格式化,或者自动从备份区恢复出厂配置。
5.4 FPGA侧的高速数据写入路径
FPGA侧处理高速采集数据并写入SD卡的路径,是我在做这套方案时花时间最多的地方。
以5MSPS、16位ADC为例,一秒钟产生10MB数据。这个速率虽然SD卡理论能扛住,但文件系统开销、SD卡磨损均衡、写放大等都会拖慢实际写入速度。更麻烦的是,STM32不能卡死在写卡上不管其他任务。所以数据路径需要拆成三段:
- ADC原始数据进入FPGA内部FIFO,FIFO快满时由FPGA内的控制逻辑把数据搬运到外部DDR缓存。
- DDR缓存攒够一个批次后,FPGA拉高"数据就绪"中断信号。这个批次大小根据项目需要调整,一般256KB到1MB。
- STM32收到中断后,通过DMA把这一批数据搬到自己的内存,再调用FatFS写入SD卡文件。
在这个流程里,FPGA不碰文件系统,它只负责"把数据从ADC挪到DDR、按批次通知STM32"。文件系统的复杂度全部由STM32承担。为什么这样设计?因为文件系统逻辑一旦出问题,在FPGA里调试极其痛苦,而STM32上跑FatFS是成熟方案,有什么问题直接仿真器单步就能定位。如果数据量真的大到STM32搬运不过来,再考虑让FPGA直接通过SDIO写裸扇区,或者干脆上eMMC,但那是更大系统才需要的方案。
6. 分级存储映射表:一份可以直接拿去用的数据落盘策略
6.1 分级映射总表与数据流向
把前面三个存储层级的内容整合成一张总表。这张表我在原理图设计时就会直接打印出来放在工作台上,随时对照。
| 数据类别 | 存储介质 | 接口 | 更新频率 | 容量规划 | 可靠性措施 |
|---|---|---|---|---|---|
| 序列号 | EEPROM | I2C | 极低 | 64B | CRC校验 |
| 标定参数 | EEPROM | I2C | 低 | 256B | 双份镜像+CRC |
| 运行参数 | EEPROM | I2C | 低 | 512B | 双份镜像+CRC |
| 运行日志 | NOR Flash | SPI | 中 | 4MB~16MB | 环形覆盖+DIRTY标志 |
| 故障录波 | NOR Flash | SPI | 中 | 预留 | 扇区级保护 |
| 波形/采样数据 | SD卡 | SDIO | 高 | 依卡容量 | 文件定期f_sync |
| 固件升级包 | SD卡 | SDIO | 极低 | 依卡容量 | 升级完成后校验 |
数据流向可以分成几条清晰的链路:
- 参数修改链路:上位机下发配置 → STM32更新内存副本 → 按页拆分写入EEPROM镜像区 → 标志位翻转。
- 日志记录链路:系统事件产生 → STM32打包事件结构体 → 写入NOR Flash当前日志块 → 更新块头序号。
- 采样数据链路:ADC连续采样 → FPGA内部FIFO → 搬移至DDR缓存 → 批次就绪中断 → STM32 DMA搬运 → FatFS追加写入SD卡文件。
6.2 数据迁移与状态降级逻辑
分级存储还有一个好处:数据可以在层级之间流动。
- NOR Flash里的老旧日志达到一定容量后,可以归档到SD卡,释放NOR Flash空间给新日志。
- 固件升级包优先放SD卡,校验通过后再把固件内容拷到NOR Flash(如果NOR容量够且支持XIP),最后跳转执行。
- SD卡没有插入或者挂载失败时,采集数据降级为只缓存不落盘,或者把数据量较小的统计特征写入NOR Flash;SD卡恢复后,再把缺失的记录补写过去。
这个"降级和恢复"逻辑在做工业控制器时非常实用。现场把SD卡拔走拷数据是常态,系统不能因为卡不在就罢工。我一般用一个简单的状态机管理存储子系统:
- 状态A(默认):只有EEPROM和NOR Flash可用,系统正常运行,采集数据只缓存或丢弃。
- 状态B(增强):检测到SD卡并挂载成功,进入正常记录模式,数据全部落盘。
- 状态C(异常):SD卡写入失败,自动降级为状态A,并在NOR Flash日志里记录一次降级事件。
7. 写在最后:几处改了三版才算稳定的存储细节
7.1 一例EEPROM页写跨越引发的数据错乱
之前调试一套控制器时,配置参数一共80字节,我在写EEPROM时图省事,直接一次性发80字节给AT24C02。结果读回来发现前8个字节正确,后面的全部错乱。查了半天才发现AT24C02的页大小是8字节,我一次跨了10个页的边界,芯片自动回卷覆盖了本页开头的数据。从那以后,我把"按页拆分写入"写成了所有EEPROM驱动的标配,不管是8字节一页还是32字节一页,统一走拆分逻辑。这个教训也说明:存储芯片的数据手册里那些不起眼的"Note",往往才是真正会咬人的地方。
7.2 NOR Flash擦除掉电后的恢复实测
有一轮老化测试里,我在设备运行过程中反复插拔电源,重启后发现日志区全是0xFF,但系统反复启动失败,连正常的事件记录都写不进去。定位到最后,发现是疑似某次掉电正好落在扇区擦除的窗口中间,块头标志变成了未定义状态,上电扫描时读到了错误信息,把后续的写入逻辑全带偏了。加了DIRTY/IN_USE双标志和启动扫描后,这个问题彻底消失了。这个测试让我意识到,电源跌落瞬间的处理才是存储可靠性的最后一道关卡。STM32的PVD中断要尽早配置,检测到电压跌落先停中断、刷缓存、标记存储状态,哪怕只有几毫秒,也能避免很多疑难问题。
7.3 SD卡热拔插的现场教训
某项目里工人习惯不等系统停止写入就直接拔SD卡拷数据,结果卡装回去后FatFS挂载失败,现场的采集数据全没了。我当时的处理是硬件上增加卡检测引脚,软件上在FatFS配置里加了个"拔卡锁",检测到CD信号变低就立刻停止所有文件操作。同时把单文件长时间写入改成了按小时轮换的小文件,即使某个文件损坏,损失也就一个小时的数据。这个设计后来又经历了一次现场验证,才让我确信文件系统的稳定性问题,七成要靠使用习惯和冗余策略来兜底,而不是指望卡自己不出错。
做了几轮改版之后我的体会是:存储方案不是选一颗芯片那么简单,而是要把数据分好类,再设计出多套互相补充的落盘路径。EEPROM、NOR Flash、SD卡这三者单独看都是老技术,但在工业控制器里,稳定才是第一位的,这套三级架构看似传统,却已经在好几个项目里扛住了现场的各种折腾。后面如果碰到新的存储坑,我再来单独开一篇聊。