W55RP20-EVB-MKR 嵌入式 C 语言开发教程:UDP 客户端 / 服务端双模式通信
前言
上一篇教程中,我们已经完成了TCP Server 服务端通信的开发,实现了面向连接、稳定可靠的 TCP 数据交互。而在局域网设备快速通信、低延迟指令下发等场景中,UDP 协议凭借更轻量、更高效的特性,成为嵌入式开发的重要选择。
WIZnet W5500 内置硬件 TCP/IP 协议栈,搭配树莓派 Pico RP2040 主控,使用 C 语言 开发可实现极致低延迟、无阻塞、高可靠的 UDP 数据交互,完美适配传感器上报、局域网设备发现、批量指令下发等场景。
本文将基于 W55RP20-EVB-MKR 开发板,使用标准 C 语言 实现UDP 客户端和UDP 服务端双模式通信,学完本文,你将掌握:
- UDP 协议 C 语言 实现核心逻辑
- W5500 硬件协议栈 C 语言 底层初始化流程
- UDP 客户端定时发送数据
- UDP 服务端非阻塞接收 + 自动回环通信
- 网络调试助手联调 C 语言 网络程序
- C 语言 版 UDP 通信异常一站式排查
系列教程学习路径
本专栏共 15 篇,循序渐进覆盖 W55RP20-EVB-MKR 模块 MicroPython 开发全流程:
1.第 1 篇:静态 IP 配置与网络基础
2.第 2 篇:DHCP 自动联网与网络诊断
3.第 3 篇:TCP Client 客户端通信
4.第 4 篇:TCP Server 服务端通信
5.第 5 篇:UDP 单播数据通信(本文)
6.第 6 篇:UDP 组播/广播数据通信
7.第 7 篇:DNS 域名解析
8.第 8 篇:NTP 从网络获取时间
9.第 9 篇:HTTP Client 客户端请求
10.第 10 篇:HTTP Server 服务端搭建
11.第 11 篇:HTTP 协议与 OneNET 平台数据上云
12.第 12 篇:MQTT 协议基础通信验证
13.第 13 篇:MQTT 协议与阿里云平台对接
14.第 14 篇:MQTT 协议与 OneNET 平台对接
15.第 15 篇:MQTT 协议与 ThingSpeak 平台对接
16.第 16 篇:Modbus 工业协议通信
建议收藏本专栏,跟随教程逐步学习,所有代码均会同步更新至官方 Gitee 仓库。
目录
1. 准备工作
1.1 软件准备
1.2 硬件准备
2. 开发环境配置
3. 硬件连接
4. UDP 通信核心原理
5. 核心代码解析(C 语言)
5.1 UDP 客户端代码(定时发送数据)
5.2 UDP 服务端代码(非阻塞接收 + 自动回复)
6. 代码关键步骤说明
6.1 UDP 客户端关键逻辑
6.2 UDP 服务端关键逻辑
7. 运行结果与测试验证
7.1 UDP 客户端运行日志
7.2 UDP 服务端运行日志
7.3 联调方法
8. 常见问题一站式排查指南
8.1 编译 / 烧录问题
8.2 网络不通
8.3 UDP 收发失败
9. W5500 硬件协议栈 C 语言 优势
10. 典型应用场景
11. 系列预告
1. 准备工作
1.1 软件准备
| 软件名称 | 用途 |
|---|---|
| Visual Studio Code + MKR SDK | C 语言 开发、编译、烧录环境 |
| 网络调试助手 | UDP 数据收发测试 |
| 串口调试工具 | 查看日志、打印信息 |
1.2 硬件准备
W55RP20-EVB-MKR 开发板
- Micro USB 数据线(支持数据传输)
- 网线
- 路由器 / 电脑网口
W55RP20-EVB-MKR模块已集成以太网相关器件,无需额外焊接飞线,配合 RP2040 开发板可快速搭建开发环境,大幅降低接线错误和硬件故障概率。
2. 开发环境配置
- 安装 Raspberry Pi MKR SDK(C 语言 编译环境)
- 集成 WIZnet 官方 W5500 驱动库
- 配置 SPI、GPIO、串口底层驱动
- 支持一键编译生成 UF2 固件
环境配置完成后,可直接编译本文提供的 C 语言 代码。
3. 硬件连接
- USB 连接:Micro USB 连接开发板与电脑,用于供电、烧录、串口日志输出
- 以太网连接:网线连接开发板网口与路由器 LAN 口 / 电脑网口
- 无需额外接线:W55RP20-EVB-MKR 板载集成 W5500,自动匹配 SPI 引脚
4. UDP 通信核心原理
UDP(用户数据报协议)是无连接、轻量、低延迟的传输层协议:
- 不需要三次握手建立连接
- 不保证数据可靠性,但速度极快
- 以数据报为单位收发,自带目标 IP / 端口
- 适合:传感器上报、设备发现、短指令、局域网广播
C 语言 + W5500 工作流程硬件初始化 → 网络配置 → 创建 UDP Socket → 数据收发 → 循环运行
5. 核心代码解析(C 语言)
本文提供两套可直接编译运行的代码:
- UDP 客户端:定时向目标设备发送数据
- UDP 服务端:非阻塞监听端口,接收后自动回复
5.1 UDP 客户端代码(定时发送数据)
功能:开发板作为 UDP 客户端,每隔 1 秒向电脑发送测试数据
#include <stdio.h> #include <string.h> #include "pico/stdlib.h" #include "port_common.h" #include "wizchip_conf.h" #include "wizchip_spi.h" #include "socket.h" // 缓冲区与Socket配置 #define ETHERNET_BUF_MAX_SIZE (1024 * 2) #define SOCKET_UDP 0 #define PORT_UDP 8083 // 网络参数(静态IP) static wiz_NetInfo g_net_info = { .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}, .ip = {192, 168, 1, 100}, .sn = {255, 255, 255, 0}, .gw = {192, 168, 1, 1}, .dns = {8, 8, 8, 8}, #if _WIZCHIP_ > W5500 .ipmode = NETINFO_STATIC_ALL #else .dhcp = NETINFO_STATIC #endif }; // 发送缓冲区 static uint8_t g_buf[ETHERNET_BUF_MAX_SIZE] = {0}; // 目标设备IP(填写你的电脑IP) static uint8_t dest_ip[4] = {192, 168, 1, 139}; int main() { // 串口初始化 stdio_init_all(); sleep_ms(100); printf("=====================================\n"); printf(" UDP Client (C Language)\n"); printf(" Board IP: 192.168.1.100\n"); printf(" Target IP: 192.168.1.139:%d\n", PORT_UDP); printf("=====================================\n"); // W5500 硬件初始化 wizchip_spi_initialize(); wizchip_cris_initialize(); wizchip_reset(); wizchip_initialize(); network_initialize(g_net_info); print_network_information(g_net_info); // 创建UDP Socket printf("UDP Socket Initializing...\n"); socket(SOCKET_UDP, Sn_MR_UDP, 0, 0); printf("UDP Socket Ready!\n"); // 加载发送数据 const char* send_data = "Hello from W5500 C Client!"; strcpy((char*)g_buf, send_data); // 主循环:定时发送 while (1) { sendto(SOCKET_UDP, g_buf, strlen(send_data), dest_ip, PORT_UDP); printf("UDP Sent: %s\n", send_data); sleep_ms(1000); } }5.2 UDP 服务端代码(非阻塞接收 + 自动回复)
功能:开发板作为 UDP 服务端,监听本地端口,收到数据后立即原路回复(回环测试)
#include <stdio.h> #include "pico/stdlib.h" #include "port_common.h" #include "wizchip_conf.h" #include "wizchip_spi.h" #include "socket.h" #define ETHERNET_BUF_MAX_SIZE (1024 * 2) #define SOCKET_UDP 0 #define PORT_UDP 8083 // 网络配置 static wiz_NetInfo g_net_info = { .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}, .ip = {192, 168, 1, 100}, .sn = {255, 255, 255, 0}, .gw = {192, 168, 1, 1}, .dns = {8, 8, 8, 8}, #if _WIZCHIP_ > W5500 .ipmode = NETINFO_STATIC_ALL #else .dhcp = NETINFO_STATIC #endif }; static uint8_t g_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0}; int main() { stdio_init_all(); sleep_ms(100); printf("=====================================\n"); printf(" UDP Server (C Language)\n"); printf(" Board IP: 192.168.1.100\n"); printf(" Listen Port: %d\n", PORT_UDP); printf("=====================================\n"); // W5500 底层初始化 wizchip_spi_initialize(); wizchip_cris_initialize(); wizchip_reset(); wizchip_initialize(); network_initialize(g_net_info); print_network_information(g_net_info); // 初始化UDP服务端(绑定端口) printf("Opening UDP Socket...\n"); if(socket(SOCKET_UDP, Sn_MR_UDP, PORT_UDP, 0) < 0) { printf("Socket Init Failed!\n"); while(1); } printf("UDP Server Started!\n"); uint16_t rx_len; uint8_t remote_ip[4]; uint16_t remote_port; // 非阻塞主循环 while(1) { // 查询是否收到数据 if((rx_len = getSn_RX_RSR(SOCKET_UDP)) > 0) { // 读取数据 + 来源IP/端口 recvfrom(SOCKET_UDP, g_ethernet_buf, rx_len, remote_ip, &remote_port); // 打印接收信息 printf("Recv %d bytes from %d.%d.%d.%d:%d\n", rx_len, remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3], remote_port); // 自动回传 sendto(SOCKET_UDP, g_ethernet_buf, rx_len, remote_ip, remote_port); printf("Data Echo Back Success!\n"); } // 每秒心跳(证明程序运行正常) static uint32_t heart_tick = 0; if(to_ms_since_boot(get_absolute_time()) - heart_tick > 1000) { printf("[Heartbeat] UDP Server Running...\n"); heart_tick = to_ms_since_boot(get_absolute_time()); } } }6. 代码关键步骤说明
6.1 UDP 客户端关键逻辑
- 硬件初始化:SPI + W5500 驱动加载
- 网络配置:静态 IP 直接入网
- Socket 创建:
socket(..., Sn_MR_UDP, ...) - 发送数据:
sendto()指定目标 IP + 端口 - 定时发送:1 秒间隔上报数据
6.2 UDP 服务端关键逻辑
- 绑定端口:监听局域网数据
- 非阻塞查询:
getSn_RX_RSR()判断是否有数据 - 读取数据:
recvfrom()自动获取发送方 IP / 端口 - 自动回环:
sendto()原路返回数据 - 心跳打印:实时监控程序运行状态
7. 运行结果与测试验证
7.1 UDP 客户端运行日志
===================================== UDP Client (C Language) Board IP: 192.168.1.100 Target IP: 192.168.1.139:8083 ===================================== Network Information: IP:192.168.1.100 UDP Socket Ready! UDP Sent: Hello from W5500 C Client! UDP Sent: Hello from W5500 C Client!7.2 UDP 服务端运行日志
===================================== UDP Server (C Language) Board IP: 192.168.1.100 Listen Port: 8083 ===================================== UDP Server Started! [Heartbeat] UDP Server Running... Recv 12 bytes from 192.168.1.139:8083 Data Echo Back Success!7.3 联调方法
- 电脑打开网络调试助手
- 选择UDP 模式
- 填写开发板 IP:
192.168.1.100,端口:8083 - 发送数据 → 开发板自动回复
- 查看串口日志验证通信
8. 常见问题一站式排查指南
8.1 编译 / 烧录问题
- 提示驱动缺失:添加 WIZnet 官方驱动库
- UF2 无法烧录:重新进入 BOOTSEL 模式
8.2 网络不通
- 网口灯不亮:更换网线 / 网口
- 无法 ping 通:检查静态 IP 是否在同一网段
- 电脑无法发送:关闭电脑防火墙
8.3 UDP 收发失败
- 收不到数据:检查端口一致、防火墙关闭
- 发送失败:确认目标 IP 正确
- 程序卡死:去掉
wizchip_check()函数
9. W5500 硬件协议栈 C 语言 优势
对比维度 | W5500 硬件协议栈方案 | 外接 PHY 芯片方案 |
|---|---|---|
BOM 成本 | 中(MCU + 网络模块,无需额外器件) | 中高(MCU + PHY 芯片 + 外围器件) |
PCB 面积 | 小(模块集成度高,仅需预留模块安装空间) | 大(需预留芯片、布线空间及外围电路) |
开发难度 | 低(MicroPython 固件已封装底层,少量代码实现 UDP 组播/广播) | 中高(需调试协议栈、编写底层驱动,对研发能力要求高) |
网络稳定性 | 极高(WIZnet 专注硬件 TCP/IP 协议栈 25 年,抗干扰能力强,UDP 丢包率低) | 不定(依赖研发人员对协议栈和网络开发的掌握程度,UDP 易丢包) |
CPU 资源占用 | 0%(协议栈完全由硬件处理,不占用 MCU 资源,不影响数据发送频率) | 50%以上(协议栈运行在 MCU 上,占用大量 CPU 和内存,影响 UDP 发送效率) |
硬件 Socket 数量 | W5500 8个独立硬件 Socket,支持多组播/广播同时进行 | 视 MCU 能力而定,理论支持多路拓展,但实际受 CPU 资源限制 |
网络吞吐量 | W5500 最高 15Mbps,UDP 数据传输流畅,无明显延迟 | 视 MCU 能力而定,普遍低于硬件协议栈方案,多设备通信易卡顿 |
接口易用性 | SPI 接口,接线简单,适配大多数 MCU,支持高速通信 | 需 MCU 带有 MII/RMII 等专用接口,适配性有限 |
部署难度 | 低(MicroPython 成熟固件,应用层协议均有库文件,多设备组网可快速部署) | 高(应用层协议需要手动移植开源库适配,调试成本高) |
10. 典型应用场景
- 工业传感器局域网数据上报
- 多设备 UDP 自动发现
- 低延迟实时指令控制
- 量产型物联网终端
- 工业串口转网口网关
11. 系列预告
下一篇我们将讲解C 语言 实现 UDP 组播 + 广播通信,实现局域网内一对多设备批量控制,为多节点物联网项目打下基础。
