全部例程

使用DHCP获取动态IPv4地址

W6300

更新于 2026年9月24日

本篇总结

在 W6300-EVB-M4 平台上通过 QSPI 接口驱动 W6300,完成 DHCP 动态获取 IP 地址、W6300 初始化并 Ping 测试验证连通性。

🔑 MH2457、QSPI、UART调试、硬件TOE引擎、DHCP

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

采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W6300-EVB-M4 开发板

本示例演示如何在 W6300 上通过 DHCP(动态主机配置协议)自动获取 IP 地址、子网掩码、网关、DNS 等网络配置参数。相比手动配置静态 IP,DHCP 实现了即插即用,设备入网即自动获取网络参数。

前置工具准备

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

DHCP 协议简介

DHCP(Dynamic Host Configuration Protocol)即动态主机配置协议,是一个应用层协议。它主要用于在 IP 网络中为客户端自动分配 IP 地址及其他相关网络配置参数,如子网掩码、默认网关、DNS 服务器地址等。这种动态分配的方式大大简化了网络管理员的工作,并且能够更有效地利用有限的 IP 地址资源。

DHCP 的工作原理分为四个阶段(DORA):

阶段方向说明
DiscoverClient → Server(广播)客户端广播寻找 DHCP 服务器
OfferServer → Client服务器分配 IP 并提供网络参数
RequestClient → Server(广播)客户端请求使用该 IP 地址
AckServer → Client服务器确认分配,客户端完成配置

DHCP 报文各主要字段说明:

字段说明
op报文类型,1=Boot Request(客户端请求),2=Boot Reply(服务器响应)
htype硬件类型,Ethernet=0x01
hlen硬件地址长度,值为 6
xid事务 ID,用于匹配请求与响应
ciaddr客户端 IP 地址
yiaddr你的(客户端)IP 地址,服务器分配的 IP
siaddr下一服务器 IP 地址
giaddr中继代理 IP 地址
chaddr客户端 MAC 地址
Magic cookieDHCP 标识,固定值 0x63825363
Option(53)DHCP 消息类型:Discover/Offer/Request/Ack/Nak

Discover 报文实例:客户端通过 UDP 广播的方式发送 DHCP 发现报文,报文中包含了客户端 MAC 地址、主机名和请求的 IP 地址等信息。

Message type: Boot Request (1)            (op code为1,客户端请求报文)
Hardware type: Ethernet (0x01)
Hardware address length: 6
Hops: 0
Transaction ID: 0xbf600589
Seconds elapsed: 0
Bootp flags: 0x0000 (Unicast)
Client IP address: 0.0.0.0
Your (client) IP address: 0.0.0.0
Next server IP address: 0.0.0.0
Relay agent IP address: 0.0.0.0
Client MAC address: HP_b1:37:11 (64:4e:d7:b1:37:11)
Client hardware address padding: 00000000000000000000
Server host name not given
Boot file name not given
Magic cookie: DHCP
Option: (53) DHCP Message Type (Discover)
Option: (61) Client identifier
Option: (50) Requested IP Address (192.168.1.115)
Option: (12) Host Name
Option: (60) Vendor class identifier
Option: (55) Parameter Request List
Option: (255) End
Padding: 0000000000000000

Offer 报文实例:DHCP 服务器收到 Discover 报文后,从 IP 地址池选一个未分配的 IP 地址,将其和子网掩码、默认网关等信息封装进 Offer 报文以广播方式发给客户端。

Message type: Boot Reply (2)              (op code为2,服务器响应报文)
Hardware type: Ethernet (0x01)
Hardware address length: 6
Hops: 0
Transaction ID: 0xbf600589
Seconds elapsed: 0
Bootp flags: 0x0000 (Unicast)
Client IP address: 0.0.0.0
Your (client) IP address: 192.168.1.115
Next server IP address: 0.0.0.0
Relay agent IP address: 0.0.0.0
Client MAC address: HP_b1:37:11 (64:4e:d7:b1:37:11)
Client hardware address padding: 00000000000000000000
Server host name not given
Boot file name not given
Magic cookie: DHCP
Option: (53) DHCP Message Type (Offer)
Option: (54) DHCP Server Identifier (192.168.1.1)
Option: (51) IP Address Lease Time
Option: (6) Domain Name Server
Option: (1) Subnet Mask (255.255.255.0)
Option: (3) Router
Option: (255) End

其他报文信息这里就不一一展示了,感兴趣的朋友可以自行抓取查看。

W6300 的优势

W6300 SDK 内置了完整的 DHCP 客户端库(dhcp.c/h),DORA 状态机、报文组包、超时重试、IP 租约续租全部由库函数处理。你只需要:注册 1 秒定时器 → 设置 NETINFO_DHCP 模式 → 调用 network_init()。DHCP 失败时自动回退到静态 IP 配置。

硬件连接与拓扑

请注意:

PC 和开发板需接入同一路由器(同一网段)。路由器需开启 DHCP 服务(家用路由器默认开启)。

  1. Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
  2. 网线连接开发板 RJ45 网口 到路由器 LAN 口
  3. 确保 W6300 与 MH2457 之间的 QSPI 连线正确(CSn、SCK、DATA0~DATA3)
┌─────────────────────────────────┐
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W6300 │ │ PC │
│(DHCP Client) │ (CMD ping) │
└────┬──────┘ └───────────┘
│ QSPI ↑
┌────┴──────┐ DEBUG USB
│ MH2457 │──────────→ WIZ UartTool
│ MCU │
└───────────┘
网络拓扑:路由器作为 DHCP Server 分配 IP,MH2457 通过 QSPI 驱动 W6300,开发板获取地址后 PC 可 ping 通

工程结构

工程核心文件如下:

Project/
├── main.c                    ← 系统入口:串口、GPIO、QSPI 初始化 + user_run()
└── USER/
    ├── user_main.c/h         ← 网络地址定义 + user_run() 主流程
    ├── wiz_platform.c/h      ← 平台适配:debug_usart_init、wizchip_gpio_init、hspi_init
    ├── wiz_interface.c/h     ← W6300 驱动接口:wizchip_initialize、network_init、PHY 检测
    ├── dhcp/
    │   ├── dhcp.c            ← DHCP 客户端状态机 (DORA)
    │   └── dhcp.h            ← DHCP 超时/端口/主机名定义
    └── wizchip_conf.h        ← WIZnet 配置与 wiz_NetInfo 定义

本例程相比 Network install 章节的核心新增:DHCP 客户端库的初始化、1 秒定时器注册、DHCP 状态机运行以及 IP 租约管理。

步骤1:注册 DHCP 定时器中断

文件:USER/wiz_interface.c — W6300 平台通过软件定时器链表为 DHCP 提供秒级时基。wiz_timer_handler() 由 1ms 定时器中断调用,累加到 1000 次后触发注册的回调函数。

void wiz_timer_handler(void)
{
    struct wiz_timer *temp;

    wiz_delay_ms_count++;

    temp = wiz_timer_head;
    while (temp != NULL)
    {
        temp->count_time++;

        if (temp->count_time >= temp->trigger_time)
        {
            temp->count_time = 0;
            temp->func();
        }

        temp = temp->next;
    }
}

在 dhcp.h 文件中,定义了 DHCP 超时时间、重试次数、端口号和主机名等内容:

/* Retry to processing DHCP */
#define MAX_DHCP_RETRY          2        ///< Maximum retry count
#define DHCP_WAIT_TIME          10       ///< Wait Time 10s

/* UDP port numbers for DHCP */
#define DHCP_SERVER_PORT        67       ///< DHCP server port number
#define DHCP_CLIENT_PORT        68       ///< DHCP client port number

#define MAGIC_COOKIE            0x63825363  ///< You should not modify it number.

#define DCHP_HOST_NAME           "WIZnet\0"

步骤2:启用 DHCP 动态获取

文件:USER/user_main.c — 将默认网络地址信息结构体中的模式改为 DHCP 模式。

/* network information */
wiz_NetInfo default_net_info = {
    .mac = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56},
    .ip = {192, 168, 1, 50},
    .sn = {255, 255, 255, 0},
    .gw = {192, 168, 1, 1},
    .dns = {0, 0, 0, 0},
    .ipmode = NETINFO_DHCP_V4,
    .dhcp = NETINFO_DHCP
};

请注意

使用 DHCP 动态获取 IP 时,必需将网络结构体配置中 dhcp 的值改为 NETINFO_DHCP,同时将 ipmode 设为 NETINFO_DHCP_V4,这样才能运行 DHCP 模式。

步骤3:DHCP 获取网络地址信息

文件:USER/user_main.c — 初始化完硬件和 W6300 之后调用 network_init 进行网络地址信息配置。

network_init(ethernet_buf, &default_net_info);

这个函数需要将 DHCP 处理用到的缓存数组以及默认网络地址信息传入,函数具体内容如下:

void network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info)
{
    int ret;
    uint8_t tx_size[8] = {2U, 2U, 2U, 2U, 2U, 2U, 2U, 2U};
    uint8_t rx_size[8] = {2U, 2U, 2U, 2U, 2U, 2U, 2U, 2U};

    if (conf_info == NULL)
    {
        return;
    }

    (void)wizchip_init(tx_size, rx_size);

    if (conf_info->dhcp == NETINFO_DHCP)
    {
        conf_info->ipmode = NETINFO_DHCP_V4;
    }
    else
    {
        conf_info->ipmode = NETINFO_STATIC_V4;
    }

    wizchip_setnetinfo(conf_info);

    if (conf_info->dhcp == NETINFO_DHCP)
    {
        ret = wiz_dhcp_process(0, ethernet_buff, conf_info);

        if (ret == 0)
        {
            conf_info->dhcp = NETINFO_STATIC;
            conf_info->ipmode = NETINFO_STATIC_V4;
            wizchip_setnetinfo(conf_info);
        }
    }

    print_network_information();
}

在这个函数中,会先设置一遍网络地址到 W6300 中,然后判断模式是否为 DHCP 模式。如果为 DHCP 模式,则使用 wiz_dhcp_process 函数来执行 DHCP 进程,在通过 DHCP 方式成功获取到网络地址后更新到 W6300 中,最后将网络地址信息打印出来。wiz_dhcp_process 函数内容如下:

static uint8_t wiz_dhcp_process(uint8_t sn, uint8_t *buffer, const wiz_NetInfo *fallback_info)
{
    wiz_NetInfo conf_info;
    uint8_t dhcp_run_flag = 1;
    uint8_t dhcp_ok_flag = 0;

    wiz_add_timer(DHCP_time_handler, 1000);
    DHCP_init(sn, buffer);
    printf("DHCP running\r\n");

    while (1)
    {
        switch (DHCP_run())
        {
        case DHCP_IP_LEASED:
            if (dhcp_ok_flag == 0)
            {
                dhcp_ok_flag = 1;
                dhcp_run_flag = 0;
            }
            break;

        case DHCP_FAILED:
            dhcp_run_flag = 0;
            break;

        default:
            break;
        }

        if (dhcp_run_flag == 0)
        {
            printf("DHCP %s!\r\n", dhcp_ok_flag ? "success" : "fail");
            DHCP_stop();
            wiz_delete_timer(DHCP_time_handler);

            if (dhcp_ok_flag != 0U)
            {
                memset(&conf_info, 0, sizeof(conf_info));

                memcpy(conf_info.mac, fallback_info->mac, sizeof(conf_info.mac));
                getIPfromDHCP(conf_info.ip);
                getGWfromDHCP(conf_info.gw);
                getSNfromDHCP(conf_info.sn);
                getDNSfromDHCP(conf_info.dns);
                conf_info.ipmode = NETINFO_DHCP_V4;
                conf_info.dhcp = NETINFO_DHCP;

                wizchip_setnetinfo(&conf_info);
                return 1U;
            }

            return 0U;
        }
    }
}

在该函数体中,首先调用 wiz_add_timer 注册 DHCP 超时回调,再调用 DHCP_init 初始化 DHCP 配置,然后在主循环中运行 DHCP_run 函数。它负责组包发送 Discover/Request 报文、解析 Offer/Ack 响应以及超时处理。获取成功后通过 getIPfromDHCP 等接口取出地址并写入 W6300。DHCP_run 函数核心状态机如下:

uint8_t DHCP_run(void)
{
  uint8_t  type;
  uint8_t  ret;

  if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED;

  if(getSn_SR(DHCP_SOCKET) != SOCK_UDP)
      socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00);

  ret = DHCP_RUNNING;
  type = parseDHCPMSG();

  switch ( dhcp_state ) {
      case STATE_DHCP_INIT     :
          DHCP_allocated_ip[0] = 0;
          DHCP_allocated_ip[1] = 0;
          DHCP_allocated_ip[2] = 0;
          DHCP_allocated_ip[3] = 0;
        send_DHCP_DISCOVER();
        dhcp_state = STATE_DHCP_DISCOVER;
        break;

    case STATE_DHCP_DISCOVER :
      if (type == DHCP_OFFER){
            DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0];
            DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1];
            DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2];
            DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3];
        send_DHCP_REQUEST();
        dhcp_state = STATE_DHCP_REQUEST;
      } else ret = check_DHCP_timeout();
          break;

    case STATE_DHCP_REQUEST :
      if (type == DHCP_ACK) {
        if (check_DHCP_leasedIP()) {
          dhcp_ip_assign();
          reset_DHCP_timeout();
          dhcp_state = STATE_DHCP_LEASED;
        } else {
          reset_DHCP_timeout();
          dhcp_ip_conflict();
          dhcp_state = STATE_DHCP_INIT;
        }
      } else if (type == DHCP_NAK) {
        reset_DHCP_timeout();
        dhcp_state = STATE_DHCP_DISCOVER;
      } else ret = check_DHCP_timeout();
    break;

    case STATE_DHCP_LEASED :
        ret = DHCP_IP_LEASED;
      if ((dhcp_lease_time/2) < dhcp_tick_1s) {
        type = 0;
        OLD_allocated_ip[0] = DHCP_allocated_ip[0];
        OLD_allocated_ip[1] = DHCP_allocated_ip[1];
        OLD_allocated_ip[2] = DHCP_allocated_ip[2];
        OLD_allocated_ip[3] = DHCP_allocated_ip[3];
        DHCP_XID++;
        send_DHCP_REQUEST();
        reset_DHCP_timeout();
        dhcp_state = STATE_DHCP_REREQUEST;
      }
    break;

    case STATE_DHCP_REREQUEST :
        ret = DHCP_IP_LEASED;
      if (type == DHCP_ACK) {
        dhcp_retry_count = 0;
        if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] ||
            OLD_allocated_ip[1] != DHCP_allocated_ip[1] ||
            OLD_allocated_ip[2] != DHCP_allocated_ip[2] ||
            OLD_allocated_ip[3] != DHCP_allocated_ip[3])
        {
          ret = DHCP_IP_CHANGED;
          dhcp_ip_update();
        }
        reset_DHCP_timeout();
        dhcp_state = STATE_DHCP_LEASED;
      } else if (type == DHCP_NAK) {
        reset_DHCP_timeout();
        dhcp_state = STATE_DHCP_DISCOVER;
      } else ret = check_DHCP_timeout();
        break;

    default :
        break;
  }

  return ret;
}

状态机说明

DHCP_run 通过状态机驱动整个流程:STATE_DHCP_INIT 发送 Discover 报文;收到 Offer 后进入 STATE_DHCP_REQUEST 发送 Request;收到 Ack 且 IP 无冲突则进入 STATE_DHCP_LEASED;租约过半时进入 STATE_DHCP_REREQUEST 续租。任一阶段超时均由 check_DHCP_timeout 处理重试。

运行验证

烧录例程运行后,首先进行 PHY 链路检测,然后运行 DHCP 获取网络地址信息并打印。打开 WIZ UartTool V1.0,连接 DEBUG USB 对应 COM 口(115200 / 8N1),预期串口输出如下:

MH2457 W6300 QSPI example start
W6300 DHCP example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
 W6300 network configuration : DHCP

 MAC         : 00:08:DC:12:34:56
 IP          : 192.168.1.6
 Subnet Mask : 255.255.255.0
 Gateway     : 192.168.1.1
 DNS         : 192.168.1.1
====================================================================================================

please try ping 192.168.1.6

请注意

测试实例需要 PC 端和 W6300 处于同一网段。

接着在 PC 端打开 CMD,PING W6300 的 IP 地址,可以正常 PING 通。

CMD ping 192.168.1.8(4 包全通,0% 丢包)+ WIZ UartTool 串口输出(PHY + DHCP + 网络地址 + Ping 提示)— DHCP 动态地址获取测试

常见问题

Q: DHCP 获取 IP 失败后如何处理

在 network_init 函数中,wiz_dhcp_process 返回 0 表示 DHCP 失败。此时会将 conf_info->dhcp 改为 NETINFO_STATIC,并调用 wizchip_setnetinfo 使用结构体中预设的静态 IP 配置网络信息,保证设备仍可联网。MAX_DHCP_RETRY 定义了最大重试次数(默认2次),超过后判定失败。

Q: IP 地址租约到期后会怎样

DHCP_run 的 STATE_DHCP_LEASED 状态会检测租约时间,当租约过半(dhcp_lease_time/2 < dhcp_tick_1s)时自动进入 STATE_DHCP_REREQUEST 状态,发送 DHCP_REQUEST 续租。若服务器回复 Ack 则继续使用原 IP(或更新为新 IP,触发 dhcp_ip_update);若回复 Nak 则回到 STATE_DHCP_DISCOVER 重新获取。

Q: 为什么 DHCP 使用 UDP 广播

DHCP 初始阶段客户端还没有 IP 地址,无法使用 TCP(TCP 需要先建立连接,依赖双方 IP)。因此使用 UDP 广播(目的 IP 255.255.255.255,源 IP 0.0.0.0)来寻找网络中的 DHCP 服务器。DHCP 服务器端口为 67,客户端端口为 68。

Q: 如何在 DHCP 和静态 IP 之间切换

只需修改 wiz_NetInfo 结构体中的 dhcp 字段:设为 NETINFO_DHCP 使用动态获取,设为 NETINFO_STATIC 使用静态 IP。同时将 ipmode 设为对应的 NETINFO_DHCP_V4 或 NETINFO_STATIC_V4。network_init 函数会根据该字段判断是否执行 DHCP 流程。切换后需重新调用 network_init 或 wizchip_setnetinfo 使配置生效。

Q: W6300 平台 DHCP 定时器如何实现

W6300 平台通过 wiz_add_timer() 向软件定时器链表注册回调,wiz_timer_handler() 由 1ms 定时器中断调用,累计到设定的触发时间(DHCP 为 1000ms)后执行 DHCP_time_handler,为 DHCP 状态机提供秒级时基。该时基用于 Discover/Request 报文的超时检测和 IP 租约计时,缺少该中断将导致 DHCP 无法正常超时重试和续租。

总结

  • 协议定位:DHCP 是应用层协议,用于在 IP 网络中自动分配 IP 地址、子网掩码、网关、DNS 等网络配置参数
  • 四大特点:便捷配置与管控、灵活资源利用、高效维护与排障、适配移动与拓展
  • 工作原理:发现(Discover)→ 提供(Offer)→ 请求(Request)→ 确认(Ack)四阶段完成地址分配
  • 实现步骤:注册 1 秒定时器 → 设置 NETINFO_DHCP 模式 → 调用 network_init 和 wiz_dhcp_process 执行 DHCP 流程
  • 失败回退:DHCP 获取失败时自动回退到静态 IP 配置,保证设备基本联网能力
  • 运行验证:烧录后完成链路检测与信息打印,PC 端可正常 PING 通设备

下载本章例程

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

开发环境: Keil MDK5

Andy

W5500 技术支持专家