1. 为什么要把采集单元从串口搬到以太网
做工业现场采集的老工程师应该都有过这种体会:早期项目里RS485加Modbus RTU几乎是无敌的组合,成本低、稳定、开发快。但最近几年,越来越多的项目开始点名要"以太网采集单元",而且不是那种可有可无的升级要求,而是直接把网络接口写进招标技术规格书里。
我去年接的一个产线能耗监测项目就是典型。现场有62个采集点,包括温度、电流、电压、开关状态,原来用的是三路RS485总线轮询,每路挂20多个节点。初期点位少的时候还能跑,等到点位加满之后,一个完整轮询周期已经拖到了8秒以上,上位机画面上的数据肉眼可见地在"跳秒"。更要命的是,偶尔一个节点掉线,整条总线的诊断要靠人去现场一台台看状态灯,运维成本和体验都非常糟糕。甲方提了两个硬性要求:一是全通道刷新周期压到1秒以内,二是设备数据要直接能进厂里的数据中台,不再经过串口服务器转接。这两个需求叠加,基本就锁定了方案形态——采集单元直接带以太网口。
很多人觉得以太网采集单元无非是把串口换成网口,底层逻辑不变。实际上这个迁移牵扯的东西比想象中多:硬件上要重新选MCU和PHY,协议栈要选型移植,采集数据和网络传输之间要做合理的缓冲设计,再加上一整套应用层协议。本文把这个方案拆开来讲,按我实际做项目的路线,从需求分析、硬件选型、协议栈移植到数据帧设计和问题排查,完整过一遍。适合正在做或者准备做工业采集、环境监测、实验室台架数据采集的朋友参考。
1.1 一个真实需求场景的指标拆解
还是以上面那个产线能耗监测项目为例。我们在项目初期把需求拆成了下面这张表,这一步非常关键,因为后面所有硬件选型和软件设计都是围绕这些指标展开的。
| 指标项 | 需求值 | 备注 |
|---|---|---|
| 模拟量输入 | 16路差分 | 支持0~10V和4~20mA,软件可切 |
| 数字量输入 | 8路 | 干接点,带光耦隔离 |
| 采样率 | 1kSPS/通道 | 每通道独立采样 |
| 全通道刷新周期 | ≤500ms | 从上位机视角看的更新周期 |
| 通信接口 | 100Base-TX | 百兆以太网,RJ45 |
| 应用协议 | Modbus TCP + 自定义UDP | 兼容现有PLC和自研中台 |
| 供电 | 24V DC | 工业现场标准电源 |
| 功耗 | ≤5W | 连续运行温升可控 |
指标定下来之后,方案的大方向就清楚了:MCU内置以太网MAC加外置PHY芯片,跑轻量级TCP/IP协议栈,模拟量采集走定时器触发ADC加DMA搬运。这套组合在工业采集单元里非常成熟,开发风险低,供应链也稳定。
1.2 为什么轮询周期和节点规模决定了方案形态
顺带说一个很多人容易忽略的点。串口采集升级到以太网采集,并不仅仅是"快一点",而是通信模型的根本变化。RS485总线上所有节点共享一条物理链路,同一时刻只能有一个节点发言,所以节点越多,轮询周期线性增长,这是物理层决定的,软件优化空间很小。
以太网这边就不一样了。虽然百兆以太网理论带宽12.5MB/s,而工业采集单元的实际数据量往往很小——16路1kSPS的16位采样数据也就32KB/s,加上协议开销也远小于带宽上限。但关键在于:单条网线连接的是采集中枢(比如交换机和上位机),而不是一条共享总线上一长串节点。你可以把以太网采集单元理解成一个自带"接驳口"的独立设备,任意两个节点之间通信互不占用彼此的通道,拓扑灵活度完全不同。
当然咯,以太网也不是没有代价。物理层从RS485的差分两线变成了变压器加RJ45,PCB设计门槛高了一截;协议栈从Modbus RTU的简单状态机变成了TCP/IP栈,对MCU资源的消耗也上了一个量级。这也就是为什么本文要花大量篇幅讲协议栈选型和数据链路设计。
2. 硬件方案选型:MCU、PHY与网络变压器的搭配逻辑
硬件选型是大方向定完之后的第一关,也是最容易被细节绊倒的一关。我见过不少朋友一上来就画板子,结果PHY芯片的时钟方式和MCU不匹配、RMII引脚复用冲突、变压器参数选错导致link up都不稳定,最后全部推倒重来。这一节把几条关键选择链路讲透。
2.1 MCU选型:为什么ST家在采集单元里最常见
工业采集单元对MCU的核心要求是:带以太网MAC、有足够DMA通道、ADC精度够且触发方式灵活、成本适中。目前市面上满足这些条件的方案里,ST的STM32系列是绝对主流,尤其是F4、F7和H7三个家族。
具体到型号选择,我的经验是分三档:
- STM32F407系列:百兆以太网MAC标配,168MHz主频,内置12位ADC,价格便宜,资料最多。适合采样率要求不高(1kSPS量级)、协议栈负载不重的中低端采集单元。
- STM32F429/F767系列:主频更高(180~216MHz),有图形加速器和更大的RAM,适合需要本地显示或有更复杂协议处理的设备。F767有硬件双精度浮点,做边缘计算类的预处理很方便。
- STM32H743系列:主频480MHz,RAM近1MB,以太网MAC集成了DMA描述符增强功能,性能余量很大。但成本和PCB要求也上去了,一般高速多通道同步采集才会用到。
做个简单对比表格:
| 型号 | 主频 | RAM | 以太网MAC | 典型定位 |
|---|---|---|---|---|
| STM32F407 | 168MHz | 192KB | 10/100M | 基础型采集单元 |
| STM32F767 | 216MHz | 512KB | 10/100M | 带显示/复杂协议 |
| STM32H743 | 480MHz | 1MB | 10/100M | 多通道高速采集 |
我那个产线能耗项目选的是STM32F407ZET6,16路模拟量加8路数字量,1kSPS采样,Modbus TCP和UDP双协议栈同时跑,CPU负载实测大概45%,RAM占用稳定在60KB以内,余量充足。
2.2 PHY芯片选型与RMII接口的底层原因
MCU确定的下一步就是选PHY芯片。STM32内置的MAC只负责数据链路层的逻辑部分,物理层的编码、电平转换、载波监听都要靠外挂PHY芯片完成。PHY选型时主要关注三件事:接口模式、时钟方案、PHY地址。
接口模式上,MII接口需要14根信号线,数据线16位,引脚占用太大,在采集单元这种对PCB面积敏感的设备上一般不划算。RMII接口只要7根信号线(TXD[1:0]、RXD[1:0]、TX_EN、CLK、CRS_DV),引脚少了一大半,代价是对时钟要求更严格——RMII必须提供一个50MHz的参考时钟。
这个50MHz时钟的来源有三种做法:板载晶振、MCU的MCO引脚输出、专用时钟芯片。第一个方案最省心,PHY自己带晶振电路;第二个方案可以省一个晶振,但要注意MCO输出时钟的稳定度和相位噪声,STM32F407的MCO1配置成PLL时钟输出50MHz是成熟做法。实际设计中我倾向于独立晶振方案,软件配置少,调试时也少一个变量。
常用PHY芯片我列一下选型参考:
| PHY型号 | 接口 | 自动协商 | 工作温度 | 备注 |
|---|---|---|---|---|
| LAN8720A | RMII | 支持 | 0~85℃ | 性价比高,量大 |
| KSZ8081 | RMII/MII | 支持 | -40~105℃ | 工业级温度范围 |
| DP83848 | RMII/MII | 支持 | -40~85℃ | TI老牌,稳定 |
PHY芯片的地址通过引脚电平设定,比如LAN8720A的PHYAD0引脚拉低是地址0x00,拉高是地址0x01。驱动代码里的PHY地址必须和板卡上的硬件设置一致,否则MDIO通信直接失败。这个问题在批量生产时尤其要小心,换PCB版本后如果改过地址引脚,固件里忘了同步,就会出现"同一套代码在这个板子上能跑,在另一个板子上link不起来"的诡异现象。
2.3 网络变压器与RJ45的细节处理
很多初学者容易忽略网络变压器的作用,以为它就是走个隔离。实际上变压器承担了三个功能:隔离直流分量、抑制共模干扰、完成阻抗匹配。选择集成变压器的RJ45插座还是分离式变压器,取决于空间和成本。
集成方案(比如HR911105A这类带变压器的RJ45)布线简单、占板面积小,适合做小型采集节点;分离方案则方便灵活调整变压器参数,维修时也容易单点替换。无论哪种方案,都建议选带中心抽头且内置共模电感的型号,抗静电和抗浪涌能力会好很多。
PCB布局上,差分走线TX_P/TX_N、RX_P/RX_N要成对等长,距离尽量短,旁边不要走高速数字信号线;RJ45附近的PCB开槽隔离是常见做法,目的是减少变压器耦合同模信号。另外PHY芯片的电源滤波要单独处理,用磁珠加去耦电容的组合,避免电源噪声串入模拟采集电路。
3. 协议栈的选型与移植:裸机lwIP还是RTOS加lwIP
硬件平台确定之后,软件架构就成了项目的核心决策点。以太网采集单元和普通MCU项目的最大区别在于要跑TCP/IP协议栈,而协议栈的选型和配置直接决定了系统稳定性、实时性以及后续可维护性。
3.1 不同软件方案的真实差别
市面上主流的方案大致有四种:
- 裸机循环加lwIP轮询:只有一个主循环,协议栈在循环里被周期调用。优点是无操作系统,代码简单,调试直观;缺点是无法真正并行处理多个任务,当采集、存储、网络传输交织时,延时控制容易失衡。
- FreeRTOS加lwIP:最常用的组合。网络协议栈运行在单独任务里,采集在高优先级任务或中断里完成,通过队列和信号量让数据在任务间传递。优点是任务隔离清晰,稳定性好;缺点是首次上手有一定学习曲线,需要理解RTOS的调度机制。
- RT-Thread加lwIP:RT-Thread把lwIP适配做得很完善,标准版系统自带完整ETH驱动框架和lwIP组件,开发效率很高,生态里也有大量现成设备驱动。
- 商用或者芯片原厂封装的协议栈:比如一些MCU厂商提供的闭源协议栈,胜在省心,但可定制性差,出了问题排查困难。
针对以太网采集单元这个具体场景,我个人的建议是:如果产品功能简单,数据量不大,而且你对RTOS不太熟悉,可以先裸机跑lwIP,数据采集用定时器中断、网络处理用主循环轮询,能把项目在最短时间内跑通。但如果计划长期迭代,有多个功能模块要往上加,那建议直接上FreeRTOS加lwIP,后面做远程升级、日志管理、多连接接入都会方便很多。
3.2 lwIP移植的几个关键配置项
无论是裸机还是带OS,lwIP的移植都绕不开lwipopts.h这个配置文件。这里把几个直接决定采集单元稳不稳定的配置项拿出来讲透。
内存配置:lwIP有两种内存机制,一种是内存池(memp),提前分配固定大小的内存块,速度快但灵活性差;另一种是内存堆(mem),动态分配,灵活但可能产生碎片。在lwipopts.h里,PBUF_POOL_SIZE决定收发缓冲区池的数量,TCP_SND_BUF和TCP_WND决定TCP发送窗口和接收窗口。采集单元的数据量一般不大,但要求传输及时、稳定,所以我通常把PBUF_POOL_SIZE调到16以上,TCP_WND设置成8KB左右,这样单个连接就能流畅传输。
内存不足是lwIP下最常见的隐性故障源。数据量上来后,如果内存池被耗尽,协议栈会静默丢包,表现就是上位机偶尔收到不完整数据或采集值"跳变"。排查时最有效的手段是把lwIP的LWIP_STATS功能打开,统计各层的丢包数和内存不足次数,基本能一眼定位。
中断处理与轮询的取舍:STM32以太网外设收到一帧数据后会产生ETH中断,中断服务函数里只做收包标记,真正的协议栈处理放到主循环或以太网任务里,这是防止中断风暴的正确做法。
3.3 移植完成后的基本验证
移植完后不要急着写应用层,先做基础联调。用一条网线把板子和电脑交换机连上,分别在板子和PC上配置静态IP,例如板子192.168.1.100、PC192.168.1.2。先ping,通了再测简单TCP回环。
这里给一个Linux下常用的网络自测指令组合,适合验证链路有没有通:
# 查看网卡状态和IP配置 ifconfig eth0 # 查看网卡协商速率和链路状态 ethtool eth0 # 用ping初步验证连通性 ping 192.168.1.100 # 用iperf3测TCP吞吐量 iperf3 -s # 板子端如果支持的话,跑服务端 iperf3 -c 192.168.1.100Linux下的以太网回环测试里,拿一根网线把两台设备直连,跳过交换机,是最快捷的排障方式。曾经有客户反馈"板子连交换机不通,但连电脑直连能通",最后发现是交换机端口VLAN配置的问题,链路协商本身是OK的。这类问题如果一上来就抓包,很容易被表象带偏。
4. 采集数据处理与以太网帧设计
数据链路层网络通了,不等于业务数据没问题。以太网采集单元的核心难题在于:采集侧是连续不断的实时数据流,网络侧是分包的传输模型,这两者之间如何高效、无损地衔接。这节讲采集链路和应用层协议的设计。
4.1 采集链路:定时器触发ADC加DMA搬运
工业采集单元的ADC采样必须保持严格的时间一致性,单纯依赖CPU周期转换是不可靠的,因为CPU要处理网络中断,转换时序会被打断。
推荐的做法是:用一个定时器产生PWM或更新事件作为ADC外部触发源,ADC在每个周期完成一次转换并把结果通过DMA直接搬运到内存缓冲区。整个过程中CPU不参与数据处理,只在DMA传输完成时收到一个中断信号,标记"这一批数据已经准备好了"。
具体到STM32F407的实现思路,是配置TIM2产生更新事件,连接到ADC1的触发输入端,ADC1开启DMA循环模式,DMA的目的地址是一个环形缓冲区。环形缓冲区做双缓冲设计——当DMA指针走到缓冲区的后半段时,说明前半段数据是完整的,可以打包发送;反过来同理。这样采集和网络传输就解耦了,采集不会因为网络拥堵而丢数据。
数据量计算:16路模拟量,采样率1kSPS,每个样本16位,每秒产生32KB原始数据。加上12字节的应用层头部,每秒约是32.5KB。100M以太网净荷带宽约11MB/s,实际占用不到0.3%,带宽完全不是瓶颈。这时候真正需要花心思的反而是时间戳打标——因为以太网传输有延迟和抖动,如果上位机需要计算三相电的相位差或者分析波形时序,每个数据包必须带上采集时刻的时间戳,而不是上位机接收时刻。
4.2 应用层协议设计与以太网帧格式
以太网帧格式本身很固定:前导码和帧起始定界符由PHY自动处理,MAC层实际收发的帧结构是目的MAC地址6字节、源MAC地址6字节、类型/长度2字节、数据46到1500字节、帧校验序列4字节。MCU的MAC外设会自动填充MAC地址、计算CRC,所以应用层代码看到的数据从"目的MAC"开始,到"数据"结束。
以下是经典以太网帧结构,理解它有助于后续设计自己的应用层数据包:
+- 前导码 7字节(硬件处理) +- 帧起始定界符 1字节(硬件处理) +- 目的MAC地址 6字节 +- 源MAC地址 6字节 +- 类型/长度 2字节 +- 载荷数据 46~1500字节 +- 帧校验序列 4字节(硬件计算)在上面的载荷数据里,lwIP会再拆出IP头(20字节)、TCP/UDP头(20或8字节)。做应用层协议设计时,真正留给业务数据的部分要扣掉这些开销来计算。
以太网数据的传输模式上,TCP和UDP的选择要结合采集单元的实际场景来权衡。Modbus TCP跑在TCP之上,适合与PLC、组态软件对接,因为TCP有重传和确认机制,传输可靠。但TCP的确认机制也带来一个问题:当采集端持续高速发送数据而接收端处理不及时的时候,TCP窗口会被占满,发送端被迫暂停,这会引入较大的延迟波动。UDP则没有这种问题,数据发出即走,代价是丢包要靠应用层自己处理。对于实时性要求高的波形采集,UDP加序列号是更务实的选择。
4.3 帧序列号与协议设计细节
自定义UDP协议时,帧序列号是一个容易被忽视但极其重要的字段。TCP协议帧本身有序列号机制,网络层可以自动重组乱序的数据包,但UDP没有。一旦网络中存在跃点或者交换机缓存压力较大,UDP包就可能乱序到达。客户端如果依赖到达顺序还原数据,就会出现数据错位的隐蔽问题。
解决办法是在应用层协议里显式加入序列号字段,比如每秒发送一个新的周期计数,每次发送一个静态自增序号。接收端在解析数据时检查序列号,如果发现跳号就说明有数据包丢失,可以做补发请求或者至少记录一条日志。以下是采集单元里常用的一种精简数据帧定义:
帧头:2字节,固定0xAA55 版本号:1字节 消息类型:1字节(0x01表示采样数据,0x02表示设备状态) 数据长度:2字节,表示后续数据的字节数 序列号:4字节,自增 时间戳:4字节,秒级时间戳 通道状态位图:2字节,标记各通道的有效性 通道数据:N * 2字节,逐通道排列 CRC16:2字节,对整个包做校验加上这么十几字节的头部,对带宽的影响可以忽略,但对排查问题、保证数据一致性帮助巨大。我做产线项目时,上位机曾出现过某通道数值间歇性跳变。靠序列号比对发现是交换机在流量突增时丢了一个UDP包,导致后续数据偏移了两个通道。加序列号和通道状态位图后,这类问题两分钟就能定位。
Modbus TCP协议本身是标准的,大多数情况下直接用现成库就行。需要注意的细节是Modbus TCP的MBAP头里有协议标识符和单元标识符,组态软件默认用0xFF作为单元标识符通配,有些实现会严格要求匹配设备地址,联调前要先确认清楚。
5. 系统调通后的实测数据与问题排查
系统开发接近尾声时,测试和问题排查才是真正体现项目水平的地方。这一节把我自己实测中遇到的高频问题和排查链路完整过一遍,很多方法属于"常规文档里不写,但现场救过命"的级别。
5.1 实测性能数据
在百兆网络中,测试用的以太网采集单元最终实测数据如下:
- 全通道采集刷新周期:大约120ms,远优于需求里的500ms
- 网络吞吐量:持续发送自定义UDP包时约2.4Mbps,完全满足32KB/s的数据量需求
- 丢包率:在无拥塞的千兆交换机环境下,连续运行72小时,总丢包率为0
- CPU占用率:STM32F407@168MHz,双协议栈加采集任务,大约45%
- 功耗:24V输入,整板功耗约3.8W
抓包验证是肯定要做的。Wireshark挂在PC端,抓板子发出的UDP帧,检查帧序列号是否连续、时间戳是否单调递增、CRC校验是否全部正确。实际抓包发现过一个问题:DMA双缓冲切换后,由于中断处理时另一个缓冲区还在写入,导致一帧数据里可能出现半个旧周期和半个新周期的拼接。之后在协议头里加了一个批次号字段,这个问题才彻底暴露出来并在驱动层做了同步修正。
5.2 常见问题的完整排查链路
问题一:板子上电后link up不上
链路层的故障排查顺序非常固定,我的经验是:先看PHY状态寄存器,再看中断,最后看DMA配置。
# 通过MDIO读取PHY状态寄存器,基本寄存器1是状态寄存器 # PHY地址为0,寄存器1的bit2是link status,为1表示链路已建立具体做法是写一个小函数循环读取PHY寄存器,把值通过串口打印出来。如果在插入网线后link status始终是0,问题基本集中在硬件(变压器、RJ45、差分线、PHY供电);如果link status能变成1但数据不通,再去查MAC的DMA描述符和lwIP的netif状态。
问题二:Windows系统弹出"以太网没有有效IP配置"
这个现象在电脑直连采集单元时很常见,根源是电脑设置了"自动获取IP地址",而采集单元往往只配置了静态IP,没有DHCP服务。解决办法有两种:一是把电脑网卡改成静态IP,例如192.168.1.2,子网掩码255.255.255.0,网关留空;二是在采集单元里实现一个简单的DHCP服务端,自动给电脑分配同网段IP。前者适合调试,后者适合产品化交付。
问题三:TCP连接时断时续,间隔数分钟一次
排查这种问题不能只盯应用层。先把lwIP的LWIP_STATS打开,统计TCP重传次数。如果重传次数高,同时伴随PBUF内存池不足,基本可以确定是内存耗尽导致的。此时增加PBUF_POOL_SIZE和TCP_SND_QUEUELEN,问题通常会缓解。另一种可能是网卡驱动的DMA描述符数量和缓冲长度不匹配,导致大帧被截断,这种情况抓包会看到大量TCP校验和错误。
问题四:以太网TC8测试规范与协议一致性
TC8是汽车电子领域以太网ECU的测试规范,别以为它只适用于车载以太网。TC8定义了从物理层一致性测试到协议层一致性测试的完整方法论,工业以太网采集单元完全可以参考它的思路来设计测试用例,比如中断唤醒测试、帧格式异常测试、高流量压力测试。虽然我们不必完整跑TC8的用例矩阵,但它的分层测试思想——先物理层后MAC层再TCP/IP层最后应用层——和实际排障的思路完全一致。合理借鉴TC8的方法论,能让采集单元在各种网络环境下的表现更可预期。
5.3 几个藏得比较深的坑
踩过的坑里,最值得说的是PHY地址配置错乱。量产后的某一批板子用了不同批次的PHY芯片,有的默认地址是0x00,有的是0x01,而固件里写死了0x00,结果一批板子在客户现场link不上。后来在软件里加了PHY地址自动扫描的逻辑,上电后依次尝试0x00到0x03的地址,直到MDIO读取状态寄存器成功,从此一劳永逸。
第二个坑是RMII参考时钟的相位要求。部分PHY芯片要求MCU提供的50MHz时钟和PHY内置的时钟域有一定相位关系,如果你的板子在低温和常温下表现不一致,优先检查时钟源和RMII走线的等长处理。这个坑在批量应用前尤其要重点做高低温测试。
第三个坑更隐蔽:TCP连接在长时间空闲后无法唤醒。原因是TCP的保活机制默认关闭,中间设备(比如路由器)在长时间没有流量时会把连接老化掉。解决方案是开启lwIP的LWIP_TCP_KEEPALIVE并配置合理的保活探测间隔,同时在应用层增加心跳机制,每5秒或10秒发一次心跳包,成本几乎为零,但能省掉大量现场运维工单。
6. 最后再分享一点实战心得
以太网采集单元相比传统串口采集,多出来的工作量和难度主要在网络这一侧。硬件上要处理好PHY、变压器、差分走线这些之前完全不涉及的东西,软件上要从裸机状态机思维切换到协议栈思维。但反过来看,一旦把以太网链路跑稳了,后面的扩展空间会大得多——远程升级、多机同步采集、边缘计算、云平台直连都是顺理成章的事。
根据我个人的经验,给正在做类似项目的朋友几句真心话:第一,需求里的刷新周期和通道数量一定要先量化,工程上最怕"大概就行",这些数字直接决定MCU选型和协议栈配置;第二,现场部署时不要图省事用默认IP段,尽量在设备里做拨码开关或者Web配置界面,可管理性在产品交付后比你想的重要得多;第三,数据帧里的序列号和CRC千万别省,这是以后排查问题的底气。做到这几条,你的以太网采集单元跑上几年都不会出大乱子。