最近學習了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的對比
特性/方面 | LWIP1.4.1 | LWIP2.2 |
協議支持 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等,還包括TLS(實驗性) |
性能 | 基本性能優化 | 改進的性能優化 |
安全性 | 有限的安全性功能 | 增強的安全性功能 |
API變更 | 穩定的API | 一些API變更和新增 |
Bug修復 | 針對已知問題的Bug修復 | 為穩定性進行的Bug修復和補丁 |
兼容性 | 與現有LWIP1.x代碼兼容 | 與現有LWIP1.x代碼兼容,但可能需要適應性修改 |
文檔 | 提供了LWIP1.4.1文檔 | 更新的LWIP2.2文檔 |
內存使用 | 適度的內存使用 | 優化的內存使用 |
多線程支持 | 有限或無多線程支持 | 改進的多線程支持 |
TLS支持 (安全增強) |
不可用 | 實驗性的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! ");
/* 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 ");
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d ",
IP_ADDR0,
IP_ADDR1,
IP_ADDR2,
IP_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d ",
NETMASK_ADDR0,
NETMASK_ADDR1,
NETMASK_ADDR2,
NETMASK_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d ",
GW_ADDR0,
GW_ADDR1,
GW_ADDR2,
GW_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d ",
COMP_IP_ADDR0,
COMP_IP_ADDR1,
COMP_IP_ADDR2,
COMP_IP_ADDR3,
COMP_PORT);
printf("%s",LCDDisplayBuf);
#endif
// printf(" KEY1: Connect TCP server ");
// printf("KEY2: Disconnect TCP server ");
while(1)
{
if ((APM_TINY_PBGetState(BUTTON_KEY1)==0)&&(flag==0))
{
APM_TINY_LEDOn(LED2);
if (EthLinkStatus == 0)
{
/* connect to tcp server */
printf(" Connect TCP server ");
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(" Disconnect TCP server ");
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, eernetif_init, eernet_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! ");
}
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 ! ");
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! ");
}
}
這段代碼是一個用于處理以太網鏈接狀態變化的回調函數 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定時器處理。
4. if((ETHTimer - LinkTimer) >= 1000):檢查鏈接狀態是否需要更新,如果超過了一定時間(這里設置為1000ms),則進行鏈接狀態檢查。
5. if((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0)):檢查網絡鏈接狀態是否正常,并且之前的狀態是斷開的,如果是,則設置網絡接口為鏈接狀態。
6. if((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1)):檢查網絡鏈接狀態是否異常,并且之前的狀態是連接的,如果是,則設置網絡接口為斷開狀態。
7. if(ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS):檢查是否需要進行DHCP的Fine定時處理,如果超過了DHCP_FINE_TIMER_MSECS(Fine定時器間隔),則調用 dhcp_fine_tmr() 函數。
8. 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..... ");
}
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! ");
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 ",
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 ",
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 ",
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 ",
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! ");
/* 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 ");
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"IP: %d.%d.%d.%d ",
IP_ADDR0,
IP_ADDR1,
IP_ADDR2,
IP_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"NETMASK: %d.%d.%d.%d ",
NETMASK_ADDR0,
NETMASK_ADDR1,
NETMASK_ADDR2,
NETMASK_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"Gateway: %d.%d.%d.%d ",
GW_ADDR0,
GW_ADDR1,
GW_ADDR2,
GW_ADDR3);
printf("%s",LCDDisplayBuf);
sprintf(LCDDisplayBuf,"TCP Server IP: %d.%d.%d.%d:%d ",
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命令,正常通信
-
以太網
+關注
關注
40文章
5458瀏覽量
172326 -
嵌入式系統
+關注
關注
41文章
3614瀏覽量
129631 -
TCP
+關注
關注
8文章
1375瀏覽量
79181 -
DHCP
+關注
關注
0文章
105瀏覽量
19745 -
LwIP
+關注
關注
2文章
88瀏覽量
27286
原文標題:APM32芯得 EP.46 | 增強以太網連接:使用LWIP 2.2實現動態主機配置協議(DHCP)
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導體】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論