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 */
}