使用DHCP获取动态地址
更新于 2026年9月17日
本篇总结
在 W55MH32 上实现 DHCP 动态获取 IP 地址,成功获取 IP 后即可正常进行网络通信。
硬件平台:本例程基于 W55MH32L-EVB 开发板运行。
采购推荐:点击前往 WIZnet 官方淘宝店铺 选购 W55MH32L-EVB
本示例演示如何在 W55MH32 上通过 DHCP(动态主机配置协议)自动获取 IP 地址、子网掩码、网关、DNS 等网络配置参数。相比手动配置静态 IP,DHCP 实现了即插即用,设备入网即自动获取网络参数。
前置工具准备
开始本例程前,请确认以下工具已安装:
- WIZ UartTool V1.0:串口调试工具:点击下载
DHCP 协议简介
DHCP(Dynamic Host Configuration Protocol)即动态主机配置协议,是一个应用层协议。它主要用于在 IP 网络中为客户端自动分配 IP 地址及其他相关网络配置参数,如子网掩码、默认网关、DNS 服务器地址等。这种动态分配的方式大大简化了网络管理员的工作,并且能够更有效地利用有限的 IP 地址资源。
DHCP 的工作原理分为四个阶段(DORA):
| 阶段 | 方向 | 说明 |
|---|---|---|
| Discover | Client → Server(广播) | 客户端广播寻找 DHCP 服务器 |
| Offer | Server → Client | 服务器分配 IP 并提供网络参数 |
| Request | Client → Server(广播) | 客户端请求使用该 IP 地址 |
| Ack | Server → 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 cookie | DHCP 标识,固定值 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: 0000000000000000Offer 报文实例: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其他报文信息这里就不一一展示了,感兴趣的朋友可以自行抓取查看。
W55MH32 的优势
W55MH32 SDK 内置了完整的 DHCP 客户端库(dhcp.c/h),DORA 状态机、报文组包、超时重试、IP 租约续租全部由库函数处理。你只需要:注册 1 秒定时器中断 → 设置 NETINFO_DHCP 模式 → 调用 network_init()。DHCP 失败时自动回退到静态 IP 配置。
硬件连接与拓扑
请注意:
PC 和开发板需接入同一路由器(同一网段)。路由器需开启 DHCP 服务(家用路由器默认开启)。
- Micro USB 线连接 DEBUG USB 口到 PC(供电 + 调试串口)
- 网线连接开发板 RJ45 网口 到路由器 LAN 口
│ 路由器(DHCP Server) │
│ 192.168.1.1 │
└────┬──────────────────┬───────────┘
│ │
┌────┴──────┐ ┌────┴──────┐
│ W55MH32 │ │ PC │
│ (DHCP Client) │ (CMD ping) │
└───────────┘ └───────────┘
↑
DEBUG USB ──→ WIZ UartTool
工程结构
工程位于 SDK 内 1.SDK\ModuleDemo\WZTOE\2.DHCP\,核心文件如下:
2.DHCP/
├── Project/
│ └── Project.uvprojx ← Keil 工程文件
└── USER/
├── main.c ← 硬件初始化 + 网络配置 + DHCP 主流程
├── wiz_interface/
│ ├── wiz_interface.c ← PHY 检测 / 网络信息打印
│ └── wiz_interface.h
├── dhcp/
│ ├── dhcp.c ← DHCP 客户端状态机 (DORA)
│ └── dhcp.h ← DHCP 超时/端口/主机名定义
├── uart/
│ ├── bsp_uart.c ← 调试串口 + printf 重定向
│ └── bsp_uart.h
├── tim/
│ └── bsp_tim.c/h ← TIM3 1ms 定时器(供 DHCP 超时)
└── delay/
└── delay.c/h ← SysTick 毫秒延时
本例程相比 Network install 章节的核心新增:DHCP 客户端库的初始化、1 秒定时器中断注册、DHCP 状态机运行以及 IP 租约管理。
步骤1:注册 DHCP 定时器中断
文件:USER/main.c — DHCP 协议依赖超时重传机制。TIM3 定时器每 1ms 触发一次,累计到 1000 次(1 秒)后调用 DHCP_time_handler(),为 DHCP 状态机提供秒级时基。
/**
* @brief 1ms timer IRQ Handler
* @param none
* @return none
*/
void TIM3_IRQHandler(void)
{
static uint32_t tim3_1ms_count = 0;
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
tim3_1ms_count++;
if (tim3_1ms_count >= 1000)
{
DHCP_time_handler();
tim3_1ms_count = 0;
}
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
在 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/main.c — 将默认网络地址信息结构体中的模式改为 DHCP 模式。
/* network information */
wiz_NetInfo default_net_info = {
.mac = {0x00, 0x08, 0xdc, 0x12, 0x22, 0x12},
.ip = {192, 168, 1, 30},
.gw = {192, 168, 1, 1},
.sn = {255, 255, 255, 0},
.dns = {8, 8, 8, 8},
.dhcp = NETINFO_DHCP
};请注意
使用 DHCP 动态获取 IP 时,必需将网络结构体配置中 dhcp 的值改为 NETINFO_DHCP,这样才能运行 DHCP 模式。
步骤3:DHCP 获取网络地址信息
文件:USER/main.c — 初始化完硬件和 TOE 之后调用 network_init 进行网络地址信息配置。
network_init(ethernet_buf, &default_net_info);这个函数需要将 DHCP 处理用到的缓存数组以及默认网络地址信息传入,函数具体内容如下:
/**
* @brief set network information
*
* First determine whether to use DHCP. If DHCP is used, first obtain the
* Internet Protocol Address through DHCP. When DHCP fails, use static IP to
* configure network information. If static IP is used, configure network
* information directly.
*
* @param ethernet_buff: socket buffer
* @param net_info: network information struct
* @return none
*/
void network_init(uint8_t *ethernet_buff, wiz_NetInfo *conf_info)
{
int ret;
wizchip_setnetinfo(conf_info); // Configuring Network Information
if (conf_info->dhcp == NETINFO_DHCP)
{
ret = wiz_dhcp_process(0, ethernet_buff);
if (ret == 0)
{
conf_info->dhcp = NETINFO_STATIC;
wizchip_setnetinfo(conf_info);
}
}
print_network_information();
}
在这个函数中,会先设置一遍网络地址到 W55MH32 中,然后判断模式是否为 DHCP 模式。如果为 DHCP 模式,则使用 wiz_dhcp_process 函数来执行 DHCP 进程,在通过 DHCP 方式成功获取到网络地址后更新到 W55MH32 中,最后将网络地址信息打印出来。wiz_dhcp_process 函数内容如下:
/**
* @brief DHCP process
* @param sn : socket number
* @param buffer : socket buffer
*/
static uint8_t wiz_dhcp_process(uint8_t sn, uint8_t *buffer)
{
wiz_NetInfo conf_info;
uint8_t dhcp_run_flag = 1;
uint8_t dhcp_ok_flag = 0;
/* Registration DHCP_time_handler to 1 second timer */
DHCP_init(sn, buffer);
printf("DHCP running\r\n");
while (1)
{
switch (DHCP_run()) // Do the DHCP client
{
case DHCP_IP_LEASED: // DHCP Acquiring network information successfully
if (dhcp_ok_flag == 0)
{
dhcp_ok_flag = 1;
dhcp_run_flag = 0;
}
break;
case DHCP_FAILED:
dhcp_run_flag = 0;
break;
}
if (dhcp_run_flag == 0)
{
printf("DHCP %s!\r\n", dhcp_ok_flag ? "success" : "fail");
DHCP_stop();
if (dhcp_ok_flag)
{
getIPfromDHCP(conf_info.ip);
getGWfromDHCP(conf_info.gw);
getSNfromDHCP(conf_info.sn);
getDNSfromDHCP(conf_info.dns);
conf_info.dhcp = NETINFO_DHCP;
getSHAR(conf_info.mac);
wizchip_setnetinfo(&conf_info); // Update network info
return 1;
}
return 0;
}
}
}
在该函数体中,首先调用 DHCP_init 初始化 DHCP 配置,然后在主循环中运行 DHCP_run 函数。它负责组包发送 Discover/Request 报文、解析 Offer/Ack 响应以及超时处理。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()) {
// Network info assignment from DHCP
dhcp_ip_assign();
reset_DHCP_timeout();
dhcp_state = STATE_DHCP_LEASED;
} else {
// IP address conflict occurred
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),预期串口输出如下:
W55MH32 DHCP example
PHY link
The current Mbtis speed : 100Mbps
The current Duplex Mode : Full-Duplex
DHCP running
DHCP success!
====================================================================================================
W55MH32 network configuration : DHCP
MAC : 00:08:DC:00:0E:9A
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 端和 W55MH32 处于同一网段。
接着在 PC 端打开 CMD,PING W55MH32 的 IP 地址,可以正常 PING 通。

常见问题
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。network_init 函数会根据该字段判断是否执行 DHCP 流程。切换后需重新调用 network_init 或 wizchip_setnetinfo 使配置生效。
Q: DHCP 定时器中断的作用是什么
DHCP 协议依赖超时重传机制。TIM3 定时器中断每 1ms 触发一次,累计到 1000 次(即 1 秒)后调用 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 通设备