尧图精选

工业控制器分级存储方案:STM32+FPGA与EEPROM/NOR/SD卡选型设计

🕒 发布时间:2026/11/6 1:03:32 📁 来源:尧图网络
工业控制器这类设备有个很现实的问题它不像消费电子那样可以随便重启大法糊弄过去一旦上了产线参数丢了、日志断了、配方没了轻则停机重调重则整批产品报废。所以存储方案的设计从一开始就不是能存就行而是要在掉电可靠性、写入寿命、容量成本、读写速度这几个互相拉扯的指标里找一个平衡点。我这些年做过的板子里单靠一颗EEPROM打天下的方案基本都活不过两年而全上SD卡又会在振动和掉电场景下翻车。比较稳妥的做法是用STM32做管理核心、FPGA做高速数据通道再配一套EEPROM、NOR Flash、SD卡三级分层的存储架构让不同性质的数据各归其位。这篇就围绕这套分级存储方案把选型逻辑、硬件连接、分区规划、驱动要点和实测踩过的坑一次讲透适合正在做工业控制器、边缘网关、通信测试终端这类项目的硬件和嵌入式工程师参考。1. 为什么工业控制器必须做分级存储1.1 单一存储介质在工业场景下的三种死法先说清楚为什么不能只用一种存储。我见过太多项目在实验室跑得好好的一到现场就出问题归根结底是没搞清楚工业环境和办公环境的差别。第一种死法是掉电写坏。EEPROM和NOR Flash在写入过程中如果突然断电正在写的那个扇区或页可能处于半写状态数据直接变成乱码。工业现场电网波动、急停断电都是家常便饭如果关键参数只存在一颗没有掉电保护机制的存储里早晚出事。第二种死法是写入寿命耗尽。NOR Flash的擦写次数通常在10万次量级SD卡里的NAND更惨消费级卡可能只有几百到几千次擦写寿命。如果拿SD卡去存每秒都在更新的运行计数或者高频日志几个月就能把某些块写废。而EEPROM虽然单字节可以擦写百万次但容量小、速度慢扛不住大数据量。第三种死法是环境应力。SD卡是插拔式结构金手指在振动环境下会接触不良工业现场的高低温、粉尘、湿度也会加速氧化。我有个做通信测试终端的朋友设备在机房跑了一年SD卡座簧片疲劳接触电阻变大读写开始随机报错查了整整两周才定位到是卡座的问题。所以分级存储的本质是按数据的性格分配住所变化慢但要求绝对可靠的关键参数住EEPROM需要频繁读写、容量中等的配置和日志住NOR Flash大容量、低频访问的历史数据和文件住SD卡。1.2 三类数据的生命周期差异决定了分层逻辑把数据按生命周期拆开看分层逻辑就非常清晰了。第一类是出生即定型的静态数据比如设备序列号、硬件版本、校准系数、出厂标定值。这类数据写入次数极少可能整个生命周期就写几次但要求几十年不丢掉电绝对不能损坏。EEPROM是天然的选择它支持字节级擦写不需要整页擦除写入功耗低数据保持时间通常标称40年以上。第二类是周期性变化的配置与运行数据比如PID参数、通信地址、报警阈值、运行小时数、故障记录。这类数据可能每天写几次到几百次容量需求在几十KB到几MB之间。NOR Flash的扇区结构适合做这种块状更新配合磨损均衡和掉电保护能撑很久。第三类是海量且低频读取的历史数据比如波形采样记录、批量测试结果、固件升级包、导出报表。这类数据动辄几十MB到几GB写入频率不高但容量要求大SD卡或eMMC是唯一在成本上能接受的选择。这里有个容易被忽略的点数据的写入频率和读取频率要分开评估。有些数据写得少但读得极频繁比如校准系数每次上电都要读这种放EEPROM没问题因为EEPROM读操作几乎不损耗寿命。但如果是频繁读写的日志索引就要考虑放到RAM里做缓存定期刷到Flash减少实际擦写次数。1.3 STM32FPGA分工谁管存储谁管数据流这套架构里STM32和FPGA的分工不是随便定的而是由两者的能力边界决定的。STM32的优势在于控制逻辑、协议栈、文件系统和事务管理。它有成熟的HAL库、FatFs文件系统支持、丰富的通信外设适合做存储管家——负责决定什么数据写到哪一级、什么时候触发刷写、掉电时如何保护。它的短板是并行处理能力弱面对高速ADC采样流或者多通道数据汇聚时容易成为瓶颈。FPGA的优势在于并行数据采集、实时预处理和高速接口。它可以同时挂多路ADC、做实时滤波和打包然后通过高速总线把数据推给STM32。但FPGA本身不擅长做复杂的文件系统和事务管理让它直接管SD卡的文件分配表会非常痛苦。所以合理的分工是FPGA负责生产数据STM32负责归档数据。FPGA把采集到的原始数据做初步打包通过SPI或FSMC总线传给STM32STM32再根据数据类型决定是缓存、写NOR Flash还是落盘到SD卡。EEPROM则通常直接挂在STM32的I2C总线上由STM32独占管理FPGA一般不直接碰它避免总线竞争。这个分工带来的一个实际好处是存储策略的调整只需要改STM32的固件不用动FPGA逻辑。工业项目后期需求变更很常见这种解耦能省下大量重新综合FPGA的时间。2. 三种存储介质的硬件选型与电路要点2.1 EEPROMI2C接口的细节决定可靠性EEPROM选型看起来简单其实坑不少。常用的型号比如AT24C系列、CAT24系列容量从2Kbit到2Mbit都有。工业场景我一般推荐选带写保护引脚WP和独立地址引脚的型号方便做硬件级写保护和多片级联。接口上I2C是最常见的选择两根线SCL/SDA就能挂多片。但这里有几个硬件细节必须注意上拉电阻的取值。I2C是开漏输出必须加上拉。阻值不是随便选4.7K就完事要根据总线电容和速率算。标准模式100kHz下总线电容不超过400pF时4.7K通常没问题但如果走线长、挂的设备多电容上去了就得减小阻值到2.2K甚至1K。我遇到过一块板子I2C偶尔通信失败最后发现是上拉用了10K上升沿太缓在长走线下被干扰误判成起始条件。地址冲突。AT24C系列用A0/A1/A2三个引脚决定低三位地址最多挂8片。如果项目里同时有EEPROM、温度传感器、RTC都走I2C一定要提前把地址表列出来避免撞车。我习惯在原理图上直接标注每颗器件的7位地址评审时一眼就能看出冲突。写保护策略。WP引脚拉高时EEPROM进入只读这个引脚不要直接接地了事。我的做法是接到STM32的一个GPIO上正常运行时拉低允许写设备进入出厂锁定模式或者检测到异常时拉高保护。这样即使固件跑飞也不会误写关键标定数据。去耦电容。每片EEPROM的VCC引脚旁边必须放0.1uF陶瓷电容位置尽量靠近引脚。EEPROM在写入瞬间电流会有尖峰去耦不好会导致写入失败或者数据错误。2.2 NOR FlashSPI NOR的容量与扇区规划NOR Flash分并行和SPI两种。并行NOR速度快但引脚多现在工业控制器里更常见的是SPI NOR比如W25Q系列、S25FL系列容量从1MB到128MB都有。选型时重点看三个参数扇区大小、擦写寿命、是否支持4字节地址。扇区大小通常是4KB有些型号支持更小的1KB子扇区。扇区越小做磨损均衡时粒度越细但管理开销越大。我一般选4KB扇区的标准型号配合STM32的Flash抽象层做管理。擦写寿命方面SPI NOR通常标称10万次。这个数字看着不少但如果某个扇区被频繁更新比如每秒写一次运行状态一天就是86400次一天多就废了。所以必须做磨损均衡不能盯着一个地址死写。容量超过16MB的NOR Flash会用到4字节地址模式需要发专门的进入命令比如W25Q系列的0xB7命令。如果固件里没处理这个访问高地址区就会出错。这个坑我在一个32MB的项目里踩过读出来的数据全是0xFF查了半天才发现是地址模式没切。电路上SPI NOR的CS片选、CLK、MOSI、MISO四根线要尽量等长CLK走线避免跨分割。如果和SD卡共用SPI总线一定要用独立的片选并且在切换设备时确保前一个设备的CS已经拉高否则会出现两个设备同时驱动MISO的冲突。2.3 SD卡工业级与消费级的真实差距SD卡这块我要多说几句因为它是整个方案里最容易出问题的一环。首先是卡的选择。消费级SD卡比如手机里用的那种在工业环境下寿命和可靠性都不够。工业级SD卡比如Swissbit、Apacer、ATP的工业系列用的是SLC或pSLC NAND擦写寿命能到几万次工作温度范围宽还带掉电保护电容。价格贵好几倍但该花还得花。我见过用消费级卡做数据记录仪半年换了三张卡最后算下来比直接买工业卡还贵。其次是接口模式。SD卡支持SPI模式和SDIO模式。SPI模式接线简单4根线但速度慢实测也就几MB/sSDIO模式用4位数据线速度能到20MB/s以上适合大数据量场景。STM32的SDIO外设很成熟我一般优先用SDIO。但要注意SDIO的CLK、CMD、D0-D3这几根线要严格等长并且加22-33欧姆的串联匹配电阻抑制反射。第三是卡座和供电。卡座要选带金属屏蔽罩和自锁结构的插拔力要够。供电方面SD卡在写入瞬间电流能到100mA以上电源要能扛住这个瞬态VDD引脚旁边放10uF0.1uF组合电容。另外SD卡的检测引脚CD最好接到STM32的GPIO上这样固件能感知卡的插拔避免在卡拔出时还在写导致文件系统损坏。第四是掉电保护。这是SD卡最脆弱的地方。如果正在写文件时断电FAT表可能损坏整个卡都读不出来。解决办法有两个一是用带掉电保护的工业卡卡内部有电容能在断电后完成当前写操作二是在STM32侧做写缓存定期刷盘把频繁的小写入攒成大批量写入减少文件系统元数据的更新次数同时在检测到掉电信号时立即停止写入并刷盘。2.4 三种介质的硬件对比与选型决策表把三种介质的特性拉平了对比选型时就有据可依特性EEPROMSPI NOR FlashSD卡工业级典型容量2Kbit~2Mbit1MB~128MB4GB~64GB接口I2C/SPISPI/QSPISDIO/SPI擦写粒度字节4KB扇区块内部管理擦写寿命100万次/字节10万次/扇区1万~3万次/块写入速度慢ms级中百us级快MB/s级数据保持40年20年10年工业级掉电风险低单字节中扇区高FAT表成本低中高适用数据标定/序列号配置/日志历史/文件这张表不是让你照搬而是帮你建立判断框架。比如你的项目只需要存几百字节的标定值那EEPROM就够了没必要上NOR Flash如果需要存几MB的波形数据又要频繁更新NOR Flash比SD卡更合适如果是要存几GB的历史记录那只能上SD卡但必须做好掉电保护。3. 存储分区规划把数据放到对的地方3.1 按数据性质划分存储区域的实操方法分区规划的核心原则是同类数据集中管理不同类数据物理隔离。我通常把整个存储空间划成这么几块EEPROM区只放最关键的、写入次数最少的、掉电绝对不能丢的数据。具体包括设备序列号16字节、硬件版本4字节、出厂日期4字节、校准系数每个通道4字节浮点假设8通道就是32字节、通信参数地址、波特率等16字节。总共不到100字节用一颗2Kbit256字节的EEPROM就够留足余量。NOR Flash区分成三个子区。第一个是配置区存用户可修改的参数比如PID参数、报警阈值、工作模式这部分做双备份CRC校验写入时先写备份区再写主区确保任何时候至少有一份完整数据。第二个是日志区用环形缓冲区结构存故障记录和运行事件每条记录带时间戳和CRC。第三个是固件备份区存一份当前固件的备份用于升级失败时回滚。SD卡区用FatFs文件系统分成几个目录。/config存导出的配置文件/log存详细运行日志/data存采样数据文件/update存固件升级包。文件按日期或批次命名方便检索。这里有个经验不要把关键配置只存在SD卡上。SD卡是可插拔的用户可能拔走也可能损坏。关键配置必须在EEPROM或NOR Flash里有一份SD卡上的只是副本或导出文件。3.2 地址映射与STM32内存视图的对应关系在STM32的固件里要把这些物理存储映射成统一的逻辑地址空间方便上层调用。我的做法是定义一个存储抽象层SAL用枚举区分存储类型用偏移量定位具体数据。typedef enum { STORAGE_EEPROM 0, STORAGE_NOR_FLASH, STORAGE_SD_CARD } storage_type_t; typedef struct { storage_type_t type; uint32_t offset; uint32_t length; uint16_t crc; } storage_item_t;然后为每类数据定义一个描述符比如#define ITEM_SERIAL_NUM {STORAGE_EEPROM, 0x0000, 16, 0} #define ITEM_CALIB_COEF {STORAGE_EEPROM, 0x0010, 32, 0} #define ITEM_PID_PARAM {STORAGE_NOR_FLASH, 0x1000, 64, 0} #define ITEM_FAULT_LOG {STORAGE_NOR_FLASH, 0x2000, 4096, 0}上层读写时只需要传描述符SAL层负责调用对应的底层驱动。这样以后换存储芯片或者调整分区只改描述符就行业务代码不动。对于NOR Flash还要在STM32的内存里维护一个逻辑地址到物理地址的映射表因为做了磨损均衡后同一个逻辑地址可能对应不同的物理扇区。这个映射表本身也要存到Flash里并且做双备份。3.3 掉电保护的分级策略掉电保护不是一句加个电容就完事要分级设计。第一级EEPROM的字节级保护。EEPROM单字节写入时间约5ms如果在这5ms内断电该字节可能写坏。但EEPROM通常有内部电荷泵和写完成检测实际风险较低。为了保险关键数据写两份读的时候比对不一致就用备份恢复。第二级NOR Flash的扇区级保护。写一个4KB扇区需要先擦除约50ms再写入约1ms。擦除过程中断电整个扇区数据丢失。所以配置区必须双备份写的时候先擦备份扇区、写新数据、校验通过后再擦主扇区、写新数据。这样任何时刻至少有一个扇区是完整的。第三级SD卡的文件系统级保护。这是最复杂的。我的策略是在STM32的RAM里开一个写缓存比如8KB所有小写入先攒着攒满或者超过一定时间比如5秒再一次性写入SD卡。同时监测掉电信号通过ADC监测输入电压或者用比较器产生中断一旦检测到电压跌落立即停止接受新数据把缓存里的数据刷盘然后关闭文件。这样能把文件系统损坏的概率降到很低。注意掉电检测的阈值要留足余量。比如系统正常供电5VSD卡写入时电流突增会导致电压瞬间跌到4.5V如果阈值设成4.5V就会误触发。我一般设成4.0V触发并且加100ms的消抖。4. STM32侧的驱动实现与关键代码4.1 I2C读写EEPROM的时序陷阱STM32的HAL库提供了I2C的读写函数但直接用HAL_I2C_Mem_Write有时候会遇到问题。最常见的是写周期等待。EEPROM在收到写命令后内部需要5ms左右完成写入这期间它不会响应任何I2C命令。如果紧接着发下一个写命令会收到NACK。正确的做法是每次写操作后用应答轮询Acknowledge Polling等待EEPROM就绪。具体就是反复发起始条件设备地址直到收到ACK为止。void eeprom_wait_ready(uint16_t dev_addr) { uint32_t timeout 10000; while (timeout--) { if (HAL_I2C_IsDeviceReady(hi2c1, dev_addr, 1, 1) HAL_OK) { return; } } // 超时处理 }另一个坑是页写入边界。EEPROM通常按页组织比如AT24C256每页64字节。如果一次写入跨越页边界地址会回卷到页首覆盖之前的数据。所以写多字节时要么分页写要么确保不跨页。void eeprom_write_buffer(uint16_t mem_addr, uint8_t *data, uint16_t len) { uint16_t page_size 64; while (len 0) { uint16_t page_offset mem_addr % page_size; uint16_t write_len page_size - page_offset; if (write_len len) write_len len; HAL_I2C_Mem_Write(hi2c1, EEPROM_ADDR, mem_addr, I2C_MEMADD_SIZE_16BIT, data, write_len, 100); eeprom_wait_ready(EEPROM_ADDR); mem_addr write_len; data write_len; len - write_len; } }4.2 SPI NOR Flash的扇区擦除与磨损均衡SPI NOR的驱动比EEPROM复杂因为写之前必须先擦除整个扇区。基本流程是写使能→擦除扇区→等待擦除完成→写使能→页编程→等待写入完成。void nor_flash_write(uint32_t addr, uint8_t *data, uint32_t len) { uint32_t sector_addr addr ~(0xFFF); // 4KB对齐 nor_flash_sector_erase(sector_addr); nor_flash_page_program(addr, data, len); }但这样直接写有个问题如果只想改扇区里的几个字节擦除会丢掉其他数据。所以要先读出整个扇区到RAM修改后再整体写回。这就是读-改-写模式。磨损均衡方面我用的是一种简单的日志式映射把NOR Flash的日志区当成一个环形缓冲区每次写新记录就追加到末尾写满后擦除最旧的扇区。同时维护一个索引表记录每条有效记录的位置。这样每个扇区的擦写次数大致均匀。typedef struct { uint32_t write_ptr; // 当前写入位置 uint32_t sector_count; // 总扇区数 uint32_t erase_count[MAX_SECTORS]; // 每个扇区的擦除次数 } nor_flash_log_t;擦除时优先选择擦除次数最少的扇区实现简单的均衡。4.3 SD卡FatFs的挂载、写入与掉电安全STM32上用FatFs挂载SD卡基本流程是初始化SDIO→挂载文件系统→打开/创建文件→写入→关闭。但工业场景下要加很多保护。首先是挂载失败的处理。SD卡可能没插、接触不良、或者文件系统损坏。挂载失败时不要死循环重试要上报错误并降级到只用NOR Flash。我见过固件在SD卡挂载失败时卡死导致整个设备无法启动。FRESULT fr f_mount(fs, 0:, 1); if (fr ! FR_OK) { // 记录错误降级运行 system_status.sd_available 0; return; } system_status.sd_available 1;其次是写入的原子性。FatFs的f_write不是原子的写大文件时如果断电文件可能不完整。我的做法是先写临时文件比如data.tmp写完后关闭再用f_rename改成正式文件名。这样即使断电最多丢一个临时文件正式文件不受影响。f_open(file, 0:/data.tmp, FA_WRITE | FA_CREATE_ALWAYS); f_write(file, buffer, len, bw); f_sync(file); // 强制刷盘 f_close(file); f_rename(0:/data.tmp, 0:/data.csv);f_sync很关键它把缓存数据真正写到卡上。但频繁调用f_sync会降低性能所以要权衡。我的策略是每写1MB或者每5秒调用一次。第三是掉电时的紧急处理。在掉电中断里不能做复杂的文件操作只能做最简单的动作设置一个标志让主循环尽快调用f_sync和f_close。如果时间实在来不及至少保证文件系统元数据不损坏。4.4 三种介质统一读写接口的封装为了让上层代码不关心底层是哪种存储我封装了一套统一接口int storage_read(storage_item_t *item, uint8_t *buf, uint32_t len); int storage_write(storage_item_t *item, uint8_t *buf, uint32_t len); int storage_erase(storage_item_t *item);内部根据item-type分发到对应的驱动。同时加一层CRC校验读出来的数据先验CRC不通过就尝试从备份恢复。int storage_read_with_crc(storage_item_t *item, uint8_t *buf) { storage_read(item, buf, item-length); uint16_t crc crc16(buf, item-length); if (crc ! item-crc) { // 尝试从备份读取 return storage_read_backup(item, buf); } return 0; }这层封装看起来多写了几百行代码但后期维护和移植的时候能省下大量时间。特别是当项目从STM32F4换到STM32H7或者从SPI NOR换成QSPI NOR时只改底层驱动上层不动。5. FPGA与STM32的数据交接设计5.1 为什么不让FPGA直接写存储前面提到FPGA负责生产数据、STM32负责归档这里展开说说为什么不让FPGA直接写SD卡或NOR Flash。技术上FPGA当然可以直接驱动SD卡有现成的SPI控制器IP或者NOR Flash。但问题在于文件系统。SD卡上要建FAT文件系统才能被PC读取而FAT的目录项更新、簇链分配、空闲簇管理这些逻辑用FPGA的状态机实现极其繁琐而且一旦掉电恢复逻辑几乎没法做。NOR Flash的磨损均衡和坏块管理同理。另外多主机访问同一存储会带来竞争问题。如果FPGA和STM32都能写NOR Flash就需要仲裁机制增加复杂度和出错概率。所以让STM32做唯一的存储管理者FPGA只负责把数据通过FIFO或双口RAM交给STM32是最清晰的分工。5.2 SPI/FSMC总线的数据吞吐与缓冲设计FPGA和STM32之间的数据通道常见的有SPI和FSMC或FMC两种。SPI方式接线简单但速度受限。STM32的SPI最高能到几十MHz实际有效吞吐在几MB/s到十几MB/s。适合数据率不高的场景比如每秒几KB的传感器数据。FSMC/FMC方式是并行总线16位数据宽度下配合DMA能到几十MB/s。适合高速ADC采样数据的传输。但FSMC接线多地址线、数据线、控制线加起来十几根PCB布线要等长成本高一些。不管用哪种缓冲设计都是关键。FPGA侧要有一个FIFOSTM32侧要有一个环形缓冲区。FPGA采集到数据先写入FIFOSTM32通过中断或DMA把FIFO数据搬到自己的环形缓冲区然后再慢慢处理写NOR Flash或SD卡。这样能吸收突发数据避免因为存储写入慢而丢数据。// STM32侧环形缓冲区 #define RING_BUF_SIZE 8192 typedef struct { uint8_t buf[RING_BUF_SIZE]; volatile uint32_t head; volatile uint32_t tail; } ring_buf_t; void ring_buf_push(ring_buf_t *rb, uint8_t *data, uint32_t len) { for (uint32_t i 0; i len; i) { uint32_t next (rb-head 1) % RING_BUF_SIZE; if (next rb-tail) { // 缓冲区满丢弃或覆盖最旧数据 rb-tail (rb-tail 1) % RING_BUF_SIZE; } rb-buf[rb-head] data[i]; rb-head next; } }5.3 数据打包格式与校验机制FPGA传给STM32的数据不能是裸流要打包成帧带帧头、长度、类型、CRC。这样STM32才能正确解析和归档。我用的帧格式是这样的字段长度说明帧头2字节0xAA55类型1字节0x01ADC数据0x02状态0x03事件长度2字节数据段长度序号4字节递增用于检测丢帧数据N字节实际载荷CRC162字节从类型到数据的校验STM32收到帧后先验帧头和CRC通过后再根据类型分发。序号用来检测是否有丢帧如果发现序号不连续记录一条错误日志。这个打包逻辑放在FPGA里用状态机实现资源消耗不大但能极大提升数据可靠性。我实测过加了CRC和序号后数据完整性问题基本绝迹。6. 实测踩坑与排查实录6.1 SD卡写入随机失败的完整排查链路这个坑我印象很深。一块工业控制器SD卡写入偶尔失败概率大概百分之一但一旦失败就丢一批数据。排查过程如下第一步确认是硬件还是软件问题。换了一张卡问题依旧换了一块板子问题依旧。初步判断是软件或设计问题不是单板故障。第二步抓波形。用示波器看SDIO的CLK和CMD线发现失败时CLK上有明显的过冲和振铃。这说明信号完整性有问题。第三步查PCB。SDIO的CLK走线没有做阻抗控制而且和一路PWM信号平行走了很长一段存在串扰。PWM信号跳变时耦合到CLK上导致SD卡采样错误。第四步验证。把PWM信号改道远离SDIO走线同时在CLK上串了22欧姆电阻。重新测试连续写入10GB数据无一次失败。这个案例的教训是SDIO是高速信号PCB设计不能当普通GPIO对待。CLK要包地走线要短远离干扰源串联匹配电阻不能省。6.2 NOR Flash擦除后数据全FF的地址模式问题另一个坑是32MB的NOR Flash访问高地址区超过16MB时读出来全是0xFF。一开始以为是芯片坏了换了一片还是这样。后来查数据手册才发现超过16MB地址需要进入4字节地址模式。默认是3字节地址模式只能访问前16MB。发0xB7命令进入4字节模式后问题解决。但这个命令有个细节进入4字节模式后所有的读写命令都要用4字节地址包括读状态寄存器。如果固件里有的地方用3字节有的用4字节就会乱套。我的做法是在初始化时统一进入4字节模式然后所有操作都用4字节地址不再切回3字节。6.3 EEPROM写入后立即读取读到旧值的时序问题这个坑比较隐蔽。写完EEPROM后立即读有时候读到的是旧值。原因是EEPROM的写周期还没完成读操作被内部逻辑延迟或者返回了旧数据。解决办法就是前面说的应答轮询。每次写操作后必须等到EEPROM响应ACK了才能进行下一次操作。我一开始图省事写完后延时10ms了事但批量写入时10ms不够而且浪费时间。改成应答轮询后既可靠又快。6.4 掉电瞬间文件系统损坏的恢复方案有一次现场断电SD卡里的文件系统损坏PC都读不出来。虽然数据不是特别关键但暴露了掉电保护的不足。后来我加了两个措施一是掉电检测中断电压低于4.0V时立即触发主循环优先处理刷盘二是文件系统自检每次上电时检查SD卡能否挂载不能挂载就尝试用f_mkfs重新格式化会丢数据但至少设备能继续运行同时把损坏的卡标记出来提示用户更换。另外对于特别关键的数据我在NOR Flash里也存一份副本。SD卡坏了至少NOR Flash里的配置和最近日志还在。7. 几个提升可靠性的工程习惯7.1 双备份与CRC校验的落地细节双备份不是简单写两份而是要交叉验证。我的做法是主备份和从备份各带一个CRC读取时两份都读出来CRC都通过且数据一致才认为有效如果一份CRC错用另一份恢复如果两份都错上报严重故障。写入时也有讲究先写从备份校验通过后再写主备份。这样任何时刻至少有一份是完整的。如果写从备份时断电主备份还是好的如果写主备份时断电从备份是新的主备份是旧的读取时以从备份为准。7.2 存储寿命的监控与预警EEPROM和NOR Flash的擦写次数是有限的虽然做了磨损均衡但还是要监控。我在每个扇区的头部留几个字节记录擦除次数每次擦除前先读出计数、加一、写回。当某个扇区的擦除次数接近标称值的80%时上报预警提示维护人员准备更换。SD卡的健康状态可以通过读CSD寄存器和查询卡的状态来粗略评估但更实用的是监控写入错误率。如果连续出现写入失败就标记该卡为不健康建议更换。7.3 固件升级时的存储回滚设计固件升级是存储方案的一个典型应用场景。我的设计是新固件先下载到SD卡的/update目录校验CRC和签名通过后复制到NOR Flash的备份区然后STM32从备份区读取并写入自己的程序Flash。如果升级过程中断电下次上电时bootloader检测到升级标志未清除就从NOR Flash的备份区重新升级。如果备份区也损坏就回滚到旧固件。这个流程的关键是升级标志和固件备份都要存在掉电安全的存储里。升级标志我存在EEPROM里带双备份固件备份存在NOR Flash里带CRC。这样即使升级失败设备也不会变砖。7.4 生产测试阶段的存储自检流程产品出厂前存储部分必须做完整自检。我的自检流程包括EEPROM自检写入测试模式0x55、0xAA交替读回比对然后恢复出厂数据。NOR Flash自检擦除一个测试扇区写入递增数据读回比对再擦除。SD卡自检创建测试文件写入1MB数据读回比对删除文件。掉电模拟在写入过程中人为断电重新上电后检查数据完整性。这个自检流程跑一遍大概几十秒但能筛掉大部分存储相关的早期故障。我见过不少板子因为EEPROM虚焊或者SD卡座接触不良在实验室没问题到了现场才暴露。产线自检能把这类问题拦在出厂前。存储方案这东西设计的时候多花一天调试的时候能省一周现场运行的时候能省一年。工业控制器的存储不是能存就行而是要在可靠性、寿命、成本、速度之间找到那个平衡点。EEPROM管关键参数、NOR Flash管配置日志、SD卡管历史数据STM32做管家、FPGA做数据源这套分级架构我在多个项目里验证过只要硬件细节做到位、掉电保护做扎实跑个五年八年问题不大。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →