STM32CubeIDE LWIPUDP网络通讯

it2023-01-22  52

        STM32CubeIDE 已经构建了一套良好的网络通讯框架,结合LWIP可实现大部分网络通信任务,现主要对UDP单播及组的配置进行说明:

        测试电路板采用STM32F4系列芯片+LAN8720方案构建的百兆网络,首先配置ETH外设,LAN8720芯片的PHYAD0引脚悬空,故PHY Address设置为0:

                            

        设置PHY为 user PHY:

                             

        其余配置保持默认即可,然后设置LWIP选项,首先勾选LWIP使能,最好同时使用FREERTOS,便于后面的数据处理。然后根据需求设置DHCP服务或者静态IP:

                              

        以上配置以实现网络功能,下载后网络连接即可正常,ping正常。要完成UDP通信,需要使能UDP服务:

                             

        组播通信需要使能组播相应设置:

                           

        注意,设置使能上面页面后,才能使能LWIP_IGMP。硬件配置完成,然后需要对生的代码进行修改,修改ethernetif.c文件中  static void low_level_init(struct netif *netif) 函数,增加NETIF_FLAG_IGMP:

              

       修改stm32f4xx_hal_eth.c文件中初始化函数 static void ETH_MACDMAConfig(ETH_HandleTypeDef *heth, uint32_t err) 

        设置 MulticastFramesFilter 类型为 ETH_MULTICASTFRAMESFILTER_NONE。

        完成以上设置后,即可开始编写用户程序,需要在程序初始化LWIP,调用MX_LWIP_Init();函数完成,一般在默认任务开始的时候调用,注意该函数可能需要较大的内存空间,需要把任务栈设置的更大一些。

        每一个UDP通道开启一个线程,线程中完成对UDP的端口绑定和回调。单播代码如下:

#include "lwip/udp.h" #include "lwip/pbuf.h" #include "lwip/igmp.h" #include "lwipopts.h" struct udp_pcb *g_upcb; #define UDP_RX_BUFSIZE 1024 //定义udp最大接收数据长度 #define UDP_REMOTE_PORT 9000 //定义udp连接的端口 #define UDP_HOME_PORT 9001 //定义udp连接的端口 void l_User_NET_Task(void) { err_t err; /* Create a new UDP control block */ g_upcb = udp_new(); if (g_upcb) { /* Bind the upcb to the UDP_PORT port */ /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */ err = udp_bind(g_upcb, IP_ADDR_ANY, UDP_HOME_PORT); if(err == ERR_OK) { /* Set a receive callback for the upcb */ udp_recv(g_upcb, udp_receive_callback, NULL); } else { udp_remove(g_upcb); } } while(1) { osDelay(100); } } //UDP服务器回调函数 void udp_receive_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { unsigned int data_len = 0; struct pbuf *q; if(p!=NULL) //接收到不为空的数据时 { memset(udp_demo_recvbuf,0,UDP_RX_BUFSIZE); //数据接收缓冲区清零 for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表 { //判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于 //的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据 if(q->len > (UDP_RX_BUFSIZE-data_len)) memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_RX_BUFSIZE-data_len));//拷贝数据 else memcpy(udp_demo_recvbuf+data_len,q->payload,q->len); data_len += q->len; if(data_len > UDP_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出 } upcb->remote_ip = *addr; //记录远程主机的IP地址 upcb->remote_port = port; //记录远程主机的端口号 //数据处理 //......... pbuf_free(p);//释放内存 } else { udp_disconnect(upcb); } } //UDP发送 外部调用 void L_udp_send(unsigned char *data, unsigned int length, unsigned int ipaddr, unsigned int port) { struct pbuf *ptr; ip_addr_t send_ipaddr; //远端ip地址 IP4_ADDR(&send_ipaddr, ipaddr&0xff,(ipaddr>>8)&0xff,(ipaddr>>16)&0xff,(ipaddr>>24)&0xff); ptr=pbuf_alloc(PBUF_TRANSPORT,length,PBUF_POOL); //申请内存 if(ptr) { ptr->payload=(void*)data; udp_sendto(g_upcb, ptr, &send_ipaddr, port); pbuf_free(ptr);//释放内存 } }

        组播代码如下:

 

