本帖最后由 DKENNY 于 2024-2-27 17:47 编辑
#申请原创# @21小跑堂
最近学习了LWIP,了解到目前LWIP的版本已经更新到了2.2版本。LWIP 2.2相较于之前的版本,在协议支持、性能、安全性等方面都有了显著的改进,我将在本帖中探讨如何利用LWIP 2.2来实现以太网的DHCP功能,并分享一些我所获得的经验。
1.LWIP简介
LWIP代表"轻量级IP"(Lightweight IP),是一个嵌入式系统中常用的开源TCP/IP协议栈。它被设计成小巧、高效,适用于资源受限的系统,如嵌入式设备、物联网设备等。LWIP提供了包括IPv4/IPv6协议、TCP、UDP、ICMP等在内的各种网络协议的实现,同时支持各种设备接口和操作系统。通过使用LWIP,开发者可以方便地将网络连接功能集成到他们的嵌入式系统中,而无需从头开始实现网络协议栈。
LWIP1.4.1与LWIP2.2的对比
特性/方面 | | | 协议支持 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等,还包括TLS(实验性) | 性能 | | | 安全性 | | | API变更 | | | Bug修复
| | | 兼容性
| | 与现有LWIP1.x代码兼容,但可能需要适应性修改 | 文档
| | | 内存使用
| | | 多线程支持
| | | TLS支持(安全增强)
| | |
这里列举了从LWIP1.4.1到目前最新的LWIP2.2版本主要的一些修改点。具体详细的组件支持可去LWIP官网了解。
2.DHCP介绍 DynamicHost Configuration Protocol(DHCP)是一种网络协议,用于在计算机网络上自动分配IP地址和其他网络配置信息。DHCP的主要目的是简化网络管理,避免手动配置每台计算机的网络参数。 以下是DHCP的一些关键特性和工作原理: 1. 自动IP地址分配:DHCP允许网络中的设备自动获取IP地址,而无需管理员手动配置。这对于大型网络特别有用,因为手动分配IP地址可能会变得繁琐和容易出错。 2. 动态分配:DHCP支持动态分配IP地址,这意味着设备在每次连接到网络时可以获得不同的IP地址。这有助于更有效地利用可用的IP地址池。 3. 配置其他网络参数:除了IP地址之外,DHCP还可以分配其他网络配置信息,如子网掩码、默认网关、DNS服务器地址等。这些信息是设备与网络通信所需的关键参数。 4. 租约机制:DHCP通过租约机制来管理分配的IP地址。设备获得一个IP地址并与DHCP服务器建立租约,这个租约在一定时间内有效。设备可以选择在租约到期前续租或请求新的租约。 5. DHCP服务器:网络中通常有一个或多个DHCP服务器,它们负责分配IP地址和配置信息。当设备连接到网络时,它们发送DHCP请求,DHCP服务器收到请求后分配一个可用的IP地址和相关配置信息。 6. DHCP客户端:设备上运行的DHCP客户端负责向网络中的DHCP服务器发送请求以获取IP地址和配置信息。DHCP客户端通常在设备启动时触发DHCP过程。 7. 广播通信:DHCP通信通常使用广播方式进行。DHCP客户端在网络上广播一个请求,DHCP服务器接收到请求后回应,然后客户端使用分配的IP地址和配置信息进行通信。 总体而言,DHCP简化了网络管理过程,使得设备可以更轻松地连接到网络而无需手动配置网络参数。 DHCP在家庭网络、企业网络和大型互联网服务提供商(ISP)网络中广泛应用。
3.使用LWIP2.2搭建ETH DHCP例程 首先,介绍一下该例程主要实现的功能。 1.可以实时检测以太网是否断开,并将信息打印在串口上。 2.开发板复位后首先获取DHCP服务器分配的IP地址。若无法查找到DHCP(设置的时间是60s),开发板会使用默认的静态IP地址。 3.1 访问官网,下载LWIP2.2源码 官网:https://savannah.nongnu.org/projects/lwip
3.2 解压,复制到工程目录下,并在工程中添加相关文件。 我这里是按文件分类添加的,可以自定义添加。
3.3 编写main函数 int main(void)
{
char LCDDisplayBuf[100] = {0};
struct ip4_addr DestIPaddr;
uint8_t flag = 0;
USART_Config_T usartConfig;
/* User config the different system Clock */
UserRCMClockConfig();
/* Configure SysTick */
ConfigSysTick();
/* Configure USART */
usartConfig.baudRate = 115200;
usartConfig.wordLength = USART_WORD_LEN_8B;
usartConfig.stopBits = USART_STOP_BIT_1;
usartConfig.parity = USART_PARITY_NONE ;
usartConfig.mode = USART_MODE_TX_RX;
usartConfig.hardwareFlow = USART_HARDWARE_FLOW_NONE;
APM_BOARD_COMInit(COM1,&usartConfig);
/* Configures LED2 and LED3 */
APM_BOARD_LEDInit(LED2);
APM_BOARD_LEDInit(LED3);
/* KEY init*/
APM_BOARD_PBInit(BUTTON_KEY1, BUTTON_MODE_GPIO);
APM_BOARD_PBInit(BUTTON_KEY2, BUTTON_MODE_GPIO);
printf("This is a ETH TCP Client Demo! \r\n");
/* Configure ethernet (GPIOs, clocks, MAC, DMA) */
ConfigEthernet();
/* Initilaize the LwIP stack */
LwIP_Init();
#ifndef USE_DHCP
/* Use Com printf static IP address*/
sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
IP_ADDR0,
IP_ADDR1,
IP_ADDR2,
IP_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
NETMASK_ADDR0,
NETMASK_ADDR1,
NETMASK_ADDR2,
NETMASK_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
GW_ADDR0,
GW_ADDR1,
GW_ADDR2,
GW_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d \r\n",
COMP_IP_ADDR0,
COMP_IP_ADDR1,
COMP_IP_ADDR2,
COMP_IP_ADDR3,
COMP_PORT);
printf("%s",LCDDisplayBuf);
#endif
// printf("\n\rKEY1: Connect TCP server \r\n");
// printf("KEY2: Disconnect TCP server \r\n");
while(1)
{
if ((APM_TINY_PBGetState(BUTTON_KEY1)==0)&&(flag==0))
{
APM_TINY_LEDOn(LED2);
if (EthLinkStatus == 0)
{
/* connect to tcp server */
printf("\n\rConnect TCP server \r\n");
IP4_ADDR( &DestIPaddr, COMP_IP_ADDR0, COMP_IP_ADDR1, COMP_IP_ADDR2, COMP_IP_ADDR3 );
tcpc_echo_init(&DestIPaddr,COMP_PORT);
flag=1;
}
}
if ((APM_TINY_PBGetState(BUTTON_KEY2)==0)&&(flag==1))
{
APM_TINY_LEDOff(LED2);
printf("\n\rDisconnect TCP server \r\n");
tcpc_echo_disable();
flag=0;
}
/* check if any packet received */
if (ETH_CheckReceivedFrame())
{
/* process received ethernet packet */
LwIP_Pkt_Handle();
}
/* handle periodic timers for LwIP */
LwIP_Periodic_Handle(ETHTimer);
}
}
main函数主要是对一些串口、GPIO的初始化操作。我在main.h中定义了一个USE_DHCP的宏,我们可以打开或注释掉这个宏,选择是否开启DHCP功能。
3.4 LWIP_Init()函数 void LwIP_Init(void)
{
struct ip4_addr ipaddr;
struct ip4_addr netmask;
struct ip4_addr gw;
/* Initializes the dynamic memory heap */
mem_init();
/* Initializes the memory pools */
memp_init();
#ifdef USE_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
#else
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif
/* Config MAC Address */
ETH_ConfigMACAddress(ETH_MAC_ADDRESS0, SetMACaddr);
/* Add a network interface to the list of lwIP netifs */
netif_add(&UserNetif, &ipaddr, &netmask, &gw, NULL, ðernetif_init, ðernet_input);
/* Registers the default network interface */
netif_set_default(&UserNetif);
if (ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR) & 1)
{
UserNetif.flags |= NETIF_FLAG_LINK_UP;
/* When the netif is fully configured this function must be called */
netif_set_up(&UserNetif);
#ifdef USE_DHCP
DHCP_state = DHCP_START;
#endif
}
else
{
netif_set_down(&UserNetif);
#ifdef USE_DHCP
DHCP_state = DHCP_LINK_DOWN;
#endif /* USE_DHCP */
printf("network cable is not connected!\r\n");
}
netif_set_link_callback(&UserNetif, ETH_link_callback);
}
这段代码是用于初始化LwIP网络堆栈的函数 LwIP_Init。让我们逐步分析其功能: 1. 初始化IP地址、子网掩码和网关地址的结构体变量 ipaddr、netmask 和 gw。 2. 初始化动态内存堆。 3. 初始化内存池。 4. 根据是否启用了DHCP,设置IP地址、子网掩码和网关地址。 5. 配置MAC地址。 6. 将一个网络接口添加到lwIP网络接口列表中。 7. 注册默认的网络接口。 8. 通过读取PHY寄存器的状态来判断网络连接状态: - 如果连接状态为链接状态,设置网络接口的标志为链接已建立,将网络接口设置为已启用,并根据是否启用了DHCP,设置DHCP状态为启动。 - 如果连接状态为断开状态,将网络接口设置为已关闭,并根据是否启用了DHCP,设置DHCP状态为链接断开,并打印消息表示网络电缆未连接。 9. 设置网络接口的链接状态回调函数为 ETH_link_callback。 综上所述,该函数主要用于初始化LwIP网络堆栈。它包括初始化内存堆和内存池、配置网络接口的IP地址信息、MAC地址、添加网络接口到lwIP列表中、判断网络连接状态并相应地设置网络接口状态和DHCP状态,最后设置网络接口的链接状态回调函数。
3.5 ETH_link_callback函数 void ETH_link_callback(struct netif *netif)
{
__IO uint32_t timeout = 0;
uint16_t RegValue;
struct ip4_addr ipaddr;
struct ip4_addr netmask;
struct ip4_addr gw;
if(netif_is_link_up(netif))
{
/* Restart the auto-negotiation */
if(ETH_InitStructure.autoNegotiation == ETH_AUTONEGOTIATION_ENABLE)
{
/* Reset Timeout counter */
timeout = 0;
/* Enable auto-negotiation */
ETH_WritePHYRegister(ETH_PHY_ADDRESS, PHY_BCR, PHY_AUTONEGOTIATION);
/* Wait until the auto-negotiation will be completed */
do
{
timeout++;
} while (!(ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_BSR) & PHY_AUTONEGO_COMPLETE) && (timeout < (uint32_t)PHY_READ_TIMEOUT));
/* Reset Timeout counter */
timeout = 0;
/* Read the result of the auto-negotiation */
RegValue = ETH_ReadPHYRegister(ETH_PHY_ADDRESS, PHY_SR);
/* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
if((RegValue & PHY_DUPLEX_STATUS) != (uint16_t)RESET)
{
/* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
ETH_InitStructure.mode = ETH_MODE_FULLDUPLEX;
}
else
{
/* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
ETH_InitStructure.mode = ETH_MODE_HALFDUPLEX;
}
/* Configure the MAC with the speed fixed by the auto-negotiation process */
if(RegValue & PHY_SPEED_STATUS)
{
/* Set Ethernet speed to 10M following the auto-negotiation */
ETH_InitStructure.speed = ETH_SPEED_10M;
}
else
{
/* Set Ethernet speed to 100M following the auto-negotiation */
ETH_InitStructure.speed = ETH_SPEED_100M;
}
/*------------------------ ETHERNET MACCR Re-Configuration --------------------*/
/* Set the FES bit according to ETH_Speed value */
/* Set the DM bit according to ETH_Mode value */
ETH->CFG_B.SSEL = ETH_InitStructure.speed;
ETH->CFG_B.DM = ETH_InitStructure.mode;
Delay(0x00000001);
}
/* Restart MAC interface */
ETH_Start();
#ifdef USE_DHCP
ipaddr.addr = 0;
netmask.addr = 0;
gw.addr = 0;
DHCP_state = DHCP_START;
#else
IP4_ADDR(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);+
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1 , NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
#endif /* USE_DHCP */
netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);
/* When the netif is fully configured this function must be called.*/
netif_set_up(&UserNetif);
/* Display message on the LCD */
printf("network cable is connected now !\r\n");
EthLinkStatus = 0;
}
else
{
ETH_Stop();
#ifdef USE_DHCP
DHCP_state = DHCP_LINK_DOWN;
dhcp_stop(netif);
#endif /* USE_DHCP */
/* When the netif link is down this function must be called.*/
netif_set_down(&UserNetif);
printf("network cable is unplugged! \r\n");
}
}
这段代码是一个用于处理以太网链接状态变化的回调函数 ETH_link_callback。让我们逐步分析它的功能: 1. 首先,声明了一些变量,包括超时计数器 timeout、存储从PHY读取的寄存器值的变量 RegValue,以及用于存储IP地址、子网掩码和网关地址的变量。 2. 如果网络接口的链接状态为链接状态(通过 netif_is_link_up(netif) 判断): - 如果启用了以太网自动协商(ETH_InitStructure.autoNegotiation== ETH_AUTONEGOTIATION_ENABLE),则重新启动自动协商过程: - 向PHY寄存器写入自动协商命令 PHY_AUTONEGOTIATION。 - 等待自动协商完成,超时时间由 PHY_READ_TIMEOUT 定义。 - 读取自动协商结果,根据结果配置以太网MAC的速度和双工模式。 - 根据速度和双工模式重新配置以太网MAC控制器的寄存器。 - 重新启动MAC接口。 - 根据是否启用了DHCP,设置IP地址、子网掩码和网关地址,并将网络接口标记为已启用。 - 打印消息,表示网络电缆已连接。 - 将 EthLinkStatus 标记为链接状态。 3. 如果网络接口的链接状态为断开状态,执行以下操作: - 停止MAC接口。 - 如果启用了DHCP,将DHCP状态设置为链接断开,并停止DHCP客户端。 - 将网络接口标记为已关闭。 - 打印消息,表示网络电缆已拔出。 综上所述,该函数主要用于处理以太网链接状态的变化。根据链接状态的变化,重新启动自动协商过程(如果启用了自动协商),配置以太网MAC的参数,并设置IP地址信息。同时,根据链接状态的变化执行相应的动作,例如重新启动或停止MAC接口,并更新相应的状态标志。
3.6 LwIP_Periodic_Handle void LwIP_Periodic_Handle(__IO uint32_t ETHTimer)
{
static uint8_t flagToggle = 0;
#if LWIP_TCP
/* TCP periodic process every 250 ms */
if (ETHTimer - TCPTimer >= TCP_TMR_INTERVAL)
{
TCPTimer = ETHTimer;
tcp_tmr();
}
#endif
/* ARP periodic process every 5s */
if ((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL)
{
ARPTimer = ETHTimer;
etharp_tmr();
}
/* Check link status */
if ((ETHTimer - LinkTimer) >= 1000)
{
if ((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0))
{
/* link goes up */
netif_set_link_up(&UserNetif);
flagToggle = 1;
}
if ((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1))
{
EthLinkStatus = 1;
/* link goes down */
netif_set_link_down(&UserNetif);
flagToggle = 0;
}
}
#ifdef USE_DHCP
/* Fine DHCP periodic process every 500ms */
if (ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS)
{
DHCPfineTimer = ETHTimer;
dhcp_fine_tmr();
if ((DHCP_state != DHCP_ADDRESS_ASSIGNED) &&
(DHCP_state != DHCP_TIMEOUT) &&
(DHCP_state != DHCP_LINK_DOWN))
{
/* toggle LED1 to indicate DHCP on-going process */
APM_TINY_LEDOn(LED2);
/* process DHCP state machine */
LwIP_DHCP_Process_Handle();
}
}
/* DHCP Coarse periodic process every 60s */
if (ETHTimer - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS)
{
DHCPcoarseTimer = ETHTimer;
dhcp_coarse_tmr();
}
#endif
}
以上函数是一个用于处理LwIP(Lightweight IP)网络堆栈的周期性任务的函数。让我们逐段分析它: 1. staticuint8_t flagToggle = 0;:定义了一个静态变量 flagToggle,用于跟踪网络连接状态的变化。 2. if(ETHTimer - TCPTimer >= TCP_TMR_INTERVAL):检查TCP定时器是否超过了TCP_TMR_INTERVAL(TCP定时器间隔),如果超过,则调用 tcp_tmr() 函数进行TCP定时器处理。 3. if((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL):检查ARP定时器是否超过了ARP_TMR_INTERVAL(ARP定时器间隔),如果超过,则调用 etharp_tmr() 函数进行ARP定时器处理。 5. if((ETHTimer - LinkTimer) >= 1000):检查链接状态是否需要更新,如果超过了一定时间(这里设置为1000ms),则进行链接状态检查。 6. if((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0)):检查网络链接状态是否正常,并且之前的状态是断开的,如果是,则设置网络接口为链接状态。 7. if((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1)):检查网络链接状态是否异常,并且之前的状态是连接的,如果是,则设置网络接口为断开状态。 9. if(ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS):检查是否需要进行DHCP的Fine定时处理,如果超过了DHCP_FINE_TIMER_MSECS(Fine定时器间隔),则调用 dhcp_fine_tmr() 函数。 10. if(ETHTimer - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS):检查是否需要进行DHCP的Coarse定时处理,如果超过了DHCP_COARSE_TIMER_MSECS(Coarse定时器间隔),则调用 dhcp_coarse_tmr() 函数。 在整个函数中,主要处理了TCP、ARP、网络链接状态和DHCP的周期性任务。这些任务包括定时器的处理以及相应的动作,例如发送ARP请求、更新网络链接状态和处理DHCP状态机等。
3.7 LwIP_DHCP_Process_Handle void LwIP_DHCP_Process_Handle(void)
{
struct ip4_addr ipaddr;
struct ip4_addr netmask;
struct ip4_addr gw;
uint8_t iptab[4] = {0};
char LCDDisplayBuf[100] = {0};
switch (DHCP_state)
{
case DHCP_START:
{
DHCP_state = DHCP_WAIT_ADDRESS;
dhcp_start(&UserNetif);
/* IP address should be set to 0
every time we want to assign a new DHCP address */
IPaddress = 0;
printf(" Looking for DHCP server, please wait..... \r\n");
}
break;
case DHCP_WAIT_ADDRESS:
{
/* Read the new IP address */
struct dhcp *dhcp = netif_dhcp_data(&UserNetif);
if (dhcp->offered_ip_addr.addr != 0 && dhcp->offered_sn_mask.addr != 0 && dhcp->offered_gw_addr.addr != 0)
{
DHCP_state = DHCP_ADDRESS_ASSIGNED;
printf("IP address assigned by a DHCP server! \r\n");
IPaddress = dhcp->offered_ip_addr.addr;
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
IP4_ADDR(&ipaddr, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
iptab[3],
iptab[2],
iptab[1],
iptab[0]);
printf("%s",LCDDisplayBuf);
IPaddress = dhcp->offered_sn_mask.addr;
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
IP4_ADDR(&netmask, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
iptab[3],
iptab[2],
iptab[1],
iptab[0]);
printf("%s",LCDDisplayBuf);
IPaddress = dhcp->offered_gw_addr.addr;
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
IP4_ADDR(&gw, iptab[3] ,iptab[2] , iptab[1] , iptab[0]);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
iptab[3],
iptab[2],
iptab[1],
iptab[0]);
printf("%s",LCDDisplayBuf);
IPaddress = dhcp->server_ip_addr.addr;
iptab[0] = (uint8_t)(IPaddress >> 24);
iptab[1] = (uint8_t)(IPaddress >> 16);
iptab[2] = (uint8_t)(IPaddress >> 8);
iptab[3] = (uint8_t)(IPaddress);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d \r\n",
iptab[3],
iptab[2],
iptab[1],
iptab[0]);
printf("%s",LCDDisplayBuf);
// IP4_ADDR( &s_ipaddr, iptab[3] ,iptab[2] , iptab[1] , iptab[0] );
// udp_connect();
/* Stop DHCP */
dhcp_stop(&UserNetif);
// UserNetif.ip_addr.addr = ipaddr.addr;
// UserNetif.netmask.addr = netmask.addr;
// UserNetif.gw.addr = gw.addr;
netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);
APM_TINY_LEDOn(LED2);
}
else
{
/* DHCP timeout */
if (dhcp->tries > 4)
{
DHCP_state = DHCP_TIMEOUT;
/* Stop DHCP */
dhcp_stop(&UserNetif);
/* Static address used */
/* Use Com printf static IP address*/
printf("DHCP timeout!\r\n");
/* Static address used */
IP4_ADDR(&ipaddr, IP_ADDR0 ,IP_ADDR1 , IP_ADDR2 , IP_ADDR3 );
IP4_ADDR(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
IP4_ADDR(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
netif_set_addr(&UserNetif, &ipaddr , &netmask, &gw);
sprintf(LCDDisplayBuf,"TINY board Static IP address \r\n");
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d \r\n",
IP_ADDR0,
IP_ADDR1,
IP_ADDR2,
IP_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d \r\n",
NETMASK_ADDR0,
NETMASK_ADDR1,
NETMASK_ADDR2,
NETMASK_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d \r\n",
GW_ADDR0,
GW_ADDR1,
GW_ADDR2,
GW_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d \r\n",
COMP_IP_ADDR0,
COMP_IP_ADDR1,
COMP_IP_ADDR2,
COMP_IP_ADDR3,
COMP_PORT);
printf("%s",LCDDisplayBuf);
APM_TINY_LEDOn(LED2);
}
}
}
break;
default: break;
}
}
这段代码是用于处理LwIP网络堆栈中DHCP过程的函数 LwIP_DHCP_Process_Handle。它通过状态机来处理DHCP过程的不同阶段。让我们逐步分析其功能: 1. 在 DHCP_START 状态下,将状态切换到 DHCP_WAIT_ADDRESS,并启动DHCP客户端。 2. 在 DHCP_WAIT_ADDRESS 状态下,如果DHCP客户端获得了IP地址、子网掩码和网关地址,将状态切换到 DHCP_ADDRESS_ASSIGNED,打印获得的IP地址、子网掩码和网关地址,并停止DHCP客户端。 3. 如果DHCP超时(尝试次数超过4次),将状态切换到 DHCP_TIMEOUT,停止DHCP客户端,并使用静态IP地址(如果配置了静态IP地址)。 综上所述,该函数主要用于处理LwIP网络堆栈中DHCP过程的不同阶段。根据DHCP状态的不同,执行不同的操作,例如启动DHCP客户端、处理获取到的IP地址信息、处理超时情况等。
4. 实验现象
1.串口打印相关信息
2.正常获取IP后,cmd执行ping命令,正常通信
附件是该例程MDK源码,有需要的可自行下载,欢迎各位讨论交流。
ETH_TCP_DHCP_client.zip
(9.59 MB)
|
非常详细的使用LWIP 2.2实现动态主机配置协议(DHCP)测试实验,针对调用的函数进行详细的注解,方便移植用户更好地理解。格式清晰,实验结果正常。