使用STM32F4标准外设库实现网线热插拔- 移植官方思路到自己实际固件工程

本文记录了在STM32工程中实现网线热插拔功能的过程,主要涉及LWIP库的使用和网卡初始化。作者通过分析官方工程,成功移植了热插拔功能,但遇到开机未插入网线时,网络无法正常工作的问题。为解决此问题,作者采取了在检测到网线插入但未初始化时重启固件的折衷方案,确保网络功能可用。此外,文章还详细列举了代码修改的部分,包括打开网络状态回调宏、增加延时函数、网线状态检测函数等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

从同事工程上改了一个版本出来, 发现如果不插入网线, 不能正常跑. 卡在网卡初始化那里了.
如果不能实现网线热插拔, 我改出的版本不能实际用在客户现场的. 如果不插入网线, 就不能正常用, 这谁受的了啊.

前面做了一个预研使用STM32F4标准外设库实现网线热插拔- 分析STM3240G-EVAL官方工程, 知道ST官方怎么玩网线热插拔了.

今天, 用ST官方工程的思路, 将网线热插拔实现移植到自己工程中, 基本可以用.
但是有一些小问题(还能接受, 也能跟同事和客户解释的通), 先这样.

官方的网线热插拔效果: 不管啥时候插拔网线, 只要再插入网线, 网络操作就好使(e.g. ping), 即使固件开机时, 没有插入网线.

我移植完的效果: 如果固件开机时, 插入了网线,正常情况网卡就能初始化成功, 这种场景下, 再插拔网线, 只要插上网线, 网络操作就好使(e.g. ping). 但是, 如果固件开机时, 没插入网线, 等程序跑起来后, 再插拔网线, 网络操作是不好使的, 很明显, 是少做了一些网卡初始化操作, 但是我暂时找不出来, 同事的工程也是乱乱的. 猜测是网卡没插网线(网卡初始化失败), 后面起的LWIP操作(起了一些LWIP相关线程), 操作的好多数据结构都是无效的, 所以简单设置网卡上线/下线是不好使的. 想了一个折中的方法, 如果检测到网线插入, 但是此时是网线没插入开机启动的情况, 因为我知道此种情况, 网络操作是不好使的, 我就重启固件. 然后就走开机时, 插入网线的操作流程, 剩下就OK了. 就是比官方工程多了一次重启固件的动作. 在我们这个工程中, 重启固件没啥大事. 能接受.

总之吧, 这次只是将问题解决了. 不完美.
以后我自己从头写的固件工程, 如果还有机会使用STM标准外设库(不过以后新工程该使用HAL库了), 我好好弄弄. 整的明明白白的.
这次就先这样.

实验记录

这次记录, 从我开始改之前的git版本和改完做的git版本比对, 看看哪里变了, 就记录下来作为笔记.

IDE

MDK5

用图形化git工具比较任意2个git版本的方法

先在git log列表中, 按住CTRL键, 选中2个版本, 再选择 git比较版本.
在这里插入图片描述

打开网络状态回调宏

opt.h

#ifndef LWIP_NETIF_LINK_CALLBACK
#define LWIP_NETIF_LINK_CALLBACK        1 // 之前是0, 无法做网络回调函数注册
#endif

如果要在LWIP函数中使用bool 类型, 需要加 stdbool.h

#include <stdbool.h>

增加网线热插拔回调函数调用


