ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

工业控制器分级存储方案:EEPROM、NOR Flash与SD卡选型及STM32+FPGA实现

工业控制器分级存储方案:EEPROM、NOR Flash与SD卡选型及STM32+FPGA实现 搞工业控制器的工程师十有八九都躲不过“数据怎么存”这一关。参数要掉电不丢日志要能追溯固件要能在线升级大容量的波形记录还得有地方放。不同的数据特性完全不一样你不可能用一颗芯片全包了。所以“分级存储”就成了很自然的选择EEPROM、NOR Flash、SD 卡各司其职再配合 STM32 和 FPGA 的分工协作基本能覆盖工业现场大部分存储需求。这篇硬件篇·12我就把近几年在项目里总结的分级存储框架、选型思路和实操细节摊开讲讲。适合正在做控制器底层硬件、或者准备给现有系统加存储功能的朋友。简单来说这套方案能帮你解决三个问题哪些数据该放哪为什么这样分以及STM32和FPGA之间怎么配合才能让存储系统稳定可靠。1. 分级存储的整体思路与硬核理由1.1 工业控制器的数据其实分四个层次在动手选存储介质之前先把数据分类搞清楚。工业控制器里看起来都是“数据”但特性和要求天差地别配置参数与校准信息比如 PID 系数、温度补偿表、设备地址、序列号。容量很小可能就几十到几百字节但要求掉电立即保存、随时改写而且不能因为频繁写入把片子写废。运行状态与关键日志比如开机时间、报警记录、累计运行时长、关键事件。容量需求从几 KB 到几 MB 不等要求稳定可靠能追加写入还要能快速读取。固件与引导程序几 MB 到几十 MB。要求掉电不丢失、支持分区更新、最好还能直接映射执行。批量监测数据与历史曲线比如振动波形、温度曲线、生产批次数据。容量往往从几十 MB 到几十 GB要求顺序写入流畅能按时间段检索最好支持文件管理。这四类数据的写入频率、容量、寿命要求和读取方式完全不同。用一颗大容量 Flash 通吃所有数据不是说不行而是会牺牲可靠性或成本。比如参数如果存在大容量 Flash 里每次改配置都要先擦一个扇区、再写入而且磨损均衡做得不好几年后某个扇区就挂了。反过来如果历史波形数据硬塞进 NOR Flash容量不够不说那写入速度也顶不住持续采样。1.2 分级存储的核心逻辑让介质特性匹配数据特性分级不是“三个存储器叠着用”这么简单核心逻辑是“按数据的生命周期与访问模型选择最合适的介质”。我习惯把它类比成办公室的文件处理EEPROM 是随身笔记本随时翻、随时改、写下就是要长期记住的关键小事。容量小但擦写寿命极高单字节威力大。NOR Flash 是常用文件柜体积适中文件不能太碎要一次整理一块区域再写。适合大块日志、固件镜像还支持现场直接执行。SD 卡是归档库房容量巨大适合整批资料入库但它有自己的“目录系统”文件系统你需要按规矩存取而且对不间断供电比较敏感。用这种思路再回头看工业控制器的存储需求就会非常清晰。1.3 STM32 和 FPGA 各管哪一段存储工业控制器里 STM32 和 FPGA 的组合很常见。STM32 通常跑应用逻辑、通信协议、文件系统FPGA 负责高速采样、实时控制、信号预处理。存储系统的分工也顺着这个思路展开STM32 主掌“管理面”访问 EEPROM 存参数管理 NOR Flash 的日志与固件分区操作 SD 卡的文件系统处理掉电保存时序甚至通过网络或者 USB 升级固件。FPGA 主掌“数据面”数据采集的频率高采样率动不动上百 kSPS 甚至 MSPS如果都经过 STM32 转发到 SD 卡CPU 会累死而且实时性也差。因此 FPGA 可以先把批量数据缓存到 SRAM/DDR再通过 DMA 或直接控制写 SD 卡或者把高速数据流给 NOR Flash 做掉电保护而 STM32 只负责发起任务、查询状态。简单说绝大多数的“存储管理决策”由 STM32 做而“高速数据的搬运”可以交给 FPGA。这样既利用了 ARM 生态下的文件系统和协议栈又发挥了 FPGA 并行吞吐的优势。2. 存储介质选型EEPROM、NOR Flash、SD 卡怎么挑2.1 EEPROM参数存储的不二之选但别拿来记日志EEPROM 的最大特点就是可以按字节擦写甚至按位操作写入寿命标称通常在 100 万次远远高于 Flash 的十万次级别。工业控制器里很多参数是运行中偶尔修改的比如产线上临时调整一个速度系数改一次就存一次数据量不大但次数很多用 EEPROM 最合适。另一个关键点是 EEPROM 的写时序简单读是随机访问等待时间极短。不需要像 Flash 那样先擦后写因此非常适合做“掉电前最后一刻的保存”。当控制器检测到掉电瞬间还有几个毫秒的保持时间时可以用中断里快速写几个关键字节到 EEPROM。选型时注意几点容量基本都用 24C02/24C04/24C64 之类的 I2C 器件如果参数较多可以选 24C25632KB。别选太大EEPROM 的性价比在 64KB 以内还算可以再大不如用 SPI NOR Flash。接口I2C 只要两根线速度 400kHz 也够了。注意上拉电阻和总线电容布线远的话 1kΩ 到 4.7kΩ 要调。备用方案如果掉电保存特别频繁、要求接近无限次擦写建议考虑 FRAM铁电引脚兼容 EEPROM寿命达 1e10 次并且写入不需要等待。2.2 NOR Flash大块日志和固件的中枢NOR Flash 的容量比 EEPROM 大得多常见的从 1MB 到 64MB甚至更高。它的最大优势是读取速度快支持 XIP片上执行所以很多板卡用它直接放启动代码。但 NOR Flash 写入前必须擦除而且擦除最小单位是扇区通常是 4KB不能按字节写。这就意味着它适合“整块记录”的数据。在工业控制器里NOR Flash 的典型任务保存固件镜像做 A/B 分区升级时bootloader 可以直接从 Flash 里加载可用分区。保存系统运行日志用循环覆盖的方式按扇区轮流写防止某个扇区被频繁擦写导致提前失效。保存中等容量的工艺文件、配置文件备份。选型需要关注扇区大小、擦写次数和写保护引脚。工业级芯片建议选 -40℃~85℃ 或者更宽温度范围的不要选消费级。写保护引脚WP要拉死或者用 GPIO 控制防止程序跑飞把固件抹了。如果要求掉电安全的日志还需要考虑 Flash 的“部分页编程”能力和上电时未完成的擦除状态恢复此时可以选择内置有状态指示的芯片。2.3 SD 卡大容量记录的核心但别让它“裸奔”SD 卡在工业设备里是最后一级“大仓库”。它的容量可以做到以 GB 为单位用 FAT 文件系统后数据可以直接在 PC 上打开分析。不过 SD 卡也有明显问题一是写寿命受块管理和磨损均衡影响杂牌卡很容易坏二是意外掉电可能损坏文件系统引起数据丢失三是温度范围普遍不如板载 Flash工业级 SD 卡价格贵且难买。因此我的建议是SD 卡只做“可以接受丢失一小段”的批量数据存储比如连续波形、历史曲线、批量报表。而像系统关键日志、固件这类数据绝对不能只存在 SD 卡上。FPGA 采集的高速数据如果直接进 SD 卡建议在数据链路上增加掉电检测和保护机制比如用大容量电容给 SD 卡供电保持足够时间同时尽量采用“先写临时文件、再重命名”的方式防止半截文件。SD 卡通信有 SDIO 和 SPI 两种。STM32 用 SDIO 性能好FPGA 里做 SD 卡控制器麻烦一些但可以用 SPI 接口降低复杂度。大数据记录场景下更建议 STM32 配 SDIO FatFs加上 DMA实时性足够。FPGA 可以把数据通过并行接口送给 STM32 的 DMA 通道然后由 STM32 负责写卡避免 FPGA 去处理文件系统的复杂性。2.4 三种介质怎么组合一张表说清我整理了一个快速选型表新项目可以直接照着定框架数据类别典型内容推荐介质主要理由配置参数/校准值PID、零点、量程、序列号EEPROM按字节改写、寿命长、掉电保存快关键运行日志报警、开关机、事件戳NOR Flash容量适中、扇区循环、掉电相对安全固件/引导程序Bootloader、App、FPGA bitNOR FlashXIP 支持、分区管理、可靠性高批量监测数据波形、曲线、批次记录SD 卡容量大、文件系统通用、易导出需要掉电即时保存的采集帧高速采样最后一段FRAM / 小 RAM无写等待、寿命极高、可断电保持当然具体还要看成本、环境温度和系统复杂度。但大方向基本不会错。3. STM32 与 FPGA 的存储分工实现3.1 通信架构STM32 是主FPGA 是高速助手常见的控制板架构里STM32 与 FPGA 之间至少有一条高速并行总线或者 SPI 总线。存储访问可以这样划分EEPROM 与 NOR Flash 挂在 STM32 的 I2C 和 SPI 上由 STM32 独占访问。FPGA 需要读写 NOR Flash 或 SD 卡时不直接连 Flash而是先把数据打包通过总线交给 STM32由 STM32 写入存储介质。这样做文件系统和坏块管理都在 STM32 侧逻辑清晰。如果数据吞吐量太高比如 ADC 采样率 10Mbps 以上STM32 可能撑不住可以让 FPGA 直接控制 SD 卡或者 NOR Flash但需要 FPGA 内部实现 SD 卡协议或 Flash 控制器同时 STM32 通过寄存器给 FPGA 下发“写哪个扇区”之类的指令。第二种方案听起来很酷但工作量增加不少。如果是普通波特率的工业过程量采集其实第一种方案足够而且开发调试快得多。我之前做过一个振动监测终端采样率 200kSPS、16bit每秒数据量 3.2Mb用 STM32H743 的 SDIO DMA 写卡毫无压力FPGA 只负责把数据打包成块并通过并行 FIFO 送给 STM32完全没必要让 FPGA 直接挂 SD 卡。3.2 数据流设计谁产生、谁缓冲、谁落盘这里最忌讳的是“每个人都往存储里写东西”。我在项目里一般把存储的数据流分成三条独立通道参数通道STM32 收到上位机的参数修改指令后先校验再写 EEPROM。写入完成后回复确认。FPGA 不直接参与但 fpga 内的算法参数可能需要同步STM32 启动时从 EEPROM 读参数并配置 FPGA。日志通道STM32 的 RTOS 任务把日志事件格式化成二进制记录追加到 NOR Flash 的日志分区。采用循环覆盖每个记录头包含时间戳、长度、CRC。波形通道FPGA 采集数据在内部 FIFO 里按帧组织通过「帧完成中断」通知 STM32STM32 用 DMA 搬运到内存大缓冲再写到 SD 卡文件。为了避免文件系统阻塞采集这里要开双缓冲或者环形缓冲。三条通道互不干扰每条通道的存储介质不同容量和寿命互不影响。如果哪个通道出了问题其他通道的恢复也不会牵连。3.3 地址与分区规划如果 NOR Flash 容量是 16MB我习惯按下面分区分区偏移地址大小用途Bootloader0x000000256KB上电引导App0x0400004MB主程序App 备份0x4400004MB升级失败回滚参数备份区0x8400001MB当前配置镜像日志区0x940000剩余 7MB循环覆盖日志FPGA 的 bit 流文件如果也放 NOR Flash可以指定一个 1MB 的区从 0xA40000 开始bootloader 启动后加载到 FPGA。分区规划好后在代码里要用宏定义固化不要散落在各处。尤其是擦除和写入逻辑要严格按分区边界操作禁止跨区。这个原则在我在项目中一次次踩坑后总结出来的不小心跨区擦掉 App 的教训太痛。4. 实操过程三级存储的高级读写实现4.1 EEPROM 读写I2C 时序和掉电保存细节以最常见的 24C64 为例I2C 地址由硬件引脚决定通常是 0xA0。读写要点写单个字节发送器件地址、寄存器地址、数据然后给一个停止位。注意 EEPROM 写周期通常 5ms写完后需要等待或者查询 ACK 轮询。写多个字节同一页内连续写入可以减少总线操作24C64 页大小 32 字节。跨页则要拆分否则地址会回卷。读多个字节跳转读 连续读设定好目标地址后依次读取。STM32 里直接使用硬件 I2C DMA 就好但要注意 I2C 的时序状态机细节出错后要清总线、释放 SCL/SDA。我在实际调试中经常遇到 I2C 卡死在 Busy 状态大概率是中断优先级和 DMA 配置没搞好。掉电保存这块我建议单独设计一个电源监测电路利用比较器监测 5V 或 3.3V。当断电时MCU 的 3.3V 由储能电容继续维持几十毫秒这个时间足够把几十个关键字节写入 EEPROM。程序上要做的是使用外部中断优先触发掉电处理而不是在轮询里等待。写入的关键数据先暂存到 RAM掉电中断时一次性连续写。写完后置一个“保存完成”标志在 EEPROM 的固定位置下次上电检查该标志若不完整则回退到上次备份区。4.2 NOR Flash 读取、擦除、写入流程NOR Flash 的读写流程不复杂但容易踩坑。以常见的 W25Q64 为例SPI 接口。一个成熟的写入流程是写使能命令 0x06允许写入。发送「扇区擦除」命令 0x20跟 24 位地址等待擦除完成查询状态寄存器位 BUSY。在页缓冲区准备数据发送「页编程」命令 0x02跟地址和数据页大小通常 256 字节。等待编程完成。关键点写一字节前必须先擦除扇区。如果只想改扇区里的几个字节要先把整个扇区读出来放到 RAM修改后再擦除、再整体写回。这个操作很危险掉电会导致整个扇区数据损坏所以重要参数不要用这种流程频繁改。状态寄存器里有个 WEL 位写使能锁存每次上电后要重新发写使能。调试时经常发现写不进去就是忘了先发 0x06。SPI 时钟频率在 PCB 布线不好时经常出问题降速到 10MHz 以下能解决很多诡异故障。如果日志是固定长度记录而且追加在尾部那么可以把日志区设计成多个记录块每次追加时先读最后一块的有效偏移再编程新记录。但别等到整个扇区满了才擦提前用一个擦写计数器做磨损均衡。在 STM32 侧用 CubeMX 生成 SPI H 库再自己封装几个基础命令函数非常简单。但在 FPGA 侧如果需要在逻辑里挂载 NOR Flash也可以用 Verilog 写 SPI 控制器。注意状态机要处理 busy 等待避免在擦除期间继续发命令导致冲突。现在很多 FPGA 开发板上都带 SPI Flash可以复用这种控制器做数据缓存。4.3 SD 卡文件系统FatFs 与高速写入调优STM32 上做 SD 卡最成熟的就是 FatFs。基础流程用 SDIO 外设初始化 SD 卡检测卡类型SDSC/SDHC/SDXC。挂载文件系统f_mount。用f_open创建数据文件以追加写方式打开。用f_write写入缓冲区注意 FatFs 内部有簇大小的概念建议一次写入 512 字节的倍数避免因字节写太碎导致性能崩。写完f_sync必要时f_close。实际调优经验用f_printf写格式化数据非常慢调试可以批量记录必须用二进制块写。一次写 1KB、4KB 甚至更大块写入速度会好非常多。采集数据需要在内存里攒块攒够再写。如果 SD 卡写速度太慢确认是不是用了 SPI 模式SPI 模式最高一般 25MHz 左右SDIO 4 线模式可以到 48MHz 甚至更高。工业记录场景尽量用 SDIO 模式。每个文件不要无限变大建议按小时或按固定大小轮转比如data_20240914_00.log。查找和备份都方便。FAT 表本身也可能损坏掉电瞬间正在写 FAT 的话文件系统可能挂掉。建议开启 FatFs 的FF_FS_MINIMIZE和FF_USE_DYN_BUFFER等选项同时加一个“上次写入是否完成”标志文件上电时检查。FPGA 如果直连 SD 卡写数据一般不搞文件系统直接按绝对扇区号写。这样性能高、逻辑简单但数据在 PC 上没法直接读。更稳妥的方案是 FPGA 把数据送到 STM32由 STM32 文件系统写卡。如果实在要 FPGA 直写也可以用 FPGA 实现 SD 卡的 SPI 协议控制器按固定扇区写裸数据再用上位机工具把扇区镜像打包成文件系统镜像。这种方案适合做数据采集记录仪但不适合工业控制器这种还需要配置管理的场景。4.4 FPGA 在存储环节里还能干什么虽然我前面反复说“别让 FPGA 干文件系统”但 FPGA 在存储链路上并非只能当数据搬运工。它还能做几件很有价值的事高速数据缓冲写 SD 卡不是实时的当 SD 卡忙于擦除或刷 FAT 时写入会被打断。FPGA 内部的大 FIFO 或者外挂 DDR/SRAM 可以做弹性缓冲把采集数据平滑地喂给 STM32。DMA 引擎FPGA 把并行 ADC 采样数据打包成 AXI-Stream 或者 8/16bit 总线通过连续地址写入 STM32 的 SRAM 缓冲这个动作基本不消耗 CPU 时钟。掉电数据保护控制器断电瞬间FPGA 里缓存的关键数据需要快速搬移到非易失介质。由于 FPGA 逻辑响应时间可以做到纳秒级由它接管掉电瞬间的存储操作比依赖 STM32 中断更可靠。比如 FPGA 检测到 PowerFail 信号后在剩余保持时间内把 FIFO 数据写到 NOR Flash 的两个固定扇区。存储状态机校验如果 FPGA 直连 NOR Flash它还可以做写入后的回读校验确保每个扇区数据完全正确后再发完成信号。工业控制器对可靠性要求高这一步很有价值。因此一个均衡的架构是FPGA 做缓冲与掉电数据保全STM32 做文件系统和参数管理EEPROM/NOR Flash/SD 卡按分级各司其职。5. 常见问题与排查技巧实录5.1 EEPROM 写入老是失败丢数据我曾经遇到过一个很隐蔽的问题设备频繁上电断电后参数偶尔回滚到出厂值。排查过程发现EEPROM 的写周期还没完成电源就掉没了。因为保存动作是在掉电中断里做的但掉电中断触发后留给 MCU 的时间只有大概 15ms24C64 写一页有时需要 5ms如果写很多字节且中断里还有别的任务就会超时。解决办法有两个方向硬件上加大储能电容把掉电保持时间从 15ms 拉到 100ms。软件上把“保存参数”这个动作提前到参数修改时立刻做而不是等掉电再做。掉电中断里只保存一个 PowOff 标志。写 I2C EEPROM 时还要注意总线死锁在调试时总线被拉低SDA 永久为低一般是某次通信被打断导致的。主机必须在通信前检查总线空闲并且有超时机制必要时重新初始化 GPIO。5.2 NOR Flash 日志写到一半数据全花了这是一个典型问题日志区循环覆盖时擦除和写入交错掉电正好发生在擦除过程中那这个扇区就既不是旧数据也不是新数据全 0xFF 或者全 0x00。我的解决思路是把日志区设计成“双扇区交替”的保险机制两个扇区 A 和 B。写入时先从 A 开始当 A 快满时先擦除 B然后写 B。每次写完一条日志在固定偏移处写一个“有效”标记。上电后扫描 A 和 B 的标记选择有效的那个使用。这样做虽然多占一倍空间但把日志损坏的概率降了几个数量级。另一个小技巧是每条日志记录尾部加 CRC32上位机解析时可以识别哪些记录是完整的碰到损坏记录就跳过。5.3 SD 卡插上无法挂载 FatFs调试中常碰到的原因有几类SD 卡供电不稳SDIO 初始化时检测不到卡。测量卡座 VDD有些 TF 卡需要 3.3V但 PCB 上可能被其他大电流电路拉低。卡座引脚虚焊尤其 DAT1/DAT2 没有上拉时4 线模式会失败。用 SPI 模式调试会省很多事但速度慢。部分工业级 SD 卡对初始化时序要求严格STM32 的 SDIO 时钟太高导致失败。初始化时把时钟分频调到 400kHz 以下成功后再拉升频率。FatFs 挂载失败可能是分区表和 MBR 不兼容。工业推荐把 SD 卡格式化为 FAT32并且不要用软件的分区工具做特殊分区。另外SD 卡热插拔建议加检测引脚和板级滤波。实时记录不要直接拔卡即使支持热插拔也要先保证文件系统 flush。5.4 FPGA 写 Flash 和 Python 上位机调试如果 FPGA 直连 NOR Flash调试时最好先写一个 Verilog Testbench 仿真把 SPI 控制器时序跑通再用逻辑分析仪抓真实信号。我吃过一次亏仿真里状态机是对的但综合后信号毛刺导致写入数据错位。后来在 FPGA 里加了一级同步寄存器并且在 SPI 时钟的边沿采样上做调整问题解决。如果要用 PC 调试存储内容我建议把 NOR Flash 或 SD 卡的镜像导出后用 Python 脚本分析按扇区/文件系统解析数据。这样能快速验证 FPGA 的写入结果尤其是大块数据的字节序和排列格式。这个习惯能省很多查异常的时间。6. 个人经验与扩展建议写到这里再聊聊我自己的几条实操心法第一分级存储方案在硬件设计阶段就要定好接口和地址映射别等固件写了一半再加芯片。EEPROM 和 NOR Flash 最好都选 SPI/I2C 接口接线简单SD 卡座要留好卡检测脚和写保护脚哪怕软件里不用也引到 GPIO 备用。第二所有存储介质都要有“回读校验”的流程。不管是 EEPROM、NOR Flash 还是 SD 卡写完必须读回来比对。很多人觉得 FPGA 或 STM32 写进去就完了实际上工业现场干扰、总线时序不稳定、介质坏块都可能让数据写偏。回读校验这一行代码能挡住无数隐藏故障。第三磨损均衡不只是 SD 卡主控芯片的事。NOR Flash 的日志区、EEPROM 的参数区非常容易盯着同一地址反反复复写。EEPROM 虽然 100 万次寿命但工业设备 7×24 运行可能一两年就写穿。所以重要参数我建议多做几个备份区轮换写入。比如 4 个备份参数区每次写不同的区配合 CRC 校验上电时选有效且最新的那个。NOR Flash 的日志区也建议设计擦写计数器每擦一个扇区就加一轮流擦。第四SD 卡的文件系统并不是唯一的大容量方案eMMC 在工业控制器里也越来越常见。eMMC 内置 Flash 控制器和坏块管理可靠性比 SD 卡好也适合 FPGA 直连实现控制器。如果你的产品需要做长期数据记录且对体积和抗振有要求可以考虑 eMMC 方案。本质上它还是分级存储里“大容量介质”那一层只是把管理和寿命问题交给了专用芯片。最后给正在画板子的朋友一句建议存储电路的电平、上拉和去耦不要省。I2C 上拉电阻、SPI 的串联电阻、SD 卡供电的钽电容这些小东西在实验室里可能看不出来差别到现场电磁环境一差全都会冒出来。把存储系统当成一个小系统来设计而不是随手连几根线你会发现后面的调试验证会顺利得多。这个内容后续还可以扩展到“基于 STM32 的 USB 虚拟串口导数据”“FPGA 直写 eMMC 的 IP 核设计”等方向。如果大家有兴趣我后面继续写。先把这套分级存储框架吃透已经能覆盖大部分工业控制器的存储需求了。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进