本文为 W55RP20-EVB-Pico 模块 MicroPython 教程专项篇,基于官方最新固件编写,代码均经过实际验证,可直接烧录运行。 版权声明:本文为 WIZnet 官方原创技术文章,转载请注明出处。

前言

上一篇教程中,我们已经完成了 UDP 组播 / 广播 开发,实现了局域网多设备批量通信。而在实际物联网项目中,设备除了需要局域网通信,还必须具备访问公网服务器的能力,这就离不开 DNS 域名解析 功能。

当我们结合 WIZnet W5500 硬件 TCP/IP 协议栈,使用 C 语言 开发时,设备的稳定性、实时性和资源利用率达到最优。W5500 芯片内置硬件协议栈,完全独立处理 DNS、DHCP、TCP、UDP 等网络功能,不占用 RP2040 任何 CPU 资源,让嵌入式设备轻松实现域名解析、远程连接、云平台接入。

本文将带你使用 C 语言 快速实现 W55RP20-EVB-Pico 模块的 DNS 域名解析,学完本文,你将掌握:

  • DNS 协议核心原理与工业级工作流程
  • W55RP20 C 语言开发环境与硬件初始化
  • DHCP + DNS 联动实现全自动域名解析
  • 多域名解析、超时重试、异常处理
  • DNS 解析测试与故障排查
  • WIZnet 硬件协议栈在 DNS 处理中的优势

系列教程学习路径

本专栏共 16 篇,循序渐进覆盖 W55RP20-EVB-Pico 模块 C 语言开发全流程:

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. 烧录 W55RP20-EVB-Pico 模块 C 语言固件

3. 硬件连接与开发环境配置

3.1 硬件连接

3.1.1 基础连接(供电 + 调试)

3.1.2 以太网连接

3.1.3 硬件默认接线

3.2 C 语言开发环境配置

4. DNS 域名解析原理

4.1 DNS 协议简介

4.2 DNS 工作流程(C 语言硬件版)

4.3 DNS 核心优势

4.4 典型应用场景

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

5.1 完整可运行代码

5.2 代码功能说明

6. 运行结果与测试验证

6.1 串口输出结果

6.2 验证方法

7. 常见问题一站式排查指南

7.1 DHCP 无法获取 IP

7.2 DNS 解析失败

7.3 串口无输出

7.4 IP 冲突

8. WIZnet 硬件协议栈核心优势对比

9. 典型应用场景

10. 系列预告与资源获取

10.1 系列预告

10.2 资源获取

1. 准备工作

1.1 软件准备

所需软件均为免费版本,按要求下载安装即可,无需额外付费。

软件名称 版本要求 说明
Raspberry Pi Pico SDK 1.5.0 及以上 W55RP20 C 语言官方开发环境
VS Code 最新版 C 语言代码编辑、编译、调试
串口调试工具 通用版 查看日志、DNS 解析结果打印
网络调试助手 最新版 辅助网络连通性测试

1.2 硬件准备

  • W55RP20-EVB-Pico × 1
  • Micro USB 数据线(支持数据传输)× 1
  • 标准网线 × 1
  • 开启 DHCP 功能的路由器 / 交换机 × 1

W55RP20-EVB-Pico 模块已集成以太网相关器件,无需额外焊接飞线,配合 RP2040 开发板可快速搭建开发环境,大幅降低接线错误和硬件故障概率。

2. 烧录 W55RP20-EVB-Pico 模块 C 语言固件

W55RP20-EVB-Pico 完全兼容树莓派 Pico UF2 烧录方式:

  1. 按住开发板上 BOOTSEL 按键不放;
  2. 使用 Micro USB 数据线连接开发板与电脑;
  3. 电脑识别出 RPI-RP2 盘符后松开按键;
  4. 将编译好的 UF2 固件 拖拽到 U 盘中;
  5. 开发板自动重启,烧录完成。