extern void ETH_link_callback(struct netif* netif);
void Ethernet_Sys_Init(void)
{			  
    struct ip_addr ipaddr;
    struct ip_addr netmask;
    struct ip_addr gw;

 //   printf("TCP/IP initializing... \r\n");
	tcpip_init(NULL, NULL);
 //   printf("TCP/IP initialized. \r\n");
	Create_MAC(CONFIG_Table.MAC);
#if LWIP_DHCP			//启用DHCP服务器 */
    ipaddr.addr  = 0;
    netmask.addr = 0;
    gw.addr 	 = 0;
#else				   	//启用静态IP地址
	IP4_ADDR(&ipaddr,  LWIP_Netcfg.ipaddr[0], LWIP_Netcfg.ipaddr[1], LWIP_Netcfg.ipaddr[2], LWIP_Netcfg.ipaddr[3]);
	IP4_ADDR(&netmask, LWIP_Netcfg.netmask[0], LWIP_Netcfg.netmask[1], LWIP_Netcfg.netmask[2], LWIP_Netcfg.netmask[3]);
	IP4_ADDR(&gw, LWIP_Netcfg.gateway[0], LWIP_Netcfg.gateway[1], LWIP_Netcfg.gateway[2], LWIP_Netcfg.gateway[3]);
#endif   
    netif_add(&stm32_netif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &tcpip_input);
    netif_set_default(&stm32_netif);
#if LWIP_DHCP
    dhcp_start(&stm32_netif);
#endif
    netif_set_up(&stm32_netif);

	// 在LWIP初始化最后面, 注册网线热插拔回调	
	/*  Registers the default network interface.*/
	netif_set_link_callback(&stm32_netif, ETH_link_callback); // 注册网线热插拔回调
}

extern void ETH_Delay(__IO uint32_t nCount);
extern struct netif  			stm32_netif;

// ETH_link_callback 是网线热插拔回调, 从官方工程中移植过来的, 注意将网卡地址, 寄存器号码等核对下.
void ETH_link_callback(struct netif* netif)
{
	__IO uint32_t timeout = 0;
	uint32_t tmpreg, RegValue;
	struct ip_addr ipaddr;
	struct ip_addr netmask;
	struct ip_addr gw;

	if (NULL != netif) {
		if (netif_is_link_up(netif)) {
			/* Restart the autonegotiation */
			if (ETH_InitStructure.ETH_AutoNegotiation != ETH_AutoNegotiation_Disable) {
				/* Reset Timeout counter */
				timeout = 0;
				/* Enable Auto-Negotiation */
				ETH_WritePHYRegister(LAN8720_PHY_ADDRESS, PHY_BCR, PHY_AutoNegotiation);

				/* Wait until the auto-negotiation will be completed */
				do {
					timeout++;
				} while (!(ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR) & PHY_AutoNego_Complete)

				         && (timeout < (uint32_t)PHY_READ_TO));

				/* Reset Timeout counter */
				timeout = 0;
				/* Read the result of the auto-negotiation */
				RegValue = ETH_ReadPHYRegister(LAN8720_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;
				}

				/*------------------------ ETHERNET MACCR Re-Configuration --------------------*/
				/* Get the ETHERNET MACCR value */
				tmpreg = ETH->MACCR;
				/* Set the FES bit according to ETH_Speed value */
				/* Set the DM bit according to ETH_Mode value */
				tmpreg |= (uint32_t)(ETH_InitStructure.ETH_Speed | ETH_InitStructure.ETH_Mode);
				/* Write to ETHERNET MACCR */
				ETH->MACCR = (uint32_t)tmpreg;
				_eth_delay_(ETH_REG_WRITE_DELAY);
				tmpreg = ETH->MACCR;
				ETH->MACCR = tmpreg;
			}

			/* Restart MAC interface */
			ETH_Start();
			IP4_ADDR(&ipaddr,  LWIP_Netcfg.ipaddr[0], LWIP_Netcfg.ipaddr[1], LWIP_Netcfg.ipaddr[2], LWIP_Netcfg.ipaddr[3]);
			IP4_ADDR(&netmask, LWIP_Netcfg.netmask[0], LWIP_Netcfg.netmask[1], LWIP_Netcfg.netmask[2], LWIP_Netcfg.netmask[3]);
			IP4_ADDR(&gw, LWIP_Netcfg.gateway[0], LWIP_Netcfg.gateway[1], LWIP_Netcfg.gateway[2], LWIP_Netcfg.gateway[3]);
			netif_set_addr(&stm32_netif, &ipaddr, &netmask, &gw);
			/* When the netif is fully configured this function must be called.*/
			netif_set_up(&stm32_netif);
		} else {
			ETH_Stop();
			/*  When the netif link is down this function must be called.*/
			netif_set_down(&stm32_netif);
		}
	}
}

加入热插拔状态检测函数调用

我用的LAN8720, 没有网络状态改变的中断通知, 只能是随便找个线程, 来主动查询网线插拔状态

