基于STM32F407的Modbus TCP服务器源程序(采用LWIP网络通讯库与LAN872...
搞嵌入式开发的兄弟应该都遇到过这样的场景:甲方爸爸突然说要用Modbus TCP协议对接设备,而你手头正好有个STM32F407的板子。别慌,今天咱们就来聊聊怎么用LWIP+LAN8720快速搭建一个稳定的Modbus TCP服务器。这个架构在多个工业网关项目里验证过稳定性,连续运行最长的设备已经3年没重启过。遇到过某个厂家的PLC会发送带额外字节的异常报文,就是靠抓包定位到问题。核心的Modbu
基于STM32F407开发调试,Modbus TCP服务器源程序。 采用LWIP网络通讯库,外部PHY采用LAN8720。 使用 modbus poll工具调试通过。 该工程可直接作为模板开发。 源码已应用于工业项目使用。
搞嵌入式开发的兄弟应该都遇到过这样的场景:甲方爸爸突然说要用Modbus TCP协议对接设备,而你手头正好有个STM32F407的板子。别慌,今天咱们就来聊聊怎么用LWIP+LAN8720快速搭建一个稳定的Modbus TCP服务器。
先上硬菜——网络初始化部分。这里有个坑要注意:LAN8720的复位引脚必须等PHY芯片内部稳压电路稳定后再操作。见过不少兄弟在这卡壳,明明配置都对就是ping不通。
// PHY硬件复位
void PHY_Reset(void)
{
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET);
HAL_Delay(100); // 关键等待时间!
HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET);
HAL_Delay(100); // PHY启动需要时间
}
LWIP的配置文件中这几个参数得重点关照,特别是TCPMSS和TCPWND。在工业现场遇到过数据包被分割的情况,调整后吞吐量直接翻倍:
#define TCP_MSS 1460 // 以太网帧有效载荷最大值
#define TCP_WND (4*TCP_MSS) // 滑动窗口大小
#define MEM_SIZE (20*1024) // 内存池大小
核心的Modbus处理逻辑其实不复杂,重点在于协议头的解析。这里用状态机的处理方式比if-else瀑布流清爽得多:
void modbus_tcp_process(struct tcp_pcb *pcb)
{
if(rcv_flag == 1){
// 提取事务标识符
trans_id = pbuf->payload[0] << 8 | pbuf->payload[1];
// 校验协议标识符是否为Modbus
if(pbuf->payload[2] != 0 || pbuf->payload[3] != 0){
tcp_close(pcb);
return;
}
// 处理功能码
switch(pbuf->payload[7]){
case 0x03:
handle_read_holding_registers(pcb, pbuf);
break;
case 0x10:
handle_write_multiple_registers(pcb, pbuf);
break;
//...其他功能码处理
}
}
}
实测中发现LWIP的tcpwrite有时候会返回ERRMEM,这里加个重试机制能有效避免数据丢失:
err_t send_modbus_response(struct tcp_pcb *pcb, uint8_t *data, uint16_t len)
{
int retry = 0;
err_t err;
while(retry++ < 3){
err = tcp_write(pcb, data, len, TCP_WRITE_FLAG_COPY);
if(err == ERR_OK) break;
HAL_Delay(5);
}
if(err == ERR_OK){
tcp_output(pcb); // 立即触发数据发送
return MODBUS_OK;
}
return MODBUS_ERROR;
}
调试时建议用Wireshark抓包看原始数据流,比Modbus Poll自带的监控更直观。遇到过某个厂家的PLC会发送带额外字节的异常报文,就是靠抓包定位到问题。
最后说下工程结构:
/Project
├── LWIP
│ └── lwipopts.h // 参数配置
├── Modbus
│ ├── mbtcp.c // TCP协议处理
│ └── mbframe.c // 数据帧解析
└── ETH
└── ethernetif.c // 网络驱动
这个架构在多个工业网关项目里验证过稳定性,连续运行最长的设备已经3年没重启过。源码里预留了RS485接口的Modbus RTU转换模块,需要时可以快速扩展成协议网关。
(注:文中代码经过简化处理,实际工程需考虑线程安全、超时重传等机制。完整源码可通过文末链接获取)

更多推荐
所有评论(0)