本篇总结
本例程在 W6300 上实现 Modbus TCP 服务器模式,监听 502 端口响应客户端的读线圈请求。
🔑 MH2457 + QSPI、UART 调试、硬件 TOE 引擎、Modbus TCP、DHCP
硬件平台 :本例程基于 W6300-EVB-M4 开发板运行。
采购推荐 :点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板
本示例演示如何在 W6300 上通过 Modbus TCP 协议 实现工业通信。开发板作为 Modbus TCP 服务器监听 502 端口,PC 上的 Modbus Poll 作为客户端发起读线圈请求,验证 Modbus TCP 报文交互。
前置工具准备
开始本例程前,请确认以下工具已安装:
WIZ UartTool V1.0 :串口调试工具:点击下载
Modbus Poll :Modbus 主机模拟工具,用于测试 Modbus TCP 通信:点击下载
Modbus TCP 协议简介
Modbus TCP 是一种基于以太网的通信协议,它是经典 Modbus 协议的扩展。它使用 TCP/IP 协议栈进行通信,运行在 OSI 模型的传输层(TCP 层)之上,数据通过 TCP 端口 502 传输,采用主从架构。Modbus 协议最初由 Modicon 公司在 1979 年开发,广泛应用于工业自动化系统,用于实现不同设备之间的数据通信。Modbus TCP 结合了 Modbus 协议的简单性和以太网的高效性,是一种开放、标准化且广泛使用的工业通信协议。
Modbus TCP 报文由以下几个部分组成:
字段 长度 说明
事务处理标识符 2字节 用于标识请求与响应之间的配对。
协议标识符 2字节 通常为 0x0000,表示该报文使用 Modbus 协议。
长度字段 2字节 表示后续数据的字节数。
单元标识符 1字节 标识目标从机设备的地址。
功能码 1字节 定义当前操作的类型(如读写线圈、寄存器等)。
数据部分 可变长度 包含具体的操作数据。
本例程使用的功能码
本例程从机地址设为 0x01,支持 功能码 0x01 (读线圈状态)。Modbus TCP 因其高效性和兼容性,已成为工业物联网(IIoT)和工业 4.0 中不可或缺的一部分。
硬件连接与拓扑
请注意:
测试本例程需要 PC 端和 W6300 处于同一网段,PC 和开发板需接入同一路由器或交换机。
Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
网线连接开发板 RJ45 网口 到路由器/交换机 LAN 口,PC 也接入同一网络
┌─────────────────────────────────┐
│ 路由器 / 交换机 │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W6300 │ │ PC │
│ (Modbus │ │ (Modbus │
│ Server) │ │ Poll) │
│ :502 │ │ │
└────┬──────┘ └───────────┘
│ QSPI ↑
┌────┴──────┐ DEBUG USB
│ MH2457 │─────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:MH2457 通过 QSPI 驱动 W6300,W6300 作为 Modbus TCP 服务器监听 502 端口,PC 运行 Modbus Poll 作为客户端
工程结构
工程核心文件如下:
Modbus_TCP/
├── main.c ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
├── user_main.c ← 网络地址定义 + 主循环
├── mbtcp.c/h ← do_Modbus() 薄封装,调用 server 层
├── modbus_tcp_server.c/h ← Modbus TCP 服务器状态机 + 报文收发
├── modbus_tcp_protocol.c/h ← Modbus TCP 报文解析与响应组装
├── wiz_platform.c/h ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
└── wiz_interface.c/h ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测、软件定时器
步骤1:主循环调用 do_Modbus() 处理通信
文件:USER/user_main.c — 在循环中不断执行 do_Modbus() 函数,处理 Modbus TCP 通信。
while (1)
{
do_Modbus(SOCKET_ID);
}
do_Modbus() 是薄封装:首次调用时初始化服务器,之后每轮调用 modbus_tcp_server_run()。
void do_Modbus(uint8_t sn)
{
if ((s_modbus_initialized == 0U) || (s_modbus_socket != sn))
{
if (modbus_tcp_server_init(sn) < 0)
{
return;
}
s_modbus_socket = sn;
s_modbus_initialized = 1U;
}
modbus_tcp_server_run();
}
步骤2:Modbus TCP 服务器状态机与报文处理
文件:USER/modbus_tcp_server.c — modbus_tcp_server_run() 依据 socket 状态执行相应操作:SOCK_CLOSED 时以非阻塞模式打开 socket;SOCK_INIT 时进入监听;SOCK_ESTABLISHED / SOCK_CLOSE_WAIT 时处理报文收发。接收数据后校验 MBAP 头并交给协议层处理,响应通过 send_pending 发送。
switch (status)
{
case SOCK_CLOSED:
result = socket(s_modbus_socket, Sn_MR_TCP4, local_port, SF_IO_NONBLOCK);
if (result == (int8_t)s_modbus_socket)
{
io_mode = SOCK_IO_NONBLOCK;
if (ctlsocket(s_modbus_socket, CS_SET_IOMODE, &io_mode) != SOCK_OK)
{
modbus_server_schedule_reopen();
}
}
break;
case SOCK_INIT:
result = listen(s_modbus_socket);
if (result == SOCK_OK)
{
if (s_listen_reported == 0U)
{
getSIPR(local_ip);
printf("Listening on %u.%u.%u.%u:%u\r\n",
local_ip[0], local_ip[1], local_ip[2], local_ip[3], local_port);
s_listen_reported = 1U;
}
}
break;
case SOCK_ESTABLISHED:
case SOCK_CLOSE_WAIT:
modbus_server_process_connection(status);
break;
default:
modbus_server_schedule_reopen();
break;
}
modbus_server_process_connection() 中先调用 modbus_server_receive() 接收数据并校验 MBAP 头,再调用 modbus_tcp_protocol_process_adu() 生成响应,最后由 modbus_server_send_pending() 发送。每次收发报文都会通过 modbus_server_log_frame() 打印十六进制内容。
超时与重开机制
状态机对中间态(SOCK_SYNRECV / SOCK_SYNSENT / SOCK_FIN_WAIT / SOCK_TIME_WAIT / SOCK_LAST_ACK)设有 5 秒超时,超时后调用 modbus_server_schedule_reopen() 关闭并重新打开 socket,避免卡死在异常状态。SOCK_CLOSED 后延迟 200ms 再重开,防止过于频繁地开关 socket。
运行验证
打开 WIZ UartTool V1.0 ,连接 DEBUG USB 对应 COM 口(115200 / 8N1)。烧录例程运行后,首先可以看到 PHY 链路检测,然后打印网络地址信息,接着开始监听 502 端口。预期串口输出如下:
MH2457 W6300 QSPI example start
W6300 TCP Modbus Server example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
W6300 network configuration : DHCP
MAC : 00:08:DC:72:30:29
IP : 192.168.2.29
Subnet Mask : 255.255.255.0
Gateway : 192.168.2.1
DNS : 192.168.2.1
====================================================================================================
Listening on 192.168.2.29:502
接着使用 Modbus Poll 工具进行测试:
F3 连接设置 :Connection → Modbus TCP/IP,Remote IP 填 W6300 的 IP(本例为 192.168.2.29),Port 填 502,Slave ID 填 1,点击 OK。
F8 读写定义 :Function 选 01 Read Coils,Address 填 0,Quantity 填 1,开启读取轮询,可以看到不断读取线圈状态。
WIZ UartTool 串口输出 — Modbus TCP 服务器初始化(PHY 链路 + 网络配置 + 监听 502 端口) Modbus Poll Connection Setup(F3)— Modbus TCP/IP,目标 192.168.2.29:502 Modbus Poll Read/Write Definition(F8)— Slave ID=1,Function 01 Read Coils,Address=0,Quantity=1 常见问题
Q: Modbus TCP 默认端口是多少,可以修改吗
Modbus TCP 默认使用 502 端口。在本例程中通过 local_port 变量指定监听端口,可根据需要修改。但使用 Modbus Poll 等标准工具测试时,建议保持 502 以保证兼容性。
Q: 从机地址(单元标识符)有什么作用
单元标识符(Unit Identifier)用于在 Modbus TCP 报文中区分逻辑设备。本例程从机地址设为 0x01,协议层会校验报文中的单元标识符,不匹配时返回异常响应。在 Modbus Poll 的 F3 连接设置中,Slave ID 需与该值一致。
Q: modbus_tcp_server_run() 的状态机是如何工作的
modbus_tcp_server_run() 通过 getSn_SR(sn) 获取 socket 状态并执行相应操作:SOCK_CLOSED 时以非阻塞模式打开 TCP socket;SOCK_INIT 时调用 listen() 开始监听;SOCK_ESTABLISHED / SOCK_CLOSE_WAIT 时处理报文收发;中间态超时后调用 modbus_server_schedule_reopen() 重开 socket。该状态机循环运行,保证通信的持续可用。
Q: 串口打印的 [Modbus RX] / [Modbus TX] 是什么
这是 modbus_server_log_frame() 打印的报文日志,格式为 [Modbus RX] N bytes: XX XX ...。RX 表示接收到的请求报文,TX 表示发出的响应报文,都以十六进制显示,方便对照 Modbus TCP 协议格式排查问题。
Q: Modbus TCP 通信出现异常如何排查
首先确保 PC 和 W6300 处于同一网段并能 ping 通;检查监听端口是否正确;使用 Modbus Poll 时确认连接的服务器 IP、端口、从机地址与例程一致;通过串口打印的 [Modbus RX] / [Modbus TX] 日志观察报文收发情况,若 MBAP 头校验失败会触发 socket 重开。
总结
本文实现了 W6300 通过 Modbus TCP 协议作为服务器与客户端通信的完整流程:
协议基础 :Modbus TCP 基于 TCP/IP,运行在 502 端口,采用主从架构,报文包含事务/协议/长度标识、单元标识符、功能码和数据部分
分层结构 :do_Modbus() 薄封装 → modbus_tcp_server_run() 状态机 → modbus_tcp_protocol_process_adu() 协议处理
状态机处理 :依据 socket 状态(CLOSED→INIT→LISTEN→ESTABLISHED)执行监听、连接和报文收发,中间态设有超时重开机制
报文收发 :接收数据到缓冲区,校验 MBAP 头后交给协议层处理,响应通过 send_pending 发送,每次收发都打印十六进制日志
测试验证 :配合 Modbus Poll 工具(F3 设置连接、F8 开启读线圈轮询)即可完成读线圈功能验证