STM32F4 UDP組播通信:填一填ST官方HAL庫的坑

先說寫作本文的原因,由于開項目開發中需要用到UDP組播接收的功能,但是ST官方沒有提供合適的參考,使用STM32CubeMX生成的代碼也是不能直接使用的,而我在網上找了一大圈,也沒有一個能夠直接解決的方案,deepseek、ChatGPT給的方案也不能直接用,羨慕迷茫階段。然而隨著項目進度告急,我也只能硬著頭皮摸著石頭過河了,總結了大多數資料的觀點,加上deepseek的幫助,最終找到了關鍵點,因此希望寫一個記錄文檔,同時也給大家參考,若有高手也可以指點指點。

開發環境

單片機型號是STM32F407VGT6,以太網控制器是LAN7820,軟件版本如下

STM32CubeMX6.12.1
IAR 9.50.2
STM32Cube FW_F4 V1.28.1

使用STM32CubeMX生成代碼

配置JTAG

在這里插入圖片描述

配置以太網外設,選擇RMII,打開以太網全局中斷

在這里插入圖片描述

打開串口1方便調試,添加DMA

在這里插入圖片描述

啟用LWIP

這里的關鍵是勾選右上角的Show Advance Parameters,然后使能LWIP_MULTICAST_TX_OPTONS (Multicast Tx support)
在這里插入圖片描述

使能LWIP_IGMP (IGMP module)

在這里插入圖片描述

PHY選擇LAN8742,實際上使用的是LAN8720

在這里插入圖片描述

設置PHY的復位引腳,默認輸出為高

在這里插入圖片描述

最后配置時鐘

在這里插入圖片描述

生成代碼

關鍵代碼

生成的代碼是不能直接使用的,需要做一些關鍵修改,這里添加了組播初始化函數和回調函數

