本篇总结
在 W55MH32 上结合 RT-Thread RTOS 多线程实现 TCP 客户端与服务器双模式并发通信。
🔑 KEIL MDK、UART调试、硬件TOE引擎、TCP、RT-Thread、DHCP
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示在 W55MH32 上结合 RT-Thread RTOS 实现 TCP 客户端与服务器双模式并发通信。利用 RT-Thread 多线程特性,创建两个独立任务分别管理 Socket 0(TCP 客户端)和 Socket 1(TCP 服务器),通过注册临界区函数保证多线程操作硬件的线程安全。
前置工具准备
开始本例程前,请确认以下工具已安装:
- RT-Thread Studio:基于 Eclipse 的 RT-Thread 集成开发环境,用于开发 RT-Thread 应用程序:点击下载
- WIZ UartTool V1.0:串口调试工具:点击下载
- SocketTester:网络调试工具:点击下载
RT-Thread 与 TCP 协议简介
RT-Thread 是一款由中国社区主导开发的开源、硬实时嵌入式操作系统(RTOS),诞生于 2006 年。它从最初的小巧内核演变成了组件丰富、生态完善的物联网操作系统平台,负责管理硬件资源、调度任务,并提供网络连接等高级功能。其 Nano 版最小仅需 3KB ROM 和 1KB RAM,采用抢占式调度,中断响应达微秒级。
TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议,用于在网络中可靠地传输数据。TCP 通过三次握手建立连接、四次挥手断开连接,提供可靠的数据传输、流量控制和拥塞控制。
- 极致轻量:Nano 版最小仅需 3KB ROM 和 1KB RAM,可运行在 Cortex-M0 等资源有限的芯片上
- 硬实时性:抢占式调度,高优先级任务立即抢占,中断响应达微秒级
- 丰富生态:内置 TCP/IP 协议栈、文件系统、GUI 引擎、MQTT 等大量软件包
- 开源友好:Apache License 2.0 协议,可免费用于商业产品,无需公开源码
W55MH32 + RT-Thread 的优势
W55MH32 的硬件 TCP/IP 协议栈(TOE)与 RT-Thread 多线程结合,可实现真正的并发网络通信。每个 Socket 在独立线程中运行状态机,代码结构清晰,且可设置不同优先级。通过注册临界区函数,保证多线程操作硬件寄存器的线程安全。
硬件连接与拓扑
请注意:
测试需要 PC 端和 W55MH32 处于同一网段。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
┌─────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W55MH32 │ │ PC │
│ TCP:8084 │ │SocketTester│
│ IP: DHCP │ │ IP: DHCP │
└───────────┘ └───────────┘
↑ ↑
DEBUG USB ──→ WIZ UartTool
网络拓扑:开发板与 PC 在同一局域网内,通过路由器互通
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\RT-Thread\TCP\,核心文件如下:
RT-Thread TCP/
├── Project/
│ └── Project.uvprojx ← Keil 工程文件
└── USER/
├── main.c ← RT-Thread 线程创建 + 网络初始化
├── Loopback/
│ ├── loopback.c ← TCP 客户端/服务器回环核心
│ └── loopback.h
├── wiz_interface/
│ ├── wiz_interface.c ← DHCP / PHY 检测 / 网络初始化
│ └── wiz_interface.h
└── uart/
├── bsp_uart.c ← 调试串口 + printf 重定向
└── bsp_uart.h
步骤1:网络参数与 Socket 分配
文件:USER/main.c。定义 TCP 客户端和服务器各自的 Socket 号、目标 IP/端口以及本地监听端口。
#define SOCKET_TCPC 0 /* TCP客户端Socket号 */
#define SOCKET_TCPS 1 /* TCP服务器Socket号 */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) /* 网络缓冲区大小(2KB) */
/* TCP客户端:目标服务器IP+端口 */
uint8_t dest_ip[4] = {192, 168, 1, 120};
uint16_t dest_port = 8087;
/* TCP服务器:本地监听端口 */
uint16_t local_port = 8084;
W55MH32 的 TOE 引擎支持 8 个独立 Socket(0~7)。本例程将 Socket 0 分配给 TCP 客户端,Socket 1 分配给 TCP 服务器,两者可同时运行。TCP 客户端主动连接 PC 端的 8087 端口,TCP 服务器监听本地 8084 端口等待 PC 连接。
步骤2:RT-Thread 线程配置
文件:USER/main.c。为 TCP 客户端和服务器分别定义线程优先级和栈大小。
/* TCP客户端线程参数 */
#define TASK_TCPC_PRIO 1
#define TASK_TCPC_STK_SIZE 256
static rt_thread_t Task_TCPC_Handler = RT_NULL;
/* TCP服务器线程参数 */
#define TASK_TCPS_PRIO 1
#define TASK_TCPS_STK_SIZE 256
static rt_thread_t Task_TCPS_Handler = RT_NULL;
两个线程优先级均为 1(相同优先级,时间片轮转),栈大小 256(单位为系统字节)。线程栈大小不可过小,否则会导致线程崩溃。
步骤3:系统初始化与线程启动
文件:USER/main.c | 函数:app_init() — 初始化 TOE 引擎、注册临界区函数、检测 PHY 链路、获取网络地址,然后创建并启动双 TCP 线程。
int app_init(void)
{
rt_kprintf("%s RTThread TCP example\n", _WIZCHIP_ID_);
wiz_toe_init(); /* W55MH32 TOE引擎初始化 */
/* 注册RT-Thread临界区函数(线程安全) */
reg_wizchip_cris_cbfunc(rt_enter_critical, rt_exit_critical);
wiz_phy_link_check(); /* 检测以太网物理链路 */
network_init(tcpc_ethernet_buf, &default_net_info);
/* 开启Keepalive */
setSn_KPALVTR(SOCKET_TCPC, 6); /* 30s保活 */
setSn_KPALVTR(SOCKET_TCPS, 6);
/* 创建并启动双TCP线程 */
Task_TCPC_Handler = rt_thread_create("Task TCP Client",
Task_TCPC, RT_NULL, TASK_TCPC_STK_SIZE, TASK_TCPC_PRIO, 20);
Task_TCPS_Handler = rt_thread_create("Task TCP Server",
Task_TCPS, RT_NULL, TASK_TCPS_STK_SIZE, TASK_TCPS_PRIO, 20);
rt_thread_startup(Task_TCPC_Handler);
rt_thread_startup(Task_TCPS_Handler);
return 0;
}
INIT_APP_EXPORT(app_init); /* RT-Thread自动初始化 */
reg_wizchip_cris_cbfunc() 注册 RT-Thread 临界区函数(rt_enter_critical/rt_exit_critical),保证多线程操作硬件寄存器的线程安全。INIT_APP_EXPORT 宏将 app_init 注册到 RT-Thread 自动初始化序列,系统启动时自动调用。
RT-Thread 多线程关键点
线程安全:必须通过 reg_wizchip_cris_cbfunc() 注册 RT-Thread 临界区函数,否则多线程操作硬件寄存器会冲突。WIZnet 驱动在访问寄存器前会自动进入临界区,保证同一时间只有一个线程操作硬件。
步骤4:TCP 线程任务函数
文件:USER/Loopback/loopback.c。TCP 客户端线程循环调用 loopback_tcpc() 连接服务器并回环数据,TCP 服务器线程循环调用 loopback_tcps() 监听端口并回环数据。
/* TCP客户端线程:循环连接服务器+数据回环 */
void Task_TCPC(void *parameter)
{
while (1)
{
loopback_tcpc(SOCKET_TCPC, tcpc_ethernet_buf, dest_ip, dest_port);
rt_thread_mdelay(1); /* 让出CPU给其他线程 */
}
}
/* TCP服务器线程:循环监听端口+数据回环 */
void Task_TCPS(void *parameter)
{
while (1)
{
loopback_tcps(SOCKET_TCPS, tcps_ethernet_buf, local_port);
rt_thread_mdelay(1); /* 让出CPU给其他线程 */
}
}
每个线程循环末尾调用 rt_thread_mdelay(1) 让出 CPU 时间片,使两个线程交替运行。两个线程需使用各自独立的 ethernet_buf(tcpc_ethernet_buf 和 tcps_ethernet_buf),不可共用。
如果你想自定义
- 改目标 IP/端口 → 修改
main.c 中 dest_ip 和 dest_port
- 改监听端口 → 修改
local_port 的值
- 增大吞吐量 → 增大
ETHERNET_BUF_MAX_SIZE 并调整 Socket 收发缓存
- 线程栈大小 → 建议 ≥ 256,过小会导致线程崩溃
运行验证
烧录例程运行后,首先进行 PHY 链路检测、DHCP 获取 IP,然后 TCP 初始化完成并开始监听端口。打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1),观察开发板串口输出。
W55MH32 串口输出 — RT-Thread TCP 初始化(PHY 链路 + DHCP + TCP 客户端/服务器启动)
- TCP 服务器测试(PC 端):打开网络调试工具设为 TCP Server 模式,IP 设为 PC 地址、端口 8087。W55MH32 的 TCP 客户端线程会主动连接该服务器,发送数据后设备原样回发。
- TCP 客户端测试(PC 端):打开另一个窗口设为 TCP Client 模式,远程地址填 W55MH32 的 IP、端口 8084。连接后发送数据,设备原样回发,验证服务器功能。
SocketTester TCP 服务器/客户端 + WIZ UartTool 串口输出 — RT-Thread TCP 数据回环测试
注意事项
- PC 端和 W55MH32 需同一网段
- TCP 客户端连接失败会自动重连
- 线程栈大小不可过小(建议 ≥ 256),否则线程崩溃
- 提高吞吐量可增大 ETHERNET_BUF_MAX_SIZE 并调整 Socket 收发缓存
常见问题
Q: 为什么必须注册临界区函数
RT-Thread 多线程环境下,如果两个线程同时操作 W55MH32 的硬件寄存器(如 Socket 状态寄存器),会导致寄存器访问冲突。reg_wizchip_cris_cbfunc() 注册 rt_enter_critical/rt_exit_critical 后,WIZnet 驱动在访问寄存器前会自动进入临界区,保证同一时间只有一个线程操作硬件。
Q: 两个线程能否共用一个 ethernet_buf
不能。TCP 客户端和服务器线程会同时接收和发送数据,如果共用一个缓冲区,一个线程写入的数据可能被另一个线程覆盖,导致数据错乱。必须为每个线程分配独立的缓冲区(如 tcpc_ethernet_buf 和 tcps_ethernet_buf)。
Q: rt_thread_mdelay(1) 的作用是什么
rt_thread_mdelay(1) 让当前线程休眠 1 毫秒,释放 CPU 给其他线程执行。如果不加延时,当前线程会持续占用 CPU(因为 while 循环中没有阻塞操作),导致另一个线程无法运行。RT-Thread 的抢占式调度器在延时期间会切换到其他就绪线程。
Q: 与裸机多 Socket 方案有什么区别
裸机方案通过轮询(socket_sn++)依次处理每个 Socket,宏观上是"伪并发"。RT-Thread 方案通过多线程实现真正的并发处理,每个 Socket 在独立线程中运行,代码结构更清晰,且可设置不同优先级。但需要额外的 RTOS 开销和线程安全保护。
Q: INIT_APP_EXPORT 的作用
INIT_APP_EXPORT(app_init) 是 RT-Thread 的自动初始化宏,它将 app_init 函数注册到 RT-Thread 的自动初始化序列中。系统启动时会自动调用该函数,无需在 main 函数中手动调用。RT-Thread 提供了多个初始化级别(BOARD/DEVICE/COMPONENT/APP),APP 级别在系统启动的最后阶段执行。
总结
本文基于 W55MH32 硬件 TCP/IP 协议栈结合 RT-Thread RTOS 实现了 TCP 客户端与服务器双模式并发通信:
- 双线程架构:RT-Thread 创建两个独立线程分别管理 TCP 客户端(Socket 0)和服务器(Socket 1)
- 线程安全:reg_wizchip_cris_cbfunc() 注册 RT-Thread 临界区函数,保证硬件操作安全
- 独立缓冲区:两个线程需使用各自独立的 ethernet_buf,不可共用
- 让出 CPU:rt_thread_mdelay(1) 释放时间片,使两个线程交替运行
- 自动初始化:INIT_APP_EXPORT 宏实现系统启动自动调用,无需手动干预