STM32H743-ARM例程35-DHCP
目录
- 实验平台
- DHCP
- STM32CubeMX生成工程
- 实验代码
- 实验现象
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35,TCP&UDP测试工具,串口工具putty
网盘资料包:链接: https://pan.baidu.com/s/1Y3nwaY4SMxfoUsdqPm2R3w?pwd=inba 提取码: inba
DHCP
DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,通常被应用在大型的局域网络环境中,主要作用是集中的管理、分配 IP 地址,使网络环境中的主机动态的获得 IP 地址、Gateway 地址、DNS 服务器地址等信息,并能够提升地址的使用率。
用于自动分配 IP 地址和其他网络配置信息(如子网掩码、默认网关、DNS 服务器)的协议。DHCP 简化了网络管理,允许设备在加入网络时自动获取必要的配置,而无需手动设置。

工作原理
DHCP 使用客户端-服务器模型,通过广播和单播通信实现 IP 地址的自动分配。
DHCP获得ip地址的4步骤:discover>offer>request>ack(nak)
-
DHCP 发现(DHCP Discover)
当设备(DHCP 客户端)加入网络时,它会发送一个 DHCP 发现广播包,寻找可用的 DHCP 服务器。

客户端广播 DHCP Discover 包,寻找可用的 DHCP 服务器。
局域网中的所有设备都会收到该请求。 -
DHCP 提供(DHCP Offer)
DHCP 服务器收到 DHCP Discover 包后,会从地址池中选择一个可用的 IP 地址,并通过 DHCP Offer 包提供给客户端。

DHCP 服务器发送 DHCP Offer 包,提供可用的 IP 地址和其他配置信息。
DHCP Offer 包可以是广播或单播的。 -
DHCP 请求(DHCP Request)
客户端收到 DHCP Offer 包后,会发送一个 DHCP 请求包,确认接受提供的 IP 地址。

客户端广播 DHCP Request 包,确认接受提供的 IP 地址。
局域网中的所有设备都会收到该请求。 -
DHCP 确认(DHCP Acknowledge)
DHCP 服务器收到 DHCP Request 包后,会发送一个 DHCP 确认包,正式分配 IP 地址和其他配置信息。

DHCP 服务器发送 DHCP Acknowledge 包,正式分配 IP 地址和其他配置信息。
DHCP Acknowledge 包可以是广播或单播的。
DHCP的核心概念
DHCP服务器:负责分配IP地址和其他网络配置的设备。最常见的例子就是我们家中的无线路由器,它内置了DHCP服务器功能。
DHCP客户端:请求并接受IP地址配置的网络设备,如电脑、手机、智能电视等。
IP地址池:DHCP服务器可以分配出去的IP地址范围。例如:192.168.1.100 到 192.168.1.200。
租期:IP地址被分配给客户端后,并非永久占有,而是有一个使用期限。租期到期前,客户端需要向服务器请求续租。这确保了IP地址资源可以被循环利用,不会因为设备离线而永久占用。
DHCP中继:由于DHCP请求最初是以广播形式发送的,而广播包通常无法跨越路由器到达其他子网。DHCP中继代理可以帮助在不同子网之间转发DHCP请求和回复,使得一个DHCP服务器能够为多个物理子网服务。
总结
DHCP是现代TCP/IP网络不可或缺的核心服务之一。它通过自动化的方式管理IP地址分配,极大地简化了网络部署和维护工作,保证了网络的稳定性和可扩展性,是实现“即插即用”网络体验的关键技术。
我们日常生活中,无论是在家中、办公室还是咖啡馆,能够轻松连接Wi-Fi并立即上网,背后都有DHCP在默默工作。
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们来看配置MPU配置、以太网部分和Lwip部分配置如下图所示:
配置以太网。选用RMII(精简的独立于介质接口)模式




MPU配置

LWIP配置



