搜索
bottom↓
回复: 14

STM32F207(裸机)- LWIP网线热插入网络不通的解决办法

[复制链接]

出0入0汤圆

发表于 2017-7-11 08:48:22 | 显示全部楼层 |阅读模式
开发背景:
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(&ETH_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
*/
void netif_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
  */
void eth_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;
                /*----------------------- ETHERNET MACFCR Configuration --------------------*/
                /* Get the ETHERNET MACFCR value */  
                tmpreg = ETH->MACFCR;
                /* Clear xx bits */
                tmpreg &= MACFCR_CLEAR_MASK;
               
                /* Set the PT bit according to ETH_PauseTime value */
                /* Set the DZPQ bit according to ETH_ZeroQuantaPause value */
                /* Set the PLT bit according to ETH_PauseLowThreshold value */
                /* Set the UP bit according to ETH_UnicastPauseFrameDetect value */
                /* Set the RFE bit according to ETH_ReceiveFlowControl value */
                /* Set the TFE bit according to ETH_TransmitFlowControl value */  
                tmpreg |= (uint32_t)((ETH_InitStructure.ETH_PauseTime << 16) |
                                                                                 ETH_InitStructure.ETH_ZeroQuantaPause |
                                                                                 ETH_InitStructure.ETH_PauseLowThreshold |
                                                                                 ETH_InitStructure.ETH_UnicastPauseFrameDetect |
                                                                                 ETH_InitStructure.ETH_ReceiveFlowControl |
                                                                                 ETH_InitStructure.ETH_TransmitFlowControl);
                /* Write to ETHERNET MACFCR */
                ETH->MACFCR = (uint32_t)tmpreg;
                /*----------------------- ETHERNET MACVLANTR Configuration -----------------*/
                /* Set the ETV bit according to ETH_VLANTagComparison value */
                /* Set the VL bit according to ETH_VLANTagIdentifier value */  
                ETH->MACVLANTR = (uint32_t)(ETH_InitStructure.ETH_VLANTagComparison |
                                                                                                                         ETH_InitStructure.ETH_VLANTagIdentifier);
                                 
                /*-------------------------------- DMA Config ------------------------------*/
                /*----------------------- ETHERNET DMAOMR Configuration --------------------*/
                /* Get the ETHERNET DMAOMR value */  
                tmpreg = ETH->DMAOMR;
                /* Clear xx bits */
                tmpreg &= DMAOMR_CLEAR_MASK;
               
                /* Set the DT bit according to ETH_DropTCPIPChecksumErrorFrame value */
                /* Set the RSF bit according to ETH_ReceiveStoreForward value */
                /* Set the DFF bit according to ETH_FlushReceivedFrame value */
                /* Set the TSF bit according to ETH_TransmitStoreForward value */
                /* Set the TTC bit according to ETH_TransmitThresholdControl value */
                /* Set the FEF bit according to ETH_ForwardErrorFrames value */
                /* Set the FUF bit according to ETH_ForwardUndersizedGoodFrames value */
                /* Set the RTC bit according to ETH_ReceiveThresholdControl value */
                /* Set the OSF bit according to ETH_SecondFrameOperate value */
                tmpreg |= (uint32_t)(ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame |
                                                                                ETH_InitStructure.ETH_ReceiveStoreForward |
                                                                                ETH_InitStructure.ETH_FlushReceivedFrame |
                                                                                ETH_InitStructure.ETH_TransmitStoreForward |
                                                                                ETH_InitStructure.ETH_TransmitThresholdControl |
                                                                                ETH_InitStructure.ETH_ForwardErrorFrames |
                                                                                ETH_InitStructure.ETH_ForwardUndersizedGoodFrames |
                                                                                ETH_InitStructure.ETH_ReceiveThresholdControl |                                   
                                                                                ETH_InitStructure.ETH_SecondFrameOperate);
                /* Write to ETHERNET DMAOMR */
                ETH->DMAOMR = (uint32_t)tmpreg;
               
                /*----------------------- ETHERNET DMABMR Configuration --------------------*/
                /* Set the AAL bit according to ETH_AddressAlignedBeats value */
                /* Set the FB bit according to ETH_FixedBurst value */
                /* Set the RPBL and 4*PBL bits according to ETH_RxDMABurstLength value */
                /* Set the PBL and 4*PBL bits according to ETH_TxDMABurstLength value */
                /* Set the DSL bit according to ETH_DesciptorSkipLength value */
                /* Set the PR and DA bits according to ETH_DMAArbitration value */         
                ETH->DMABMR = (uint32_t)(ETH_InitStructure.ETH_AddressAlignedBeats |
                                                                                                                ETH_InitStructure.ETH_FixedBurst |
                                                                                                                ETH_InitStructure.ETH_RxDMABurstLength | /* !! if 4xPBL is selected for Tx or Rx it is applied for the other */
                                                                                                                ETH_InitStructure.ETH_TxDMABurstLength |
                                                                                                         (ETH_InitStructure.ETH_DescriptorSkipLength << 2) |
                                                                                                                ETH_InitStructure.ETH_DMAArbitration |
                                                                                                                ETH_DMABMR_USP); /* Enable use of separate PBL for Rx and Tx */
                                                                                                               
                #ifdef USE_ENHANCED_DMA_DESCRIPTORS
                        /* Enable the Enhanced DMA descriptors */
                        ETH->DMABMR |= ETH_DMABMR_EDE;
                #endif /* USE_ENHANCED_DMA_DESCRIPTORS */
                                                                                                                               
                /* Return Ethernet configuration success */
//                return ETH_SUCCESS;
//                ETH_Start();
        }
        else{/*link down process*/
                               
        }
}

