研究了好几天stm32+lwip动态链路的处理方法,发现大多是写一些lwip移植,tcp/ip移植之类的东西,没有与实际项目相关的内容,今天发些干货,说说这些天stm32+lwip+ucosii实际项目的一些问题:
1,怎么实现系统网线的热插拔?
以我的项目为例,PHY为lan8720a。首先要介绍一个重要的函数---GET_PHY_LINK_STATUS();
其实这个函数就是一个宏,用于读取lan8720a的一个寄存器来查看网线连接状态,返回值为0x04为连接状态,0 未连接。
#define GET_PHY_LINK_STATUS() (ETH_ReadPHYRegister(LAN8720_PHY_ADDRESS, PHY_BSR) & 0x00000004)
物理层初始化部分,这样处理
物理层包括LAN8720a和stm32 eth DMA+MAC。系统初始化的时候不要初始化MAC和DMA
1 void LAN8720_Init(void) 2 { 3 /*打开该打开的时钟*/ 4 /*网络引脚设置 RMII接口 初始化 不写了。。。。*/ 5 LAN8720_RST=0; //硬件复位LAN8720 6 delay_ms(50); 7 LAN8720_RST=1; //复位结束 8 ETHERNET_NVICConfiguration(); //设置中断优先级 9 ETH_MACDMA_Config(); 10 } 11 12 void ETH_MACDMA_Config(void) 13 { 14 // u8 rval; 15 //使能以太网MAC以及MAC接收和发送时钟 16 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_ETH_MAC | RCC_AHB1Periph_ETH_MAC_Tx |RCC_AHB1Periph_ETH_MAC_Rx, ENABLE); 17 ETH_DeInit(); //AHB总线重启以太网 18 ETH_SoftwareReset(); //软件重启网络 19 while (ETH_GetSoftwareResetStatus() == SET);//等待软件重启网络完成 20 // rval = MAC_DMA_Init(); 这些就不在这初始化了 21 // return rval; 22 } 23 u8 MAC_DMA_Init(void) 24 { 25 u8 rval; 26 ETH_StructInit(Ð_InitStructure); //初始化网络为默认值 27 ///网络MAC参数设置 28 ETH_InitStructure.ETH_AutoNegotiation = ETH_AutoNegotiation_Enable; //开启网络自适应功能 29 ETH_InitStructure.ETH_LoopbackMode = ETH_LoopbackMode_Disable; //关闭反馈 30 ETH_InitStructure.ETH_RetryTransmission = ETH_RetryTransmission_Disable; //关闭重传功能 31 ETH_InitStructure.ETH_AutomaticPadCRCStrip = ETH_AutomaticPadCRCStrip_Disable; //关闭自动去除PDA/CRC功能 32 ETH_InitStructure.ETH_ReceiveAll = ETH_ReceiveAll_Disable; //关闭接收所有的帧 33 ETH_InitStructure.ETH_BroadcastFramesReception = ETH_BroadcastFramesReception_Enable;//允许接收所有广播帧 34 ETH_InitStructure.ETH_PromiscuousMode = ETH_PromiscuousMode_Disable; //关闭混合模式的地址过滤 35 ETH_InitStructure.ETH_MulticastFramesFilter = ETH_MulticastFramesFilter_Perfect;//对于组播地址使用完美地址过滤 36 ETH_InitStructure.ETH_UnicastFramesFilter = ETH_UnicastFramesFilter_Perfect; //对单播地址使用完美地址过滤 37 #ifdef CHECKSUM_BY_HARDWARE 38 ETH_InitStructure.ETH_ChecksumOffload = ETH_ChecksumOffload_Enable; //开启ipv4和TCP/UDP/ICMP的帧校验和卸载 39 #endif 40 //当我们使用帧校验和卸载功能的时候,一定要使能存储转发模式,存储转发模式中要保证整个帧存储在FIFO中, 41 //这样MAC能插入/识别出帧校验值,当真校验正确的时候DMA就可以处理帧,否则就丢弃掉该帧 42 ETH_InitStructure.ETH_DropTCPIPChecksumErrorFrame = ETH_DropTCPIPChecksumErrorFrame_Enable; //开启丢弃TCP/IP错误帧 43 ETH_InitStructure.ETH_ReceiveStoreForward = ETH_ReceiveStoreForward_Enable; //开启接收数据的存储转发模式 44 ETH_InitStructure.ETH_TransmitStoreForward = ETH_TransmitStoreForward_Enable; //开启发送数据的存储转发模式 45 46 ETH_InitStructure.ETH_ForwardErrorFrames = ETH_ForwardErrorFrames_Disable; //禁止转发错误帧 47 ETH_InitStructure.ETH_ForwardUndersizedGoodFrames = ETH_ForwardUndersizedGoodFrames_Disable; //不转发过小的好帧 48 ETH_InitStructure.ETH_SecondFrameOperate = ETH_SecondFrameOperate_Enable; //打开处理第二帧功能 49 ETH_InitStructure.ETH_AddressAlignedBeats = ETH_AddressAlignedBeats_Enable; //开启DMA传输的地址对齐功能 50 ETH_InitStructure.ETH_FixedBurst = ETH_FixedBurst_Enable; //开启固定突发功能 51 ETH_InitStructure.ETH_RxDMABurstLength = ETH_RxDMABurstLength_32Beat; //DMA发送的最大突发长度为32个节拍 52 ETH_InitStructure.ETH_TxDMABurstLength = ETH_TxDMABurstLength_32Beat; //DMA接收的最大突发长度为32个节拍 53 ETH_InitStructure.ETH_DMAArbitration = ETH_DMAArbitration_RoundRobin_RxTx_2_1; 54 rval=ETH_Init(Ð_InitStructure,LAN8720_PHY_ADDRESS); //配置ETH 这一步 如果网线没插 会等很长时间 55 if(rval==ETH_SUCCESS)//配置成功 56 { 57 ETH_DMAITConfig(ETH_DMA_IT_NIS|ETH_DMA_IT_R,ENABLE); //使能以太网接收中断 58 } 59 return rval; 60 }
是时候编写链路动态处理函数了:
1 void CableLink_changed(void *pdata) 2 { 3 u8 Cable_state = 0;//初始化网线没插 4 u8 Link_state = 0; //网线连接状态 中间变量 5 u8 Link_mode = WorkMode;//开启DHCP了吗 6 Link_state=GET_PHY_LINK_STATUS(); //先检测了一次 7 if(Link_state==0x04) Cable_state = 1; //网线连接了 Cable_state 置 1 8 while(1) 9 { 10 Link_state = GET_PHY_LINK_STATUS();//检测了网线连接 11 if((Link_state == 0)&&(Cable_state == 1)) //网线断开了(以前是连接着的) 12 { 13 Cable_state = 0; 14 netif_set_link_down(&lwip_netif); 15 if(Link_mode == LINK_DHCP) 16 { 17 lwipdev.dhcpstatus = DHCP_LINK_DOWN; 18 #if LWIP_DHCP 19 dhcp_stop(&lwip_netif); 20 #endif 21 } 22 LCD_ShowString(400,30,60,20,12," "); 23 LCD_ShowString(400,30,60,20,12,"LINK Down"); 24 } 25 if((Link_state == 0x04)&&(Cable_state == 0))//网线重新连上了 26 { 27 Cable_state = 1; 28 MAC_DMA_Init(); //重新配置MAC和DMA 甭管是第一次 还是第N次 都要重新配置一次 29 if(Link_mode == LINK_STATIC) //静态IP 30 { 31 IP4_ADDR(&(lwip_netif.ip_addr),lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]); 32 IP4_ADDR(&(lwip_netif.netmask),lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]); 33 IP4_ADDR(&(lwip_netif.gw),lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]); 34 netif_set_link_up(&lwip_netif); 35 OSSemPost(Comm_Dis);//这里 只在静态IP 释放信号 因为DHCP任务中会释放 给显示任务发信号 36 } 37 else //开启了DHCP 38 { 39 IP4_ADDR(&(lwip_netif.ip_addr),0,0,0,0);//这几步很重要 40 IP4_ADDR(&(lwip_netif.netmask),0,0,0,0);//当网络环境发生变化 比如路由器dhcp允许分配的IP发送变化或者系统网线插到别的路由器了 41 IP4_ADDR(&(lwip_netif.gw),0,0,0,0); //重新开启了dhcp_start(&gnetif),会重新分配IP,不然申请不下来 42 lwip_netif.flags |= NETIF_FLAG_LINK_UP; //关于这里,感觉只用把NETIF_FLAG_LINK_UP置一就行了,开启dhcp_start后,随后会调用 43 //netif_set_link_up(&lwip_netif); //netif_set_up() 来刷存在感 44 lwip_comm_dhcp_Resume(); //开启dhcp管理任务 45 } 46 LCD_ShowString(400,30,60,20,12," "); //dchp 打开 47 LCD_ShowString(400,30,60,20,12,"LINK ON"); //dchp 打开 48 } 49 delay_ms(500); 50 } 51 }
这是一个ucos任务,差不多 1秒执行2次。
这样就可以处理动态连理的情况了。
2 静态IP情况下系统上电刷两次存在感?
先上图:
会有两个Gratutious arp request,一个mac是02:00:00:2a:00:2a 一个是02:00:00:42:00:42 而且他们一个是16进制 1个是十进制。他们都是系统发出的吗?
我查看了好多文档,仿真查看寄存器,都不知道为什么,无意中我发现了路由器中的静态arp绑定设置。
原来是当初手贱绑定了。
正在试验阶段,写的比较粗糙。将就看吧。