STM32H743以太网数据采集与SD卡存储方案设计

📅 发布时间:2026/9/24 9:17:09
STM32H743以太网数据采集与SD卡存储方案设计
1. 项目缘起与整体设计思路STM32H743IIK6 这颗芯片在工业数据采集圈子里热度一直不低480MHz 的 Cortex-M7 内核、2MB Flash、1MB RAM加上自带以太网 MAC 控制器做一台带网络上报能力的数据记录仪几乎是“量身定做”的配置。我手上这个 Sensor Gateway 项目核心目标很明确把现场多路传感器数据采集上来通过以太网实时上报同时把原始数据完整落盘到本地存储保证断网期间数据不丢网络恢复后能补传。这个需求听起来简单但真正落地时会遇到几个绕不开的问题。第一传感器接口五花八门有 SPI 的、I2C 的、UART 的还有 4-20mA 模拟量的STM32H743IIK6 的外设资源虽然丰富但引脚分配和 DMA 通道冲突需要提前规划。第二以太网数据上报和本地数据记录如果放在同一个任务里串行执行网络抖动会直接拖慢采集节奏导致采样周期不稳定。第三数据落盘涉及文件系统掉电保护、写入均衡、文件碎片这些问题在长时间运行中会逐渐暴露。我最终采用的方案是FreeRTOS 多任务架构采集任务、网络任务、存储任务各自独立通过消息队列传递数据以太网使用 LwIP 协议栈跑 TCP 长连接上报本地存储用 FatFs 挂载 SD 卡按小时切分文件配合环形缓冲区做掉电保护。这个架构的好处是各模块解耦网络断了不影响采集和存储存储满了也不影响网络上报系统鲁棒性明显提升。选 STM32H743IIK6 而不是用 Linux 方案主要是考虑成本和实时性。Linux 方案虽然开发快但启动时间长、功耗高、BOM 成本至少翻倍。STM32H743IIK6 裸机加 FreeRTOS 的方案启动时间可以压到 200ms 以内整机功耗控制在 1W 左右对于电池供电或者 PoE 供电的场景都更友好。当然代价就是开发复杂度高网络协议栈、文件系统、任务调度都要自己调优这也是这个项目最有挑战的地方。提示STM32H743IIK6 的以太网 MAC 支持 RMII 和 MII 两种接口模式RMII 引脚少但需要外部 50MHz 时钟MII 引脚多但时钟灵活。我选的是 RMII 模式配合 LAN8720A PHY 芯片整体布线更简洁。2. 硬件选型与关键参数计算2.1 主控与外设资源分配STM32H743IIK6 是 BGA 封装176 个引脚实际可用的 GPIO 有 140 多个听起来很多但真正分配起来还是要精打细算。我把外设分成三组传感器接口组、通信接口组、存储与调试组。传感器接口组包括 2 路 SPI、2 路 I2C、3 路 UART 和 4 路 ADC。SPI1 跑 20MHz 接高速 IMUSPI2 跑 10MHz 接气压计I2C1 接温湿度传感器I2C2 接 OLED 显示屏做本地状态显示UART2 和 UART3 接 RS485 转换芯片用于工业传感器ADC1 的 4 个通道接 4-20mA 电流环通过 165 欧姆精密电阻转换成 0.66V-3.3V 电压。通信接口组就是以太网 RMII 接口占用 9 个引脚REF_CLK、MDIO、MDC、CRS_DV、RXD0、RXD1、TXEN、TXD0、TXD1。这里有个坑要注意STM32H743 的 RMII REF_CLK 必须由外部 PHY 提供 50MHz 时钟不能直接用 MCU 的 MCO 输出否则时钟抖动会导致丢包。我一开始图省事用 MCO 输出结果 ping 包丢包率 5% 左右换成 PHY 的 50MHz 输出后丢包率降到 0.01% 以下。存储与调试组包括 SDMMC1 接口和 SWD 调试口。SDMMC1 跑 4 位模式时钟 50MHz理论带宽 25MB/s实际写入速度受 SD 卡本身限制我用的是工业级 pSLC 卡持续写入能稳定在 8MB/s 左右足够应付 1kHz 采样率下的数据吞吐。2.2 电源与时钟树设计电源部分STM32H743IIK6 需要 1.2V 核心电压和 3.3V IO 电压。1.2V 用内部 SMPS 降压得到效率比 LDO 高不少实测在 480MHz 全速运行时核心电流约 220mASMPS 效率 85% 左右LDO 只有 60% 左右发热差距很明显。3.3V 用外部 DC-DC 从 12V 或 PoE 降压得到留足 1A 余量。时钟树配置是 STM32H743 的难点之一。外部晶振用 25MHz经过 PLL1 倍频到 480MHz 给 CPUPLL2 倍频到 200MHz 给以太网 MACPLL3 倍频到 48MHz 给 USB 和 SDMMC。这里要注意 PLL 的 VCO 输入频率必须在 1MHz-16MHz 之间25MHz 晶振需要先分频到 5MHz 再进 PLL否则锁相环不稳定。我见过有人直接 25MHz 进 PLL结果系统跑着跑着就死机查了半天才发现是 VCO 输入超范围。注意STM32H743 的 AXI 总线、AHB 总线、APB 总线时钟比例要合理配置AXI 和 AHB 建议 240MHzAPB 建议 120MHz。如果 APB 时钟超过 120MHz部分外设如 I2C的时序计算会出错导致通信失败。2.3 以太网 PHY 与变压器选型PHY 芯片我选的是 LAN8720A性价比高、驱动成熟、RMII 接口简单。变压器用 HanRun HR911105A集成 RJ45 座支持 10/100Mbps 自适应。这里有个细节LAN8720A 的 RBIAS 电阻必须用 12.1kΩ 精度 1% 的电阻这个电阻决定 PHY 内部偏置电流阻值偏差大会导致信号眼图变差长距离网线通信不稳定。PCB 布线方面RMII 的 50MHz 时钟线要走等长差分长度差控制在 5mil 以内TX 和 RX 差分对阻抗控制 100Ω包地处理。我第一版 PCB 没注意这些网线插 3 米以上就丢包重新布线后 30 米网线跑满 100Mbps 无压力。3. 软件架构与核心模块实现3.1 FreeRTOS 任务划分与优先级设计整个系统跑 FreeRTOS任务划分如下表任务名称优先级栈大小职责SensorTask51024字周期采集传感器数据写入队列NetTask42048字从队列取数据TCP 上报StorageTask32048字从队列取数据写入 SD 卡MonitorTask2512字系统状态监测看门狗喂狗LedTask1256字状态指示灯控制优先级设计的原则是采集任务最高保证采样周期稳定网络和存储任务次之允许一定延迟监测和指示灯任务最低不影响核心功能。这里有个经验网络任务的栈要给足LwIP 的 TCP 发送函数在重传时会递归调用栈不够会直接硬件异常。我一开始给 1024 字跑几个小时就死机加到 2048 字后稳定运行。任务间通信统一用消息队列队列元素是一个结构体包含时间戳、传感器 ID、数据长度和数据缓冲区指针。数据缓冲区用内存池管理避免频繁 malloc/free 导致内存碎片。内存池大小根据最大突发数据量计算假设 8 路传感器每路 100Hz 采样每包 32 字节突发 1 秒的数据量是 8×100×3225.6KB内存池给 64KB 留足余量。3.2 LwIP 协议栈移植与 TCP 长连接实现LwIP 版本用的是 2.1.3移植到 STM32H743 主要改三个文件ethernetif.c 负责底层网卡驱动lwipopts.h 负责协议栈配置sys_arch.c 负责操作系统适配。ethernetif.c 里最关键的是 low_level_output 和 low_level_input 两个函数。low_level_output 把 pbuf 里的数据拷贝到以太网 DMA 描述符然后启动发送low_level_input 从 DMA 描述符读取数据封装成 pbuf 返回给协议栈。这里要注意 DMA 描述符的对齐问题STM32H743 的以太网 DMA 要求描述符地址 4 字节对齐pbuf 数据缓冲区也要 4 字节对齐否则 DMA 传输会出错。lwipopts.h 里几个关键配置TCP_MSS 设为 1460TCP_SND_BUF 设为 4×TCP_MSSTCP_WND 设为 4×TCP_MSSMEM_SIZE 设为 16KBPBUF_POOL_SIZE 设为 16。这些参数根据实际网络环境调整如果网络延迟大TCP_SND_BUF 要加大否则发送窗口太小会影响吞吐量。TCP 长连接实现上我采用“心跳重连”机制。客户端每 5 秒发一个心跳包服务端收到后回 ACK如果连续 3 个心跳没收到 ACK判定连接断开主动关闭 socket 并重新连接。重连间隔采用指数退避第一次 1 秒第二次 2 秒第三次 4 秒最大 30 秒。这样既能快速恢复又不会在网络故障时疯狂重连导致网络风暴。实操心得LwIP 的 TCP 发送函数 tcp_write 返回 ERR_MEM 时不要立即重试要等一段时间再试。我一开始在 while 循环里死等结果网络任务卡死采集任务也受影响。后来改成返回 ERR_MEM 就退出等下一个周期再发系统稳定性大幅提升。3.3 FatFs 文件系统与掉电保护策略SD 卡文件系统用 FatFs R0.14b配置为 _FS_REENTRANT1 支持多任务访问_USE_LFN2 支持长文件名_MAX_SS512 扇区大小 512 字节。文件组织方式是按小时切分文件名格式为 LOG_YYYYMMDD_HH.log比如 LOG_20250101_14.log 表示 2025 年 1 月 1 日 14 点的数据。每个文件内部是二进制格式每条记录包含 4 字节时间戳、1 字节传感器 ID、1 字节数据长度、N 字节数据、2 字节 CRC 校验。这种格式比 CSV 紧凑写入速度快解析也方便。掉电保护是重点。SD 卡写入不是原子操作如果写到一半掉电文件系统会损坏。我的做法是每写 512 字节一个扇区就调用 f_sync 同步一次确保数据真正落盘。f_sync 会触发 FatFs 更新 FAT 表和目录项虽然会降低写入速度但能保证掉电后文件系统不损坏。实测 f_sync 一次约 2ms对于 8MB/s 的写入速度来说开销可以接受。另外我在 SD 卡根目录放了一个 LAST_FILE.TXT记录当前正在写入的文件名。系统启动时先读这个文件如果存在且文件末尾有未完成的记录就截断到最后一个完整记录然后继续追加写入。这样即使掉电最多丢失最后一条记录不会丢失整个文件。3.4 数据采集与传感器驱动实现传感器驱动我封装成统一接口每个传感器实现 init、read、get_id 三个函数上层采集任务通过函数指针数组调用新增传感器只需要实现接口并注册到数组即可。以 SPI 接口的 IMU 为例初始化流程是配置 SPI1 为 Master 模式时钟极性 CPOL1、相位 CPHA1波特率预分频到 20MHz配置 DMA 通道SPI1_TX 和 SPI1_RX 各用一个 DMA 流配置 IMU 的中断引脚数据就绪时触发外部中断中断里给采集任务发信号量。读取流程是采集任务等待信号量收到后启动 DMA 传输读取 IMU 的 6 轴数据加速度 3 轴角速度 3 轴共 12 字节。DMA 传输完成后触发回调在回调里把数据写入消息队列。整个过程 CPU 占用极低实测 1kHz 采样率下 CPU 占用不到 5%。I2C 接口的温湿度传感器稍微麻烦一点因为 I2C 是半双工读写要分两次。我的做法是用 I2C 的 DMA 模式先发寄存器地址再读数据中间用中断处理状态切换。这里要注意 I2C 的时钟延展问题有些传感器在转换期间会拉低 SCL如果 MCU 不支持时钟延展通信会失败。STM32H743 的 I2C 支持时钟延展配置 I2C_TIMINGR 寄存器时把 SCLDEL 和 SDADEL 设大一点即可。4. 系统调试与常见问题排查4.1 以太网通信不稳定问题排查以太网调试是最耗时间的环节我遇到过的典型问题有三个。第一个是 ping 不通。排查思路先看 PHY 的 LINK 灯亮不亮不亮说明物理层没连通检查网线、变压器、RJ45 座焊接亮但 ping 不通用示波器看 RMII 的 50MHz 时钟有没有没有说明 PHY 没工作检查 PHY 的复位引脚和配置引脚有时钟但 ping 不通检查 MAC 地址配置STM32H743 的 MAC 地址在 ETH_MACA0HR 和 ETH_MACA0LR 寄存器里如果全 0 或者全 F 会导致通信异常。第二个是丢包严重。我遇到过一次丢包率 10% 的情况查了半天发现是 DMA 描述符数量太少。STM32H743 的以太网 DMA 支持多描述符环形缓冲我一开始只配了 4 个网络流量大时描述符用完就丢包。改成 16 个后丢包率降到 0.1% 以下。描述符数量根据网络流量计算假设 100Mbps 满速每个包 1500 字节每秒约 8000 个包描述符至少要能缓冲 10ms 的数据即 80 个包所以 16 个描述符在非满速场景下够用满速场景建议 32 个以上。第三个是 TCP 连接频繁断开。排查发现是 LwIP 的 TCP 保活时间太短默认 2 小时但我的心跳机制是 5 秒两者冲突。解决办法是在 lwipopts.h 里把 TCP_KEEPIDLE 设为 10 秒TCP_KEEPINTVL 设为 5 秒TCP_KEEPCNT 设为 3 次这样 TCP 层的心跳和应用层心跳协调工作连接稳定性明显提升。4.2 SD 卡写入失败与文件系统损坏修复SD 卡问题主要集中在写入失败和文件系统损坏两方面。写入失败常见原因SD 卡进入写保护状态、卡容量满、SPI/SDMMC 通信错误。排查时先用 f_getfree 查剩余空间空间够就看 SDMMC 的错误寄存器STM32H743 的 SDMMC 有专门的错误状态寄存器能看出是 CRC 错误、超时错误还是 FIFO 错误。CRC 错误一般是时钟太快把 SDMMC 时钟从 50MHz 降到 25MHz 试试超时错误一般是卡响应慢加大超时时间FIFO 错误一般是 DMA 配置问题检查 DMA 的突发长度和 FIFO 阈值。文件系统损坏修复如果 FatFs 挂载失败先用 f_mount 返回 FR_NO_FILESYSTEM说明 FAT 表损坏。这时候不要急着格式化先用 winhex 之类的工具看 SD 卡原始数据如果数据区还有有效数据可以用 FatFs 的 f_mkfs 重建 FAT 表但会丢失目录结构。更稳妥的做法是平时就做好文件切分每个文件不超过 100MB损坏一个文件不影响其他文件。避坑技巧SD 卡不要用消费级的 TLC 卡写入寿命短长时间记录容易坏块。用工业级 pSLC 卡或者高耐久 TF 卡虽然贵一点但稳定性好很多。我实测某品牌消费级卡连续写入 72 小时就出现坏块换成工业级卡后连续跑 30 天没问题。4.3 系统死机与看门狗复位问题系统死机是最头疼的问题因为现场设备死机后无法调试。我的做法是第一启用独立看门狗 IWDG超时时间 2 秒MonitorTask 每 500ms 喂一次狗第二启用硬件异常记录在 HardFault_Handler 里把寄存器状态写入备份 SRAM重启后读取分析第三关键任务加栈溢出检测FreeRTOS 的 configCHECK_FOR_STACK_OVERFLOW 设为 2栈溢出时触发回调记录任务名。我遇到过两次死机一次是网络任务的栈溢出LwIP 的 tcp_write 在重传时递归调用导致栈不够另一次是 SD 卡写入时 DMA 传输错误中断里访问了空指针。两次都是通过备份 SRAM 里的异常记录定位的。所以强烈建议在项目初期就把异常记录机制做好后期调试能省很多时间。4.4 常见问题速查表现象可能原因排查方法解决方案ping 不通PHY 未工作测 50MHz 时钟检查 PHY 复位和配置引脚丢包严重DMA 描述符不足看 ETH_DMAMFBOCR 寄存器增加描述符数量到 16 以上TCP 频繁断开保活时间冲突抓包看断开原因调整 TCP_KEEPIDLE 参数SD 卡写入失败时钟太快看 SDMMC 错误寄存器降低 SDMMC 时钟文件系统损坏掉电时正在写入检查 FAT 表增加 f_sync 频率系统死机栈溢出看备份 SRAM 记录加大任务栈采集周期不稳任务优先级低看任务切换频率提高采集任务优先级网络吞吐量低TCP 窗口太小抓包看窗口大小加大 TCP_SND_BUF5. 性能优化与长期运行稳定性5.1 网络吞吐量优化网络吞吐量优化主要从三个层面入手TCP 参数调优、DMA 描述符优化、任务调度优化。TCP 参数方面TCP_MSS 设为 1460 是标准值但如果网络 MTU 支持 9000 字节的巨帧可以设到 8960吞吐量能提升 30% 左右。不过巨帧需要交换机支持普通场景还是用 1460 稳妥。TCP_SND_BUF 和 TCP_WND 设为 4 倍 MSS即 5840 字节这样发送窗口足够大不会因为等待 ACK 而阻塞。DMA 描述符方面除了增加数量还可以启用 DMA 的增强模式STM32H743 的以太网 DMA 支持增强描述符每个描述符可以带两个缓冲区减少描述符切换开销。配置 ETH_DMABMR 寄存器的 EDFE 位启用增强模式然后描述符的 buffer1 和 buffer2 分别指向两个 pbufDMA 自动拼接发送。任务调度方面网络任务的优先级要高于存储任务因为网络发送有实时性要求存储可以缓冲。另外网络任务里不要做耗时操作比如 DNS 解析、文件读写这些放到独立任务里做。我一开始在网络任务里直接写 SD 卡结果网络吞吐量只有 2Mbps拆开后跑到 8Mbps。5.2 存储写入速度优化SD 卡写入速度优化主要靠多块写入和缓存。多块写入FatFs 的 f_write 默认是单块写入每次写 512 字节。如果数据量大可以攒够 4KB 再调用 f_writeFatFs 会自动用 CMD25 多块写入命令速度比单块快 3 倍左右。我的做法是在 StorageTask 里维护一个 4KB 缓冲区攒够 4KB 或者超时 100ms 就写一次。缓存方面STM32H743 的 SDMMC 有 128 字节的 FIFO配置 DMA 时把 FIFO 阈值设到最大突发长度设到 16能减少 DMA 传输次数。另外SDMMC 的时钟可以超频到 100MHz但要看 SD 卡支持不支持高速卡支持 100MHz普通卡只支持 50MHz超频会导致 CRC 错误。5.3 长期运行稳定性验证长期运行稳定性验证我做了三轮第一轮连续跑 24 小时观察内存泄漏和任务栈使用情况第二轮连续跑 7 天观察文件系统是否损坏、网络是否稳定第三轮连续跑 30 天模拟现场环境包括网络断开重连、SD 卡热插拔、电源波动。24 小时测试发现网络任务栈使用率 85%有点高加到 3072 字后降到 60%。7 天测试发现文件系统有轻微碎片但不影响读写30 天测试发现 SD 卡写入速度从 8MB/s 降到 6MB/s原因是卡内垃圾回收变慢换用 pSLC 卡后速度稳定在 8MB/s。实操心得长期运行测试一定要在真实环境做实验室环境和现场环境差别很大。我在实验室跑 7 天没问题到现场跑 3 天就死机后来发现是现场电网波动导致电源纹波变大MCU 的 1.2V 核心电压不稳加了 LC 滤波后解决。6. 项目扩展与后续改进方向这个 Sensor Gateway 目前已经稳定运行了半年多采集了超过 2TB 的传感器数据网络上报成功率 99.9% 以上。后续我打算从三个方向改进。第一增加 MQTT 协议支持。现在用的是裸 TCP数据格式自定义对接云平台需要额外开发。改成 MQTT 后可以直接对接主流物联网平台数据格式用 JSON 或者 CBOR兼容性更好。第二增加边缘计算能力。STM32H743 的算力还有富余可以在本地做简单的数据预处理比如滑动平均滤波、阈值报警、异常检测减少上报数据量降低网络带宽和云端存储成本。第三增加 OTA 升级功能。现在固件升级要拆机接 SWD很不方便。可以基于 LwIP 实现 TFTP 或者 HTTP 固件下载配合 STM32H743 的双 Bank Flash实现无缝升级升级失败还能回滚。这个项目最大的体会是嵌入式网络数据采集系统硬件设计占 30%软件架构占 40%调试优化占 30%。硬件设计要留足余量软件架构要解耦调试优化要有耐心。特别是以太网和 SD 卡这两个模块坑最多一定要在项目初期就做好异常记录和看门狗机制后期调试能省很多时间。