天天看点

LWIP 热插拔调试 -- 不同品牌路由器引起的问题

要完成热插拔,需要在MX_LWIP_Process中加入一个热插拔的处理函数,如下

ethernetif_set_link

void MX_LWIP_Process(void)
{
/* USER CODE BEGIN 4_1 */ 
/* USER CODE END 4_1 */
  ethernetif_input(&gnetif);
  
/* USER CODE BEGIN 4_2 */
/* USER CODE END 4_2 */  
  /* Handle timeouts */
  sys_check_timeouts();

/* USER CODE BEGIN 4_3 */
  ethernetif_set_link(&gnetif);  
/* USER CODE END 4_3 */
}      

这个函数的源码内容很简单

void ethernetif_set_link(struct netif *netif)
{
  uint32_t regvalue = 0;
  /* Ethernet Link every 200ms */
  if (HAL_GetTick() - EthernetLinkTimer >= 200)
  {
    EthernetLinkTimer = HAL_GetTick(); 
    
    /* Read PHY_BSR*/
    HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &regvalue);
    
    regvalue &= PHY_LINKED_STATUS;
    
    /* Check whether the netif link down and the PHY link is up */
    if(!netif_is_link_up(netif) && (regvalue))
    {
      /* network cable is connected */ 
      netif_set_link_up(netif);        
    }
    else if(netif_is_link_up(netif) && (!regvalue))
    {
      /* network cable is disconnected */
      netif_set_link_down(netif);
    }
  }
}      

这里,HAL_ETH_ReadPHYRegister(&heth, PHY_BSR, &regvalue); 反复读取当前PHY寄存器中的状态参数。我用的是LAN8720,PHY_BSR=1,具体对应LAN8720手册中第4.2.2 BASIC STATUS REGISTER这一节。然后测试第3位(PHY_LINKED_STATUS位),如果为1 ,表明已经建立物理连接,直接连上。如果为0,则通过netif_set_link_down来清除各个设置参数。

无论是netif_set_link_up还是netif_set_link_down,其中都调用了一个函数,

NETIF_LINK_CALLBACK(netif);

这个回调函数是我们在LWIP_MX_Init中设置的,也就是ethernetif_update_config,该函数由cubemx自动生成,用户无需修改,

/* Set the link callback function, this function is called on change of link status*/

  netif_set_link_callback(&gnetif, ethernetif_update_config);

在ethernetif_update_config中,最后会调用ethernetif_notify_conn_changed这个函数,这个函数是提供给用户的,用户可以根据需求修改,空着不用也没关系。比如我的改成了这样,

void ethernetif_notify_conn_changed(struct netif *netif)
{
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
  
  if(netif_is_link_up(netif))
  {
    print_msg("The network cable is now connected \n");
    
    if(cfg_init_param & CFG_BIT_STATICIP){
      /* IP addresses initialization without DHCP (IPv4) */
      IP4_ADDR(&ipaddr, IP_ADDRESS[0], IP_ADDRESS[1], IP_ADDRESS[2], IP_ADDRESS[3]);
      IP4_ADDR(&netmask, NETMASK_ADDRESS[0], NETMASK_ADDRESS[1] , NETMASK_ADDRESS[2], NETMASK_ADDRESS[3]);
      IP4_ADDR(&gw, GATEWAY_ADDRESS[0], GATEWAY_ADDRESS[1], GATEWAY_ADDRESS[2], GATEWAY_ADDRESS[3]);
      
      netif_set_addr(netif, &ipaddr , &netmask, &gw);
      print_msg("Static IP address: %s\n", ip4addr_ntoa((const ip4_addr_t *)&netif->ip_addr));
    }
    else {
      DHCP_state = DHCP_START;  // Update DHCP state machine
    }   
    
    print_msg("Sate: line link detected...\n");     
  } 
  else
  {
    if(0==(cfg_init_param & CFG_BIT_STATICIP)) { //#ifdef USE_DHCP
      DHCP_state = DHCP_LINK_DOWN;             // Update DHCP state machine
    }                                            //#endif USE_DHCP    
  
    print_msg ("The network cable is not connected.");
    main_flag &= ~((uint16_t)(EMF_TCP_HWLINKED | EMF_TCP_LISTENBIND | EMF_TCP_CLIENTCONNECTED));
  }
}
      

基本上都是一些自定义的参数或报告输出,就是说连上后要用DHCP还是静态IP之类的。

好,这些都很好理解。

问题出在 ethernetif_set_link这个函数。我使用华为的路由器WS5200,其一是热插拔根本无法连接上;RESET重启后,多数情况下,启动时都会首先进入netif_set_link_up,几个循环之后,马上进入netif_set_link_down。

根据我个人的测定,华为路由器设定了一个奇怪的规则:大约2分钟或几十秒之内,可能根据具体情况的不同(是什么情况无法判定),新断开的IP不允许重新连接。只要过一段时间后再试才能成功。

换了一个TPlinkWR842N测试,完全没有这些问题,不论热插拔还是重启,反复测试,都能很快实现连接。

继续阅读