ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

工业控制器存储实战:STM32+FPGA三级存储与掉电保护方案

工业控制器存储实战:STM32+FPGA三级存储与掉电保护方案 做工业控制器这些年存储这块一直是“看着不起眼出事就要命”的地方。设备参数丢了产线直接停日志丢了故障原因根本追不到。我的处理思路是让 STM32 和 FPGA 各自负责擅长的部分再用 EEPROM、NOR Flash、SD 卡三级存储把数据分门别类放好。这篇文章把整套分级存储方案从选型、电路、驱动到掉电保护完整拆一遍适合正在搞工业控制器、边缘网关、数据采集板这类项目的朋友参考。为什么会想到用三级存储说白了就是“一个介质解决不了所有问题”。下面先聊聊整体设计思路再逐个剖析每种介质的分工和实现细节。1. 为什么工业控制器存储要搞三级分层1.1 一个介质解决不了所有存储需求工业控制器里的数据按性质大致能分成三类。第一类是参数类比如设备配置、校准系数、序列号、用户设定值特点是小几十到几百字节写入频繁每次参数修改、每次关机都可能写而且绝对不能丢。第二类是日志类比如运行记录、报警事件、故障快照特点是个头中等MB 级别允许按块写丢了最近几秒问题不大但不能成段丢失。第三类是历史数据类比如趋势曲线、工艺数据、批次追溯特点是大动不动就上 GB需要文件系统来管理可以容忍一定损坏但要能快速定位到时间区间。如果只用一种介质问题马上就来了。只靠 EEPROM容量撑死几十 Kbit历史数据根本没地方放。只靠 SD 卡参数每次上电改一下频繁掉电写入对文件系统是长期折磨哪天 FAT 表坏了参数和日志一起玩完。所以分级存储的核心思路很朴素每种介质用在它最合适的场景EEPROM 应付参数NOR Flash 应付固件和近期日志SD 卡应付大数据量的历史记录。边界划清楚后续的驱动和管理都会轻松很多。1.2 STM32 和 FPGA 各管一段在存储链路上FPGA 负责数据入口。多路 ADC 采样、编码器计数、高速 IO 状态记录这些数据实时性要求高如果全交给 STM32 的中断去处理CPU 根本忙不过来而且很容易丢采样点。FPGA 把采集任务做完把数据按帧打包先缓到 FIFO 里再通知 STM32 来搬走。STM32 则负责数据出口它本身就集成 I2C、SPI、SDIO 这些存储接口又有成熟的库和文件系统生态做存储调度和文件管理是最合适的。这两颗芯片的分工我打一个比方FPGA 是流水线上的快速分拣员把所有物料按规格装进周转箱STM32 是仓库管理员负责把周转箱登记、码放、归档。分拣员的速度不能再被仓库管理员的登记速度拖累否则整条线就得降速。所以 FPGA 侧 FIFO 深度要留够我建议做到一帧数据长度的四倍以上再配合“半满中断”通知 STM32 来提货实测下来基本不会溢出。2. 三种存储介质的选型逻辑与关键参数2.1 EEPROM参数存储的小而精选手EEPROM 最大的特点就是按字节读写不需要先擦除一整块。写一个参数直接改对应的字节就行这对于频繁小量修改简直太合适了。工业上常用的 AT24C02/04/16、M24C02 系列容量从 2Kbit 到 64Kbit 都有I2C 接口地址由 A2/A1/A0 三个引脚决定一条总线上最多能挂八个。选型时最该盯住的是写寿命。常见标称是 100 万次擦写有些型号能到 400 万次。别小看这个数字如果一个参数每秒写一次一天就是 8 万多次十来天就顶到寿命极限了。所以参数写入一定不能裸奔得做磨损均衡和双备份策略后面软件部分我会详细说。页写大小也要注意AT24C02 一页是 8 字节AT24C04/08/16 一页是 16 字节如果写入跨过了页边界地址会自动回卷到页首前面的数据就被覆盖了。另外EEPROM 的写周期典型值是 5ms这期间器件处于忙状态要对 ACK 进行轮询不能立刻再发下一条写命令。2.2 NOR Flash代码加日志的中坚力量NOR Flash 支持按字节随机读可以直接在上面执行代码工业控制器做 IAP 在线升级时升级文件一般先存到 NOR Flash 的启动区校验通过再搬进运行区。除了放固件还能划一块区域做运行日志断电不丢速度也比 SD 卡可靠。常用的是 W25Q 系列容量 16Mbit 到 128Mbit 都有SPI 接口四线 QSPI 模式下能跑得很快。NOR Flash 有几个参数必须刻在脑子里。擦除粒度最小是 4KB 扇区编程粒度是 256 字节一页。写入之前必须先擦除因为 NOR Flash 的位只能从 1 变 0擦除的作用就是把整块恢复成全 1。擦写寿命约 10 万次比 EEPROM 低一个数量级所以日志区必须做成轮换扇区不能盯着一个块反复擦。擦除时间要预留够扇区擦除典型值 150ms慢的型号能到 400ms如果代码里看门狗喂得勤这一段时间千万别卡在轮询里要么喂狗要么把存储操作放到独立任务里。编程之后要轮询状态寄存器的 BUSY 位确认操作完成再发下一条命令。2.3 SD 卡海量历史数据的仓库SD 卡容量大、便宜、可拆卸工业上主要拿它存趋势曲线、批次记录、诊断快照。接口有两种SDIO 模式走 4 位数据线速度快但占用的引脚多SPI 模式兼容性最好很多卡在 SPI 模式下的初始化成功率比 SDIO 更高代价是速度慢够用就行。我一般建议如果传输量不是特别大优先用 SPI 模式调试起来省心太多。SD 卡上电初始化有个固定套路CMD0 进入 SPI 模式CMD8 做电压握手ACMD41 启动初始化流程等卡返回 ready 之后再进入读写状态。电压不匹配是最常见的玄学问题CMD8 的响应会告诉你卡支持的是 2.7V 还是 3.3V别略过这一步。文件系统用 FAT32配合 FatFS 这类库移植起来不难难点在掉电保护。SD 卡内部有 FTL 层做磨损均衡和坏块管理底层磨损我们不用管但文件系统层面的掉电损坏是真实的数据块写一半、FAT 表更新一半、目录项分裂任何一个都可能让卡上的文件系统整个崩掉。2.4 三种介质快速对比参数EEPROMNOR FlashSD 卡容量2Kbit~64Kbit16~128Mbit数 GB接口I2CSPI/QSPISDIO/SPI写粒度字节/页(8~16B)页(256B)扇区(512B)擦除粒度无需擦除扇区(4KB)内部 FTL 管理写寿命约 100 万次约 10 万次MLC 几千次(FTL 兜底)掉电风险低中(擦除中掉电损坏)高(文件系统层面)主要用途参数、校准数据固件、日志、小型数据库历史曲线、批量数据这张表是我自己项目里的经验值不同型号会有差异选型时一定以手册为准。重点想说“掉电风险”这一行指的是应用层需要额外防护的力度EEPROM 单字节写内部有冗余设计相对最稳NOR Flash 擦除过程被打断最容易留下坏块SD 卡最伤的不是闪存颗粒而是 FAT 表。3. 硬件电路设计与接口分配实战3.1 STM32 的外设资源怎么分配以 STM32F407 为例比较顺手的接法是这样的I2C1 挂 EEPROMSPI1 挂 NOR FlashSDIO 挂 SD 卡。但引脚分配有个大坑就是复用冲突。JTAG 调试口默认占用 PA15、PB3、PB4如果这几个引脚还要复用到存储接口第一件事就是禁掉 JTAG只保留 SWD。标准库的风格是GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);HAL 库则是__HAL_AFIO_REMAP_SWJ_NOJTAG();这一步漏了后面程序一跑就容易莫名其妙死机。具体分配参考EEPROM 挂在 I2C1SCL 用 PB6SDA 用 PB7外接 4.7k 上拉到 3.3VNOR Flash 挂在 SPI1SCK 用 PA5MISO 用 PA6MOSI 用 PA7CS 随便挑一个 GPIO我用的是 PA4SD 卡如果用 SDIO会占用 PC8 到 PC12 一组引脚如果用 SPI 模式可以挂到 SPI2 上引脚冲突好解决。SPI 从机的 CS 不要直接接地留出 GPIO 控制以后想加第二片 Flash 或者换总线模式都有余量。3.2 FPGA 数据通路与握手数据从 FPGA 到 STM32最常见的方案有三种FSMC 并行总线速度快适合大数据吞吐但占引脚太多SPI引脚少速率和稳定性都能兼顾双口 RAM 或异步 FIFO性能最强但电路复杂。中小批量数据的项目我推荐 SPI 加一条中断线的组合。FPGA 作为 SPI 从机数据准备好了把 IRQ 拉高STM32 响应中断后发起 SPI 读一帧读完FPGA 把 IRQ 拉低握手逻辑非常清晰线上出了问题也好定位。帧格式一定不能省。我长期用的格式是帧头 0xAA55加长度字节加设备 ID加 4 字节时间戳加数据载荷最后加 2 字节 CRC16。CRC16 多项式用 0x8005可以直接查表开销很小。STM32 收到帧之后先校验帧头和 CRC校验不过就丢弃并计数绝对不让坏数据进存储介质。这个校验逻辑是后来加上的因为早期直接写盘出现过几次从 SD 卡里读出一段乱码曲线排查到源头就是 SPI 传输出错。3.3 电路设计里的几个坑第一个坑是 I2C 上拉阻值。3.3V 系统经典值是 4.7k如果总线上挂了多个器件总线电容变大上升沿会被拖慢时序一紧张读回的数据就可能错位。这时候把上拉换成 2.2k 能改善代价是待机功耗稍微增加工业板上这点功耗无所谓。第二个坑是 SPI 的 MISO 线没有保护。工业现场干扰大走线又长MISO 上最好串一个 22 到 33 欧的电阻有条件再并一个小电容滤波。CS、SCK、MOSI 一样串电阻靠近 MCU 引脚放置。别觉得多此一举我遇到过现场电焊机一开控制器存储数据就乱的情况后来就是在 SPI 线上加了串阻和滤波问题才压下去。第三个坑是 SD 卡座的选型。工业设备振动大卡座一定要选带锁的推入式别用弹出式否则设备运行过程中卡自己弹出来就尴尬了。卡座的引脚要做 ESD 保护因为现场拔卡非常常见静电打坏控制器的案例不少。数据线按 SD 规范串联 22R 电阻靠近卡座放置位置别放反了。第四个坑是电源去耦。EEPROM、NOR Flash、SD 卡的电源引脚都要单独放 0.1uF 陶瓷电容而且必须靠近引脚。SD 卡上电瞬间电流需求大在卡座附近预留一个 10uF 钽电容位调试时如果发现卡初始化不稳定先怀疑供电。3.4 掉电保持电路掉电时设备还能撑多久直接决定能存多少数据。工业 24V 输入经过 DCDC 降到 3.3V在 3.3V 母线上放储能电容。粗略估算保持时间用这个公式t C * ΔV / I。假设系统总电流 300mA电压允许从 3.3V 降到 2.7VΔV 就是 0.6V用 4700uF 电容那么t 4700uF * 0.6V / 300mA大概 9.4ms。9.4ms 能干什么够把关键的几个参数写进 EEPROM因为一个页写周期才 5ms。但如果想在掉电中断里跑 FatFS 写 SD 卡日志大概率完不成因为 SD 卡上电初始化加文件打开就得几十毫秒。检测掉电用 STM32 的 PVD 或者外部比较器都行。PVD 简单阈值设到 2.9V 左右但要注意 PVD 有滞后和响应延迟实测大概几十微秒到几百微妙设计时序时要留余量。外部比较器方案更可控用电阻分压检测 24V 输入侧一旦掉到阈值以下立刻触发中断比等电源侧电容放完电再动作早得多留给软件的时间也更充裕。4. 软件实现驱动、文件系统与数据管理4.1 EEPROM 驱动别忽略页写和总线死锁I2C 底层用轮询还是中断我建议用轮询简单可靠反正 EEPROM 本来就不快。写 EEPROM 时有两个细节必须处理好。第一个是页写回卷。AT24C02 页大小 8 字节如果起始地址在页内偏移 5连续写 6 个字节第 6 个字节会回到页首地址把前面刚写的数据覆盖掉。解决办法是写之前检查剩余空间如果跨页就拆成两次写或者干脆限制每次写不超过页内剩余字节数。第二个是 I2C 死锁。有时候从机异常会把 SDA 拉低不放主控要能恢复翻转 SCL 九次把总线从死锁状态拽回来重新发 START。这个 reset 函数一定要做而且要在每次写 EEPROM 之前检查总线状态。参数写入的磨损均衡我用双备份加软标记来实现。地址 0 到 N 存参数区 AN1 到 2N 存参数区 B两份都带校验。另外有一个字节专门记录当前有效区是 A 还是 B。每次写入先写非当前区写完后整个数据校验没问题再翻转标记字节。读的时候先看标记区如果标记指示的区和内容校验对不上就自动回退到另一个区。这个方案的优点是即使写一半掉电最多丢一次修改不会出现参数全区损坏重启后还能恢复。代价是容量减半但对 EEPROM 实在的参数数据来说完全划算。4.2 NOR Flash 操作细节与磨损均衡NOR Flash 的编程流程就三件事写使能、页编程、轮询 BUSY。扇区擦除流程也是三件事写使能、扇区擦除、轮询 BUSY。这里有个容易忽略的细节每次操作前都必须发0x06写使能命令别以为之前发过一次就一直有效写了之后该命令状态就清了。页编程每次最多写 256 字节如果要写的数据跨页了要拆成多条 Page Program 命令。擦除后读数据是全 FF这是正常的别当成故障。日志区的设计我用环形扇区。假设划出 N 个扇区每个 4KB写入顺序从扇区 0 到扇区 N-1再回到扇区 0。每个扇区的头部 4 字节存本扇区的写入序号启动时扫描所有扇区头部找到序号最大的那个就是最新日志的起点。写日志时当前扇区写满了先擦除下一个扇区再继续写。这样每个扇区的擦除次数会基本均匀10 万次寿命一个扇区一天擦一次理论能用两百多年完全够用。再提醒一句不要在 NOR Flash 擦除或编程过程中让中断服务程序来访问同一片 Flash。扇区擦除要占 150 到 400ms如果中断里恰好有读 Flash 的操作时序就乱了。所以存储操作要放到任务上下文里执行掉电紧急保存时只写 EEPROM不碰 NOR Flash。4.3 SD 卡 FatFS 移植与掉电安全FatFS 移植需要改三个文件diskio.c 里的disk_initialize、disk_read、disk_write、disk_status、disk_ioctl以及 ffconf.h 里的配置选项。上电后先初始化 SD 卡再 f_mount 挂载文件系统。如果返回 FR_NOT_READY先别慌多数是卡没准备好或者电压握手不匹配。底层初始化函数里最好加重试机制比如重试三次每次间隔 50ms实测能解决不少玄学问题。文件写入策略我总结了一个固定套路。f_open 用FA_OPEN_ALWAYS | FA_WRITE每写满一个对齐到 512 字节的缓存块就 f_write 一次。定期调用 f_sync把缓存刷到卡上建议每写完一条记录或者每十秒做一次。最后关闭文件时 f_close然后 f_mount(NULL) 卸载避免异常拔出时卡在挂载状态。这里再补充一点有时候嵌入式设备里的 SD 卡拿到 PC 上会遇到“根目录无法授权”之类的问题本质上就是卡上的分区文件系统不完整或者被标记成了只读挂载和我们在嵌入式侧强调的“写完全部数据再 f_sync、再更新目录项”是同一件事。为什么要强调 512 字节对齐因为 SD 卡底层最小读写单位是扇区文件系统又是按簇分配通常是 4KB。如果记录边界没有对齐每次写都要先读整个簇、修改、再写回既慢了还增加了 FTL 的写入放大卡寿命会受损。对齐之后写一条记录基本就是一次连续写效率高很多。4.4 掉电保护流程完整设计掉电保护是整个存储方案的灵魂。完整流程按三档设计。第一档掉电中断里只做最小必要操作关闭不必要的全局中断把运行状态、当前配方号、累计计数这些关键参数打包成几十字节写进 EEPROM然后在另一个固定地址写一个“掉电标记”字节比如 0xA5。做完这些就退出中断等系统断电。第二档上电恢复时先读掉电标记如果是 0xA5说明上次是异常掉电做一次恢复处理比如补写一条“上次掉电时间”的日志更新计数器最后清除掉电标记。这套流程里最关键的约束就是掉电中断里绝对不能初始化外设绝对不能跑 FatFS 写 SD 卡。很多同事一掉电就急着把日志刷到 SD 卡结果卡在 FatFS 挂载里主程序还没写完参数电压就没了。记住一句话掉电时只干最小的事能保住关键参数就算赢日志这类后续可以补记的数据等电源恢复再说。4.5 数据帧格式与 FPGA 侧打包逻辑FPGA 采集数据后要用状态机把原始数据打包成统一帧。核心逻辑是FPGA 内部维护一个异步 FIFO写侧是 ADC 时钟域比如 25MHz 采样时钟读侧是 SPI 时钟或系统时钟域。状态机检测到 FIFO 里已经攒够一帧长度后置位“帧就绪”标志拉高 IRQSTM32 收到中断后通过 SPI 把整帧读走。读完后 STM32 回一个 ACK 字节FPGA 收到 ACK 再把 IRQ 拉低开始准备下一帧。状态机一般包括 IDLE、READY、FILLED、REQ、WAIT_ACK、SEND、DONE 这几个状态。跨时钟域处理一定要谨慎。ADC 时钟域到 FPGA 系统时钟域最简单的做法是用两级同步器处理单比特信号多比特数据则必须走异步 FIFO不能直接打拍捕获。这里再加一个小建议在 FPGA 内部给每帧数据打上 32 位本地时间戳。用 FPGA 计数器产生的时间戳比 STM32 读系统时钟更稳定因为 FPGA 不跑操作系统时间抖动小。STM32 只管把它原样存下来事后分析曲线时所有帧在时间轴上自然对齐排查问题会省很多力气。5. 常见问题与排查技巧实录5.1 问题速查表现象可能原因排查/解决EEPROM 读回来全 FF地址错、I2C 无应答、WP 引脚被拉高检查 A0-A2 地址匹配示波器看时序WP 接地I2C 总线 SDA 被拉死从机异常、干扰翻转 SCL 九次复位总线上拉改为 2.2kNOR Flash 擦除后读还有数据擦除命令没发出、BP 保护位、CS 毛刺擦除前读状态寄存器确认发写保护解除命令检查 CS 时序SD 卡 f_mount 失败电压握手、上电时序、SPI 模式卡兼容初始化重试 3 次先 CMD0 再 CMD8换一张卡交叉验证掉电后参数是旧值写序列没完成、双备份逻辑 bug掉电中断里只写关键参数验证标记翻转逻辑FPGA 帧偶发 CRC 错误跨时钟域亚稳态、EMI 干扰、SPI 速率太高加异步 FIFOSPI 降速帧内 CRC16状态机加超时5.2 几个印象深刻的踩坑案例案例一EEPROM 参数在设备运行几天后神秘恢复出厂值。排查了很久发现是 I2C 总线上有别的设备抢占导致 EEPROM 写了一半校验失败恢复逻辑误回退到了出厂备份区。解决方式很干脆写入前先检查总线状态写事务期间关中断避免任务切换打断 I2C 时序。从那之后所有 I2C 写 EEPROM 的代码都加了个临界区保护。案例二SD 卡在写入过程中被拔掉再插上 f_mount 直接失败拿到电脑上也提示格式化。这就是典型的 FAT 表损坏。从那之后我在写日志代码里加了双文件轮替加每次 f_sync文件系统损坏的概率大幅下降。硬件上又加了掉电检测电压低于阈值时系统会在断电前主动关闭 SD 卡事务。这套组合拳下来半年没再出过卡损坏的问题。案例三掉电保存有时候成功有时候失败。用示波器实测发现掉电持续过程只有 6ms而写 EEPROM 需要 5ms加上 I2C 启动和地址字节刚好卡在临界位置。解决办法是把储能电容从 2200uF 加到 4700uF把掉电检测阈值调低一点点晚一点触发留给软件更多电压余量。掉电时间的估算不能只看理论值实测电流和实际掉电波形之后才能定容值。案例四FPGA 和 STM32 之间用 SPI 传数据快速模式 18Mbps 时丢包率很高降到 8Mbps 就好了。原因是 FIFO 读时序和 SPI 时钟的配合不够好不是带宽不够用。这类问题在小批量打样时不容易暴露批量生产之后因为走线差异会更明显所以设计时一定留好降速手段比如配置寄存器里放一个 SPI 分频系数现场出了问题可以通过软件降速。最后再分享一点个人体会这套 EEPROM 管参数、NOR Flash 管固件和日志、SD 卡管历史数据的分级存储思路不止能用在这一个控制器上。边缘网关、变频器、机器人控制器只要数据有“频繁小量”和“偶发大量”的差异都可以照这个框架来做。关键是把三级存储的“业务边界”提前划清楚哪个数据放哪一级别混着放后期运维会舒服很多。如果你正在做类似项目建议先把这一张表画明白再动手写代码。
返回列表