基于W5500芯片实现DHCP自动获取IP功能

一、硬件

1. 电路连接

W5500引脚 STM32引脚 功能说明
SCLK PA5 SPI时钟
MOSI PA7 主出从入
MISO PA6 主入从出
CS PA4 片选信号
INT PA3 中断引脚
RESET PA2 复位引脚
3.3V 3.3V 电源
GND GND 地线

2. 网络拓扑

[STM32] --SPI--> [W5500] --RJ45--> 局域网交换机
                  ↑
              DHCP服务器

二、软件实现步骤

1. 寄存器初始化

// W5500复位配置
void W5500_Reset() {
    GPIO_ResetBits(GPIOA, GPIO_Pin_2);  // 拉低RESET引脚
    Delay_ms(100);
    GPIO_SetBits(GPIOA, GPIO_Pin_2);    // 释放RESET
    Delay_ms(200);
}

// SPI接口配置(STM32 HAL库)
SPI_HandleTypeDef hspi1;

void MX_SPI1_Init(void) {
    hspi1.Instance = SPI1;
    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
    hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_256;
    HAL_SPI_Init(&hspi1);
}

2. DHCP状态机实现

typedef enum {
    DHCP_INIT,
    DHCP_DISCOVER,
    DHCP_OFFER,
    DHCP_REQUEST,
    DHCP_ACK,
    DHCP_TIMEOUT
} DHCP_State;

DHCP_State dhcp_state = DHCP_INIT;
uint8_t dhcp_mac[6] = {0x00, 0x08, 0xDC, 0x12, 0x34, 0x56}; // 默认MAC地址

void DHCP_Init() {
    // 设置MAC地址
    setSHAR(dhcp_mac);
    
    // 配置网络模式为DHCP
    setNETINFO(NETINFO_DHCP);
    
    // 初始化DHCP定时器
    HAL_TIM_Base_Start_IT(&htim3); // 100ms定时器
}

void DHCP_Process() {
    switch(dhcp_state) {
        case DHCP_INIT:
            send_DHCP_Discover();
            dhcp_state = DHCP_DISCOVER;
            break;
            
        case DHCP_DISCOVER:
            if(receive_DHCP_Offer()) {
                dhcp_state = DHCP_OFFER;
            }
            break;
            
        case DHCP_OFFER:
            send_DHCP_Request();
            dhcp_state = DHCP_REQUEST;
            break;
            
        case DHCP_REQUEST:
            if(receive_DHCP_ACK()) {
                dhcp_state = DHCP_ACK;
                save_network_params(); // 保存IP等参数
            }
            break;
            
        case DHCP_TIMEOUT:
            // 超时处理:重试或切换模式
            break;
    }
}

3. DHCP报文处理

// 构造DHCP Discover报文
void send_DHCP_Discover() {
    uint8_t dhcp_pkt[576] = {0};
    
    dhcp_pkt[0] = 1;    // OP: BOOTREQUEST
    dhcp_pkt[1] = 1;    // HTYPE: ETHERNET
    dhcp_pkt[2] = 6;    // HLEN: 6字节MAC
    dhcp_pkt[3] = 0;    // HOPS: 0
    
    // 填充事务ID
    memcpy(&dhcp_pkt[4], &xid, 4);
    
    // 设置客户端MAC地址
    memcpy(&dhcp_pkt[28], dhcp_mac, 6);
    
    // 设置选项字段
    dhcp_pkt[53] = 53;  // DHCP消息类型
    dhcp_pkt[54] = 1;   // 消息类型长度
    dhcp_pkt[55] = 1;   // DHCP_DISCOVER
    
    send_udp_packet(DHCP_SERVER_PORT, dhcp_pkt, 576);
}

// 解析DHCP Offer报文
void parse_dhcp_offer(uint8_t *pkt) {
    // 提取服务器IP
    server_ip[0] = pkt[16];
    server_ip[1] = pkt[17];
    server_ip[2] = pkt[18];
    server_ip[3] = pkt[19];
    
    // 提取分配IP
    assigned_ip[0] = pkt[16+2+16]; // yiaddr字段
    assigned_ip[1] = pkt[16+2+17];
    assigned_ip[2] = pkt[16+2+18];
    assigned_ip[3] = pkt[16+2+19];
}

