STM32f429+基于DP83848的LwIP协议栈移植 (TCP客户端)

相比于网络芯片dp83848,现在可能有其它类似于W5500这类内置了硬件协议栈的芯片使用起来更加简易,但是dp83848还是有一些优势,使用更加灵活,适用范围也更广。

LwIP协议栈STM32官方有提供例程,修改后可以使用,首先是DP83848硬件初始化,使用HAL库。

//DP83848初始化
//返回值:0,成功;
//    其他,失败
u8 DP83848_Init(void)
{      
    u8 macaddress[6];
	
    GPIO_InitTypeDef GPIO_InitStructure;	  
	  __HAL_RCC_GPIOE_CLK_ENABLE();			//开启GPIOB时钟
	
    INTX_DISABLE();                         //关闭所有中断,复位过程不能被打断!
//    PCF8574_WriteBit(ETH_RESET_IO,1);       //硬件复位
//    delay_ms(100);
//    PCF8574_WriteBit(ETH_RESET_IO,0);       //复位结束
//    delay_ms(100);
	  GPIO_InitStructure.Pin = GPIO_PIN_3;
	  GPIO_InitStructure.Mode = GPIO_MODE_IT_FALLING;
	  GPIO_InitStructure.Pull = GPIO_PULLUP;
	  HAL_GPIO_Init(GPIOE, &GPIO_InitStructure);  
	
		HAL_NVIC_SetPriority(EXTI3_IRQn,0,0); 
		HAL_NVIC_EnableIRQ(EXTI3_IRQn);	
    INTX_ENABLE();                          //开启所有中断  

    macaddress[0]=lwipdev.mac[0]; 
	  macaddress[1]=lwipdev.mac[1]; 
	  macaddress[2]=lwipdev.mac[2];
	  macaddress[3]=lwipdev.mac[3];   
	  macaddress[4]=lwipdev.mac[4];
  	macaddress[5]=lwipdev.mac[5];
        
  	ETH_Handler.Instance=ETH;
    ETH_Handler.Init.AutoNegotiation=ETH_AUTONEGOTIATION_ENABLE;//使能自协商模式 
    ETH_Handler.Init.Speed=ETH_SPEED_100M;//速度100M,如果开启了自协商模式,此配置就无效
    ETH_Handler.Init.DuplexMode=ETH_MODE_FULLDUPLEX;//全双工模式,如果开启了自协商模式,此配置就无效
    ETH_Handler.Init.PhyAddress=DP83848_PHY_ADDRESS;//DP83848地址  
    ETH_Handler.Init.MACAddr=macaddress;            //MAC地址  
    ETH_Handler.Init.RxMode=ETH_RXINTERRUPT_MODE;   //轮训接收模式 
    ETH_Handler.Init.ChecksumMode=ETH_CHECKSUM_BY_HARDWARE;//硬件帧校验  
    ETH_Handler.Init.MediaInterface=ETH_MEDIA_INTERFACE_RMII;//RMII接口  
    if(HAL_ETH_Init(&ETH_Handler)==HAL_OK)
    {
				Eth_Link_PHYITConfig();
        return 0;   //成功
    }
    else 
		{
			Eth_Link_PHYITConfig();
			return 1;  //失败
		}
}

IO口都是固定那几个引脚,不用多说,dp83848的PHY地址根据实际情况确定

#define DP83848_PHY_ADDRESS             0x01