注意:如果电脑没有识别出 RPI-RP2 U 盘,请尝试更换 USB 数据线、重新插拔开发板,或更换电脑 USB 接口(优先使用 USB 2.0 接口)。

3. 硬件连接与开发环境配置

3.1 硬件连接

3.1.1 基础连接(供电 + 调试)

使用 Micro USB 数据线连接开发板与电脑,用于供电、代码烧录、串口打印。

3.1.2 以太网连接

使用网线连接 W55RP20-EVB-Pico 网口与路由器 LAN 口,确保设备能访问外网。

3.1.3 硬件默认接线

模块集成 SPI 硬件驱动,无需手动接线,即插即用。

3.2 C 语言开发环境配置

  1. 安装 Raspberry Pi Pico SDK
  2. 集成 WIZnet 官方驱动库
  3. 配置串口打印、SPI 通信、定时器
  4. 编译工程生成 UF2 固件
  5. 烧录并打开串口查看日志

4. DNS 域名解析原理

4.1 DNS 协议简介

DNS(Domain Name System)域名系统,是互联网核心服务之一。作用:将人类易记的域名(如 www.baidu.com)转换为机器识别的 IP 地址

相当于互联网的 “电话簿”。

4.2 DNS 工作流程(C 语言硬件版)

  1. 开发板上电 → W5500 硬件初始化
  2. 启动 DHCP → 自动获取 IP、网关、DNS 服务器
  3. 向 DNS 服务器发送解析请求
  4. W5500 硬件独立处理请求,不占用 MCU
  5. 接收 DNS 服务器返回的 IP 地址
  6. 解析成功 → 输出 IP → 可用于 TCP/HTTP/MQTT 连接

4.3 DNS 核心优势

  • 无需记忆复杂 IP 地址
  • 服务器 IP 变更不影响设备使用
  • 支持负载均衡、容灾切换
  • 物联网设备上云必备基础功能

4.4 典型应用场景

  • 物联网设备接入云平台
  • HTTP/HTTPS 访问服务器 API
  • MQTT 远程通信
  • 远程控制、固件升级
  • 工业网关数据上传

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

5.1 完整可运行代码

/**
    Copyright (c) 2021 WIZnet Co.,Ltd
    SPDX-License-Identifier: BSD-3-Clause
*/

#include <stdio.h>
#include "port_common.h"
#include "wizchip_conf.h"
#include "wizchip_spi.h"
#include "dhcp.h"
#include "dns.h"
#include "timer.h"

/* Buffer */
#define ETHERNET_BUF_MAX_SIZE (1024 * 2)

/* Socket */
#define SOCKET_DHCP 0
#define SOCKET_DNS 1

/* Retry count */
#define DHCP_RETRY_COUNT 5
#define DNS_RETRY_COUNT 5

/* Network */
static wiz_NetInfo g_net_info = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56},
    .ip = {192, 168, 11, 2},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 11, 1},
    .dns = {8, 8, 8, 8},
    .dhcp = NETINFO_DHCP
};

static uint8_t g_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {0};
static uint8_t g_dhcp_get_ip_flag = 0;
static uint8_t g_dns_target_domain[] = "www.wiznet.io";
static uint8_t g_dns_target_ip[4] = {0};
static uint8_t g_dns_get_ip_flag = 0;
static volatile uint16_t g_msec_cnt = 0;

/* DHCP */
static void wizchip_dhcp_init(void);
static void wizchip_dhcp_assign(void);
static void wizchip_dhcp_conflict(void);

/* Timer */
static void repeating_timer_callback(void);

