全部例程

Modbus TCP示例

W6300

更新于 2026年10月14日

本篇总结

本例程在 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 和开发板需接入同一路由器或交换机。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 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 开启读线圈轮询)即可完成读线圈功能验证

下载本章例程

我们提供完整的工程文件以及配套开发板,方便你随时测试,快速完成产品开发:

开发环境: Keil MDK5

Andy

W5500 技术支持专家