全部例程

W55MH32-RTThread-UDP通信测试

W55MH32

更新于 2026年9月23日

本篇总结

在 W55MH32 上结合 RT-Thread RTOS 多线程实现 UDP 客户端与服务器双模式并发通信。

🔑 KEIL MDK、UART调试、硬件TOE引擎、UDP、RT-Thread、DHCP

硬件平台:本例程基于 W55MH32L-EVB 开发板运行。

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB

本示例演示在 W55MH32 上结合 RT-Thread RTOS 实现 UDP 客户端与服务器双模式并发通信。利用 RT-Thread 多线程特性,创建两个独立任务分别管理 Socket 0(UDP 客户端)和 Socket 1(UDP 服务器),通过注册临界区函数保证多线程操作硬件的线程安全。UDP 无连接特性使数据收发更简洁高效。

前置工具准备

开始本例程前,请确认以下工具已安装:

  • RT-Thread Studio:基于 Eclipse 的 RT-Thread 集成开发环境,用于开发 RT-Thread 应用程序:点击下载
  • WIZ UartTool V1.0:串口调试工具:点击下载
  • SocketTester:网络调试工具:点击下载

RT-Thread 与 UDP 协议简介

RT-Thread 是一款由中国社区主导开发的开源、硬实时嵌入式操作系统(RTOS),诞生于 2006 年。它从最初的小巧内核演变成了组件丰富、生态完善的物联网操作系统平台,负责管理硬件资源、调度任务,并提供网络连接等高级功能。其 Nano 版最小仅需 3KB ROM 和 1KB RAM,采用抢占式调度,中断响应达微秒级。

UDP(User Datagram Protocol)是一种无连接的、不可靠的传输层协议。与 TCP 不同,UDP 在发送数据前不需要建立连接,发送方直接将数据报发送给接收方,但不保证数据的可靠到达。UDP 适用于对实时性要求高、可容忍少量丢包的场景,如视频流、DNS 查询、IoT 传感器数据上报等。

  • 极致轻量:Nano 版最小仅需 3KB ROM 和 1KB RAM,可运行在 Cortex-M0 等资源有限的芯片上
  • 硬实时性:抢占式调度,高优先级任务立即抢占,中断响应达微秒级
  • 丰富生态:内置 TCP/IP 协议栈、文件系统、GUI 引擎、MQTT 等大量软件包
  • 开源友好:Apache License 2.0 协议,可免费用于商业产品,无需公开源码

W55MH32 + RT-Thread 的优势

W55MH32 的硬件 TCP/IP 协议栈(TOE)与 RT-Thread 多线程结合,可实现真正的并发网络通信。UDP 无需建立/断开连接,数据收发更简洁高效。每个 Socket 在独立线程中运行状态机,代码结构清晰,且可设置不同优先级。

硬件连接与拓扑

请注意:

测试需要 PC 端和 W55MH32 处于同一网段。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口
┌─────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W55MH32 │ │ PC │
│ UDP:8080 │ │SocketTester│
│ IP: DHCP │ │ IP: DHCP │
└───────────┘ └───────────┘
↑ ↑
DEBUG USB ──→ WIZ UartTool
网络拓扑:开发板与 PC 在同一局域网内,通过路由器互通

工程结构

工程位于 SDK 内 1.SDK\ModuleDemo\RT-Thread\UDP\,核心文件如下:

RT-Thread UDP/
├── Project/
│   └── Project.uvprojx       ← Keil 工程文件
└── USER/
    ├── main.c                ← RT-Thread 线程创建 + 网络初始化
    ├── Loopback/
    │   ├── loopback.c        ← UDP 客户端/服务器回环核心
    │   └── 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。定义 UDP 客户端和服务器各自的 Socket 号、目标 IP/端口以及本地监听端口。

#define SOCKET_UDPC           0  /* UDP客户端Socket号 */
#define SOCKET_UDPS           1  /* UDP服务器Socket号 */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) /* 网络缓冲区大小(2KB) */

