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(Ð_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 -------------------------> PA2ETH_MDC --------------------------> PC1ETH_RMII_REF_CLK------------------> PA1ETH_RMII_CRS_DV ------------------> PA7ETH_RMII_RXD0 --------------------> PC4ETH_RMII_RXD1 --------------------> PC5ETH_RMII_TX_EN -------------------> PB11ETH_RMII_TXD0 --------------------> PG13ETH_RMII_TXD1 --------------------> PG14ETH_RESET-------------------------> PCF8574扩展IO*///PA1,2,7GPIO_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); //初始化//PB11GPIO_Initure.Pin=GPIO_PIN_11|GPIO_PIN_12|GPIO_PIN_13; //PB11HAL_GPIO_Init(GPIOB,&GPIO_Initure); //始化//PC1,4,5GPIO_Initure.Pin=GPIO_PIN_1|GPIO_PIN_4|GPIO_PIN_5; //PC1,4,5HAL_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 //使用动态IPipaddr.addr = 0;netmask.addr = 0;gw.addr = 0;
#else //使用静态IPIP4_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]);
#endifNetif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,ðernetif_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(Ð_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(Ð_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_DHCPlwip_comm_dhcp_creat(); //创建DHCP任务#endifbreak;} }
// #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;}}
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