PHY_Linked_Status 的定义如下, 就是检测uint16_t的bit2是否为1

/** @defgroup PHY_basic_status_register 
  * @{
  */ 
#define PHY_AutoNego_Complete           ((uint16_t)0x0020)      /*!< Auto-Negotiation process completed */
#define PHY_Linked_Status               ((uint16_t)0x0004)      /*!< Valid link established */
#define PHY_Jabber_detection            ((uint16_t)0x0002)      /*!< Jabber condition detected */

extern void check_net_RJ45_hot_plug_inout(void);

// 检测网线热插拔
void check_net_RJ45_hot_plug_inout(void)
{
	// #define PHY_BCR                          0          // LAN8720 Basic Control Register
	// #define PHY_BSR                          1          // LAN8720 Basic Status Register
	// #define PHY_SR				((uint16_t)31) 		// LAN8720 PHY Special Control/Status Register
	uint16_t RegValue_PHY_BSR = 0;
	OSTimeDlyHMSM(0, 0, 1, 0); // 1秒检查一下网线热插拔
	/* Get Ethernet link status*/
	RegValue_PHY_BSR = ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR); // net link status on BSR's bit2

	if (RegValue_PHY_BSR & PHY_Linked_Status) {
		if (gb_ETH_Init_ok) {
			// 固件开机后, 网线是插入的, 且初始化成功过
			if ((RegValue_PHY_BSR & PHY_Linked_Status) != (g_RegValue_PHY_BSR & PHY_Linked_Status)) {
				// 保证只在状态变化时才执行一次
				// 掉线变为在线
				g_RegValue_PHY_BSR = RegValue_PHY_BSR;
				netif_set_link_up(&stm32_netif);
			}
		} else {
			// 开始没插入网线, 导致第一次网卡没初始化成功
			// 这时, 网线插上了, 再咋弄网络操作(e.g. ping)都不好使.
			// 只能尝试重启一次固件, 走正常流程初始化网卡
			set_flag_system_restart(true); // 做个重启标记, 转出去之后, 主程序看到就重启了
		}
	} else {
		if ((RegValue_PHY_BSR & PHY_Linked_Status) != (g_RegValue_PHY_BSR & PHY_Linked_Status)) {
			// 保证只在状态变化时才执行一次
			// 在线变为掉线
			g_RegValue_PHY_BSR = RegValue_PHY_BSR;
			netif_set_link_down(&stm32_netif);
		}
	}

}

在网卡初始化之后, 调用LWIP初始化

void exec_lan8720_init()
{
	LAN8720_Init(); // 这里是不用等网卡是否初始化成功的, 我们后面通过其他任务中转圈来检测网线热插拔状态
	Ethernet_Sys_Init(); // LWIP初始化
}

void start_task(void* p_arg)
{
		// other process
		// ...

		// 在开始任务中调用网卡初始化和LWIP初始化
		#ifndef DEBUG_FLAG_NOT_USE_HTTP_SERVER_INSIDE
		exec_lan8720_init();
		#endif // #ifndef DEBUG_FLAG_NOT_USE_HTTP_SERVER_INSIDE

		// 在开始任务尾巴上, 转圈检测网线热插拔状态
		do {
			// other process
			// ...
			check_net_RJ45_hot_plug_inout(); // 检测网线热插拔
		} while (true);

}

打开 USE_Delay 宏, 使用自己的LWIP延时函数

#define USE_Delay    	//使用默认延时函数,因此注销掉
#ifdef USE_Delay
	#define _eth_delay_    my_ETH_Delay     //用户自己提供的延时函数
                                    
#else
	#define _eth_delay_    ETH_Delay // 默认的_eth_delay功能函数延时精度差
#endif

重新实现LWIP操作延时函数

以前维护工程时, 发现LAN8720初始化不是每次都成功(但是最多初始化10次, 就能初始化成功, 感觉好奇怪, 最后改为只有网卡初始化成功了, 才往下执行), 今天发现是LWIP自用的延时函数时间短了, 重新包装了一个ms延时的函数, 每次LAN8720都能初始化成功(只要先用网线将设备和HUB连在一起)

