关于解决使用STM32F407+LAN8742A的网线热插拔问题

原料

硬件

  1. STM32F407ZGT6
  2. LAN8742A
  3. 一根RJ45网线

软件

  1. STM32CubeMX
  2. Keil v5.30
  3. FreeRTOS

问题描述

STM32一侧作为client向server发送数据过程中,如果将网线直接拔掉再插上,就再也连接不上server,也就是说不支持网线热插拔

解决办法

先使用STM32CubeMX生成工程文件

创建过程就不多讲了,和一般的工程没什么区别,请注意我这里是选用了FreeRTOS,另外需要注意的有以下两点:

  1. 勾选Middleware->LWIP->Key Options->LWIP_NETIF_STATUS_CALLBACK
  2. 勾选Middleware->LWIP->Key Options->LWIP_NETIF_LINK_CALLBACK
    关于解决使用STM32F407+LAN8742A的网线热插拔问题_第1张图片

对生成的工程勘误

LAN8742A芯片内部可以通过设置寄存器Interrupt Mask Resgisterbit4来使能link status中断。
关于解决使用STM32F407+LAN8742A的网线热插拔问题_第2张图片
然而在CubeMX所生成的工程中,对于这个设置有错误。

出错的函数是:

static void low_level_init(struct netif *netif)

具体代码片是:

  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_ISFR, &regvalue);
  regvalue |= (PHY_ISFR_INT4);

  /* Enable Interrupt on change of link status */ 
  HAL_ETH_WritePHYRegister(&heth, PHY_ISFR , regvalue );
  
  /* Read Register Configuration */
  HAL_ETH_ReadPHYRegister(&heth, PHY_ISFR , &regvalue);

错误原因是 LAN8742A 的ISFR寄存器属于只读寄存器,是不能对它设置的
在这里插入图片描述

当然这里要不要使能这个中断,我没有去做实验验证,因为最终我是通过查询如下图所示的LAN8742A的BSR的bit2来获取连接状态的。我怀疑开启这个中断后,当连接状态发生改变后,LAN8742A的14号引脚会产生低电平,但我这边因为用的是RII模式,这个引脚已经被占用了。回头这个疑问我如果有空解决,再更新吧。
在这里插入图片描述

修正后的代码是(用这段代码替换上面出问题的代码片):

#define PHY_IMR 0x001E
#define PHY_IMR_INT4 0x0010
HAL_ETH_WritePHYRegister(&heth, PHY_IMR, PHY_IMR_INT4); 

创建检测网线状态的任务

其实由于我们在CubeMX中勾选相关选项,CubeMX会在void MX_LWIP_Init(void)中为我们创建一个ethernetif_set_link的任务,这个任务还附带有一个信号量,但是在实际代码中这个信号量并没有用到。我怀疑这可能与某个ST官方的开发板有关,在这个开发板上是有一个按键,按下按键就会释放这个信号量,写这个文档时,我没有再去找这部分代码。大家如果想找的话,可以在CubeMX下载的软件包里找,大致位置是在STM32Cube\Repository\STM32Cube_FW_F4_V1.25.0\Projects,另外这里面有很多参考工程可以学习。
我在实际测试时发现这个任务无法运行,因此我就直接删除了这个任务,然后新建了一个任务。

具体代码如下:

  osThreadDef(Link_Status_T,NetCable_Dect_Task, osPriorityBelowNormal,0,configMINIMAL_STACK_SIZE);
  LINKHandle = osThreadCreate(osThread(Link_Status_T),&gnetif);
/**
  * @brief  检测网线连接状态线程.
  * @param  argument: gnetif
  * @retval None
  */
void NetCable_Dect_Task(void const *argument)
{
     
  uint32_t regvalue = 0;
  struct netif *gnetif = (struct netif *)argument;
  for(;;)
  {
     
    /* 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(gnetif) && (regvalue))
    {
     
      /* network cable is connected */ 
      netif_set_link_up(gnetif);        
    }
    else if(netif_is_link_up(gnetif) && (!regvalue))
    {
     
      /* network cable is dis-connected */
      netif_set_link_down(gnetif);
    }
    
    /* Suspend thread for 200 ms */
    osDelay(200);
  }
}

当网线连接时gnetif->flags的bit2将会被置1,否则清0。因此我就在TCP发送任务中添加了一下代码片:

for(;;)
{
     
	if((gnetif)->flags & NETIF_FLAG_LINK_UP) //检测网线是否连接
	{
     
		break; //只有网线连接了才会进行接下来的操作
	}
	osDelay(200);
}

只有网线连接了才会执行new、connect、write等操作。如果发现网线被拔掉,将立即关闭删除相关连接,然后等待网线重新插好。

if((((gnetif)->flags & NETIF_FLAG_LINK_UP) == 0))
{
     
	netconn_close(conn);
	netconn_delete(conn);
	break;
}

结语

FreeRTOS的任务创建机制以及内存分配还需要仔细研究,如果大家有问题可以留言或者私聊,看到后我会尽力回复,如果想找我要源代码,请免开金口。

你可能感兴趣的:(小问题的解决,单片机,stm32,嵌入式)