前言
从同事工程上改了一个版本出来, 发现如果不插入网线, 不能正常跑. 卡在网卡初始化那里了.
如果不能实现网线热插拔, 我改出的版本不能实际用在客户现场的. 如果不插入网线, 就不能正常用, 这谁受的了啊.
前面做了一个预研使用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, ðernetif_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(Ð_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(Ð_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库), 然后一点一点的实验(网络初始化的寄存器操作调用链, 网络连通时的函数调用链, 网线拔下函数的调用链, 网线重新插入时的函数调用链, 要和官方工程一致, 并记录特征点的值). 如果和官方工程不一致, 就找区别, 调整代码实现.
最后达到热插拔网线时, 达到和官方工程同样的效果.
然后再实验, 产品的实际固件工程网络实现部分, 和自己实验好的工程的区别, 然后用换件法去实验. 最后让实际固件工程达到和官方工程一样的效果.