// .h 加入函数声明 或 暴力的在.c前面extern fuction
extern void my_ETH_Delay(__IO uint32_t nCount);

void my_ETH_Delay(__IO uint32_t nCount)
{
	__IO uint32_t index = 0;

	for (index = nCount; index != 0; index--) {
		OSTimeDlyHMSM(0, 0, 0, 1);
	}
}

增加 ETH_Stop() 实现

检测到网线拔出时用的.
这个函数DISABLE的顺序, 最好和ETH_Start()是反的.

// .h 中加入函数声明 void ETH_Stop(void); 
void ETH_Stop(void)
{  
  /* Stop DMA reception */
  ETH_DMAReceptionCmd(DISABLE); // havee

  /* Stop DMA transmission */
  ETH_DMATransmissionCmd(DISABLE); // have

  /* Disable receive state machine of the MAC for reception from the MII */
  ETH_MACReceptionCmd(DISABLE); // have

  /* Flush Transmit FIFO */
  ETH_FlushTransmitFIFO(); // have

  /* Disable transmit state machine of the MAC for transmission on the MII */
  ETH_MACTransmissionCmd(DISABLE); // have
}

// ETH_Start() 的实现, 可以和ETH_Stop()比较ENABEL, DISABLE的顺序
// ETH_Start 最先ENABLE哪条, ETH_Stop就要最后DISABLE哪条.
void ETH_Start(void)
{
  /* Enable transmit state machine of the MAC for transmission on the MII */  
  ETH_MACTransmissionCmd(ENABLE);
  /* Flush Transmit FIFO */
  ETH_FlushTransmitFIFO();
  /* Enable receive state machine of the MAC for reception from the MII */  
  ETH_MACReceptionCmd(ENABLE);
 
  /* Start DMA transmission */
  ETH_DMATransmissionCmd(ENABLE); 
  /* Start DMA reception */
  ETH_DMAReceptionCmd(ENABLE);   
}

LAN8720用到的几个寄存器

ST官方板子用的DP83848, 我的板子用的LAN8720. 从官方工程移植时, 注意自己板子的网卡地址(硬件原理图相关, 不是固定的地址),网卡寄存器号码, 网卡寄存器含义(datasheet有描述).

LAN8720主要是以下3个寄存器, 网卡插拔状态在PHY_BSR的bit2, PHY_BSR.bit2 = 1 为网线插入, 0 为网线拔出.
BSR means : Basic Status Register

#define PHY_BCR                          0          // LAN8720 Basic Control Register
#define PHY_BSR                          1          // LAN8720 Basic Status Register
#define PHY_SR				((uint16_t)31) 		// LAN8720 PHY Special Control/Status Register

这些寄存器和寄存器定义, 可以看LAN8720 datasheet <<C17146_LAN8720AI-CP-TR_2017-12-12.PDF>> 42页
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

LAN8720_Init()实现

这函数以前是返回是否初始化成功的, 现在不管是否初始化成功了, 管了也没用.
如果插入有效网线(网线一端连接设备,一端连接HUB), 一定能初始化成功
如果没有插入有效网线(包括网线一端连接设备,一端悬空), 一定初始化失败
不管网卡是否初始化成功, 都要往下走, 不能卡在这.

void LAN8720_Init(void)
{
	//	u8 rval=0;
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD,
	                       ENABLE);//使能GPIO时钟 RMII接口
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);   //使能SYSCFG时钟
	SYSCFG_ETH_MediaInterfaceConfig(SYSCFG_ETH_MediaInterface_RMII); //MAC和PHY之间使用RMII接口
	/*网络引脚设置 RMII接口
	  ETH_MDIO -------------------------> PA2
	  ETH_MDC --------------------------> PC1
	  ETH_RMII_REF_CLK------------------> PA1
	  ETH_RMII_CRS_DV ------------------> PA7
	  ETH_RMII_RXD0 --------------------> PC4
	  ETH_RMII_RXD1 --------------------> PC5
	  ETH_RMII_TX_EN -------------------> PB11
	  ETH_RMII_TXD0 --------------------> PB12
	  ETH_RMII_TXD1 --------------------> PB13
	  ETH_RESET-------------------------> PD3*/
	//配置PA1 PA2 PA7
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_ETH);
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_ETH);
	//配置PC1,PC4 and PC5
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_4 | GPIO_Pin_5;
	GPIO_Init(GPIOC, &GPIO_InitStructure);
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource1, GPIO_AF_ETH); //引脚复用到网络接口上
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource4, GPIO_AF_ETH);
	GPIO_PinAFConfig(GPIOC, GPIO_PinSource5, GPIO_AF_ETH);
	//配置PB11, PB12 and PB13
	GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_ETH); //引脚复用到网络接口上
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource12, GPIO_AF_ETH);
	GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_ETH);
	//配置PD3为推完输出
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;	//推完输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOD, &GPIO_InitStructure);
	LAN8720_RST = 0;					//硬件复位LAN8720
	delay_ms(50);
	LAN8720_RST = 1;				 	//复位结束
	delay_ms(50);
	ETH_MACDMA_Config();		//配置MAC及DMA
}

