最近學(xué)習(xí)了LWIP,了解到目前LWIP的版本已經(jīng)更新到了2.2版本。LWIP 2.2相較于之前的版本,在協(xié)議支持、性能、安全性等方面都有了顯著的改進(jìn),我將在本帖中探討如何利用LWIP 2.2來實現(xiàn)以太網(wǎng)的DHCP功能,并分享一些我所獲得的經(jīng)驗。
1.LWIP簡介
LWIP代表"輕量級IP"(Lightweight IP),是一個嵌入式系統(tǒng)中常用的開源TCP/IP協(xié)議棧。它被設(shè)計成小巧、高效,適用于資源受限的系統(tǒng),如嵌入式設(shè)備、物聯(lián)網(wǎng)設(shè)備等。LWIP提供了包括IPv4/IPv6協(xié)議、TCP、UDP、ICMP等在內(nèi)的各種網(wǎng)絡(luò)協(xié)議的實現(xiàn),同時支持各種設(shè)備接口和操作系統(tǒng)。通過使用LWIP,開發(fā)者可以方便地將網(wǎng)絡(luò)連接功能集成到他們的嵌入式系統(tǒng)中,而無需從頭開始實現(xiàn)網(wǎng)絡(luò)協(xié)議棧。
LWIP1.4.1與LWIP2.2的對比
特性/方面 | LWIP1.4.1 | LWIP2.2 |
協(xié)議支持 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等 | TCP、UDP、IP、ICMP、DHCP、DNS、PPP等,還包括TLS(實驗性) |
性能 | 基本性能優(yōu)化 | 改進(jìn)的性能優(yōu)化 |
安全性 | 有限的安全性功能 | 增強的安全性功能 |
API變更 | 穩(wěn)定的API | 一些API變更和新增 |
Bug修復(fù) | 針對已知問題的Bug修復(fù) | 為穩(wěn)定性進(jìn)行的Bug修復(fù)和補丁 |
兼容性 | 與現(xiàn)有LWIP1.x代碼兼容 | 與現(xiàn)有LWIP1.x代碼兼容,但可能需要適應(yīng)性修改 |
文檔 | 提供了LWIP1.4.1文檔 | 更新的LWIP2.2文檔 |
內(nèi)存使用 | 適度的內(nèi)存使用 | 優(yōu)化的內(nèi)存使用 |
多線程支持 | 有限或無多線程支持 | 改進(jìn)的多線程支持 |
TLS支持 (安全增強) |
不可用 | 實驗性的TLS支持 |
這里列舉了從LWIP1.4.1到目前最新的LWIP2.2版本主要的一些修改點。具體詳細(xì)的組件支持可去LWIP官網(wǎng)了解。
2.DHCP介紹
DynamicHost Configuration Protocol(DHCP)是一種網(wǎng)絡(luò)協(xié)議,用于在計算機網(wǎng)絡(luò)上自動分配IP地址和其他網(wǎng)絡(luò)配置信息。DHCP的主要目的是簡化網(wǎng)絡(luò)管理,避免手動配置每臺計算機的網(wǎng)絡(luò)參數(shù)。
以下是DHCP的一些關(guān)鍵特性和工作原理:
1. 自動IP地址分配:DHCP允許網(wǎng)絡(luò)中的設(shè)備自動獲取IP地址,而無需管理員手動配置。這對于大型網(wǎng)絡(luò)特別有用,因為手動分配IP地址可能會變得繁瑣和容易出錯。
2. 動態(tài)分配:DHCP支持動態(tài)分配IP地址,這意味著設(shè)備在每次連接到網(wǎng)絡(luò)時可以獲得不同的IP地址。這有助于更有效地利用可用的IP地址池。
3. 配置其他網(wǎng)絡(luò)參數(shù):除了IP地址之外,DHCP還可以分配其他網(wǎng)絡(luò)配置信息,如子網(wǎng)掩碼、默認(rèn)網(wǎng)關(guān)、DNS服務(wù)器地址等。這些信息是設(shè)備與網(wǎng)絡(luò)通信所需的關(guān)鍵參數(shù)。
4. 租約機制:DHCP通過租約機制來管理分配的IP地址。設(shè)備獲得一個IP地址并與DHCP服務(wù)器建立租約,這個租約在一定時間內(nèi)有效。設(shè)備可以選擇在租約到期前續(xù)租或請求新的租約。
5. DHCP服務(wù)器:網(wǎng)絡(luò)中通常有一個或多個DHCP服務(wù)器,它們負(fù)責(zé)分配IP地址和配置信息。當(dāng)設(shè)備連接到網(wǎng)絡(luò)時,它們發(fā)送DHCP請求,DHCP服務(wù)器收到請求后分配一個可用的IP地址和相關(guān)配置信息。
6. DHCP客戶端:設(shè)備上運行的DHCP客戶端負(fù)責(zé)向網(wǎng)絡(luò)中的DHCP服務(wù)器發(fā)送請求以獲取IP地址和配置信息。DHCP客戶端通常在設(shè)備啟動時觸發(fā)DHCP過程。
7. 廣播通信:DHCP通信通常使用廣播方式進(jìn)行。DHCP客戶端在網(wǎng)絡(luò)上廣播一個請求,DHCP服務(wù)器接收到請求后回應(yīng),然后客戶端使用分配的IP地址和配置信息進(jìn)行通信。
總體而言,DHCP簡化了網(wǎng)絡(luò)管理過程,使得設(shè)備可以更輕松地連接到網(wǎng)絡(luò)而無需手動配置網(wǎng)絡(luò)參數(shù)。 DHCP在家庭網(wǎng)絡(luò)、企業(yè)網(wǎng)絡(luò)和大型互聯(lián)網(wǎng)服務(wù)提供商(ISP)網(wǎng)絡(luò)中廣泛應(yīng)用。
3.使用LWIP2.2搭建ETH DHCP例程
首先,介紹一下該例程主要實現(xiàn)的功能。
1.可以實時檢測以太網(wǎng)是否斷開,并將信息打印在串口上。
2.開發(fā)板復(fù)位后首先獲取DHCP服務(wù)器分配的IP地址。若無法查找到DHCP(設(shè)置的時間是60s),開發(fā)板會使用默認(rèn)的靜態(tài)IP地址。
3.1 訪問官網(wǎng),下載LWIP2.2源碼
官網(wǎng):https://savannah.nongnu.org/projects/lwip
3.2 解壓,復(fù)制到工程目錄下,并在工程中添加相關(guān)文件。
我這里是按文件分類添加的,可以自定義添加。
3.3 編寫main函數(shù)
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函數(shù)主要是對一些串口、GPIO的初始化操作。我在main.h中定義了一個USE_DHCP的宏,我們可以打開或注釋掉這個宏,選擇是否開啟DHCP功能。
3.4 LWIP_Init()函數(shù)
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網(wǎng)絡(luò)堆棧的函數(shù) LwIP_Init。讓我們逐步分析其功能:
1. 初始化IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址的結(jié)構(gòu)體變量 ipaddr、netmask 和 gw。
2. 初始化動態(tài)內(nèi)存堆。
3. 初始化內(nèi)存池。
4. 根據(jù)是否啟用了DHCP,設(shè)置IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址。
5. 配置MAC地址。
6. 將一個網(wǎng)絡(luò)接口添加到lwIP網(wǎng)絡(luò)接口列表中。
7. 注冊默認(rèn)的網(wǎng)絡(luò)接口。
8. 通過讀取PHY寄存器的狀態(tài)來判斷網(wǎng)絡(luò)連接狀態(tài):
- 如果連接狀態(tài)為鏈接狀態(tài),設(shè)置網(wǎng)絡(luò)接口的標(biāo)志為鏈接已建立,將網(wǎng)絡(luò)接口設(shè)置為已啟用,并根據(jù)是否啟用了DHCP,設(shè)置DHCP狀態(tài)為啟動。
- 如果連接狀態(tài)為斷開狀態(tài),將網(wǎng)絡(luò)接口設(shè)置為已關(guān)閉,并根據(jù)是否啟用了DHCP,設(shè)置DHCP狀態(tài)為鏈接斷開,并打印消息表示網(wǎng)絡(luò)電纜未連接。
9. 設(shè)置網(wǎng)絡(luò)接口的鏈接狀態(tài)回調(diào)函數(shù)為 ETH_link_callback。
綜上所述,該函數(shù)主要用于初始化LwIP網(wǎng)絡(luò)堆棧。它包括初始化內(nèi)存堆和內(nèi)存池、配置網(wǎng)絡(luò)接口的IP地址信息、MAC地址、添加網(wǎng)絡(luò)接口到lwIP列表中、判斷網(wǎng)絡(luò)連接狀態(tài)并相應(yīng)地設(shè)置網(wǎng)絡(luò)接口狀態(tài)和DHCP狀態(tài),最后設(shè)置網(wǎng)絡(luò)接口的鏈接狀態(tài)回調(diào)函數(shù)。
3.5 ETH_link_callback函數(shù)
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! ");
}
}
這段代碼是一個用于處理以太網(wǎng)鏈接狀態(tài)變化的回調(diào)函數(shù) ETH_link_callback。讓我們逐步分析它的功能:
1. 首先,聲明了一些變量,包括超時計數(shù)器 timeout、存儲從PHY讀取的寄存器值的變量 RegValue,以及用于存儲IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址的變量。
2. 如果網(wǎng)絡(luò)接口的鏈接狀態(tài)為鏈接狀態(tài)(通過 netif_is_link_up(netif) 判斷):
- 如果啟用了以太網(wǎng)自動協(xié)商(ETH_InitStructure.autoNegotiation== ETH_AUTONEGOTIATION_ENABLE),則重新啟動自動協(xié)商過程:
- 向PHY寄存器寫入自動協(xié)商命令 PHY_AUTONEGOTIATION。
- 等待自動協(xié)商完成,超時時間由 PHY_READ_TIMEOUT 定義。
- 讀取自動協(xié)商結(jié)果,根據(jù)結(jié)果配置以太網(wǎng)MAC的速度和雙工模式。
- 根據(jù)速度和雙工模式重新配置以太網(wǎng)MAC控制器的寄存器。
- 重新啟動MAC接口。
- 根據(jù)是否啟用了DHCP,設(shè)置IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址,并將網(wǎng)絡(luò)接口標(biāo)記為已啟用。
- 打印消息,表示網(wǎng)絡(luò)電纜已連接。
- 將 EthLinkStatus 標(biāo)記為鏈接狀態(tài)。
3. 如果網(wǎng)絡(luò)接口的鏈接狀態(tài)為斷開狀態(tài),執(zhí)行以下操作:
- 停止MAC接口。
- 如果啟用了DHCP,將DHCP狀態(tài)設(shè)置為鏈接斷開,并停止DHCP客戶端。
- 將網(wǎng)絡(luò)接口標(biāo)記為已關(guān)閉。
- 打印消息,表示網(wǎng)絡(luò)電纜已拔出。
綜上所述,該函數(shù)主要用于處理以太網(wǎng)鏈接狀態(tài)的變化。根據(jù)鏈接狀態(tài)的變化,重新啟動自動協(xié)商過程(如果啟用了自動協(xié)商),配置以太網(wǎng)MAC的參數(shù),并設(shè)置IP地址信息。同時,根據(jù)鏈接狀態(tài)的變化執(zhí)行相應(yīng)的動作,例如重新啟動或停止MAC接口,并更新相應(yīng)的狀態(tài)標(biāo)志。
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
}
以上函數(shù)是一個用于處理LwIP(Lightweight IP)網(wǎng)絡(luò)堆棧的周期性任務(wù)的函數(shù)。讓我們逐段分析它:
1.staticuint8_t flagToggle = 0;:定義了一個靜態(tài)變量 flagToggle,用于跟蹤網(wǎng)絡(luò)連接狀態(tài)的變化。
2. if(ETHTimer - TCPTimer >= TCP_TMR_INTERVAL):檢查TCP定時器是否超過了TCP_TMR_INTERVAL(TCP定時器間隔),如果超過,則調(diào)用 tcp_tmr() 函數(shù)進(jìn)行TCP定時器處理。
3. if((ETHTimer - ARPTimer) >= ARP_TMR_INTERVAL):檢查ARP定時器是否超過了ARP_TMR_INTERVAL(ARP定時器間隔),如果超過,則調(diào)用 etharp_tmr() 函數(shù)進(jìn)行ARP定時器處理。
4. if((ETHTimer - LinkTimer) >= 1000):檢查鏈接狀態(tài)是否需要更新,如果超過了一定時間(這里設(shè)置為1000ms),則進(jìn)行鏈接狀態(tài)檢查。
5. if((ETH_GET_LINK_STATUS != 0) && (flagToggle == 0)):檢查網(wǎng)絡(luò)鏈接狀態(tài)是否正常,并且之前的狀態(tài)是斷開的,如果是,則設(shè)置網(wǎng)絡(luò)接口為鏈接狀態(tài)。
6. if((ETH_GET_LINK_STATUS == 0) && (flagToggle == 1)):檢查網(wǎng)絡(luò)鏈接狀態(tài)是否異常,并且之前的狀態(tài)是連接的,如果是,則設(shè)置網(wǎng)絡(luò)接口為斷開狀態(tài)。
7. if(ETHTimer - DHCPfineTimer >= DHCP_FINE_TIMER_MSECS):檢查是否需要進(jìn)行DHCP的Fine定時處理,如果超過了DHCP_FINE_TIMER_MSECS(Fine定時器間隔),則調(diào)用 dhcp_fine_tmr() 函數(shù)。
8. if(ETHTimer - DHCPcoarseTimer >= DHCP_COARSE_TIMER_MSECS):檢查是否需要進(jìn)行DHCP的Coarse定時處理,如果超過了DHCP_COARSE_TIMER_MSECS(Coarse定時器間隔),則調(diào)用 dhcp_coarse_tmr() 函數(shù)。
在整個函數(shù)中,主要處理了TCP、ARP、網(wǎng)絡(luò)鏈接狀態(tài)和DHCP的周期性任務(wù)。這些任務(wù)包括定時器的處理以及相應(yīng)的動作,例如發(fā)送ARP請求、更新網(wǎng)絡(luò)鏈接狀態(tài)和處理DHCP狀態(tài)機等。
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網(wǎng)絡(luò)堆棧中DHCP過程的函數(shù) LwIP_DHCP_Process_Handle。它通過狀態(tài)機來處理DHCP過程的不同階段。讓我們逐步分析其功能:
1. 在 DHCP_START 狀態(tài)下,將狀態(tài)切換到 DHCP_WAIT_ADDRESS,并啟動DHCP客戶端。
2. 在 DHCP_WAIT_ADDRESS 狀態(tài)下,如果DHCP客戶端獲得了IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址,將狀態(tài)切換到 DHCP_ADDRESS_ASSIGNED,打印獲得的IP地址、子網(wǎng)掩碼和網(wǎng)關(guān)地址,并停止DHCP客戶端。
3. 如果DHCP超時(嘗試次數(shù)超過4次),將狀態(tài)切換到 DHCP_TIMEOUT,停止DHCP客戶端,并使用靜態(tài)IP地址(如果配置了靜態(tài)IP地址)。
綜上所述,該函數(shù)主要用于處理LwIP網(wǎng)絡(luò)堆棧中DHCP過程的不同階段。根據(jù)DHCP狀態(tài)的不同,執(zhí)行不同的操作,例如啟動DHCP客戶端、處理獲取到的IP地址信息、處理超時情況等。
4. 實驗現(xiàn)象
1.串口打印相關(guān)信息
2.正常獲取IP后,cmd執(zhí)行ping命令,正常通信
-
以太網(wǎng)
+關(guān)注
關(guān)注
40文章
5424瀏覽量
171702 -
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3593瀏覽量
129466 -
TCP
+關(guān)注
關(guān)注
8文章
1353瀏覽量
79070 -
DHCP
+關(guān)注
關(guān)注
0文章
105瀏覽量
19716 -
LwIP
+關(guān)注
關(guān)注
2文章
86瀏覽量
27168
原文標(biāo)題:APM32芯得 EP.46 | 增強以太網(wǎng)連接:使用LWIP 2.2實現(xiàn)動態(tài)主機配置協(xié)議(DHCP)
文章出處:【微信號:geehysemi,微信公眾號:Geehy極海半導(dǎo)體】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論