//ETH底层驱动,时钟使能,引脚配置
//此函数会被HAL_ETH_Init()调用
//heth:以太网句柄
void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
{
    GPIO_InitTypeDef GPIO_Initure;
    
    __HAL_RCC_ETH_CLK_ENABLE();             //开启ETH时钟
    __HAL_RCC_GPIOA_CLK_ENABLE();			//开启GPIOA时钟
  	__HAL_RCC_GPIOB_CLK_ENABLE();			//开启GPIOB时钟
    __HAL_RCC_GPIOC_CLK_ENABLE();			//开启GPIOC时钟
    __HAL_RCC_GPIOG_CLK_ENABLE();			//开启GPIOG时钟
    
    /*网络引脚设置 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 --------------------> PG13
    ETH_RMII_TXD1 --------------------> PG14
    ETH_RESET-------------------------> PCF8574扩展IO*/
    
    //PA1,2,7
    GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_7; 
    GPIO_Initure.Mode=GPIO_MODE_AF_PP;          //推挽复用
    GPIO_Initure.Pull=GPIO_NOPULL;              //不带上下拉
    GPIO_Initure.Speed=GPIO_SPEED_HIGH;         //高速
    GPIO_Initure.Alternate=GPIO_AF11_ETH;       //复用为ETH功能
    HAL_GPIO_Init(GPIOA,&GPIO_Initure);         //初始化
    
    //PB11
    GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13;               //PB11
    HAL_GPIO_Init(GPIOB,&GPIO_Initure);         //始化
    
    //PC1,4,5
    GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5
    HAL_GPIO_Init(GPIOC,&GPIO_Initure);         //初始化
	
//    //PG13,14
//    GPIO_Initure.Pin=GPIO_PIN_13|GPIO_PIN_14;   //PG13,14
//    HAL_GPIO_Init(GPIOG,&GPIO_Initure);         //初始化
    
    HAL_NVIC_SetPriority(ETH_IRQn,0,0);         //网络中断优先级应该高一点
    HAL_NVIC_EnableIRQ(ETH_IRQn);
}

PHY初始化完成之后是LwIP协议初始化

