STM32H723 FreeRTOS LAN8742
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- 一、硬件
- 二、CubeMX配置步骤详解
- 1. 创建工程:新建一个项目,选择STM32H723ZGT6芯片
- 2. RCC和时钟树配置
- 3. SYS
- 4. 使能ETH外设
- 5. 配置CORTEM_M7&MPU(内存保护单元)
- 6. GPIO配置-PHY的复位引脚配置
- 7. LWIP配置
- 8. FreeRTOS配置
- 9. 生成工程
- 三、代码部分
- 1. 根据编译器,设置 memp_memory_RX_POOL_base
- 2.更改ARM Compiler(armclang / armcc)的分散加载文件的内存布局
- 3.使能D2SRAM2时钟
- 4.ping 网络
- 四、TCP连接和关闭
前言
本项目基于 FreeRTOS,在 STM32H723 平台上实现了以太网通信功能。为此,我从 CubeMX 工程配置入手,完整梳理了从代码生成、网络连通性测试(Ping)到 TCP 连接建立与断开的全流程。本文既是对个人开发过程的复盘总结,也希望能为正在调试 STM32 以太网功能的同学提供一份实操参考。文中如有疏漏,恳请各位前辈指正,欢迎交流探讨~
一、硬件
MCU芯片:STM32H723ZGT6
以太网芯片:LAN8742A-CZ-TR
RJ45:HR915310A
原理图如下:
以太网PHY芯片接线方式如上图所示。
二、CubeMX配置步骤详解
1. 创建工程:新建一个项目,选择STM32H723ZGT6芯片
2. RCC和时钟树配置
启用外部高速时钟 Crystal/Ceramic Resonator,FreeRTOS不适合内置晶振,开启内置LDO,设置0档,支持芯片内核跑到500MHz以上。
- 配置时钟树:选择PLLCLK,输入频率按实际来,直接选择最大时钟频率550MHz。
3. SYS
使用FreeRTOS系统,原本的systick要被FreeRTOS占用,用于操作系统时钟节拍和上下文切换等,所以SYS选择一个没用到的计时器TIM1
4. 使能ETH外设
在Connectivity选项卡中激活ETH,模式选择RMII。
4.1 参数设置
- MAC地址随意;
- STM32H7 系列采用多域总线架构,ETH 外设挂载在 D2 域,其内部的 DMA 控制器主要用于处理收发描述符和数据缓冲区:
ETH DMA 可访问范围:ETH DMA 能够稳定访问的是 D2 域的 SRAM1 / SRAM2 / SRAM3(0x30000000 起)。 - STM32H723ZGTx的SRAM1从0x30000000开始。
- STM32H723ZGT6的以太网ETH DMA 要求 32-byte 对齐,每个描述符占用32bytes,RxDescriptor和TX Descriptor都是4个描述符,共占用32*4=128bytes 设置ETH的RxDesc:0x3000 0000;对应的TxDesc:0x3000 0080
4.2 ETH GPIO设置
- GPIO的速度选择 Very high
4.3 开启ETH全局中断
5. 配置CORTEM_M7&MPU(内存保护单元)
- 在System Core的 CORTEX_M7中编辑内存保护单元:
配置这里,需要说明一下,因为本人在配置时,对这里云里雾里的,嫌烦直接抄配置哈。
在 STM32H723 中,以太网(ETH)外设的 DMA 只能访问 D2 域的 SRAM。为了避免数据错乱(如 HardFault 或丢包),通常需要将这块内存划分为两个 MPU 区域。
Region 0:以太网 DMA 描述符区(Device 类型):4.0配置的ETH描述符空间
作用:专门存放以太网的发送(Tx)和接收(Rx)描述符表。ETH DMA 控制器会频繁读写这里,必须配置为 Device 或 Strongly Ordered 属性(即禁用 Cache,开启 Write Buffer),以保证外设与 CPU 之间的数据强一致性。
Base Address: 0x30000000
Size:1KByte(通常 2 个描述符表占用不到 256 字节,留一点余量即可)到0x3000 0400,可以存放ETH rx和tx的描述符。Region 1:LWIP 堆栈/数据缓冲区(Normal 类型):保护 LWIP 的内存池
作用:存放 LWIP 协议栈的动态内存池(pbuf)、收发数据缓冲区等。这里可以配置为 Normal 属性,但为了配合 LWIP 的 Cache 一致性处理,通常也建议禁用 Cache(Non-cacheable)。
Base Address:0x3000 4000(紧接在描述符区之后,避免重叠)
Size:16KB(STM32H723ZGT6 的 D2 SRAM 总共只有 32KB,前 1KB 给描述符,剩下的约31KB 可划给 LWIP。配置 16KB 是比较保守且安全的做法,具体取决于 LWIP 内存池大小)。
配置参考:
6. GPIO配置-PHY的复位引脚配置
常态设置为高电平
7. LWIP配置
7.1 配置IP
STM32H723 / STM32H733 的 D2 RAM 是0x3000000开始,H723内存与其他H7系列不同,H723的RAM_D2 整体连续地址范围为:
0x3000 0000 ~ 0x3000 7FFF,总计 32 KB。
- LWIP 堆栈/数据缓冲区必须从0x3000 4000开始
- LWIP_NETIF_LINK_CALLBACK(检测网线插拔的)要使能就行,其他配置默认。
- 选择PHY芯片
- 这里直接选择LAN8742,如果用的不是8742,生成代码后要在源码中修改PHY地址。
- 使能SNTP,其余的均保持默认即可
8. FreeRTOS配置
接口选择CMSIS_V1,使能FPU
总堆栈改大一点,防止任务运行不起来,一直进入HardFault
修改默认任务的堆栈为1024
9. 生成工程
9.1 选择MDK-ARM v5.32版本
最小堆栈改成0x800开始。9.2 代码生成勾选参考,完成后点击GENERATE CODE。
三、代码部分
1. 根据编译器,设置 memp_memory_RX_POOL_base
在Application/Users/LWIP/Target/ethernetif.c源码中,第102行添加如下代码
可以拷贝我的
#ifdefined(__ICCARM__)/*!< IAR Compiler 也可以用注释掉的代码*//* #pragma location=0x30000000 ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */#pragmalocation=0x30000080ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];/* Ethernet Tx DMA Descriptors *///#pragma location=0x30000000//ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];//#pragma location=0x30000040//ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];//#pragma location=0x30000080//uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE];#elifdefined(__CC_ARM)/* MDK ARM Compiler *///__attribute__((at(0x30000000))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];//__attribute__((at(0x30000040))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];//__attribute__((at(0x30000080))) uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE];__attribute__((at(0x30000000)))ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];/* Ethernet Rx DMA Descriptors */__attribute__((at(0x30000080)))ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];/* Ethernet Tx DMA Descriptors */#elifdefined(__GNUC__)/* GNU Compiler */__attribute__((section(".RxDecripSection")))//ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT];//__attribute__((section(".TxDecripSection")))//ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT];//__attribute__((section(".Rx_PoolSection")))//uint8_t Rx_Buff[ETH_RX_DESC_CNT][ETH_RX_BUFFER_SIZE];ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]__attribute__((section(".RxDecripSection")));/* Ethernet Rx DMA Descriptors */ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]__attribute__((section(".TxDecripSection")));/* Ethernet Tx DMA Descriptors */#endif2.更改ARM Compiler(armclang / armcc)的分散加载文件的内存布局
在 STM32CubeMX 生成、使用 GCC 工具链(如 STM32CubeIDE、VSCode+CMake 等) 的工程中,是.ld文件
如果使用的是 Keil MDK,工程里是 .sct 分散加载文件;如果是 IAR,则是 .icf 文件
增加
RW_IRAM3 0x30000000 0x00000200 { ; 512B - SRAM1 ´æ·ÅETH DMA RXÃèÊö·û
.ANY(.RxDecripSection)
}RW_IRAM4 0x30000200 0x00000200 { ; 512B - SRAM1 ´æ·ÅETH DMA TXÃèÊö·û
.ANY(.TxDecripSection)
}RW_IRAM5 0x30000400 0x00003C00 { ; 15KB - SRAM1 ´æ·ÅRX_POOLÄÚ´æ³Ø
.ANY(.Rx_PoolSection)
}如下图所示:
3.使能D2SRAM2时钟
4.ping 网络
完成上述配置,就可以ping通了,可以ping一下试试
四、TCP连接和关闭
TCP连接遵循3次握手、TCP断开遵循4次挥手
目前是新建了一个tcpclient的任务码代码如下:
#include"lwip/tcp.h"#include"lwip/init.h"#include"netif/etharp.h"#include"lwip/udp.h"#include"lwip/pbuf.h"#include<stdio.h>#include<string.h>#include"usart.h"voidTcpClient_task(voidconst*argument);//声明tcpclient任务函数voidTcpclient_init(void){osThreadDef(TcpClient_Task,TcpClient_task,osPriorityNormal,0,1024);osThreadCreate(osThread(TcpClient_Task),NULL);}voidTcpClient_task(voidconst*argument){structnetconn*conn;//netconn 是LWIP的网络连接对象指针,用于TCP和UDP连接的创建、连接、收发和关闭structnetbuf*rece_buf;//netbuf 用于存放接收数据的结构体,定义缓冲区指针err_terr;//err_t 是LWIP的错误码变量,LWIP所有API的返回值都用err_t ,ERR_OK代表返回正确,其他值表示错误ip_addr_tserver_ip;//ip_addr_t 定义服务器IP地址结构体,用IPV4charrx_buffer[256];//本地接收缓冲区,用netbuf 中的数据拷贝出来chartx_buffer[]="Hello from stm32h723 tcpclient \r\n";//发送给服务器的数据/******** 设置服务器 Ip(192.168.1.100) ********/IP4_ADDR(&server_ip,192,168,1,100);while(1){/*** 创建tcp连接 ***/conn=netconn_new(NETCONN_TCP);//创建一个tcp连接if(conn==NULL){//连接失败,等待500ms后从while(1)开始vTaskDelay(500);continue;}/*** 连接服务器 ***/err=netconn_connect(conn,&server_ip,5000);if(err!=ERR_OK){netconn_close(conn);netconn_delete(conn);vTaskDelay(1000);continue;}/*** 发送数据 ***/err=netconn_write(conn,tx_buffer,strlen(tx_buffer),NETCONN_COPY);if(err!=ERR_OK){netconn_close(conn);netconn_delete(conn);vTaskDelay(1000);continue;}/*** 接收数据 ***/err=netconn_recv(conn,&rece_buf);if(err==ERR_OK){u16_tlen;void*data;netbuf_data(rece_buf,&data,&len);if(len>sizeof(rx_buffer)-1)len=sizeof(rx_buffer)-1;memcpy(rx_buffer,data,len);rx_buffer[len]='\0';/*这里可以解析服务器返回的数据 *///printf("RX: %s\r\n", rx_buffer);netbuf_delete(rece_buf);}/* 关闭连接 */netconn_close(conn);netconn_delete(conn);/* 间隔1s重连 */vTaskDelay(pdMS_TO_TICKS(1000));}}在freertos.c中增加该任务
调试是OK的,我实验的时候是3s重连一次,后边改成1s重连一次的。
本文记录了基于STM32H723和LAN8742A芯片,从CubeMX工程配置到实现以太网Ping通及TCP连接和关闭的基本流程。核心步骤包括工程创建与LwIP配置、PHY驱动适配、协议栈初始化、网络连通性测试以及TCP应用开发。希望这份记录能为有类似需求的开发者提供清晰的参考。配置过程中如遇问题,欢迎在评论区交流讨论。
