STM32H743-ARM例程35-DHCP

目录

实验平台

硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器

软件:最新版本STM32CubeH7固件库STM32CubeMX v6.10.0,开发板环境MDK v5.35TCP&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)

  1. DHCP 发现(DHCP Discover)

    当设备(DHCP 客户端)加入网络时,它会发送一个 DHCP 发现广播包,寻找可用的 DHCP 服务器。

    客户端广播 DHCP Discover 包,寻找可用的 DHCP 服务器。

    局域网中的所有设备都会收到该请求。

  2. DHCP 提供(DHCP Offer)

    DHCP 服务器收到 DHCP Discover 包后,会从地址池中选择一个可用的 IP 地址,并通过 DHCP Offer 包提供给客户端。

    DHCP 服务器发送 DHCP Offer 包,提供可用的 IP 地址和其他配置信息。

    DHCP Offer 包可以是广播或单播的。

  3. DHCP 请求(DHCP Request)

    客户端收到 DHCP Offer 包后,会发送一个 DHCP 请求包,确认接受提供的 IP 地址。

    客户端广播 DHCP Request 包,确认接受提供的 IP 地址。

    局域网中的所有设备都会收到该请求。

  4. 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. 主函数

c 复制代码
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相关函数

c 复制代码
#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 等待打印结果。

相关推荐
boneStudent4 小时前
Day30:I2C 与其他通信协议对比
stm32·单片机·嵌入式硬件
小尧嵌入式4 小时前
C语言中的面向对象思想
c语言·开发语言·数据结构·c++·单片机·qt
k期4 小时前
STM32初学---RCC / NVIC / EXTI
stm32
一杯美式 no sugar4 小时前
数据结构——单向无头不循环链表
c语言·数据结构·链表
li星野4 小时前
打工人日报#20251215
单片机·嵌入式硬件
清风6666664 小时前
基于单片机的压力机润滑油泵与主电机控制系统设计
单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
飞来客isdn4 小时前
GD32F407ZGT6在FreeRTOS下串口中断接收异常情况及解决方法
单片机·mcu·freertos·信息与通信
枫叶机关录4 小时前
有刷直流电机与无刷直流电机——结构、原理与控制
嵌入式硬件·机器人·直流电机
南棱笑笑生5 小时前
20251215给飞凌OK3588-C开发板适配Rockchip原厂的Buildroot【linux-5.10】后调通typeC1接口
linux·c语言·开发语言·rockchip
点灯小铭5 小时前
基于51单片机的频率可调多波形函数发生器设计与实现
单片机·嵌入式硬件·毕业设计·51单片机·课程设计·期末大作业