ETHEthernet以太网是STM32H743内置的高速网络外设兼容IEEE 802.3标准支持10/100Mbps自适应速率、全双工/半双工通信集成DMA控制器和硬件校验单元可实现嵌入式设备的网络通信如TCP/IP、HTTP、MQTT。ETH是物联网、工业控制、远程监控等场景的核心接口。一、STM32H743 ETH 核心硬件特性STM32H743的ETH外设基于RMIIReduced Media Independent Interface简化媒体独立接口设计核心特性如下特性说明通信速率10Mbps/100Mbps自适应全双工同时收发/半双工分时收发接口类型RMII仅需7根线简化硬件支持外接PHY芯片如LAN8720、DP83848传输机制硬件DMA2个发送DMA通道、2个接收DMA通道无需CPU参与数据搬运校验能力硬件CRC校验、帧长度检查、错误检测降低CPU开销缓冲区管理支持描述符链表Descriptor List灵活管理收发数据缓冲区协议兼容原生支持以太网帧收发结合LWIP协议栈可实现TCP、UDP、HTTP、MQTT等高层协议典型引脚RMIIREF_CLKPA1、MDIOPA2、MDCPC1、RXD0PC4、RXD1PC5、TX_ENPB11、TXD0PB12、TXD1PB13典型应用网络数据传输、远程控制、物联网MQTT通信、HTTP服务器、TCP/IP通信ETH 核心信号RMII模式RMII是ETH外设与PHY芯片如LAN8720的通信接口相比传统MII接口减少了引脚数量信号名称功能STM32H743引脚PHY芯片LAN8720引脚备注REF_CLK参考时钟50MHzPA1XTAL1/CLKIN可由PHY提供给STM32或STM32提供给PHYMDIO管理数据输入/输出PA2MDIO配置PHY芯片如速率、双工模式MDC管理时钟PC1MDCMDIO的时钟≤2.5MHzRXD0/RXD1接收数据0/1PC4/PC5RXD0/RXD1从PHY接收以太网数据TX_EN发送使能PB11TX_EN指示STM32正在发送数据TXD0/TXD1发送数据0/1PB12/PB13TXD0/TXD1向PHY发送以太网数据nINT/Link中断/链路状态可选如PA0INT/Link检测网络连接状态、链路速率关键PHY芯片如LAN8720需外接25MHz晶振生成50MHz REF_CLK或由STM32的MCO引脚提供50MHz时钟以太网接口需接网络隔离变压器如HR911105A并在PHY芯片与变压器间接100Ω终端电阻STM32H743的ETH时钟需配置为精准的50MHzRMII参考时钟。二、ETH 通信原理以太网通信的核心是“以帧为单位传输数据通过MAC地址寻址结合TCP/IP协议实现跨网络通信”STM32 ETH外设的工作流程如下1. 核心概念PHY芯片物理层芯片负责将STM32的RMII信号转换为以太网差分信号RJ45接口并实现10/100Mbps自适应、链路检测MAC层STM32 ETH外设实现媒体访问控制层负责帧的封装/解封装、MAC地址过滤、DMA传输以太网帧最小传输单元包含目标MAC、源MAC、帧类型、数据、CRC校验硬件自动添加/校验LWIP协议栈轻量级TCP/IP协议栈STM32CubeMX可直接集成将以太网帧封装为TCP/UDP/IP数据包实现高层协议通信DMA描述符ETH外设通过描述符链表管理收发缓冲区每个描述符包含缓冲区地址、长度、状态如是否完成。2. 通信流程结合LWIP初始化配置ETH引脚、时钟、RMII接口参数初始化PHY芯片设置速率、双工模式初始化LWIP协议栈配置IP地址、子网掩码、网关数据发送应用层如TCP客户端调用LWIP API发送数据LWIP将数据封装为IP/TCP/UDP包再封装为以太网帧ETH DMA将帧数据写入发送缓冲区硬件自动发送到PHY芯片PHY芯片将RMII信号转为差分信号通过RJ45发送到网络数据接收PHY芯片接收以太网差分信号转为RMII信号给STM32 ETHETH DMA将接收的帧数据写入接收缓冲区硬件完成CRC校验LWIP协议栈解析帧数据提取IP/TCP/UDP包传递给应用层。三、STM32H743 ETH 配置步骤LWIPTCP客户端/服务器CubeMX配置步骤如下步骤1硬件准备核心板STM32H743引出RMII引脚PA1/PA2/PC1/PC4/PC5/PB11/PB12/PB13PHY模块LAN8720HR911105A网络模块RMII接口带RJ45接线LAN8720为例STM32引脚LAN8720引脚功能PA1CLKIN50MHz参考时钟PA2MDIO管理数据PC1MDC管理时钟PC4RXD0接收数据0PC5RXD1接收数据1PB11TX_EN发送使能PB12TXD0发送数据0PB13TXD1发送数据13.3VVCC供电GNDGND共地步骤2CubeMX 配置ETH配置MPU配置LWIP配置步骤3LWIP核心APILWIP提供了简洁的API实现TCP/UDP通信核心函数如下函数功能tcp_new()创建TCP控制块用于客户端/服务器tcp_bind()绑定TCP端口服务器端tcp_listen()监听TCP端口服务器端tcp_accept()接受TCP连接服务器端回调tcp_connect()发起TCP连接客户端tcp_write()发送TCP数据tcp_recv()注册TCP接收回调函数tcp_sent()注册TCP发送完成回调函数udp_new()创建UDP控制块udp_bind()绑定UDP端口udp_sendto()发送UDP数据到指定IP/端口udp_recv()注册UDP接收回调函数sys_thread_new()创建LWIP线程处理网络事件四、实战代码示例示例1LWIP TCP服务器接收PC数据并回显STM32作为TCP服务器监听8080端口接收PC端网络调试助手的数据并回显1. 核心代码main.c#include stm32h7xx_hal.h #include lwip/tcp.h #include lwip/inet.h #include stdio.h #include string.h /* ETH句柄CubeMX自动生成 */ ETH_HandleTypeDef heth; /* USART1句柄串口输出115200 8N1 */ UART_HandleTypeDef huart1; /* TCP服务器参数 */ #define TCP_SERVER_PORT 8080 struct tcp_pcb *tcp_server_pcb; // TCP控制块 /* 错误处理函数 */ void Error_Handler(void) { while(1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); // LED快闪表示错误 HAL_Delay(200); } } /** * brief 重定向printf到串口 */ int fputc(int ch, FILE *f) { HAL_UART_Transmit(huart1, (uint8_t*)ch, 1, 10); return ch; } /** * brief TCP接收数据回调函数 */ err_t tcp_server_recv_callback(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) { if(p ! NULL) { // 1. 点亮LED表示接收到数据 HAL_GPIO_SetPin(GPIOA, GPIO_PIN_5); // 2. 回显数据到客户端PC tcp_write(tpcb, p-payload, p-len, TCP_WRITE_FLAG_COPY); tcp_output(tpcb); // 3. 打印接收的数据串口输出 printf(TCP Server Received: ); for(uint16_t i0; ip-len; i) { printf(%02X , ((uint8_t*)p-payload)[i]); } printf(\r\n); // 4. 释放pbuf缓冲区 pbuf_free(p); // 5. 延时后熄灭LED HAL_Delay(500); HAL_GPIO_ResetPin(GPIOA, GPIO_PIN_5); } else if(err ERR_OK) { // 客户端关闭连接 tcp_close(tpcb); tcp_server_pcb NULL; printf(TCP Client Disconnected!\r\n); } return ERR_OK; } /** * brief TCP连接接受回调函数 */ err_t tcp_server_accept_callback(void *arg, struct tcp_pcb *newpcb, err_t err) { if(err ERR_OK) { // 注册接收回调函数 tcp_recv(newpcb, tcp_server_recv_callback); tcp_server_pcb newpcb; printf(TCP Client Connected! IP: %s:%d\r\n, inet_ntoa(newpcb-remote_ip), ntohs(newpcb-remote_port)); } return ERR_OK; } /** * brief 初始化TCP服务器 */ void tcp_server_init(void) { // 1. 创建TCP控制块 tcp_server_pcb tcp_new(); if(tcp_server_pcb NULL) { printf(TCP Server Create Failed!\r\n); Error_Handler(); } // 2. 绑定端口 ip_addr_t ip_addr; IP4_ADDR(ip_addr, 192, 168, 1, 100); // 本地IP err_t err tcp_bind(tcp_server_pcb, ip_addr, TCP_SERVER_PORT); if(err ! ERR_OK) { printf(TCP Server Bind Failed! Err: %d\r\n, err); Error_Handler(); } // 3. 监听端口 tcp_server_pcb tcp_listen(tcp_server_pcb); if(tcp_server_pcb NULL) { printf(TCP Server Listen Failed!\r\n); Error_Handler(); } // 4. 注册连接接受回调 tcp_accept(tcp_server_pcb, tcp_server_accept_callback); printf(TCP Server Initialized! Listen on %s:%d\r\n, inet_ntoa(ip_addr), TCP_SERVER_PORT); } int main(void) { /* 初始化HAL库、系统时钟、GPIO、ETH、USART1 */ HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); // PA5为LED推挽输出 MX_ETH_Init(); // ETH初始化 MX_LWIP_Init(); // LWIP协议栈初始化 MX_USART1_UART_Init(); // 串口初始化 // 初始化LED为熄灭状态 HAL_GPIO_ResetPin(GPIOA, GPIO_PIN_5); printf(STM32H743 ETH TCP Server Test\r\n); printf(IP Address: 192.168.1.100\r\n); // 初始化TCP服务器 tcp_server_init(); while (1) { // LWIP主循环必须调用处理网络事件 MX_LWIP_Process(); HAL_Delay(10); } }2. 测试步骤编译下载代码到STM32H743确保STM32与PC连接到同一路由器或直连PC端设置IP为192.168.1.xxx与STM32同网段如192.168.1.101打开网络调试助手如NetAssist选择TCP客户端输入STM32的IP192.168.1.100和端口8080点击连接PC发送数据STM32接收后回显数据并点亮LED串口打印接收的数据。示例2LWIP TCP客户端主动连接PC服务器STM32作为TCP客户端主动连接PC端的TCP服务器定时发送数据/* TCP客户端参数 */ #define TCP_SERVER_IP 192.168.1.101 // PC的IP #define TCP_SERVER_PORT 8080 struct tcp_pcb *tcp_client_pcb; /** * brief TCP连接成功回调函数 */ err_t tcp_client_connected_callback(void *arg, struct tcp_pcb *tpcb, err_t err) { if(err ERR_OK) { printf(TCP Client Connected to %s:%d!\r\n, TCP_SERVER_IP, TCP_SERVER_PORT); // 定时发送数据每隔1秒 while(1) { char send_buf[32]; sprintf(send_buf, Hello TCP! Time: %lu, HAL_GetTick()); tcp_write(tpcb, send_buf, strlen(send_buf), TCP_WRITE_FLAG_COPY); tcp_output(tpcb); printf(Sent: %s\r\n, send_buf); HAL_Delay(1000); } } else { printf(TCP Client Connect Failed! Err: %d\r\n, err); Error_Handler(); } return ERR_OK; } /** * brief 初始化TCP客户端 */ void tcp_client_init(void) { // 1. 创建TCP控制块 tcp_client_pcb tcp_new(); if(tcp_client_pcb NULL) { printf(TCP Client Create Failed!\r\n); Error_Handler(); } // 2. 配置服务器IP和端口 ip_addr_t server_ip; IP4_ADDR(server_ip, 192, 168, 1, 101); // PC的IP // 3. 发起连接 err_t err tcp_connect(tcp_client_pcb, server_ip, TCP_SERVER_PORT, tcp_client_connected_callback); if(err ! ERR_OK) { printf(TCP Client Connect Request Failed! Err: %d\r\n, err); Error_Handler(); } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ETH_Init(); MX_LWIP_Init(); MX_USART1_UART_Init(); printf(STM32H743 ETH TCP Client Test\r\n); printf(Connect to %s:%d...\r\n, TCP_SERVER_IP, TCP_SERVER_PORT); // 初始化TCP客户端 tcp_client_init(); while (1) { MX_LWIP_Process(); HAL_Delay(10); } }示例3LWIP UDP通信无连接简单高效UDP无需建立连接适合小数据高频传输如传感器数据/* UDP参数 */ #define UDP_LOCAL_PORT 8080 #define UDP_REMOTE_IP 192.168.1.101 #define UDP_REMOTE_PORT 8080 struct udp_pcb *udp_pcb; /** * brief UDP接收回调函数 */ void udp_recv_callback(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *addr, u16_t port) { if(p ! NULL) { printf(UDP Received from %s:%d: , inet_ntoa(*addr), port); for(uint16_t i0; ip-len; i) { printf(%02X , ((uint8_t*)p-payload)[i]); } printf(\r\n); // 回显数据 udp_sendto(upcb, p, addr, port); pbuf_free(p); } } /** * brief 初始化UDP */ void udp_init(void) { // 1. 创建UDP控制块 udp_pcb udp_new(); if(udp_pcb NULL) { printf(UDP Create Failed!\r\n); Error_Handler(); } // 2. 绑定本地端口 ip_addr_t local_ip; IP4_ADDR(local_ip, 192, 168, 1, 100); udp_bind(udp_pcb, local_ip, UDP_LOCAL_PORT); // 3. 注册接收回调 udp_recv(udp_pcb, udp_recv_callback, NULL); printf(UDP Initialized! Local: %s:%d\r\n, inet_ntoa(local_ip), UDP_LOCAL_PORT); // 4. 定时发送数据 while(1) { char send_buf[32]; sprintf(send_buf, Hello UDP! Time: %lu, HAL_GetTick()); ip_addr_t remote_ip; IP4_ADDR(remote_ip, 192, 168, 1, 101); struct pbuf *p pbuf_alloc(PBUF_TRANSPORT, strlen(send_buf), PBUF_RAM); memcpy(p-payload, send_buf, strlen(send_buf)); udp_sendto(udp_pcb, p, remote_ip, UDP_REMOTE_PORT); pbuf_free(p); printf(UDP Sent: %s\r\n, send_buf); HAL_Delay(1000); } } int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_ETH_Init(); MX_LWIP_Init(); MX_USART1_UART_Init(); printf(STM32H743 ETH UDP Test\r\n); udp_init(); while (1) { MX_LWIP_Process(); HAL_Delay(10); } }五、常见问题与避坑指南1. ETH初始化失败HAL_ETH_Init返回错误原因1时钟配置错误ETH参考时钟RMII不是50MHz或ETH_MAC时钟不是100MHz→ 解决CubeMX中“Clock Configuration”确保PLL2Q50MHzRMIIETH_MAC时钟100MHz原因2PHY地址错误CubeMX中PHY Address设为1LAN8720默认0DP83848默认1导致无法通信→ 解决核对PHY芯片手册设置正确的PHY地址原因3RMII引脚接线错误REF_CLK/RXD0/RXD1/TX_EN/TXD0/TXD1接反尤其是REF_CLKPA1→ 解决逐根核对引脚确保PHY的CLKIN接STM32的PA1或PHY提供CLK给STM32原因4PHY芯片未供电LAN8720的3.3V供电未接或电压不足→ 解决确保PHY芯片供电稳定3.3V/100mA以上。2. 能ping通但TCP/UDP无法通信原因1LWIP主循环未调用主循环中未执行MX_LWIP_Process()LWIP无法处理网络事件→ 解决主循环中高频调用间隔≤10ms原因2端口未开放/被占用PC端防火墙阻止了目标端口如8080或STM32端口绑定失败→ 解决关闭PC防火墙更换未被占用的端口如8081原因3IP地址不在同一网段STM32 IP192.168.1.100PC IP192.168.2.101网段不同→ 解决将PC和STM32设为同一网段如192.168.1.xxx原因4TCP回调函数未注册服务器未注册tcp_accept()/tcp_recv()客户端未注册tcp_connect()→ 解决确保所有回调函数正确注册。3. ping不通STM32网络不可达原因1网线未接/链路未通RJ45接口的Link灯未亮PHY芯片未检测到链路→ 解决检查网线是否插紧更换网线/路由器端口原因2PHY芯片初始化失败自动协商失败未正确设置10/100Mbps、全双工模式→ 解决手动配置PHY速率而非自动协商// 在MX_ETH_Init后添加 ETH_MACConfigTypeDef mac_config; HAL_ETH_GetMACConfig(heth, mac_config); mac_config.Speed ETH_SPEED_100M; mac_config.DuplexMode ETH_MODE_FULLDUPLEX; HAL_ETH_SetMACConfig(heth, mac_config);原因3静态IP配置错误网关/子网掩码设置错误如网关192.168.1.0应为192.168.1.1→ 解决确保IP、子网掩码、网关与路由器一致。4. 数据发送/接收乱码/丢包原因1缓冲区未对齐ETH DMA缓冲区未按32位对齐导致数据传输错误→ 解决用__attribute__((aligned(4)))修饰缓冲区原因2pbuf未释放接收数据后未调用pbuf_free(p)导致缓冲区耗尽→ 解决每次接收完成后释放pbuf原因3网络速率不匹配STM32设为100Mbps路由器设为10Mbps协商失败→ 解决开启自动协商或两端统一速率原因4LWIP参数配置过小发送/接收缓冲区大小不足导致大数据丢包→ 解决CubeMX中LWIP配置增大缓冲区如TCP_MSS1460。5. ETH DMA中断不触发原因1NVIC中断未开启CubeMX中未勾选“ETH_RX_DMA_IRQn”/“ETH_TX_DMA_IRQn”→ 解决NVIC Settings中启用ETH DMA中断优先级设为High原因2DMA描述符配置错误收发描述符链表未正确初始化导致DMA无法识别缓冲区→ 解决使用CubeMX自动生成的描述符初始化代码不手动修改原因3PHY芯片未使能中断LAN8720的INT引脚未接STM32无法触发链路状态中断→ 解决接PHY的INT引脚到STM32或轮询链路状态。六、调试顺序1. 入门步骤第一步完成ETH初始化ping通测试验证硬件和IP配置第二步实现TCP服务器/客户端完成双向数据传输第三步实现UDP通信对比TCP无连接的差异第四步拓展到HTTP服务器网页控制LED、MQTT物联网通信第五步优化性能如DMA缓存、大缓冲区解决丢包/乱码问题。2. 调试技巧Wireshark抓包在PC端用Wireshark抓取以太网数据包分析帧结构、IP/TCP/UDP包定位通信错误串口日志调试在ETH初始化、LWIP启动、连接建立/断开、数据收发等节点打印日志PHY状态查询读取PHY芯片的状态寄存器如LAN8720的0x01寄存器查看链路状态、速率uint16_t phy_status ETH_ReadPHYRegister(heth, 0x01); printf(PHY Status: 0x%X\r\n, phy_status);分步测试先测试ping通再测试TCP先测试小数据1字节再测试大数据1KB。3. 重点注意事项时钟精准性RMII参考时钟必须为50MHz误差≤0.1%否则PHY无法同步硬件隔离以太网接口必须接隔离变压器避免静电/浪涌损坏PHY/STM32LWIP主循环MX_LWIP_Process()必须高频调用否则网络事件无法处理资源释放pbuf、TCP/UDP控制块使用后必须释放避免内存泄漏兼容性不同路由器/PHY芯片的协商机制不同优先用自动协商。4. 实际应用练习远程控制通过TCP/IP控制STM32的LED、电机PC端发送指令如“LED_ON”数据采集STM32采集传感器数据温湿度、ADC通过MQTT上传到阿里云/腾讯云HTTP服务器STM32搭建简易HTTP服务器PC浏览器访问IP地址显示传感器数据网络升级通过TCP接收固件bin文件写入Flash实现远程固件升级FOTA。