STM32CubeIDE LWIP UDP 网络通讯

本文介绍如何在STM32CubeIDE环境下配置并实现基于LWIP的UDP单播和组播通信。通过详细步骤指导如何设置网络外设、LWIP选项,并提供关键代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

### STM32 CubeIDE 中实现 IAP 的教程 #### 环境搭建 为了在STM32 CubeIDE中顺利实现IAP,在开始前需确保开发环境已正确配置完毕,这包括安装并熟悉使用STM32CubeIDE集成开发环境及其配套支持库[^3]。 #### 配置项目 依据具体的硬件平台和应用需求来调整源码内的各项设定。对于采用网口或串口方式进行固件更新的情况,应特别注意端口号、波特率等通信参数的匹配性设置;而针对不同型号的MCU,则可能还需适当修改部分寄存器初始化代码以适应目标芯片特性。 #### 测试升级流程 - **串口升级**:当选择通过UART接口执行在线编程操作时,务必按照既定时间窗口及时下发待烧写的二进制映像文件(bin),一旦超时未接收到有效数据流则会触发系统回滚机制恢复至先前版本。 - **UDP升级**:若是经由局域网络传播途径来进行空中下载(OTA)作业的话,那么先要向服务器提交认证密钥请求访问权限之后才能正式开启文件传送环节,完成后装置将会自行重启并将控制权交给最新版软件镜像运行。 ```c // 示例代码片段展示了一个简单的IAP入口函数定义方式 void JumpToUserProgram(uint32_t *Src, uint32_t Dest){ typedef void (*pFunction)(void); pFunction jump_to_app; SystemInit(); // 初始化系统 /* 用户应用程序跳转 */ SCB->VTOR = FLASH_USER_START_ADDRESS; // 设置向量表偏移地址为用户区起始位置 jump_to_app = (pFunction)Dest; // 获取用户程序入口指针 __set_MSP(*(__IO uint32_t*) Src); // 加载栈顶值到主堆栈指针(MSP) jump_to_app(); // 执行跳转指令启动用户APP } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值