前言

上一篇教程中,我们已经完成了 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. 开发环境配置

  1. 安装 Raspberry Pi MKR SDK(C 语言 编译环境)
  2. 集成 WIZnet 官方 W5500 驱动库
  3. 配置 SPI、GPIO、串口底层驱动
  4. 支持一键编译生成 UF2 固件

环境配置完成后,可直接编译本文提供的 C 语言 代码。

3. 硬件连接

  1. USB 连接:Micro USB 连接开发板与电脑,用于供电、烧录、串口日志输出
  2. 以太网连接:网线连接开发板网口与路由器 LAN 口 / 电脑网口
  3. 无需额外接线:W55RP20-EVB-MKR 板载集成 W5500,自动匹配 SPI 引脚

4. UDP 通信核心原理

UDP(用户数据报协议)是无连接、轻量、低延迟的传输层协议:

  • 不需要三次握手建立连接
  • 不保证数据可靠性,但速度极快
  • 以数据报为单位收发,自带目标 IP / 端口
  • 适合:传感器上报、设备发现、短指令、局域网广播

C 语言 + W5500 工作流程硬件初始化 → 网络配置 → 创建 UDP Socket → 数据收发 → 循环运行

5. 核心代码解析(C 语言)

本文提供两套可直接编译运行的代码:

  1. UDP 客户端:定时向目标设备发送数据
  2. 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 客户端关键逻辑

  1. 硬件初始化:SPI + W5500 驱动加载
  2. 网络配置:静态 IP 直接入网
  3. Socket 创建socket(..., Sn_MR_UDP, ...)
  4. 发送数据sendto() 指定目标 IP + 端口
  5. 定时发送:1 秒间隔上报数据

6.2 UDP 服务端关键逻辑

  1. 绑定端口:监听局域网数据
  2. 非阻塞查询getSn_RX_RSR() 判断是否有数据
  3. 读取数据recvfrom() 自动获取发送方 IP / 端口
  4. 自动回环sendto() 原路返回数据
  5. 心跳打印:实时监控程序运行状态

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 联调方法

  1. 电脑打开网络调试助手
  2. 选择 UDP 模式
  3. 填写开发板 IP:192.168.1.100,端口:8083
  4. 发送数据 → 开发板自动回复
  5. 查看串口日志验证通信

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 组播 + 广播通信,实现局域网内一对多设备批量控制,为多节点物联网项目打下基础。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