STM32移植LWIP网线热插入网络不通的解决办法
扫描二维码
随时随地手机看文章
开发背景:
1、主芯片—STM32F207VCT6;
2、TCP/IP协议栈—LWIP,依托ST例程移植;
3、操作系统—无(裸机);
异常现象:
1、网线不插入的情况下先给设备上电,之后再插入网线无法ping通;(如果上电前网线插入,网络正常);
2、网络已经正常的情况下,电脑PC端修改传输模式(比如从原来的100M全双工修改为10M全双工)导致网络不通;
原因分析:
1、针对第一种异常情况,是由于上电时网线未插入,导致ETH初始化部分未能成功完成,之后即使再插入网线,程序中没有再次进行初始化的逻辑补充,从而导致网络异常;
2、针对第二种情况,情况是上电时完成了ETH的初始化并与PC协商成功,此时网络正常。但当PC端修改传输模式后,程序中未能执行再次协商与MAC的初始化工作,导致网络异常;
解决方法:
首先,要明确上述问题的关键点所在,所有的异常均是网线的拔插导致(PC端修改连接传输方式时也相当于网线的拔掉重插),因此主程序中必须要有对当前网络连接与断开的检测或者利用PHY芯片的中断引脚;
其次,无论利用轮询或是PHY中断配置引脚,根本的原理都是一样的,就是感知到网络的连接与断开,下面给出采用的查询方式:
void Eth_Link_ITHandler(struct netif *netif)
{
/* Check whether the link interrupt has occurred or not */
if(((ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_MISR)) & PHY_LINK_STATUS) != 0){/*检测插拔中断*/
uint16_t status = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_BSR);
if(status & (PHY_AutoNego_Complete | PHY_Linked_Status)){/*检测到网线连接*/
if(EthInitStatus == 0){/*之前未成功初始化过*/
/*Reinit PHY*/
ETH_Reinit();
}
else{/*之前已经成功初始化*/
/*set link up for re link callbalk function*/
netif_set_link_up(netif);
}
}
else{/*网线断开*/
/*set link down for re link callbalk function*/
netif_set_link_down(netif);
}
}
}
备注说明:将该检测函数放入主循环,程序中标注的部分为解决网线热拔插问题的关键点。
1、标注红色的部分执行的条件是检测到网线插入且之前ETH部分未成功初始化过(即之前一直处在上电但网线未插入)的情况,此时需要对ETH重新初始化,从而解决异常现象的第一种情况,具体执行内容为:
/**
* @brief : first time power on but init failed, do again
* @param : None
*
* @retval : None
* @author : xuk
*/
void ETH_Reinit(void){
/* Configure Ethernet */
EthInitStatus =ETH_Init(Ð_InitStructure, DP83848_PHY_ADDRESS);
}
其中ETH_InitStructure已设为全局结构体;
2、标注蓝色部分的执行条件是已经成功初始化过ETH,但之后出现了网线的拔插情况,此时需要在每次检测到网络连接时重新进行自协商并初始化MAC,具体的执行流程如下介绍:
A、检测到该条件时,首先调用:
netif_set_link_up(netif);
netif_set_link_down(netif);
B、追溯两个函数的定义处,如下:
#if LWIP_NETIF_LINK_CALLBACK
/**
* Called by a driver when its link goes up
*/
void netif_set_link_up(struct netif *netif )
{
netif->flags |= NETIF_FLAG_LINK_UP;
#if LWIP_DHCP
if (netif->dhcp) {
dhcp_network_changed(netif);
}
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
if (netif->autoip) {
autoip_network_changed(netif);
}
#endif /* LWIP_AUTOIP */
if (netif->flags & NETIF_FLAG_UP) {
#if LWIP_ARP
/* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
if (netif->flags & NETIF_FLAG_ETHARP) {
etharp_gratuitous(netif);
}
#endif /* LWIP_ARP */
#if LWIP_IGMP
/* resend IGMP memberships */
if (netif->flags & NETIF_FLAG_IGMP) {
igmp_report_groups( netif);
}
#endif /* LWIP_IGMP */
}
NETIF_LINK_CALLBACK(netif);
}
/**
* Called by a driver when its link goes down
*/
void netif_set_link_down(struct netif *netif )
{
netif->flags &= ~NETIF_FLAG_LINK_UP;
NETIF_LINK_CALLBACK(netif);
}
/**
* Ask if a link is up
*/
u8_t netif_is_link_up(struct netif *netif)
{
return (netif->flags & NETIF_FLAG_LINK_UP) ? 1 : 0;
}
/**
* Set callback to be called when link is brought up/down
*/
voidnetif_set_link_callback(struct netif *netif, void (* link_callback)(struct netif *netif ))
{
if (netif) {
netif->link_callback = link_callback;
}
}
#endif /* LWIP_NETIF_LINK_CALLBACK */
注意:I:从上述看出,若要这两个函数有效编译,则必须定义宏LWIP_NETIF_LINK_CALLBACK 为1,请自行设置;
II:函数netif_set_link_callback的作用是指定网络连接发生改变时的回调函数;
III:详细的讲一下主要思路,Eth_Link_ITHandler执行中检测到网线拔插时分别调用netif_set_link_up(netif)、netif_set_link_down(netif);这两个函数的调用会引发netif_set_link_callback的执行,从而执行指定的网络连接或断开的回调函数;
Ⅳ:通过netif_set_link_callback该函数在LWIP初始化的时候指定网络连接变化的回调函数,可放置如下位置:
void LwIP_Init(void){
......
......
......
......
/*set the link up or link down callback function - xuk*/
netif_set_link_callback(&netif,eth_re_link);
}
其中,回调函数eth_re_link的具体内容如下,实现网络拔插后的重新自协商与MAC初始化:
/**
* @brief : process the relink of eth
* @param : netif - - specify the ETH netif
*
* @retval : none
* @author : xuk
*/
voideth_re_link(struct netif *netif){
__IO uint32_t tickstart = 0;
uint32_t regvalue = 0, tmpreg = 0;
if(netif_is_link_up(netif)){/*link up process*/
if(ETH_InitStructure.ETH_AutoNegotiation == ETH_AutoNegotiation_Enable){/*AutoNegotiation_Enable*/
/* Enable Auto-Negotiation */
ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation);
/* Wait until the auto-negotiation will be completed */
do
{
tickstart++;
} while (!(ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_BSR) & PHY_AutoNego_Complete) && (tickstart < (uint32_t)PHY_READ_TO));
/* Return ERROR in case of timeout */
if(tickstart == PHY_READ_TO)
{
// return ETH_ERROR;
}
/* Reset Timeout counter */
tickstart = 0;
/* Read the result of the auto-negotiation */
regvalue = ETH_ReadPHYRegister(DP83848_PHY_ADDRESS, PHY_SR);
/* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */
if((regvalue & PHY_DUPLEX_STATUS) != (uint32_t)RESET)
{
/* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */
ETH_InitStructure.ETH_Mode = ETH_Mode_FullDuplex;
}
else
{
/* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */
ETH_InitStructure.ETH_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.ETH_Speed = ETH_Speed_10M;
}
else
{
/* Set Ethernet speed to 100M following the auto-negotiation */
ETH_InitStructure.ETH_Speed = ETH_Speed_100M;
}
}
else{/*AutoNegotiation_Disable*/
if(!ETH_WritePHYRegister(DP83848_PHY_ADDRESS, PHY_BCR, ((uint16_t)(ETH_InitStructure.ETH_Mode >> 3) |
(uint16_t)(ETH_InitStructure.ETH_Speed >> 1))))
{
/* Return ERROR in case of write timeout */
// return ETH_ERROR;
}
/* Delay to assure PHY configuration */
// _eth_delay_(PHY_CONFIG_DELAY);
}
/*------------------------ ETHERNET MACCR Configuration --------------------*/
/* Get the ETHERNET MACCR value */
tmpreg = ETH->MACCR;
/* Clear WD, PCE, PS, TE and RE bits */
tmpreg &= MACCR_CLEAR_MASK;
/* Set the WD bit according to ETH_Watchdog value */
/* Set the JD: bit according to ETH_Jabber value */
/* Set the IFG bit according to ETH_InterFrameGap value */
/* Set the DCRS bit according to ETH_CarrierSense value */
/* Set the FES bit according to ETH_Speed value */
/* Set the DO bit according to ETH_ReceiveOwn value */
/* Set the LM bit according to ETH_LoopbackMode value */
/* Set the DM bit according to ETH_Mode value */
/* Set the IPCO bit according to ETH_ChecksumOffload value */
/* Set the DR bit according to ETH_RetryTransmission value */
/* Set the ACS bit according to ETH_AutomaticPadCRCStrip value */
/* Set the BL bit according to ETH_BackOffLimit value */
/* Set the DC bit according to ETH_DeferralCheck value */
tmpreg |= (uint32_t)(ETH_InitStructure.ETH_Watchdog |
ETH_InitStructure.ETH_Jabber |
ETH_InitStructure.ETH_InterFrameGap |
ETH_InitStructure.ETH_CarrierSense |
ETH_InitStructure.ETH_Speed |
ETH_InitStructure.ETH_ReceiveOwn |
ETH_InitStructure.ETH_LoopbackMode |
ETH_InitStructure.ETH_Mode |
ETH_InitStructure.ETH_ChecksumOffload |
ETH_InitStructure.ETH_RetryTransmission |
ETH_InitStructure.ETH_AutomaticPadCRCStrip |
ETH_InitStructure.ETH_BackOffLimit |
ETH_InitStructure.ETH_DeferralCheck);
/* Write to ETHERNET MACCR */
ETH->MACCR = (uint32_t)tmpreg;
/*----------------------- ETHERNET MACFFR Configuration --------------------*/
/* Set the RA bit according to ETH_ReceiveAll value */
/* Set the SAF and SAIF bits according to ETH_SourceAddrFilter value */
/* Set the PCF bit according to ETH_PassControlFrames value */
/* Set the DBF bit according to ETH_BroadcastFramesReception value */
/* Set the DAIF bit according to ETH_DestinationAddrFilter value */
/* Set the PR bit according to ETH_PromiscuousMode value */
/* Set the PM, HMC and HPF bits according to ETH_MulticastFramesFilter value */
/* Set the HUC and HPF bits according to ETH_UnicastFramesFilter value */
/* Write to ETHERNET MACFFR */
ETH->MACFFR = (uint32_t)(ETH_InitStructure.ETH_ReceiveAll |
ETH_InitStructure.ETH_SourceAddrFilter |
ETH_InitStructure.ETH_PassControlFrames |
ETH_InitStructure.ETH_BroadcastFramesReception |
ETH_InitStructure.ETH_DestinationAddrFilter |
ETH_InitStructure.ETH_PromiscuousMode |
ETH_InitStructure.ETH_MulticastFramesFilter |
ETH_InitStructure.ETH_UnicastFramesFilter);
/*--------------- ETHERNET MACHTHR and MACHTLR Configuration ---------------*/
/* Write to ETHERNET MACHTHR */
ETH->MACHTHR = (uint32_t)ETH_InitStructure.ETH_HashTableHigh;
/* Write to ETHERNET MACHTLR */
ETH->MACHTLR = (uint32_t)ETH_InitStructure.ETH_HashTableLow;
/*-----------------------