int main() {
    uint8_t retval = 0;
    uint8_t dhcp_retry = 0;
    uint8_t dns_retry = 0;

    stdio_init_all();
    sleep_ms(3000);
    wizchip_spi_initialize();
    wizchip_cris_initialize();

    wizchip_reset();
    wizchip_initialize();
    wizchip_check();

    wizchip_delay_ms(2000);
    wizchip_1ms_timer_initialize(repeating_timer_callback);

    if (g_net_info.dhcp == NETINFO_DHCP) {
        wizchip_dhcp_init();
    } else {
        network_initialize(g_net_info);
        print_network_information(g_net_info);
    }

    DNS_init(SOCKET_DNS, g_ethernet_buf);

    while (1) {
        /* DHCP 运行 */
        if (g_net_info.dhcp == NETINFO_DHCP) {
            retval = DHCP_run();

            if (retval == DHCP_IP_LEASED) {
                if (g_dhcp_get_ip_flag == 0) {
                    printf("DHCP 配置成功\n");
                    g_dhcp_get_ip_flag = 1;
                }
            } else if (retval == DHCP_FAILED) {
                g_dhcp_get_ip_flag = 0;
                dhcp_retry++;
                if (dhcp_retry <= DHCP_RETRY_COUNT) {
                    printf("DHCP 超时,重试:%d\n", dhcp_retry);
                }
            }

            if (dhcp_retry > DHCP_RETRY_COUNT) {
                printf("DHCP 失败\n");
                DHCP_stop();
                while (1);
            }
            wizchip_delay_ms(1000);
        }

        /* DNS 解析 */
        if ((g_dns_get_ip_flag == 0) && (retval == DHCP_IP_LEASED)) {
            while (1) {
                if (DNS_run(g_net_info.dns, g_dns_target_domain, g_dns_target_ip) > 0) {
                    printf("\nDNS 解析成功!\n");
                    printf("目标域名: %s\n", g_dns_target_domain);
                    printf("解析IP: %d.%d.%d.%d\n", 
                        g_dns_target_ip[0], g_dns_target_ip[1], 
                        g_dns_target_ip[2], g_dns_target_ip[3]);
                    g_dns_get_ip_flag = 1;
                    break;
                } else {
                    dns_retry++;
                    if (dns_retry <= DNS_RETRY_COUNT) {
                        printf("DNS 超时,重试: %d\n", dns_retry);
                    }
                }

                if (dns_retry > DNS_RETRY_COUNT) {
                    printf("DNS 解析失败\n");
                    while (1);
                }
                wizchip_delay_ms(1000);
            }
        }
    }
}

/* DHCP 初始化 */
static void wizchip_dhcp_init(void) {
    printf("DHCP 客户端运行中...\n");
    DHCP_init(SOCKET_DHCP, g_ethernet_buf);
    reg_dhcp_cbfunc(wizchip_dhcp_assign, wizchip_dhcp_assign, wizchip_dhcp_conflict);
}

static void wizchip_dhcp_assign(void) {
    getIPfromDHCP(g_net_info.ip);
    getGWfromDHCP(g_net_info.gw);
    getSNfromDHCP(g_net_info.sn);
    getDNSfromDHCP(g_net_info.dns);
    g_net_info.dhcp = NETINFO_DHCP;
    network_initialize(g_net_info);
    print_network_information(g_net_info);
    printf("DHCP 租期: %ld 秒\n", getDHCPLeasetime());
}

static void wizchip_dhcp_conflict(void) {
    printf("DHCP IP 冲突\n");
    while (1);
}

/* 定时器回调 */
static void repeating_timer_callback(void) {
    g_msec_cnt++;
    if (g_msec_cnt >= 1000 - 1) {
        g_msec_cnt = 0;
        DHCP_time_handler();
        DNS_time_handler();
    }
}

5.2 代码功能说明

  • 硬件初始化:SPI、W5500、定时器、串口全硬件启动
  • DHCP 自动获取:无需配置静态 IP,即插即用
  • DNS 解析:调用官方 DNS 库,硬件处理,稳定高效
  • 超时重试机制:DHCP 5 次重试,DNS 5 次重试
  • 异常处理:IP 冲突、解析失败自动停机保护
  • 串口打印:实时输出 IP、域名、解析结果
  • 工业级鲁棒性:适合长期稳定运行