对网卡初始化成功做个标记

gb_ETH_Init_ok 为标记, 如果固件启动后, 网线不插, ETH_Init就会在中途返回失败
将 ETH_Init() 结果赋值给 gb_ETH_Init_ok , 这样就知道固件启动时, 是否插入了网线启动的.
如果(gb_ETH_Init_ok == true), 说明固件启动时, 是插入网线(网线一端接设备, 一端接HUB)启动的.
如果(gb_ETH_Init_ok == false), 说明固件启动时, 是没插入网线启动的, 如果再检测到网线插入, 就要重启固件, 走插入网线启动固件的正常流程.

在(gb_ETH_Init_ok == true), 用 g_RegValue_PHY_BSR保存一下当前网线插入状态, 这样在网线热插拔时, 可以保证只在网线插拔状态改变时, 执行一次有效的网卡上线或下线操作.

void ETH_MACDMA_Config(void)
{
	// u8 rval;
	//使能以太网MAC以及MAC接收和发送时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx | RCC_AHB1Periph_ETH_MAC_Rx, ENABLE);
	ETH_DeInit();  								//AHB总线重启以太网
	ETH_SoftwareReset();  						//软件重启网络

	while (ETH_GetSoftwareResetStatus() == SET);//等待软件重启网络完成

	ETH_StructInit(&ETH_InitStructure); 	 	//初始化网络为默认值
	///网络MAC参数设置
	ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable;   			//开启网络自适应功能
	ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable;					//关闭反馈
	ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; 		//关闭重传功能
	ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; 	//关闭自动去除PDA/CRC功能
	ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable;						//关闭接收所有的帧
	ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;//允许接收所有广播帧
	ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable;			//关闭混合模式的地址过滤
	ETH_InitStructure.ETH_MulticastFramesFilter =
	    ETH_MulticastFramesFilter_Perfect;//对于组播地址使用完美地址过滤
	ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect;	//对单播地址使用完美地址过滤
#ifdef CHECKSUM_BY_HARDWARE
	ETH_InitStructure.ETH_ChecksumOffload =
	    ETH_ChecksumOffload_Enable; 			//开启ipv4和TCP/UDP/ICMP的帧校验和卸载
#endif
	//当我们使用帧校验和卸载功能的时候,一定要使能存储转发模式,存储转发模式中要保证整个帧存储在FIFO中,
	//这样MAC能插入/识别出帧校验值,当真校验正确的时候DMA就可以处理帧,否则就丢弃掉该帧
	ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame =
	    ETH_DropTCPIPChecksumErrorFrame_Enable; //开启丢弃TCP/IP错误帧
	ETH_InitStructure.ETH_ReceiveStoreForward =
	    ETH_ReceiveStoreForward_Enable;     //开启接收数据的存储转发模式
	ETH_InitStructure.ETH_TransmitStoreForward =
	    ETH_TransmitStoreForward_Enable;   //开启发送数据的存储转发模式
	ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable;     	//禁止转发错误帧
	ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable;	//不转发过小的好帧
	ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable;  		//打开处理第二帧功能
	ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable;  	//开启DMA传输的地址对齐功能
	ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable;            			//开启固定突发功能
	ETH_InitStructure.ETH_RxDMABurstLength =
	    ETH_RxDMABurstLength_32Beat;     		//DMA发送的最大突发长度为32个节拍
	ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat;			//DMA接收的最大突发长度为32个节拍
	ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1;

	// 配置ETH
	if (ETH_SUCCESS == ETH_Init(&ETH_InitStructure, LAN8720_PHY_ADDRESS)) {
		gb_ETH_Init_ok =
		    true; // 只有在固件启动时, 插入网线初始化成功了, 后面的网线热插拔才好使. 暂时是这样
		// 备份一下第一次网卡初始化的网线插入状态
		g_RegValue_PHY_BSR = ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR); // net link status on BSR's bit2
	}

	ETH_DMAITConfig(ETH_DMA_IT_NIS | ETH_DMA_IT_R, ENABLE);  	//使能以太网接收中断
}