实验代码
1. 主函数
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MPU Configuration--------------------------------------------------------*/MPU_Config();
/* Enable the CPU Cache *//* Enable I-Cache---------------------------------------------------------*/SCB_EnableICache();/* Enable D-Cache---------------------------------------------------------*/SCB_EnableDCache();/* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_LWIP_Init();MX_USART6_UART_Init();/* USER CODE BEGIN 2 */uart6.initialize(115200);uart6.printf("\x0c");uart6.printf("GT7000 OK!\r\n");HAL_GPIO_WritePin(PHYAD0_GPIO_Port,PHYAD0_Pin,GPIO_PIN_RESET);eth_udp.initialize();eth_tcps_init();uart6.printf("initialize OK!\r\n");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){MX_LWIP_Process();if(uart6.receive_ok_flag == 1){uart6.receive_ok_flag = 0;memset(buffer,0,20);memcpy(buffer,uart6.receive_buffer,20); for(i = 0;i < 20;i ++){buffer[i] = tolower(buffer[i]);} if(memcmp(buffer,"dhcp",strlen("dhcp")) == 0){ lwip_dhcp_task(); }else if(memcpy(buffer,"clear",strlen("clear")) == 0){uart6.printf("\x0c");}else{uart6.printf("\r\nCommand not found!\r\n");} }/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
2.DHCP相关函数
#include "eth_udp.h"
#include <string.h>
#include "dhcp.h"
#include "uart6.h"
//--------------------- Function Prototype ----------------------//
void initialize(void);
void receive_data(void *arg,struct udp_pcb *upcb,struct pbuf *p,const ip_addr_t *addr,u16_t port);
void send_data(struct udp_pcb *upcb);
void connection_close(struct udp_pcb *upcb);
void udp_client_send(struct udp_pcb *upcb, char *pData);
void lwip_dhcp_task(void);
//--------------------------- Variable --------------------------//
ETH_UDP_T eth_udp = {.initialize = initialize,.receive_data = receive_data,.send_data = send_data,.connection_close = connection_close,.receive_ok_flag = 0
};__lwip_dev lwipdev;
uint8_t PCIP_ADDRESS[4] = {192, 168, 31, 220};
extern struct netif gnetif;void initialize(void)
{err_t err;struct ip4_addr rmtipaddr;eth_udp.udppcb = udp_new();//创建一个新的UDP协议控制块IP4_ADDR(&rmtipaddr ,PCIP_ADDRESS[0] ,PCIP_ADDRESS[1] ,PCIP_ADDRESS[2] ,PCIP_ADDRESS[3]);//设置服务器(PC)IP地址udp_connect(eth_udp.udppcb ,&rmtipaddr,REMOTE_PORT); //连接至远程客户端if (eth_udp.udppcb){err = udp_bind(eth_udp.udppcb ,IP_ADDR_ANY ,LOCAL_PORT); //给UDP协议控制块绑定端口号和IP地址if(err == ERR_OK){udp_recv(eth_udp.udppcb ,eth_udp.receive_data ,NULL); //设置接收回调函数}else{udp_remove(eth_udp.udppcb);}}
}static void receive_data(void *arg,struct udp_pcb *upcb,struct pbuf *p,const ip_addr_t *addr,u16_t port)
{uint32_t data_len = 0;struct pbuf *q;if(p!=NULL){memset(eth_udp.receive_buffer,0,EHT_BUFFER_SIZE);for(q=p;q!=NULL;q=q->next){if(q->len > (EHT_BUFFER_SIZE-data_len)) memcpy(eth_udp.receive_buffer + data_len,q->payload,(EHT_BUFFER_SIZE - data_len));else memcpy(eth_udp.receive_buffer+data_len,q->payload,q->len);data_len += q->len; if(data_len > EHT_BUFFER_SIZE) break;}eth_udp.receive_ok_flag = 1;pbuf_free(p);}else{udp_disconnect(upcb); }
} void send_data(struct udp_pcb *upcb)
{struct pbuf *ptr;memcpy(eth_udp.send_buffer,eth_udp.receive_buffer,1024);ptr=pbuf_alloc(PBUF_TRANSPORT,strlen((char*)eth_udp.send_buffer),PBUF_RAM); //申请内存if(ptr){pbuf_take(ptr,(char *)eth_udp.send_buffer,strlen((char*)eth_udp.send_buffer)); //拷贝数据udp_send(upcb,ptr); //发送数据pbuf_free(ptr); //释放内存}
} void connection_close(struct udp_pcb *upcb)
{udp_disconnect(upcb); udp_remove(upcb);
}void lwip_dhcp_task(void)
{uint32_t ip=0,netmask=0,gw=0;lwipdev.dhcpstatus=0; //正在DHCP uart6.printf("正在获取地址...\r\n");ip=gnetif.ip_addr.addr; //读取新IP地址netmask=gnetif.netmask.addr;//读取子网掩码gw=gnetif.gw.addr; //读取默认网关 if(ip!=0) //当正确读取到IP地址的时候{//flag = 1;lwipdev.dhcpstatus=2; //DHCP成功uart6.printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",\lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);//解析出通过DHCP获取到的IP地址lwipdev.ip[3]=(uint8_t)(ip>>24); lwipdev.ip[2]=(uint8_t)(ip>>16);lwipdev.ip[1]=(uint8_t)(ip>>8); lwipdev.ip[0]=(uint8_t)(ip);uart6.printf("通过DHCP获取到IP地址..............%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);//解析通过DHCP获取到的子网掩码地址lwipdev.netmask[3]=(uint8_t)(netmask>>24);lwipdev.netmask[2]=(uint8_t)(netmask>>16);lwipdev.netmask[1]=(uint8_t)(netmask>>8);lwipdev.netmask[0]=(uint8_t)(netmask);uart6.printf("通过DHCP获取到子网掩码............%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);//解析出通过DHCP获取到的默认网关lwipdev.gateway[3]=(uint8_t)(gw>>24);lwipdev.gateway[2]=(uint8_t)(gw>>16);lwipdev.gateway[1]=(uint8_t)(gw>>8);lwipdev.gateway[0]=(uint8_t)(gw);uart6.printf("通过DHCP获取到的默认网关..........%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);}else if(netif_dhcp_data(&gnetif)->tries > LWIP_MAX_DHCP_TRIES) //通过DHCP服务获取IP地址失败,且超过最大尝试次数netif_dhcp_data(gnetif) (netif)->client_data[(id)]{lwipdev.dhcpstatus=0XFF;//DHCP失败.//使用静态IP地址IP4_ADDR(&(gnetif.ip_addr),lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);IP4_ADDR(&(gnetif.netmask),lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);IP4_ADDR(&(gnetif.gw),lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);printf("DHCP服务超时,使用静态IP地址!\r\n");printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);}
}
实验现象
本实验例程采用的 LWIP 版本位 2.1.2,其中包含了 DHCP 协议,在对 LWIP 的 DHCP功能进行使能和启动后,即可通过路由器对设备进行 IP 地址的分配。
我们需要一根网线将GT7000的网口与路由器的网口连接,然后运行程序在putty串口工具输入“dhcp”进行自动分配IP。如下图所示

按“Win+R” 输入命令“cmd”,输入 ping 加获取到的 IP,如:ping 192.168.31.119 等待打印结果。