6. 运行结果与测试验证

6.1 串口输出结果

DHCP 客户端运行中...
MAC: 00:08:DC:12:34:56
IP: 192.168.1.100
SN: 255.255.255.0
GW: 192.168.1.1
DNS: 8.8.8.8
DHCP 租期: 86400 秒
DHCP 配置成功

DNS 解析成功!
目标域名: www.wiznet.io
解析IP: 203.253.128.164

6.2 验证方法

  1. 电脑 ping 解析出的 IP
    ping 203.253.128.164
    
  2. 更换域名测试修改代码中的:
    static uint8_t g_dns_target_domain[] = "www.baidu.com";
    
  3. 重新编译烧录,查看是否解析成功

7. 常见问题一站式排查指南

7.1 DHCP 无法获取 IP

  • 网线未插好 / 路由器未开 DHCP
  • 网口接在 WAN 口,应接 LAN 口
  • 更换网线 / 网口
  • 重启开发板与路由器

7.2 DNS 解析失败

  • 设备无法访问外网
  • DNS 配置错误(改为 8.8.8.8 或 114.114.114.114)
  • 域名拼写错误
  • 防火墙屏蔽 UDP 53 端口
  • 更换手机热点测试

7.3 串口无输出

  • 数据线不支持数据传输
  • 串口波特率错误
  • 固件烧录失败

7.4 IP 冲突

  • 局域网内有重复 IP
  • DHCP 分配异常,重启路由器解决

8. WIZnet 硬件协议栈核心优势对比

对比维度

W5500 硬件协议栈方案

外接 PHY 芯片方案

外接串口转以太网模块方案

BOM 成本

中(MCU + 网络模块,无需额外器件)

中高(MCU + PHY 芯片 + 外围器件)

高(MCU + 串口转网口模块)

PCB 面积

小(模块集成度高,仅需预留模块安装空间)

大(需预留芯片、布线空间及外围电路)

中(需预留模块安装空间)

开发难度

低(MicroPython 固件已封装底层,一行代码实现联网)

中高(需调试协议栈、编写底层驱动,对研发能力要求高)

低(模块已集成驱动,仅需串口通信)

网络稳定性

极高(WIZnet 专注硬件 TCP/IP 协议栈 25 年,抗干扰能力强)

不定(依赖研发人员对协议栈和网络开发的掌握程度)

不定(视模块厂商研发能力和产品质量)

CPU 资源占用

0%(协议栈完全由硬件处理,不占用 MCU 资源)

50%以上(协议栈运行在 MCU 上,占用大量 CPU 和内存)

0%(模块独立处理网络逻辑)

硬件 Socket 数量

W5500 8个独立硬件 Socket

视 MCU 能力而定,理论支持多路拓展

一般为单路透传,多连接需额外配置

网络吞吐量

W5500 最高 15Mbps

视 MCU 能力而定,普遍低于硬件协议栈方案

约 3-5Mbps,速率较低

接口易用性

SPI 接口,接线简单,适配大多数 MCU

需 MCU 带有 MII/RMII 等专用接口,适配性有限

TTL 串口接口,适配性强,但速率受限

部署难度

低(MicroPython 成熟固件,应用层协议均有库文件,可灵活部署)

高(应用层协议需要手动移植开源库适配,调试成本高)

中(无集成功能需自行封包拆包,灵活性不足)

9. 典型应用场景

  • 物联网设备远程上云
  • 工业数据采集与上传
  • 智能硬件远程控制
  • 环境监测传感器网络
  • 嵌入式网关、DTU
  • 教育实验平台

10. 系列预告与资源获取

10.1 系列预告

下一篇教程我们将讲解 NTP 网络时间同步(C 语言),实现嵌入式设备精准计时、时间戳、定时任务、防时钟漂移等工业级功能,为日志记录、数据上报、时序控制打下基础。

10.2 资源获取

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