//LWIP初始化(LWIP启动的时候使用)
//返回值:0,成功
//      1,内存错误
//      2,dp83848初始化失败
//      3,网卡添加失败.
u8 lwip_comm_init(void)
{
    u8 retry=0;
	struct netif *Netif_Init_Flag;		//调用netif_add()函数时的返回值,用于判断网络初始化是否成功
	struct ip_addr ipaddr;  			//ip地址
	struct ip_addr netmask; 			//子网掩码
	struct ip_addr gw;      			//默认网关 
  
	if(Mem_init_flag==0)
	{
	if(ETH_Mem_Malloc())return 0;		//内存申请失败
	if(lwip_comm_mem_malloc())return 0;	//内存申请失败
    lwip_comm_default_ip_set(&lwipdev);	//设置默认IP等信息
		Mem_init_flag=1;
	}
	while(DP83848_Init())		        //初始化DP83848,如果失败的话就重试5次
    {
        retry++;
        if(retry>5) {retry=0;return 0;} //DP83848初始化失败   //5s
    }
	tcpip_init(NULL,NULL);				//初始化tcp ip内核,该函数里面会创建tcpip_thread内核任务

#if LWIP_DHCP		//使用动态IP
	ipaddr.addr = 0;
	netmask.addr = 0;
	gw.addr = 0;
#else				//使用静态IP
	IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
	IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
	IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
	printf("网卡en的MAC地址为:................%d.%d.%d.%d.%d.%d\r\n",lwipdev.mac[0],lwipdev.mac[1],lwipdev.mac[2],lwipdev.mac[3],lwipdev.mac[4],lwipdev.mac[5]);
	printf("静态IP地址........................%d.%d.%d.%d\r\n",lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
	printf("子网掩码..........................%d.%d.%d.%d\r\n",lwipdev.netmask[0],lwipdev.netmask[1],lwipdev.netmask[2],lwipdev.netmask[3]);
	printf("默认网关..........................%d.%d.%d.%d\r\n",lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
#endif
	Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&tcpip_input);//向网卡列表中添加一个网口
	if(Netif_Init_Flag==NULL)return 4;//网卡添加失败 
	else//网口添加成功后,设置netif为默认值,并且打开netif网口
	{
		netif_set_default(&lwip_netif); //设置netif为默认网口
		netif_set_up(&lwip_netif);		//打开netif网口
	}
	return 1;//操作OK.
}

如果需要使用DHCP,则创建DHCP任务来获取IP地址,在一段时间不能获取到IP地址则使用默认的静态IP地址。任务完成后删除DHCP任务本身。

接下来需要使用TCP/IP客户端模式进行通信,则创建TCP客户端线程,任务函数如下

//tcp客户端任务函数
static void tcp_client_thread(void *arg)
{
	CPU_SR_ALLOC();
	OS_ERR      oserr;	
	u32 data_len = 0;
	struct pbuf *q;
	err_t err,recv_err;
	static ip_addr_t server_ipaddr,loca_ipaddr;
	static u16_t 		 server_port,loca_port;

	LWIP_UNUSED_ARG(arg);
	server_port = eth_remote_port;
	IP4_ADDR(&server_ipaddr, lwipdev.remoteip[0],lwipdev.remoteip[1], lwipdev.remoteip[2],lwipdev.remoteip[3]);
	
	while (1) 
	{
		tcp_clientconn = netconn_new(NETCONN_TCP);  //创建一个TCP链接
		err = netconn_connect(tcp_clientconn,&server_ipaddr,server_port);//连接服务器
		if(err != ERR_OK)  netconn_delete(tcp_clientconn); //返回值不等于ERR_OK,删除tcp_clientconn连接
		else if (err == ERR_OK)    //处理新连接的数据
		{ 
			Tcp_Work_State=STATE_NET_READY;
			led2_state=LED_TWINKLE;
			struct netbuf *recvbuf;
			tcp_clientconn->recv_timeout = 10;
			netconn_getaddr(tcp_clientconn,&loca_ipaddr,&loca_port,1); //获取本地IP主机IP地址和端口号
			printf("连接上服务器%d.%d.%d.%d,本机端口号为:%d\r\n",lwipdev.remoteip[0],lwipdev.remoteip[1], lwipdev.remoteip[2],lwipdev.remoteip[3],loca_port);
			while(1)
			{
				if((tcp_client_flag & LWIP_SEND_DATA) == LWIP_SEND_DATA) //有数据要发送
				{
//					err = netconn_write(tcp_clientconn ,tcp_client_sendbuf,strlen((char*)tcp_client_sendbuf),NETCONN_COPY); //发送tcp_server_sentbuf中的数据
					err = netconn_write(tcp_clientconn ,tcp_client_sendbuf,tcp_client_sendbuf_len,NETCONN_COPY); //发送tcp_server_sentbuf中的数据
					if(err != ERR_OK)
					{
						printf("发送失败\r\n");
					}
					tcp_client_flag &= ~LWIP_SEND_DATA;
				}
					
				if((recv_err = netconn_recv(tcp_clientconn,&recvbuf)) == ERR_OK)  //接收到数据
				{	
					OS_CRITICAL_ENTER(); //关中断
					memset(tcp_client_recvbuf,0,TCP_CLIENT_RX_BUFSIZE);  //数据接收缓冲区清零
					for(q=recvbuf->p;q!=NULL;q=q->next)  //遍历完整个pbuf链表
					{
						//判断要拷贝到TCP_CLIENT_RX_BUFSIZE中的数据是否大于TCP_CLIENT_RX_BUFSIZE的剩余空间,如果大于
						//的话就只拷贝TCP_CLIENT_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据
						if(q->len > (TCP_CLIENT_RX_BUFSIZE-data_len)) memcpy(tcp_client_recvbuf+data_len,q->payload,(TCP_CLIENT_RX_BUFSIZE-data_len));//拷贝数据
						else memcpy(tcp_client_recvbuf+data_len,q->payload,q->len);
						data_len += q->len;  	
						if(data_len > TCP_CLIENT_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出	
					}
					OS_CRITICAL_EXIT();  //开中断
//					printf_HEX(tcp_client_recvbuf,data_len);
					data_len=0;  //复制完成后data_len要清零。					
//					printf("%s\r\n",tcp_client_recvbuf);
					Tcp_Rxdone_flag=1;
					netbuf_delete(recvbuf);
				}else if(recv_err == ERR_CLSD||(User_Cfg.Main_Channel==ETH_PATH&&Net_Error_Cnt>=2))  //关闭连接
				{
					Net_Error_Cnt=0;
					Tcp_Work_State=STATE_CONNECT_CLSD;
					led2_state=LED_FREE;
					netconn_close(tcp_clientconn);
					netconn_delete(tcp_clientconn);
					printf("服务器%d.%d.%d.%d断开连接\r\n",lwipdev.remoteip[0],lwipdev.remoteip[1], lwipdev.remoteip[2],lwipdev.remoteip[3]);
					break;
				}
				
			}
		 OSTimeDly ( 2, OS_OPT_TIME_DLY, & oserr );  
		}
		OSTimeDly ( 5, OS_OPT_TIME_DLY, & oserr );        
	}
}

需要注意的是移植的LwIP协议栈是不支持网线热插拔的,需要自己写处理函数来检测网线的连接状态,方法是通过芯片提供的中断来实现,在DP83848硬件初始化的时候有进行外部中断IO口的初始化,

如果移植的文件中没有的话,需要添加两个寄存器的地址

#define PHY_MICR                        ((uint16_t)0x11)
#define PHY_MISR                        ((uint16_t)0x12)

中断回调函数处理,置一个插入或拔出的标志

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	uint32_t status;
	if(GPIO_Pin==GPIO_PIN_3)
	{
		HAL_ETH_ReadPHYRegister(&ETH_Handler, PHY_MISR,&status);
//		if((status & PHY_LINK_STATUS) != 0)
//		{
		 Plug_flag=1;
//		}
	}
}

需要创建一个任务检测网线连接,网线连接的时候主要有两种情况,一种是之前未成功初始化,那么就从PHY开始初始化一遍;另一种是之前已经成功初始化,那么只需要调用netif_set_link_up(&lwip_netif);重新建立连接。

网线断开的时候调用netif_set_link_down(&lwip_netif); 断开连接。测试能正常进行网线热插拔,没有问题。

void Eth_Link_ITHandler(void)
{
	uint32_t status;
	uint8_t retry;
 /* Check whether the link interrupt has occurred or not */
	  if(Plug_flag)
		{
			Plug_flag=0;
			HAL_ETH_ReadPHYRegister(&ETH_Handler, PHY_BSR, &status);
			if(status & ( PHY_AUTONEGO_COMPLETE |PHY_LINKED_STATUS))/*检测到网线连接*/
			{
				if(Tcp_Work_State==STATE_NET_READY)
				{
				  led2_state=LED_TWINKLE;
				}
				if(EthInitStatus == 0)/*之前未成功初始化过*/
				{
					/*Reinit PHY*/
            retry=5;
						while(retry) 		//lwip初始化
						{
							retry--;
							delay_ms(500);
							delay_ms(500);
              if(lwip_comm_init()==1)
							{
								EthInitStatus=1;
						   #if LWIP_DHCP
							  lwip_comm_dhcp_creat();                                   //创建DHCP任务
						   #endif
								break;
							}								
						}
//						#if LWIP_DHCP
//							lwip_comm_dhcp_creat();                                   //创建DHCP任务
//						#endif
				}
				else/*之前已经成功初始化*/
				{
					/*set link up for re link callbalk function*/
					netif_set_link_up(&lwip_netif);
				}
			}
			else/*网线断开*/
			{
				/*set link down for re link callbalk function*/
				netif_set_link_down(&lwip_netif);		
				led2_state=LED_FREE;
			}
	  }
}

 

  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值