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 等待打印结果。

相关推荐
GilgameshJSS3 小时前
STM32H743-ARM例程34-BootROM
c语言·arm开发·stm32·单片机·嵌入式硬件
robin8611093 小时前
Keil(MDK-ARM)和 STM32CubeIDE对比
arm开发·stm32·嵌入式硬件
烟雨彷徨~~Xun4 小时前
esp8266+dht11传感器实时上报温湿度
单片机·mcu·物联网
Jack电子实验室4 小时前
深入理解C语言函数指针:从基础到实战应用
java·c语言·算法
La Pulga4 小时前
【STM32】FLASH闪存
android·c语言·javascript·stm32·单片机·嵌入式硬件·mcu
sheepwjl5 小时前
《嵌入式硬件(十九):基于IMX6ULL的SPI操作》
单片机·嵌入式硬件·spi·adxl345
zhangx1234_5 小时前
C语言题目1
c语言·开发语言·数据结构
小年糕是糕手5 小时前
【C/C++刷题集】二叉树算法题(一)
c语言·数据结构·c++·算法·leetcode·学习方法·改行学it
GalaxySpaceX5 小时前
STM32-音频播放
stm32·嵌入式硬件·音视频