#define UDP_MULTICASE_RECV_PORT 1178 // multicast port for recive #define UDP_MULTICASE_SEND_PORT 1180 // multicast port for send #define LWIP_DEMO_BUF 2048 struct udp_pcb* udp_server_multi_pcb; ip_addr_t ipgroup_rev, ipgroup_send; unsigned short lwip_demo_buf_len = 0; unsigned char lwip_demo_buf[LWIP_DEMO_BUF]; void udp_multi_rev(void *arg, struct udp_pcb *pcb, struct pbuf *p,const ip_addr_t *addr, u16_t port); void Multicast_Config() { err_t err; IP4_ADDR(&ipgroup_rev, 226,0,0,80);//用于接收组播的地址 IP4_ADDR(&ipgroup_send, 226,0,0,80);//用于发送组播的地址 #if LWIP_IGMP igmp_joingroup(IP_ADDR_ANY, &ipgroup_rev); // 只需要将接收地址放入igmp组,发送的不需要 #endif udp_server_multi_pcb = udp_new(); if ( udp_server_multi_pcb ) { /* Bind the upcb to the UDP_PORT port */ /* Using IP_ADDR_ANY allow the upcb to be used by any local interface */ err = udp_bind(udp_server_multi_pcb,IP_ADDR_ANY,UDP_MULTICASE_RECV_PORT); if(err == ERR_OK) { /* Set a receive callback for the upcb */ udp_recv(udp_server_multi_pcb,udp_multi_rev,NULL); } else { udp_remove(udp_server_multi_pcb); //PRINT("can not bind pcb"); } } else { // PRINT("can not create pcb"); } } void multicast_send_data(unsigned char * data,unsigned short len) { struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT,len, PBUF_RAM); memcpy(p->payload, data, len); udp_sendto(udp_server_multi_pcb, p,&ipgroup_send,UDP_MULTICASE_SEND_PORT); pbuf_free(p); } void l_User_Multicast_Task(void) { Multicast_Config(); while(1) { osDelay(100); } } // 组播接收,回调函数 void udp_multi_rev(void *arg, struct udp_pcb *upcb, struct pbuf *p,const ip_addr_t *addr, u16_t port) { unsigned int data_len = 0; struct pbuf *q; if(p!=NULL) //接收到不为空的数据时 { memset(udp_demo_recvbuf,0,UDP_RX_BUFSIZE); //数据接收缓冲区清零 for(q=p;q!=NULL;q=q->next) //遍历完整个pbuf链表 { //判断要拷贝到UDP_DEMO_RX_BUFSIZE中的数据是否大于UDP_DEMO_RX_BUFSIZE的剩余空间,如果大于 //的话就只拷贝UDP_DEMO_RX_BUFSIZE中剩余长度的数据,否则的话就拷贝所有的数据 if(q->len > (UDP_RX_BUFSIZE-data_len)) memcpy(udp_demo_recvbuf+data_len,q->payload,(UDP_RX_BUFSIZE-data_len));//拷贝数据 else memcpy(udp_demo_recvbuf+data_len,q->payload,q->len); data_len += q->len; if(data_len > UDP_RX_BUFSIZE) break; //超出TCP客户端接收数组,跳出 } upcb->remote_ip = *addr; //记录远程主机的IP地址 upcb->remote_port = port; //记录远程主机的端口号 // lwipdev.remoteip[0]=upcb->remote_ip.addr&0xff; //IADDR4 // lwipdev.remoteip[1]=(upcb->remote_ip.addr>>8)&0xff; //IADDR3 // lwipdev.remoteip[2]=(upcb->remote_ip.addr>>16)&0xff;//IADDR2 // lwipdev.remoteip[3]=(upcb->remote_ip.addr>>24)&0xff;//IADDR1 udp_demo_recvbuf; data_len; LED1_Control_NON(); pbuf_free(p);//释放内存 } else { udp_disconnect(upcb); } }

        UDP处理建议将数据发送到其他线程中,避免数据量大导致线程溢出问题。经过测试,组播和单播数据正常,长时间测试无卡死现象。

最新回复(0)