至此,对于STM32F207(裸机)- LWIP网线热插入网络不通遇到的问题以及解决办法介绍完毕。

新手一枚,编辑用的不是很熟练,一些格式不会排版,具体的可见附件PDF版本!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入4汤圆

发表于 2017-7-11 08:56:23 | 显示全部楼层
讲得很好。

你用的phy是哪个?

一般phy都有中断引脚的,

出0入0汤圆

发表于 2017-7-11 12:23:01 | 显示全部楼层
LZ用的PHY芯片是?

出40入42汤圆

发表于 2017-7-11 13:54:55 | 显示全部楼层
楼主输入代码段时,可以在工具栏的<>处点一下,然后输入代码,这样格式会好些

楼下的注意点啊,措辞要温和点啊,楼主是位lady

出0入0汤圆

发表于 2017-7-11 14:07:14 | 显示全部楼层
好贴,思路适用于所有嵌入式网络应用

出0入0汤圆

发表于 2017-7-11 14:28:15 | 显示全部楼层
收藏了,谢谢,真心很想知道做技术的妹子是不是恐农

出0入0汤圆

发表于 2017-7-11 16:57:16 | 显示全部楼层
STM32检测PHY的link脚即可知道网络连接断开

出0入0汤圆

 楼主| 发表于 2017-7-13 14:32:38 | 显示全部楼层
huarana 发表于 2017-7-11 08:56
讲得很好。

你用的phy是哪个?

PHY 是DP83848,因为当时画硬件的时候没注意这个点,所以只能用查询方式了,不过原理是一样的!

出50入8汤圆

发表于 2017-8-17 13:25:27 | 显示全部楼层
楼主搞了半天,大部分做的是无用功,真正起作用的只是其中少量几行代码

这个问题的关键是MAC的模式和PHY的模式没有匹配导致无法通讯
PHY可以自动协商,协商后模式和速率改变了,但是MAC的模式和速率没有跟着改变,两者的工作模式不匹配当然无法通讯了

解决方法也很简单
简单的方法:关掉PHY的自动协商模式,把MAC和PHY的模式和速率设成一致,缺点是,网线的另一端要自动协商,或者要硬件匹配,否则无法通讯

智能的方法:开启PHY的自动协商模式中断或者轮询查询PHY的连接状态以及工作模式和速率,发现有改变,设置MAC的速率和模式与PHY匹配即可(只需修改ETH->DMAOMR寄存器)
头像被屏蔽

出0入0汤圆

发表于 2017-8-28 15:12:33 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

 楼主| 发表于 2017-9-12 19:03:30 | 显示全部楼层
airbox 发表于 2017-8-17 13:25
楼主搞了半天,大部分做的是无用功,真正起作用的只是其中少量几行代码

这个问题的关键是MAC的模式和PHY的 ...

多谢大神的宝贵的意见,我回头试试,新人一枚,多谢提点!

出0入0汤圆

发表于 2017-10-24 09:00:21 | 显示全部楼层
感觉好像是用的库函数,看习惯了HAL,再看这个感觉怪怪的

出0入0汤圆

发表于 2017-10-24 09:16:03 | 显示全部楼层
这个学习了

出100入101汤圆

发表于 2017-10-24 11:04:42 | 显示全部楼层
airbox 发表于 2017-8-17 13:25
楼主搞了半天,大部分做的是无用功,真正起作用的只是其中少量几行代码

这个问题的关键是MAC的模式和PHY的 ...

LWIP,拔掉网线再插上会出一些古怪的问题,并不一定是MAC和PHY不匹配。

出0入0汤圆

发表于 2017-10-24 11:17:27 | 显示全部楼层
谢谢楼主分享,还在学习中
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-19 12:06

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表