使用STM32F407结合CUBEMX、FreeRTOS和lwIP实现UDP通信记录

STM32F407+CUBEMX+FreeRTOS+lwIP之UDP记录

  • 基本信息
  • cubemx配置
  • GPIO
  • NVIC
  • RCC
  • SYS
  • ETH
  • FREERTOS
  • lwIP
  • UDP(SOCKET)
  • 效果
  • UDP广播(SOCKET)
  • 效果
  • UDP组播(SOCKET)
  • cubemx注意以下
  • ethernetif.c
  • 效果
  • 可参考正点原子和野火的手册
  • 基本信息

    正点原子F407探索者开发板
    cubemx v6.10.0
    STM32Cube FM_f4 v1.28.0
    8720A

    cubemx配置

    GPIO

    NVIC


    RCC

    SYS

    ETH



    FREERTOS

    这个按需配置

    lwIP

    这个按需配置

    8720A可以选下图中这个,其他为适配的可以生成代码后自己修改

    UDP(SOCKET)

    本地端口是本地对外开放的端口
    远程端口目标IP的端口

    一些定义

    #include <lwip/sockets.h>
    #include "lwip/opt.h"
    #include "lwip/sys.h"
    #include "lwip/api.h"
    #include "lwip/udp.h"
    #include "queue.h"
    
    osThreadId_t led_TaskHandle;
    const osThreadAttr_t led_Task_attributes = {
      .name = "led_Task",
      .stack_size = 128 * 4,
      .priority = (osPriority_t) (osPriorityNormal-10),
    };
    void Start_led_Task(void *argument);
    
    
    #ifdef lwip_socket_udp_base
    osThreadId_t socket_udp_TaskHandle;
    const osThreadAttr_t socket_udp_Task_attributes = {
      .name = "socket_udp_Task",
      .stack_size = 128 * 8,
      .priority = (osPriority_t) (osPriorityNormal-9),
    };
    void Start_socket_udp_Task(void *argument);
    #define LWIP_DEMO_PORT 8081
    #define LWIP_DEMO_RX_BUFSIZE         200    /* 最大接收数据长度 */
    //#define IP_ADDR   "192.168.123.92"		/* 单播 一对一*/
    socklen_t sock_fd;                     /* 定义一个Socket接口 */
    struct sockaddr_in local_info;         /* 定义Socket地址信息结构体 */
    /* 接收数据缓冲区 */
    uint8_t g_lwip_demo_recvbuf[LWIP_DEMO_RX_BUFSIZE];
    
    osThreadId_t lwip_recv_TaskHandle;
    const osThreadAttr_t lwip_recv_Task_attributes = {
      .name = "lwip_recv_Task",
      .stack_size = 128 * 4,
      .priority = (osPriority_t) (osPriorityNormal-9),
    };
    /* 显示消息队列的数量 */
    #define DISPLAYMSG_Q_NUM    20              /* 显示消息队列的数量 */
    QueueHandle_t g_display_queue;              /* 显示消息队列句柄 */
    
    

    创建led任务

    led_TaskHandle = osThreadNew(Start_led_Task, NULL, &led_Task_attributes);
    

    lwIP初始化后UDP、接收、队列

    void StartDefaultTask(void *argument)
    {
      /* init code for LWIP */
      MX_LWIP_Init();
      /* USER CODE BEGIN StartDefaultTask */
    	taskENTER_CRITICAL();           /* 进入临界区 */
    	socket_udp_TaskHandle = osThreadNew(Start_socket_udp_Task, NULL, &socket_udp_Task_attributes);
    	lwip_recv_TaskHandle = osThreadNew(lwip_recv_Task, NULL, &lwip_recv_Task_attributes);
    	g_display_queue = xQueueCreate(DISPLAYMSG_Q_NUM,200);/* 创建消息Message_Queue,队列项长度是200长度 */
    	taskEXIT_CRITICAL();            /* 退出临界区 */
      /* Infinite loop */
      for(;;)
      {
        osDelay(1);	//ticks
      }
      /* USER CODE END StartDefaultTask */
    }
    

    udp配置

    void Start_socket_udp_Task(void *argument){
    
    	/* 发送数据内容 */
    	char g_lwip_demo_sendbuf[] = "ALIENTEK DATA \r\n";
    	memset(&local_info, 0, sizeof(struct sockaddr_in)); /* 将服务器地址清空 */
        local_info.sin_len = sizeof(local_info);
        local_info.sin_family = AF_INET;                    /* IPv4地址 */
        local_info.sin_port = htons(LWIP_DEMO_PORT);        /* 设置端口号 */
        local_info.sin_addr.s_addr = htons(INADDR_ANY);     /* 设置本地IP地址 */
    	
    	
    	sock_fd = socket(AF_INET,SOCK_DGRAM,0);/* 建立一个新的socket连接 */
    	if (sock_fd < 0)
        {
            printf("socket failed!\n");
        }
    	int ret = bind(sock_fd,(struct sockaddr *)&local_info, sizeof(struct sockaddr_in));/* 建立绑定 */
    	if (ret < 0)
        {
            printf(" bind error!\n ");
        }
    	while(1){
    		local_info.sin_addr.s_addr = inet_addr(IP_ADDR);                /* 需要发送的远程IP地址 */
    		sendto(sock_fd,                                         /* scoket */
                      (char *)g_lwip_demo_sendbuf,                        /* 发送的数据 */
                      sizeof(g_lwip_demo_sendbuf), 0,                     /* 发送的数据大小 */
                      (struct sockaddr *)&local_info,                   /* 接收端地址信息 */ 
                      sizeof(local_info));                              /* 接收端地址信息大小 */
    		vTaskDelay(400);
    		LL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
    	}
    }
    

    将从队列收到的消息打印出来

    void Start_led_Task(void *argument){
    	uint8_t buffer[200];
    	while(1){
    		LL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
    		vTaskDelay(500);
    		if (xQueueReceive(g_display_queue,&buffer,portMAX_DELAY))
    		{
    			printf("%s\n",buffer);
    			memset(buffer,0,200);       /* 清除缓冲区 */
    		}
    	}
    }
    
    void lwip_recv_Task(void *argument){
    	BaseType_t lwip_err;
    	
    	struct sockaddr_in sender;/*存放发送方信息*/
        int sender_len = sizeof(sender);/*发送方信息长度*/
    	
    	while(1){
    		memset(g_lwip_demo_recvbuf, 0, sizeof(g_lwip_demo_recvbuf));
    //		recv(sock_fd, (void *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0);/*这个只有通信数据*/
    		recvfrom(sock_fd, (void *)g_lwip_demo_recvbuf, sizeof(g_lwip_demo_recvbuf), 0,(struct sockaddr*)&sender,(socklen_t *)&sender_len);/*这个只有通信数据、还有发送方信息*/
    		lwip_err = xQueueSend(g_display_queue,&g_lwip_demo_recvbuf,0);
    		if (lwip_err == errQUEUE_FULL)
    		{
    			printf("队列Key_Queue已满,数据发送失败!\r\n");
    		}else{
    			/*IP*/
    			printf("%s %d \n", inet_ntoa(sender.sin_addr), ntohs(sender.sin_port));/*打印发送放IP和端口*/
    		}
    		vTaskDelay(10);
    	}
    }
    

    效果

    UDP广播(SOCKET)

    #define IP_ADDR   "192.168.123.255"		/* 广播所有设备255.255.255.255 通常只在本地网络中使用*/
    

    效果


    UDP组播(SOCKET)

    添加一些定义

    /* 组播D类IP:224.0.0.0至239.255.255.255 */
    /* 多播/组播 IP 地址 */
    #define GROUP_IP "224.0.1.0"
    /* 多播信息 */
    struct ip_mreq_t
    {
        struct ip_mreq mreq;            /* 多播信息控制块 */
        socklen_t mreq_len;             /* 多播信息长度 */
    };
    struct ip_mreq_t mreq_info;
    

    bind后加这个

    /*组播 LWIP_IGMP为1*/
    	mreq_info.mreq.imr_multiaddr.s_addr = inet_addr(GROUP_IP);     /* 多播组 IP 地址设置 */
        mreq_info.mreq.imr_interface.s_addr = htonl(INADDR_ANY);       /* 待加入多播组的 IP 地址 */
        mreq_info.mreq_len = sizeof(struct ip_mreq);
    	/* 添加多播组成员(该语句之前,socket 只与 某单播IP地址相关联 执行该语句后 将与多播地址相关联) */
        ret = setsockopt(sock_fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq_info.mreq,mreq_info.mreq_len);
        if (ret < 0)
        {
            printf("setsockopt failed !");
        }
        else
        {
            printf("setsockopt success\n");
        }
    

    切到组播IP,发送数据

    local_info.sin_addr.s_addr = inet_addr(GROUP_IP); /* 组播ip */
    sendto(sock_fd,                                         /* scoket */
                      (char *)g_lwip_demo_sendbuf,                        /* 发送的数据 */
                      sizeof(g_lwip_demo_sendbuf), 0,                     /* 发送的数据大小 */
                      (struct sockaddr *)&local_info,                   /* 接收端地址信息 */ 
                      sizeof(local_info));                              /* 接收端地址信息大小 */
    		vTaskDelay(400);
    		LL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
    
    

    cubemx注意以下



    ethernetif.c

    netif初始化前添加

    netif->flags |=NETIF_FLAG_IGMP;/*组播*/
    

    PHY初始化前添加

    HAL_ETH_GetMACFilterConfig(&heth,&g_eth_macfilterconfig_handler);
    	g_eth_macfilterconfig_handler.ReceiveAllMode = ENABLE;
    	g_eth_macfilterconfig_handler.PassAllMulticast =ENABLE;
    	HAL_ETH_SetMACFilterConfig(&heth,&g_eth_macfilterconfig_handler);
    

    这个定义到外面

    	ETH_MACFilterConfigTypeDef g_eth_macfilterconfig_handler;
    

    效果

    可参考正点原子和野火的手册

    作者:亦哟啊哈

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用STM32F407结合CUBEMX、FreeRTOS和lwIP实现UDP通信记录

    发表回复