/* USER CODE BEGIN Header */
/********************************************************************************* @file           : main.c* @brief          : Main program body******************************************************************************* @attention** Copyright (c) 2025 STMicroelectronics.* All rights reserved.** This software is licensed under terms that can be found in the LICENSE file* in the root directory of this software component.* If no LICENSE file comes with this software, it is provided AS-IS.********************************************************************************/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "lwip.h"
#include "usart.h"
#include "gpio.h"/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <string.h>
#include "lwip/udp.h"
#include "lwip/igmp.h"
/* USER CODE END Includes *//* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD *//* USER CODE END PTD *//* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD *//* USER CODE END PD *//* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM *//* USER CODE END PM *//* Private variables ---------------------------------------------------------*//* USER CODE BEGIN PV *//* USER CODE END PV *//* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP *//* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
// 定義組播地址和端口
u16_t multicast_port=8554;
uint8_t multicast_ip[4]={226,0,0,80};void multicast_receive_callback(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) {    if (p != NULL) {static uint8_t buff[128];memcpy(buff, p->payload, p->len);HAL_UART_Transmit_DMA(&huart1, (uint8_t *)&buff, p->len);pbuf_free(p);}
}// 初始化MULTICAST接收協議控制塊
void multicast_receive_init(void) {struct udp_pcb *pcb;ip4_addr_t ip;IP4_ADDR(&ip, multicast_ip[0], multicast_ip[1], multicast_ip[2], multicast_ip[3]);igmp_joingroup(IP4_ADDR_ANY, &ip);pcb = udp_new();pcb->so_options |= SOF_REUSEADDR; // 允許地址重用
//    pcb->mcast_ttl   = 1;            // 組播TTL(默認1,限制在本地網絡)if (pcb != NULL) {udp_bind(pcb, IP4_ADDR_ANY, multicast_port);udp_recv(pcb, multicast_receive_callback, NULL);}
}
/* USER CODE END 0 *//*** @brief  The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_DMA_Init();MX_USART1_UART_Init();MX_LWIP_Init();/* USER CODE BEGIN 2 */multicast_receive_init();/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){MX_LWIP_Process();/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}/*** @brief System Clock Configuration* @retval None*/
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};/** Configure the main internal regulator output voltage*/__HAL_RCC_PWR_CLK_ENABLE();__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);/** Initializes the RCC Oscillators according to the specified parameters* in the RCC_OscInitTypeDef structure.*/RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;RCC_OscInitStruct.HSIState = RCC_HSI_ON;RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;RCC_OscInitStruct.PLL.PLLM = 8;RCC_OscInitStruct.PLL.PLLN = 168;RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;RCC_OscInitStruct.PLL.PLLQ = 4;if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK){Error_Handler();}/** Initializes the CPU, AHB and APB buses clocks*/RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK){Error_Handler();}
}/* USER CODE BEGIN 4 *//* USER CODE END 4 *//*** @brief  Period elapsed callback in non blocking mode* @note   This function is called  when TIM1 interrupt took place, inside* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment* a global variable "uwTick" used as application time base.* @param  htim : TIM handle* @retval None*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{/* USER CODE BEGIN Callback 0 *//* USER CODE END Callback 0 */if (htim->Instance == TIM1) {HAL_IncTick();}/* USER CODE BEGIN Callback 1 *//* USER CODE END Callback 1 */
}/*** @brief  This function is executed in case of error occurrence.* @retval None*/
void Error_Handler(void)
{/* USER CODE BEGIN Error_Handler_Debug *//* User can add his own implementation to report the HAL error return state */__disable_irq();while (1){}/* USER CODE END Error_Handler_Debug */
}#ifdef  USE_FULL_ASSERT
/*** @brief  Reports the name of the source file and the source line number*         where the assert_param error has occurred.* @param  file: pointer to the source file name* @param  line: assert_param error line source number* @retval None*/
void assert_failed(uint8_t *file, uint32_t line)
{/* USER CODE BEGIN 6 *//* User can add his own implementation to report the file name and line number,ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) *//* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

另外一個關鍵的地方是需要在.\LWIP\Target\ethernetif.c文件中的low_level_init函數添加 強制接收所有組播包 以及 確保啟用 IGMP 支持

/*******************************************************************************LL Driver Interface ( LwIP stack --> ETH)
*******************************************************************************/
/*** @brief In this function, the hardware should be initialized.* Called from ethernetif_init().** @param netif the already initialized lwip network interface structure*        for this ethernetif*/
static void low_level_init(struct netif *netif)
{HAL_StatusTypeDef hal_eth_init_status = HAL_OK;/* Start ETH HAL Init */uint8_t MACAddr[6] ;heth.Instance = ETH;MACAddr[0] = 0x00;MACAddr[1] = 0x80;MACAddr[2] = 0xE1;MACAddr[3] = 0x00;MACAddr[4] = 0x00;MACAddr[5] = 0x00;heth.Init.MACAddr = &MACAddr[0];heth.Init.MediaInterface = HAL_ETH_RMII_MODE;heth.Init.TxDesc = DMATxDscrTab;heth.Init.RxDesc = DMARxDscrTab;heth.Init.RxBuffLen = 1536;/* USER CODE BEGIN MACADDRESS *//* USER CODE END MACADDRESS */hal_eth_init_status = HAL_ETH_Init(&heth);memset(&TxConfig, 0 , sizeof(ETH_TxPacketConfig));TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;/* End ETH HAL Init *//* Initialize the RX POOL */LWIP_MEMPOOL_INIT(RX_POOL);#if LWIP_ARP || LWIP_ETHERNET/* set MAC hardware address length */netif->hwaddr_len = ETH_HWADDR_LEN;/* set MAC hardware address */netif->hwaddr[0] =  heth.Init.MACAddr[0];netif->hwaddr[1] =  heth.Init.MACAddr[1];netif->hwaddr[2] =  heth.Init.MACAddr[2];netif->hwaddr[3] =  heth.Init.MACAddr[3];netif->hwaddr[4] =  heth.Init.MACAddr[4];netif->hwaddr[5] =  heth.Init.MACAddr[5];/* maximum transfer unit */netif->mtu = ETH_MAX_PAYLOAD;/* Accept broadcast address and ARP traffic *//* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */#if LWIP_ARPnetif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;#elsenetif->flags |= NETIF_FLAG_BROADCAST;#endif /* LWIP_ARP *//* USER CODE BEGIN PHY_PRE_CONFIG *//* USER CODE END PHY_PRE_CONFIG *//* Set PHY IO functions */LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx);/* Initialize the LAN8742 ETH PHY */if(LAN8742_Init(&LAN8742) != LAN8742_STATUS_OK){netif_set_link_down(netif);netif_set_down(netif);return;}if (hal_eth_init_status == HAL_OK){/* Get link state */ethernet_link_check_state(netif);}else{Error_Handler();}
#endif /* LWIP_ARP || LWIP_ETHERNET *//* USER CODE BEGIN LOW_LEVEL_INIT */// --- 核心修復代碼:直接操作寄存器 ---ETH->MACFFR |= ETH_MACFFR_PAM;  // 強制接收所有組播包netif->flags |= NETIF_FLAG_IGMP;  // 確保啟用 IGMP 支持/* USER CODE END LOW_LEVEL_INIT */
}

上位機軟件

為了配合測試,這里用python寫了一個簡單的上位機測試軟件

import socket
import structMCAST_GROUP = '226.0.0.80'
MCAST_PORT = 8554
LOCAL_IP = '192.168.88.2'  # 替換為你的實際 IPv4 地址# 創建 UDP 套接字
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', MCAST_PORT))# 通過 IP 地址綁定到指定接口(Windows 兼容方案)
sock.setsockopt(socket.IPPROTO_IP,socket.IP_ADD_MEMBERSHIP,socket.inet_aton(MCAST_GROUP) + socket.inet_aton(LOCAL_IP)
)print(f"監聽組播 {MCAST_GROUP}:{MCAST_PORT}...")
while True:data, addr = sock.recvfrom(1024)print(f"來自 {addr} 的消息: {data.decode()}")
# sender.py
import socketMCAST_GROUP = '226.0.0.80'
MCAST_PORT = 8554
INTERFACE_IP = '192.168.88.2'  # <--- 替換為你的實際 IPv4 地址sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(INTERFACE_IP))message = "Multicast Test"
sock.sendto(message.encode(), (MCAST_GROUP, MCAST_PORT))
print("消息已發送")
sock.close()

實測效果

可以看到,使用腳本發送數據可以上位機軟件成功接收。同時單片機收到數據之后通過串口發送到了串口助手,實驗成功。
在這里插入圖片描述

參考代碼

https://github.com/dwgan/STM32-Multicast

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/71763.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/71763.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/71763.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

leetcode日記(85)驗證二叉搜索樹

不難&#xff0c;有兩種解法&#xff08;看答案才想到中序遍歷&#xff09;。 我用的是普通遞歸&#xff0c;和上一題差不多&#xff0c;規定min和max&#xff0c;每次遍歷縮小范圍: /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNod…

如何在rust中解析 windows 的 lnk文件(快捷方式)

一、從標題二開始看&#x1f601; 這些天在使用rust寫一個pc端應用程序&#xff0c;需要解析lnk文件獲取lnk的圖標以及原程序地址&#xff0c;之前并沒有過pc端應用程序開發的經驗&#xff0c; 所以在廣大的互聯網上游蕩了兩天。額&#x1f97a; 今天找到了這個庫 lnk_parse很…

啟動wsl里的Ubuntu24報錯:當前計算機配置不支持 WSL2,HCS_E_HYPERV_NOT_INSTALLED

問題&#xff1a;啟動wsl里的Ubuntu24報錯 報錯信息&#xff1a; 當前計算機配置不支持 WSL2。 請啟用“虛擬機平臺”可選組件&#xff0c;并確保在 BIOS 中啟用虛擬化。 通過運行以下命令啟用“虛擬機平臺”: wsl.exe --install --no-distribution 有關信息&#xff0c;請訪…

Python使用FastAPI結合Word2vec來向量化200維的語言向量數值

準備 pip install fastapi>0.68.0 pip install uvicorn[standard]>0.15.0 pip install gensim>4.0.0 pip install jieba>0.42.1 pip install numpy>1.21.0 pip install scikit-learn>1.0.0少了的就直接補充就好 代碼 from fastapi import FastAPI, HTTPExc…

ControlNet

文章目錄 摘要abstract1.ControlNet1.1 原文摘要1.2 模型架構1.3 架構細節1.4 訓練損失函數1.5 實驗1.6 結論 2.總結參考文獻 摘要 本周學習的ControlNet 是一種用于文本到圖像擴散模型&#xff08;如 Stable Diffusion&#xff09;的條件控制方法。它通過凍結預訓練的擴散模型…

Visual-RFT視覺強化微調:用「試錯學習」教會AI看圖說話

&#x1f4dc; 文獻卡 英文題目: Visual-RFT: Visual Reinforcement Fine-Tuning;作者: Ziyu Liu; Zeyi Sun; Yuhang Zang; Xiaoyi Dong; Yuhang Cao; Haodong Duan; Dahua Lin; Jiaqi WangDOI: 10.48550/arXiv.2503.01785摘要翻譯: 像OpenAI o1這樣的大型推理模型中的強化微調…

Hadoop管理頁看不到任務的問題

這個yarn分配任務了但是為空 在$HADOOP_HOME/conf/mapred-site.xml 原來的配置文件基礎之上添加&#xff1a; <property><name>mapreduce.framework.name</name><value>yarn</value></property> 重啟之后就好了

傅里葉變換:跨越時空的數學魔法

引言&#xff1a;從振動到信息——傅里葉的智慧 傅里葉變換&#xff08;Fourier Transform&#xff09;是數學與工程領域最具影響力的工具之一。它的核心思想是將復雜的信號分解為簡單的正弦波和余弦波的疊加&#xff0c;從而揭示隱藏在數據背后的頻率信息。自19世紀法國數學家…

DR和BDR的選舉規則

在 OSPF&#xff08;開放最短路徑優先&#xff09;協議中&#xff0c;DR&#xff08;Designated Router&#xff0c;指定路由器&#xff09; 和 BDR&#xff08;Backup Designated Router&#xff0c;備份指定路由器&#xff09; 的選舉是為了在廣播型網絡&#xff08;如以太網…

【linux網絡編程】套接字編程API詳細介紹

在C語言中&#xff0c;套接字&#xff08;Socket&#xff09;編程主要用于網絡通信&#xff0c;尤其是在基于TCP/IP協議的應用程序開發中。常用的套接字編程API主要基于Berkeley Sockets&#xff08;伯克利套接字&#xff09;接口&#xff0c;這些函數通常在<sys/socket.h&g…

Linux和gcc/g++常用命令總結

目錄 Linux命令總結 文件操作相關命令 ls cd pwd cp mv rm cat mkdir rmdir touch 文本處理操作命令 grep awk sed 進程管理操作相關命令 ps top htop kill pkill killall chmod chown 網絡操作相關命令 ping ifconfig netstat ss lsof curl …

VUE的第二天

1. 指令修飾符 1.1什么是指令修飾符&#xff1f; ? 所謂指令修飾符就是通過“.”指明一些指令后綴 不同的后綴封裝了不同的處理操作 —> 簡化代碼 1.2按鍵修飾符 keyup.enter —>當點擊enter鍵的時候才觸發 代碼演示&#xff1a; <div id"app"><…

WSL with NVIDIA Container Toolkit

一、wsl 下安裝 docker 會提示安裝 docekr 桌面版&#xff0c;所以直接安裝 docker 桌面版本即可 二、安裝 NVIDIA Container Toolkit NVIDIA Container Toolkit倉庫 https://github.com/NVIDIA/nvidia-container-toolkit?github.com/NVIDIA/nvidia-container-toolkit 安裝…

mysql下載

目錄 下載地址&#xff1a; 1.MSI安裝包下載 2.ZIP壓縮包下載 卸載MySQL&#xff1a; 下載地址&#xff1a; MySQL :: Download MySQL Community Server到mysql官網進行下載&#xff1a;MySQL :: Download MySQL Community Server &#xff08;下面二選一&#xff0c;選擇一…

基于Kubernetes部署MySQL主從集群

以下是一個基于Kubernetes部署MySQL主從集群的詳細YAML示例&#xff0c;包含StatefulSet、Service、ConfigMap和Secret等關鍵配置。MySQL主從集群需要至少1個主節點和多個從節點&#xff0c;這里使用 StatefulSet 初始化腳本 實現主從自動配置。 1. 創建 Namespace (可選) ap…

如何使用 GPT-4o 翻譯播客聲音

Voice Translation into Different Languages | OpenAI Cookbook 如何使用 GPT-4o 將播客翻譯并配音成您的母語 您是否曾想過將播客翻譯成您的母語&#xff1f;翻譯和配音音頻內容可以讓全球更多的觀眾獲取信息。而現在&#xff0c;借助 GPT-4o 的音頻輸入&#xff08;audio-i…

Lab17_ Blind SQL injection with out-of-band data exfiltration

文章目錄 前言&#xff1a;進入實驗室構造 payload 前言&#xff1a; 實驗室標題為&#xff1a; 帶外數據泄露的 SQL 盲注 簡介&#xff1a; 本實驗包含一個SQL盲目注入漏洞。應用程序使用跟蹤Cookie進行分析&#xff0c;并執行包含提交的Cookie值的SQL查詢。 SQL查詢是異…

深入解析 configService.addListener 使用中的注意事項

在使用 Nacos 的 configService.addListener 方法進行配置監聽時&#xff0c;為了確保程序的穩定性、可靠性以及高效性&#xff0c;有諸多注意事項需要我們關注。下面將對這些關鍵要點進行詳細闡述。 一、連接穩定性 1.1 網絡連接問題 Nacos 客戶端與服務端通過網絡進行通信&…

C/C++藍橋杯算法真題打卡(Day4)

一、P11041 [藍橋杯 2024 省 Java B] 報數游戲 - 洛谷 算法代碼&#xff1a; #include<bits/stdc.h> using namespace std;// 計算第 n 個滿足條件的數 long long findNthNumber(long long n) {long long low 1, high 1e18; // 二分查找范圍while (low < high) {lo…

【Python 數據結構 10.二叉樹】

目錄 一、二叉樹的基本概念 1.二叉樹的定義 2.二叉樹的特點 3.特殊的二叉樹 Ⅰ、斜樹 Ⅱ、滿二叉樹 Ⅲ、完全二叉樹 Ⅳ、完全二叉樹和滿二叉樹的區別 4.二叉樹的性質 5.二叉樹的順序存儲 Ⅰ、完全二叉樹 Ⅱ、非完全二叉樹 Ⅲ、稀疏二叉樹 6.二叉樹的鏈式存儲 7.二叉樹的遍歷概念…