STM32CubeIDE LWIP UDP 网络通讯

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

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

                            

        设置PHY为 user PHY:

                             

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

                              

         在初始化HAL_ETH_Init前,注意复位网络芯片,对于8720A,应拉低复位引脚再拉高。

         以上配置以实现网络功能,下载后网络连接即可正常,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处理建议将数据发送到其他线程中,避免数据量大导致线程溢出问题。经过测试,组播和单播数据正常,长时间测试无卡死现象。

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: STM32CubeIDE是ST公司推出的一款开发工具,用于开发STM32系列微控制器。 LWIP(Lightweight IP)是一个轻量级的开源TCP/IP协议栈,适用于嵌入式系统。 在STM32CubeIDE中使用LWIP库进行UDP通信,可以通过以下步骤实现: 1. 首先,需要创建一个新的STM32项目,并选择合适的型号和外设。在项目配置中,选择LWIP库,并启用UDP协议。 2. 在代码中,需要进行一些配置。首先,在"LwIP SYS"组件中,设置正确的IP地址和子网掩码。然后,在"LwIP UDP"组件中,配置正确的端口号,以及设置允许UDP广播和多播。 3. 接下来,需要创建UDP通信的客户端和服务器端。客户端需要创建一个socket,并设置目标IP地址和端口号。然后,可以使用lwip_sendto函数发送UDP数据包。 4. 服务器端需要创建一个socket,并绑定到本地IP地址和端口号。然后,使用lwip_recvfrom函数接收来自客户端的UDP数据包。 5. 在接收到UDP数据包后,可以对数据进行处理,并根据需要进行相应的操作。例如,可以将数据发送到其他设备,或者执行相应的程序逻辑。 通过以上步骤,就可以在STM32CubeIDE中使用LWIP库实现UDP通信。在实际应用中,根据具体需求,可以进一步优化代码和功能,以实现更复杂的通信功能。 ### 回答2: STM32CubeIDE是STMicroelectronics推出的一款面向STM32微控制器的集成开发环境。它集成了STM32CubeMX配置工具和TrueSTUDIO IDE,可以方便地进行STM32微控制器的开发和调试。 lwIP是一个轻量级的嵌入式TCP/IP协议栈,它为嵌入式系统提供了网络通信功能。在STM32CubeIDE中,可以通过lwIP协议栈实现UDP通信。 在使用STM32CubeIDE进行lwIP UDP通信时,首先需要通过STM32CubeMX配置工具进行初始化设置。在配置工具中,可以选择启用lwIP协议栈,并设置相关的网络参数,如IP地址、子网掩码、网关等。同时,还可以选择启用UDP协议,并设置相关的端口号。 配置完成后,生成代码并导入到STM32CubeIDE中。接下来,可以在工程中编写相关的代码实现UDP通信功能。首先,需要创建一个UDP Socket,并设置相关的参数,如IP地址和端口号。然后,可以使用lwIP提供的API函数发送和接收UDP数据包。 发送UDP数据包时,可以使用lwIP提供的函数lwip_sendto(),将要发送的数据和目标地址通过参数传入。接收UDP数据包时,可以使用lwIP提供的函数lwip_recvfrom(),指定接收数据的缓冲区和缓冲区大小,并将接收到的数据和发送方的地址存储在传入的参数中。 通过以上的步骤,就可以在STM32CubeIDE中实现lwIP UDP通信。用户可以根据实际需求进行相关的配置和代码编写,以实现网络通信功能。需要注意的是,在使用lwIP协议栈时,需要了解其相关的API函数和使用方法,以确保正确实现所需功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值