/* UDP客户端:目标服务器IP+端口 */
uint8_t  dest_ip[4] = {192, 168, 1, 147};
uint16_t dest_port  = 8080;
/* UDP服务器:本地监听端口 */
uint16_t local_port = 8080;

W55MH32 的 TOE 引擎支持 8 个独立 Socket(0~7)。本例程将 Socket 0 分配给 UDP 客户端,Socket 1 分配给 UDP 服务器,两者可同时运行。UDP 客户端向 PC 端的 8080 端口发送数据,UDP 服务器监听本地 8080 端口等待 PC 发送数据。

步骤2:RT-Thread 线程配置

文件:USER/main.c。为 UDP 客户端和服务器分别定义线程优先级和栈大小。

/* UDP客户端线程参数 */
#define TASK_UDPC_PRIO     1
#define TASK_UDPC_STK_SIZE 256
static rt_thread_t Task_UDPC_Handler = RT_NULL;

/* UDP服务器线程参数 */
#define TASK_UDPS_PRIO     1
#define TASK_UDPS_STK_SIZE 256
static rt_thread_t Task_UDPS_Handler = RT_NULL;

两个线程优先级均为 1(相同优先级,时间片轮转),栈大小 256(单位为系统字节)。线程栈大小不可过小,否则会导致线程崩溃。

步骤3:系统初始化与线程启动

文件:USER/main.c | 函数:app_init() — 初始化 TOE 引擎、注册临界区函数、检测 PHY 链路、获取网络地址,然后创建并启动双 UDP 线程。

