lwip+freeRTOS 实现热插拔功能
1、lwip啟用LWIP_NETIF_CALLBACK 宏,cubeMX會自動生成相關函數()。
?
/* Set the link callback function, this function is called on change of link status*/netif_set_link_callback(&gnetif, ethernetif_update_config);?
回調函數在void ethernetif_update_config(struct netif *netif),其中主要完成通信協商之類的初始化。
最后調用__weak void ethernetif_notify_conn_changed(struct netif *netif),用戶的代碼在這里邊實現。
比如,我是每當發現網卡出現插拔后,重新執行DHCP,動態獲取IP,代碼如下:
__weak void ethernetif_notify_conn_changed(struct netif *netif) {/* NOTE : This is function could be implemented in user filewhen the callback is needed,*/int err;ipaddr.addr = 0;if (netif_is_link_up(&gnetif)){DEBUG("net link is up\r\n");DEBUG("starting dhcp...\n");err = dhcp_start(&gnetif);if (err == ERR_OK) {DEBUG("starting dhcp success!\n");} else {DEBUG("starting dhcp fail!\n");}int res = 0;do{res = ip_addr_cmp(&(gnetif.ip_addr),&ipaddr);if (res){osDelay(1000);DEBUG("wait dhcp...\r\n");}} while (res);DEBUG("dhcp get local ip :%d.%d.%d.%d\n\n", \((gnetif.ip_addr.addr)&0x000000ff), \(((gnetif.ip_addr.addr)&0x0000ff00)>>8), \(((gnetif.ip_addr.addr)&0x00ff0000)>>16), \((gnetif.ip_addr.addr)&0xff000000)>>24);} else {DEBUG("net link is down\r\n");} }2、開機沒接網線,無法初始化網卡,程序崩潰問題。
程序開機運行時如果由網線連接,那么正常初始化后按照上面的配置是可以實現熱插拔的。但是,如果在初始化網卡之前沒有連接網線,則程序會運行失敗,甚至崩潰。
網卡初始化,調用HAL庫的?hal_eth_init_status = HAL_ETH_Init(&heth); 進一步可以發現該函數中由以下內容:
if((heth->Init).AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE){/* Get tick */tickstart = HAL_GetTick();/* We wait for linked status */do{HAL_ETH_ReadPHYRegister(heth, PHY_BSR, &phyreg);/* Check for the Timeout */if((HAL_GetTick() - tickstart ) > ETH_TIMEOUT_LINKED_STATE){/* In case of write timeout */err = ETH_ERROR;/* Config MAC and DMA */ETH_MACDMAConfig(heth, err);heth->State= HAL_ETH_STATE_READY;/* Process Unlocked */__HAL_UNLOCK(heth);return HAL_TIMEOUT;}} while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));該部分描述的是如果啟用了速率自協商,那么會一直判斷讀取PHY芯片的BSR寄存器當前是否由連接,否則等待直到超時。超時時間是ETH_TIMEOUT_LINKED_STATE,也即5s。程序一直卡在這里,如果啟用了操作系統,那么這個任務將阻塞其他的任務,導致報錯。
解決方案是在網卡初始化和LWIP初始化之前,檢查是否由網線連接,讀PHY_BSR寄存器,然后判斷即可。當然,需要先初始化網卡的基本接口,保證寄存器能夠讀取。
等待網絡連接的接口如下:
int waitNetLink(uint32_t timeout_s) {ETH_HandleTypeDef heth;heth.Instance = ETH;heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;heth.Init.PhyAddress = LAN8720_PHY_ADDRESS;heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;HAL_ETH_MspInit(&heth);osDelay(PHY_RESET_DELAY);uint32_t checkCnt = 0;if((heth.Init).AutoNegotiation != ETH_AUTONEGOTIATION_DISABLE) {uint32_t tickstart = HAL_GetTick();uint32_t phyreg = 0U;do {HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &phyreg);osDelay(1000);if (0 == (checkCnt++ % 3)) {DEBUG("wait net link, timeout %u s, now %u s...\r\n", timeout_s, checkCnt);}if (checkCnt >= timeout_s - 1) {if (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS)) {HAL_ETH_MspDeInit(&heth);return -1;}}} while (((phyreg & PHY_LINKED_STATUS) != PHY_LINKED_STATUS));}HAL_ETH_MspDeInit(&heth);if (checkCnt <= timeout_s - 1) { return 0; }else { return 1; } }使用方法:
/* USER CODE END Header_lwIPAppTaskFunc */ void lwIPAppTaskFunc(void *argument) {/* USER CODE BEGIN lwIPAppTaskFunc */DEBUG("lwip app task started\r\n");/* init code for LWIP */int link_res = waitNetLink(osWaitForever);if (0 != link_res){DEBUG("wait net link timeout\r\n");} else {/*! lwip init */MX_LWIP_Init();/*! creat socket client */int creat_res = creat_tcp_client(&client_id,SOCKET_SERVER_ADDR,SOCKET_SERVER_PORT);if (creat_res != 0) {DEBUG("creat_tcp_client error: res = %d\r\n", creat_res);} else {DEBUG("creat_tcp_client success\r\n");}}/* Infinite loop */for(;;){osDelay(1);}/* USER CODE END lwIPAppTaskFunc */ }?
?
總結
以上是生活随笔為你收集整理的lwip+freeRTOS 实现热插拔功能的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: gdb查看空指针 linux_5 个鲜为
- 下一篇: 华为的鸿蒙系统是海思_死心了!华为鸿蒙系