三、配置参数

1. DHCP超时设置

#define DHCP_TIMEOUT_MS  10000  // 总超时时间
#define DHCP_RETRY_INTERVAL 2000 // 重试间隔

volatile uint32_t dhcp_timer = 0;

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
    if(htim->Instance == TIM3) {
        dhcp_timer += 100;
        if(dhcp_timer >= DHCP_TIMEOUT_MS) {
            dhcp_state = DHCP_TIMEOUT;
            dhcp_timer = 0;
        }
    }
}

2. 网络参数保存

uint8_t assigned_ip[4] = {0};
uint8_t subnet_mask[4] = {255,255,255,0};
uint8_t gateway_ip[4] = {192,168,1,1};

void save_network_params() {
    // 从DHCP ACK报文提取参数
    memcpy(assigned_ip, &ack_pkt[16+2+16], 4);  // yiaddr
    memcpy(subnet_mask, &ack_pkt[16+2+1], 4);   // subnet_mask
    memcpy(gateway_ip, &ack_pkt[16+2+2], 4);    // routers
}

四、调试与验证

1. 逻辑分析仪抓包示例

Time    | 操作类型      | 关键字段
---------------------------------------
0.00s   | DHCP Discover | MAC:00:08:DC:12:34:56
0.52s   | DHCP Offer    | ServerIP:192.168.1.254
0.85s   | DHCP Request  | RequestIP:192.168.1.100
1.20s   | DHCP ACK      | LeaseTime:86400s

2. 常见问题排查

现象 解决方案
无DHCP响应 检查MAC地址唯一性,确认网络连通性
IP地址冲突 重启设备或联系网络管理员
租约时间异常 检查DHCP服务器配置

五、扩展功能实现

1. 静态IP切换

void set_static_ip() {
    setNETINFO(NETINFO_STATIC);
    setIPADDR(192,168,1,100);    // 设置静态IP
    setGATEWAY(192,168,1,1);     // 设置网关
    setSUBNET(255,255,255,0);    // 设置子网掩码
}

2. 多网卡支持

// 配置多个W5500芯片
void multi_w5500_init() {
    for(int i=0; i<MAX_W5500_NUM; i++) {
        W5500_Reset(i);          // 复位不同片选
        setSHAR(mac_addr[i]);    // 设置不同MAC
        setNETINFO(NETINFO_DHCP);// 启动DHCP
    }
}

六、性能优化

  1. DMA传输:启用SPI DMA模式提升吞吐量

    hspi1.Init.Mode = SPI_MODE_MASTER;
    hspi1.Init.Direction = SPI_DIRECTION_2LINES;
    hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
    hspi1.Init.NSS = SPI_NSS_SOFT;
    hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_64; // 10MHz
    hspi1.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE; // 保持IO状态
    HAL_SPI_Init(&hspi1);
    
  2. 低功耗模式

    // 进入睡眠模式前关闭网络
    WIZCHIP_CS_HIGH();
    HAL_GPIO_WritePin(GPIOA, GPIO_Pin_4, GPIO_PIN_SET);
    HAL_PWR_EnterSLEEPMode(PWR_MAINREGULATOR_ON, PWR_SLEEPENTRY_WFI);
    

参考代码 W5500 实现DHCP自动获取IP功能 www.youwenfan.com/contentcnk/71379.html

七、完整代码结构

├── Drivers/
│   ├── W5500/
│   │   ├── w5500.c       # 底层驱动
│   │   └── w5500.h       # 寄存器定义
│   └── STM32/
│       ├── spi.c         # SPI接口实现
│       └── tim.c         # 定时器配置
├── Middlewares/
│   └── DHCP_Client/      # DHCP协议栈
│       ├── dhcp.c
│       └── dhcp.h
└── Applications/
    └── main.c            # 主程序入口
posted @ 2025-11-03 14:43  alloutlove  阅读(12)  评论(0)    收藏  举报