本次实验结束

就这些改动了, 研究了4,5天.

补充 - 2022_1123_1123

今天, 有个csdn同学想让我分享这个实验工程.
分享不了工程(不是专门做的实验工程, 是在公司工程上改的).
分享的是解决问题的思路, 这个笔记也是将问题解决后, 习惯性记录一下, 自己以后也会用到.

这个工程是在<<使用STM32F4标准外设库实现网线热插拔- 分析STM3240G-EVAL官方工程>> 这个预研笔记上, 直接在公司工程上改的, 然后对照git记录, 做的这个笔记.
不方便分享, 没这个权利.

大家解决问题, 主要是找到一个线索和思路, 然后就是自己折腾了. 如果不是面对面的同事, 真帮不上大忙.

软件(上位机)方面, 可能还好说点. 你将自己工程脱敏后, 作为一个demo工程给愿意帮你解决问题的工程师, 让他来本地调试. 环境也好搭建.
固件工程是和硬件相关的, 差一点, 跑的都不对(可能都跑不起来). 基本别人帮不上你(连调试环境都没有).

上次有个csdn同学, 用的网卡芯片也是DP83848. 正好我手头有STM3240G-EVAL官方板子(网卡芯片也是DP83848).
他按照官方工程改不好使, 想让我帮忙看看.
让他将网卡芯片连接部分的原理图截出来, 将脱敏后的demo工程发来.
实验看网络操作不好使, 就去对官方原理图. 官方原理图是MII连接方式, csdn同学的原理图是RMII连接方式.
那就无法帮他在本地调试热插拔的问题了. 改了也不知道是否好使, 没办法验证.
所以说, 和硬件相关的固件问题, 如果不是面对面的同事, 是很难给你帮忙的.

我这次笔记, 只是解决了我们公司自己设备的问题. 解决的并不完善.
我们设备是一个测量设备, 自己管自己的事, 可以中途重启. 我改成发现网线重新插拔了, 我就重启设备.

上次csdn同学的脱敏工程发来, 我将工程浏览了一下, 发现这个固件工程是控制一个工业流程的设备(脱敏脱的不干净).
他这个设备就不能发现网线热插拔了就重启, 会出大事的.

要想彻底解决问题(发现热插拔后, 网络可以直接连通, 不用重启), 还需要做更细致的实验.
官方工程是可以达到这一点的.

我的这个实验之所以做了半拉子, 就是因为我遇到的问题, 通过这个半拉子方法是可以接受的, 没有刚需, 也没有时间(如果不是产品研发阶段, 只是维护和解决bug, 应该大部分人将眼前的问题解决完就不管了吧?).
如果不能接受, 我就会继续实验, 将问题彻底搞定.

所以要彻底解决这个网线热插拔的问题, 需要从头搭建实验工程(按照官方工程使用的LWIP版本, 固件库或HAL库), 然后一点一点的实验(网络初始化的寄存器操作调用链, 网络连通时的函数调用链, 网线拔下函数的调用链, 网线重新插入时的函数调用链, 要和官方工程一致, 并记录特征点的值). 如果和官方工程不一致, 就找区别, 调整代码实现.
最后达到热插拔网线时, 达到和官方工程同样的效果.
然后再实验, 产品的实际固件工程网络实现部分, 和自己实验好的工程的区别, 然后用换件法去实验. 最后让实际固件工程达到和官方工程一样的效果.

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值