int app_init(void)
{
    rt_kprintf("%s RTThread UDP 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(udpc_ethernet_buf, &default_net_info);

    /* 创建并启动双UDP线程 */
    Task_UDPC_Handler = rt_thread_create("Task UDP Client",
        Task_UDPC, RT_NULL, TASK_UDPC_STK_SIZE, TASK_UDPC_PRIO, 20);
    Task_UDPS_Handler = rt_thread_create("Task UDP Server",
        Task_UDPS, RT_NULL, TASK_UDPS_STK_SIZE, TASK_UDPS_PRIO, 20);
    rt_thread_startup(Task_UDPC_Handler);
    rt_thread_startup(Task_UDPS_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:UDP 线程任务函数

文件:USER/Loopback/loopback.c。UDP 客户端线程循环调用 loopback_udpc() 发送数据并接收回环,UDP 服务器线程循环调用 loopback_udps() 监听端口并回环数据。

/* UDP客户端线程:循环向服务器发送数据+接收回环 */
void Task_UDPC(void *parameter)
{
    while (1)
    {
        loopback_udpc(SOCKET_UDPC, udpc_ethernet_buf, dest_ip, dest_port);
        rt_thread_mdelay(1); /* 让出CPU给其他线程 */
    }
}

/* UDP服务器线程:循环监听端口+回环数据 */
void Task_UDPS(void *parameter)
{
    while (1)
    {
        loopback_udps(SOCKET_UDPS, udps_ethernet_buf, local_port);
        rt_thread_mdelay(1); /* 让出CPU给其他线程 */
    }
}

每个线程循环末尾调用 rt_thread_mdelay(1) 让出 CPU 时间片,使两个线程交替运行。两个线程需使用各自独立的 ethernet_buf(udpc_ethernet_buf 和 udps_ethernet_buf),不可共用。UDP 无连接,sendto() 直接指定目标地址,recvfrom() 获取来源地址。

如果你想自定义

  • 改目标 IP/端口 → 修改 main.c 中 dest_ip 和 dest_port
  • 改监听端口 → 修改 local_port 的值
  • 增大吞吐量 → 增大 ETHERNET_BUF_MAX_SIZE 并调整 Socket 收发缓存
  • 线程栈大小 → 建议 ≥ 256,过小会导致线程崩溃

运行验证

烧录例程运行后,首先进行 PHY 链路检测、DHCP 获取 IP,然后 UDP 初始化完成并开始监听端口。打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1),观察开发板串口输出。

W55MH32 串口输出 — RT-Thread UDP 初始化(PHY 链路 + DHCP + UDP 客户端/服务器启动)
  • UDP 服务器测试(PC 端):打开网络调试工具设为 UDP 模式,远程地址填 W55MH32 的 IP、端口 8080。发送数据后设备原样回发,验证服务器回环功能。
  • UDP 客户端测试(PC 端):打开另一个窗口设为 UDP 模式,本地端口设为 8080。W55MH32 的 UDP 客户端线程会向该端口发送数据,PC 端接收后可回复,设备会打印接收到的数据。
SocketTester UDP 客户端 + WIZ UartTool 串口输出 — RT-Thread UDP 数据回环测试

注意事项

  • PC 端和 W55MH32 需同一网段
  • UDP 不保证可靠传输,少量丢包属正常现象
  • 线程栈大小不可过小(建议 ≥ 256),否则线程崩溃
  • 两个线程需使用各自独立的 ethernet_buf,不可共用

常见问题

Q: 为什么必须注册临界区函数

RT-Thread 多线程环境下,如果两个线程同时操作 W55MH32 的硬件寄存器(如 Socket 状态寄存器),会导致寄存器访问冲突。reg_wizchip_cris_cbfunc() 注册 rt_enter_critical/rt_exit_critical 后,WIZnet 驱动在访问寄存器前会自动进入临界区,保证同一时间只有一个线程操作硬件。

Q: 两个线程能否共用一个 ethernet_buf

不能。UDP 客户端和服务器线程会同时接收和发送数据,如果共用一个缓冲区,一个线程写入的数据可能被另一个线程覆盖,导致数据错乱。必须为每个线程分配独立的缓冲区(如 udpc_ethernet_buf 和 udps_ethernet_buf)。

Q: UDP 和 TCP 的区别是什么

UDP 是无连接协议,无需建立/断开连接,发送时直接指定目标地址(sendto),接收时获取来源地址(recvfrom)。TCP 是面向连接的协议,需三次握手建立连接、四次挥手断开连接。UDP 更轻量高效但不保证可靠传输,TCP 保证可靠传输但开销更大。UDP 适用于实时性要求高、可容忍丢包的场景。

Q: rt_thread_mdelay(1) 的作用是什么

rt_thread_mdelay(1) 让当前线程休眠 1 毫秒,释放 CPU 给其他线程执行。如果不加延时,当前线程会持续占用 CPU(因为 while 循环中没有阻塞操作),导致另一个线程无法运行。RT-Thread 的抢占式调度器在延时期间会切换到其他就绪线程。

Q: 与裸机多 Socket 方案有什么区别

裸机方案通过轮询(socket_sn++)依次处理每个 Socket,宏观上是"伪并发"。RT-Thread 方案通过多线程实现真正的并发处理,每个 Socket 在独立线程中运行,代码结构更清晰,且可设置不同优先级。但需要额外的 RTOS 开销和线程安全保护。

总结

本文基于 W55MH32 硬件 TCP/IP 协议栈结合 RT-Thread RTOS 实现了 UDP 客户端与服务器双模式并发通信:

  • 双线程架构:RT-Thread 创建两个独立线程分别管理 UDP 客户端(Socket 0)和服务器(Socket 1)
  • 线程安全:reg_wizchip_cris_cbfunc() 注册 RT-Thread 临界区函数,保证硬件操作安全
  • 无连接特性:UDP 无需建立/断开连接,sendto() 直接发送,recvfrom() 获取来源地址
  • 独立缓冲区:两个线程需使用各自独立的 ethernet_buf,不可共用
  • 自动初始化:INIT_APP_EXPORT 宏实现系统启动自动调用,无需手动干预

下载本章例程

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

开发环境: Keil MDK5 配套开发板

Administrator

Wiznet 雇员