ARTICLE DETAIL

资讯详情

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

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

STM32+FPGA三级存储方案:工业控制器数据分级与掉电保护 1. 整体思路工业控制器数据分级本质是给数据分“居住地”做工业控制器这么多年我踩过最深的一个坑就是试图用一颗芯片解决所有数据存储需求。系统里既有需要每次上电都读的配置参数也有需要频繁更新的运行计数值还有动不动几MB的故障录波和操作日志最初我全部塞进一片大容量EEPROM结果量产之后问题接踵而至EEPROM擦写寿命被日志数据迅速耗尽小容量又塞不下录波系统只要断电瞬间写得不对配置直接丢客户现场三天两头报故障。直到后来改成STM32FPGA的双处理器架构配合EEPROM、NOR Flash、SD卡三级存储才彻底把这个问题理顺。这套方案的核心逻辑很简单不要把不同生命周期的数据存在同一个地方按访问频率、数据量和可靠性要求把数据分配到对应特性的介质上。EEPROM负责“少而精”的关键参数NOR Flash负责“中等容量、频繁更新”的运行数据SD卡负责“大容量、顺序写入”的日志和记录。STM32作为主控负责业务逻辑和文件系统FPGA负责高速数据采集、预处理和对外设的时序控制两边通过内部总线交换数据。这篇文章是“硬件篇”的第12篇我会从方案拆解开始把三级存储的选型逻辑、STM32和FPGA各自的驱动实现、完整的数据流设计、掉电保护方法和排障实录都过一遍适合正在做工业控制器、边缘网关、采集终端的工程师参考。内容里有大量我实测过的代码思路和寄存器细节可以直接抄作业但建议你结合自己的硬件改一改再用。2. 三个存储介质的角色拆解为什么是EEPROM、NOR Flash、SD卡2.1 EEPROM关键参数的“保险柜”按字节改写是最大优势工业控制器里有一类数据比如设备序列号、校验密钥、校准系数、IP地址配置大小通常只有几百字节到几KB但要求极高的可靠性而且更新频次不高。这类数据最适合放EEPROM。EEPROM的核心特征是支持按字节擦写不需要像Flash那样先擦除整个扇区再写入。这在频繁修改单个参数时优势非常明显。比如现场工程师通过调试接口修改一个PID系数如果存的是NOR Flash每次都要先备份整个扇区擦除、写入、再搬回过程繁琐且掉电风险大EEPROM直接把对应的地址单元改写即可代码简单、时序可靠而且写入次数普遍在100万次以上远超普通Flash扇区的1万到10万次擦写寿命。还有一个容易忽略的点EEPROM的原子性操作更容易保证。很多型号支持“写完成”引脚或内部写超时机制硬件设计上可以用GPIO检测写忙状态软件上可以做成“先备份旧值、再写新值、最后校验”的三步流程。我在方案里选用的是I2C接口的AT24C25632KB或M24128256字节一页写页时序和Verilog I2C控制器都很好调。不过EEPROM的容量上限很低最大也就几Mb而且随机写速度慢单字节写入一般要4~5ms所以它绝对不能承担日志、录波这类大流量数据。有些工程师想用EEPROM存历史曲线那就是拿“保险柜”当“仓库”必然出问题。2.2 NOR Flash运行数据的“工作台”快速启动与XIP是杀手锏工业控制器上电后FPGA的配置数据、STM32的启动代码、周期性更新的运行趋势数据比如温度曲线、压力曲线、亚健康故障前后一段时间的缓存这些数据量在几百KB到几十MB之间更新频率可能几秒一次数据完整性要求高。这里就要引入NOR Flash。NOR Flash最大的特点是支持随机寻址读取可以像内存一样直接XIPExecute in Place执行代码很多FPGA配置芯片本身就是NOR Flash。同时它的读速度比EEPROM快得多SPI接口下通常能跑到几十MB/s读吞吐写之前需要擦除但擦写粒度通常是4KB扇区比SD卡底层块擦除更方便做各种管理。现实中的工业设备很多人直接拿NOR Flash做“数据记录卡”配合文件系统或自定义的环形缓冲。它比SD卡可靠的地方在于没有SD卡那种控制器内部状态机和文件分配表的复杂性板载焊接不存在接触不良工作温度范围通常能做到-40℃~85℃甚至更高。缺点是容量普遍偏低典型8MB、16MB、32MB而且受限于擦写寿命频繁擦写同一扇区也会磨损。我在这套方案里选的是W25Q648MB SPI NOR Flash主控通过SPI访问。为了降低磨损我把数据分为“热区”和“冷区”。热区存放频繁更新的运行参数每5秒写一次使用双扇区交替方案A/B扇区轮换冷区存放固件备份和配置镜像只有升级时才会擦写。热区的磨损均衡见后面第4章。2.3 SD卡日志与历史数据的“档案馆”大容量但可靠性需要额外保护第三个层级是SD卡用于存数据量最大、访问频率相对较低但也可以周期性追加的内容故障录波文件、每天24小时不间断的运行历史、操作日志、统计数据报表。一张32GB的工业SD卡按每次录波2MB算能存上万条足够跑好几年。SD卡的优势无需多说容量大、可插拔方便现场工程师拷数据回后台分析。劣势也很明显一是接口复杂度高SDIO/SPI两种模式SD卡内部还有复杂的寄存器状态机初始化序列错了或卡内部寄存器锁死就识别不到二是管理复杂需要文件系统FatFS来组织文件而文件系统对掉电敏感突然断电可能损坏文件分配表三是接触式接口在振动环境容易接触不良工业场合最好用带卡帽的座子并为SD卡提供独立供电和掉电检测。所以在分级方案里SD卡只负责“顺序写大块数据”不做频繁小数据写入。日志写入采用“先写满一个块再一次性flush”的策略数据文件按天命名配合CRC校验和文件头信息即使掉电也会丢最近几行不会损坏整个文件。这三者的组合本质上是把“容量、速度、寿命、可靠性”这组矛盾按数据特征化解开了EEPROM管“关键低频”NOR Flash管“中等高频”SD卡管“海量顺序”。下面我讲讲STM32和FPGA具体怎么分工以及它们之间怎么把数据流倒腾起来。3. STM32与FPGA分工主控管脑FPGA管手和眼睛3.1 STM32侧接口设计从I2C到SDIO驱动模型长什么样STM32在这套系统里是“大脑”。它负责初始化三个存储介质、运行FatFS文件系统、解释FPGA上报的数据并落盘、响应上位机指令。对应到外设EEPROM通过I2C1连接STM32作为主机400kHz快速模式即可原子操作靠软件时序保证。读取时我喜欢用“连续读”一次拉出整个参数块写入时按页写每页不超过256字节每次写前读回校验。NOR Flash挂在SPI1上最大时钟拉到45MHzW25Q64支持50MHz的话就用45MHz预留一点裕量。SPI通信我用的DMA方式触发条件从定时器来保证写录音波缓存时CPU不被阻塞。SD卡使用SDIO外设跑4-bit模式配上eMMC/SD卡时钟分频。注意SD卡初始化时要按“低时钟慢速识别、高时钟快速传输”的流程来否则很多卡兼容性不好。文件系统用的FatFS配置了_USE_MKFS和长文件名支持盘符映射到SD卡。三个存储设备在程序设计上是三层结构设备驱动层封装AT24C256的读写函数、W25Q64的读/擦/写函数、SD卡初始化/读块/写块函数。抽象存储层定义统一接口比如store_read(uint8_t device_id, uint32_t addr, void* buf, uint32_t len)上层业务完全不关心当前操作的是EEPROM还是Flash。业务层参数管理模块读配置→校验→应用、日志模块格式化日志头→追加记录→自动切换文件、升级模块接收固件→写入NOR Flash备用区→校验→启动切换。这样的好处是以后如果EEPROM换成FRAM铁电存储或者NOR Flash换成更大的型号只要改设备驱动层业务代码一行不动。3.2 FPGA侧的接口与状态机Verilog写出I2C、SPI和UART调试口FPGA在这套方案里的角色是“数据采集前端 时序处理器”。工业控制器的外部信号模拟量、编码器脉冲、高速开关量、来自通信FPGA的并行数据要先经过FPGA做滤波、抽取、格式转换整理成一定长度的数据包再送给STM32去存储。FPGA需要实现的存储相关逻辑主要有三个I2C Controller用于读取板载温度传感器、EEPROM里的校准参数如果EEPROM直接挂在FPGA侧省一条总线到STM32。Verilog写I2C从机/主机都要注意开漏输出和上拉电阻的模拟核心状态机是START、SLAVE_ADDR、ACK、DATA、STOP每个状态里用计数器精确产生SCL时钟。我建议直接把时序参数做成局部变量方便按不同频率调整。SPI Master/Follower如果NOR Flash由FPGA直接管理有些高速采集设备不让STM32频繁进中断FPGA要能发SPI命令读JEDEC ID、擦除、写页、读数据。SPI时序是四根线MOSI/MISO/SCLK/CSFPGA实现的重点是CPOL/CPHA配置要和芯片手册一致W25Q64是Mode0CPOL0CPHA0这个错了会读到全0。UART调试口FPGA把内部状态通过UART打印出来比如记录缓冲FIFO的占有率、最近一次存储操作的返回值。FPGA实现UART_RX接收仿真是我写过很多次的模块核心就是检起始位、以16倍波特率时钟采样8个数据位、检测停止位用三段式状态机避免亚稳态。最容易被忽略的是FPGA的内部FIFO。当外部信号速率很高比如AD采集持续每秒2M点每点2字节瞬间就要写4MB/s而存储介质并不能持续接收这个速率。FPGA内部要做一个“弹性队列”数据进来先进FIFOFIFO接近半满时通过GPIO中断通知STM32来搬数据。FIFO深度我通常选至少512×32bit并用Xilinx/Vivado或Intel的RAM IP核读侧和写侧重置逻辑要处理好防止读写同时操作时出现数据跳变。3.3 STM32与FPGA之间的数据流双口RAM、SPI从机、并行总线怎么选两个处理器之间的通信方式决定整个系统的吞吐瓶颈。工业控制器里我见过三种常用方式SPI从机方式STM32做SPI主机FPGA做从机。FPGA内部寄存器映射到一组地址STM32通过发送地址写入数据来投递命令读取FPGA状态则发送读命令然后接收。这种方式连线少4根线速率在10~30MHz左右适合命令交互和小包数据。缺点是大块录波数据搬运效率低每传输一个16位字都要走SPI协议。并行总线方式FPGA模拟异步SRAM接口STM32的FSMC外设直接访问FPGA内部地址空间。STM32写某个地址时FSMC自动产生地址信号、片选、写使能FPGA在总线上采样地址和数据写入内部寄存器或FIFO。这种方式吞吐率高一个总线周期就能完成一次16位数据交换适合大量波形传输。双口RAM方式在FPGA内例化一块DPRAMSTM32和FPGA各接一侧通过中断/标志位做邮箱握手。STM32写DPRAM的特定地址FPGA那边轮询或者读标志。代价是占用FPGA内部Block RAM资源敏捷开发时不容易管理容量。我在硬件篇第7篇里详细比较过这三种这里直接给结论如果产品要支持8kHz/16通道同步采样录波文件没想好半小时一小结那就必须用并行总线或双口RAM如果只是温湿度、压力、流量这类低频数据的记录SPI从机完全够。这套方案里因为涉及故障录波最高采样率1MSPS我选择并行FSMC方式并且加了握手寄存器地址0x0000写命令寄存器地址0x0002写数据长度寄存器地址0x0004写数据FIFO端口地址0x0006读状态寄存器bit0FIFO空bit1FIFO半满bit2新数据就绪STM32端用FSMC的Bank1片选信号NE116位数据宽度时序配置里最关键的是ADDSET和DATAST参数要根据STM32的HCLK频率和FPGA内部逻辑延迟来调调错了FRAM的读时序很容易超时或者读到跳变。实测STM32F407主频168MHz一块FPGA内部逻辑带3个时钟周期的读延迟ADDSET设15、DATAST设30比较稳。4. 实操过程从零搭建STM32FPGA分级存储系统4.1 环境与工具准备STM32CubeMX Keil5 工程配置开始敲代码之前先准备好硬件和软件环境。我用的开发板是自研的工业控制板核心芯片是STM32F407ZGT6 Xilinx Artix-7XC7A35T板载一片AT24C256、一片W25Q64、一个MicroSD卡座。你可以用等价开发板替代只要保证EEPROM、NOR Flash和SD卡接口占用对应的IO即可。软件环境STM32CubeMX 6.x生成初始化代码Keil MDK 5.x编译和下载STM32程序要注意Keil5兼容C51和STM32安装时两个包要分开装否则器件库会冲突我遇到过装完C51后STM32工程找不到芯片后来把工具链定位到ARM目录才解决Vivado 2020.2或更高FPGA工程或者用QuartusModelSim仿真Verilog模块FatFS源码R0.15或新版SFUD串行通用Flash驱动库或者手写W25Q64驱动需要特别说明的是STM32CubeMX里配置I2C/SPI/SDIO时注意GPIO的速率等级。SPI时钟超过10MHz时对应引脚速度必须选Very High否则方波会变形通信距离稍远就会误码。SDIO-D0~D3同样如此。另外如果你的磁珠或电容在SD卡电源附近上电时序最好用软启动避免卡初始化时电流冲击供电。4.2 EEPROM读写HAL库I2C与Verilog I2C参考代码STM32侧用HAL库操作EEPROM建议不要每次调用都查忙而是写一个带超时的写流程#define EEPROM_ADDR 0xA0 // AT24C256 8位地址A2接地时的设备地址 HAL_StatusTypeDef eeprom_write_page(uint16_t mem_addr, uint8_t *data, uint32_t len) { HAL_StatusTypeDef err; uint32_t timestamp HAL_GetTick(); err HAL_I2C_Mem_Write(hi2c1, EEPROM_ADDR, mem_addr, I2C_MEMADD_SIZE_16BIT, data, len, 50); if (err ! HAL_OK) return err; // 等待写周期完成 while (HAL_GetTick() - timestamp 1000) { err HAL_I2C_Mem_Read(hi2c1, EEPROM_ADDR, mem_addr, I2C_MEMADD_SIZE_16BIT, tmp, 2, 50); if (err HAL_OK) break; HAL_Delay(1); } return HAL_OK; }AT24C256的页大小是64字节注意不是所有EEPROM都是256字节AT24C256是64字节一页跨页写会自动回卷所以业务层写参数时如果跨越页边界必须拆成两次写。我在实际项目中见过同事直接一次性写200字节结果最后一部分写到了页首参数被错乱覆盖后面排查了很久才发现是页边界问题。FPGA侧的Verilog版I2C控制器我贴一个状态机骨架重点是发送7位从机地址后读ACK的时机module i2c_master ( input clk, rst_n, input start, wr_rd, input [6:0] dev_addr, input [7:0] mem_addr, mem_addr16, input [7:0] data_in, output reg ack, done, inout sda, output scl ); // 状态机IDLE - START - DEV_ADDR - ACK1 - MEM_ADDR_H/L - ACK2 - DATA - ACK3 - STOP endmodule写I2C状态机多年最大的经验是靠参数化分频器生成SCL采样SDA时要在SCL高电平中段采样并且最好对SDA做两级同步打拍避免采样亚稳态。我在ModelSim里用uvm或普通testbench仿真建议在最顶层加上拉模型否则signalsim只会看到Z状态误以为总线没驱动。4.3 NOR Flash的擦写、SFUD与磨损均衡W25Q64操作代码思路W25Q64是用SPI访问的NOR Flash指令集有0x9F读ID、0x06写使能、0x20扇区擦除 4KB、0x02页编程 256B、0x03读数据等。初始化时先读JEDEC ID确认型号避免上位机软件误操作。使用SFUD库能省去大量底层时间。但直接上SFUD会有一个问题它的默认配置是通用模板如果你的Flash型号不在SFUD内置列表里它会自动进入QSPI模式判断失败。所以用SFUD之前建议先手动用HAL库读一次ID打印出来确认是0xEF4017再做静态配置。如果不想引入SFUD手写驱动也很简单关键是对扇区擦除状态轮询擦除或写入完成后读状态寄存器bit0BUSY为0说明就绪不要无脑延时因为不同环境温度和老化程度下擦除时间差异很大。磨损均衡我在工程里是这样实现的用一个4KB扇区作为“状态扇区”保存当前活跃数据所在的扇区编号和擦写计数器每擦写一次活跃扇区切换到下一个可用扇区同时把擦写计数加1并同步更新状态扇区状态扇区本身也做备份两个互为镜像。这样W25Q64 16Mbit约512个4KB扇区成本很低却把寿命延长了几十倍。但要注意频繁写状态扇区也会导致状态扇区先损毁所以状态扇区要双份且启动时要校验选择较新的那个。逻辑上更稳妥的还是给每个数据记录头部加24位递增序号。写数据时先写数据再写“已提交”标志。如果掉电在写数据过程中下次启动发现数据头无效或校验失败就丢弃该记录如果掉电在“已提交”写一半则用标志位区域的数据来判断。这套“日志事务”思想在NOR Flash上比依赖文件系统更可靠。4.4 SD卡初始化和FatFS移植识别慢卡与寄存器锁死处理SD卡的初始化过程比较“讲究”。SD卡支持SDIO和SPI两种模式如果你用SDIO 4-bit初始化步骤是发送 CMD0进入空闲态注意CMD0必须在低于400kHz的时钟下如果时钟太快很多卡直接无响应。发送 CMD8带电压校验判断是否支持高容量SDHC。重复发送 ACMD41需要在CMD55 0x41直到卡返回ready。读取 CSR 寄存器CMD9确认卡容量、CRC支持然后再提高时钟到25MHz左右。我在现场遇到过一张“新卡”始终初始化失败现象是上电后卡直接不响应CMD8。手动上电发现卡内部寄存器锁死这时候除了重新插拔或给卡供电复位释放没有软件办法。所以硬件上一定要给SD卡座加一个可由STM32控制的电源开关比如MOS管软件初始化失败时自动断电再上电发送CMD0重新复位成功率能提高很多。FatFS移植注意三点disk_read/disk_write函数要返回RES_PARERR等错误码并且只有在扇区地址合法时才操作否则疯狂的指针错误会让你调试到崩溃。长文件名功能会占大量内存STM32F407没问题但小于128K SRAM的MCU要谨慎。SD卡写入是块512字节为单位你如果调用f_write写几个字节每字节都会触发读-改-写性能和寿命都很差。所以日志模块必须做一个内部缓冲区至少512字节攒满一块再调用一次写入。这也是为什么很多设备的日志按小时或按天分段写起来是顺序流不需要随机改文件。Android Studio无法对SD卡根目录授权的问题那是手机开发场景和嵌入式SD卡不一样工业设备用FatFS没有权限模型不涉及这个只是最近搜热词经常关联到顺便提醒一下别把两个SD卡概念搞混。4.5 异常掉电保护与数据完整性双备份、CRC、顺序日志如何组合工业控制器掉电是常态而且是“随机时刻掉电”所以每级存储都要有自己的保护手段。EEPROM存储配置参数时我采用“双槽位CRC”。定义两个相同的参数区每个区末尾放16位CRC和“有效标志”。启动时先读A区校验失败读B区两个都失败则载入出厂默认值。写参数时先写A再写B如果写A后掉电B还是旧值启动时比较A/B的版本号每次写都会把版本号1选版本号大的那个同时把另一个补写。NOR Flash用于存趋势数据时每条记录内部有一个magic头比如0x5A5A、长度、CRC32。写入顺序是从扇区头往后顺序写写完一条后更新扇区头部的“写位置指针”。掉电后启动扫描指针所指位置如果下一条记录校验失败说明那是掉电碎片直接废弃从指针处重新开始。这种“只后加、不修改”的机制配合磨损均衡能让NOR Flash在频繁掉电环境下十年不坏。SD卡文件系统掉电容易丢目录所以日志文件不要开放随机写。我这样设计SD卡上每个日志文件固定block大小4KB写完一整个block才触发f_sync平时数据先缓存在SRAM/FPGA FIFO里。一个文件的最后一个block总是“未关闭”下次启动时通过查找文件尾部的“最后一条记录ID”决定是续写还是新建文件。另外文件头保存创建时间和记录格式版本这样即使文件系统故障也能用专用恢复工具从块设备层面找回数据。CRC和双备份会占用一些存储空间但对工业设备完全值得。EEPROM或NOR Flash里的数据最终要以“无论何时掉电恢复后数据状态一致”为目标这比追求最大容量更重要。5. 常见问题与排查实录十几个坑帮你提前踩平现象可能原因排查方法与解决EEPROM写入后读回全是0xFFI2C地址不对A0/A1/A2引脚电平没对上用逻辑分析仪抓START后的从机地址字节确认7位地址和引脚电平一致EEPROM写长串数据错乱跨页边界写自动回卷覆盖了页首数据写之前判断剩余长度超过页剩余字节就拆包NOR Flash读ID全0x00SPI极性/相位配置错误或者CS拉低时序不对先用1MHz慢时钟试逐项检查CPOL/CPHA用示波器看MISO在时钟上升沿是否有效NOR Flash写不进数据擦除后没有写使能或者状态寄存器BUSY一直为1每次写/擦命令前发0x06并轮询状态寄存器而不是固定delayFlash寿命很快耗尽热门扇区被反复擦写实现双扇区交替或者环形块管理提高磨损均衡等级SD卡初始化失败上电时时钟太快或卡内部锁死把初始化时钟降到250kHz调慢增加卡电源硬复位电路SD卡插上后读取扇区0错误文件系统损坏或SPI模式时钟极性不对尝试用逻辑分析仪抓CMD0-ACMD41时序用读卡器在PC上格式化并用恢复工具修复FatFSf_mount挂载失败分区表损坏或卡是GPT格式而非MBRFatFS默认只支持MBR分区SD卡格式化时选FAT32/MBR不要用GPT格式化上位机看到录波文件里的数据有跳变FSMC时序过紧FPGA端读到半更新数据调大ADDSET/DATAST或者在FPGA数据总线上加三态缓冲与握手信号FPGA与STM32并行通信偶发乱码没有做总线仲裁两端同时写加读写互斥寄存器或者用信号量/令牌机制同一时刻只允许一端占用数据总线掉电后配置参数丢失写EEPROM过程中掉电新值写一半改为双备份版本号机制启动时选择高版本掉电后SD卡日志文件打不开FatFS文件分配表损坏日志写满一个块才sync减少掉电窗口做块级日志恢复程序STM32的delay函数卡死SysTick被占用或中断优先级设置异常检查是否有高优先级中断长时间占用CPUHAL_Delay依赖SysTick禁止在定时器中断里长时间调用Keil工程编译后无法下载程序芯片的JTAG/SWD引脚被复用简单时用Flash Loader擦除或在代码里禁用JTAG前预留一段时间窗口STM32禁用JTAG实测里还有一个很容易忽略的当NOR Flash和SD卡同时工作两路数据并发时STM32的DMA请求优先级可能冲突。我建议把SDIO的DMA配置成高优先级SPI Flash的DMA配置成低优先级同时写数据前做一个“互斥信号量”避免两个DMA同时访问同一块SRAM缓冲否则会看到数据错位很难排查。6. 最后再说两个小细节第一工业控制器的存储方案不要迷信单一大容量芯片。把关键参数、运行变量、历史记录混在一个存储设备里出问题时整个数据全丢最后造成的损失远大于省下的几块钱物料成本。我见过不少产品的售后问题最后定位都是“Flash里配置区被日志区写坏了”这种问题在项目初期把数据分层设计好就能避免。第二FPGA在方案里并不仅仅是被动搬运工。比如I2C读写EEPROM的Verilog模块、UART_RX接收仿真这些在FPGA工程里单独实现后我习惯在仿真阶段就验证完毕等到实际联调时省很多时间。另外所有跨时钟域的数据FPGA内部时钟 vs STM32的FSMC异步总线都要用FIFO或同步握手打拍这比优化算法更影响系统稳定性。这套分级存储方案后续还能扩展的方向是在NOR Flash上实现简单的OTA升级备份区、把SD卡换成eMMC板上焊死防震动、在EEPROM里记录硬件健康状态当前运行时长、高温累计时间。如果你正在做工业边缘网关或通信测试终端欢迎照着这套架构搭一遍有问题评论区交流我会在后面的“硬件篇·13”里继续拆一个和存储相关的话题。
返回列表