如何使用以太网和LwIP堆栈为STM32H7创建项目
如何在以太网和LwIP堆栈正常工作的情况下为STM32H7创建项目?本自述文件适用于STM32CubeIDE版本1.9.0和STM32CubeH7版本1.10.0。对于较旧的工具版本,请参阅存储库中此自述文件的较旧版本Examp。。。
如何在以太网和LwIP堆栈正常工作的情况下为STM32H7创建项目?
本自述文件适用于STM32CubeIDE版本1.9.0和STM32CubeH7版本1.10.0。对于较旧的工具版本,请参阅存储库中此自述文件的较旧版本
Github上提供了本文的示例项目代码和旧版本:https://github.com/stm32-hotspot/STM32H7-LwIP-Examples.下面提供了详细的分步操作方法。
特点
- 固定IP地址192.168.1.10
- 即使在STM32CubeMX中重新生成代码,代码也应该工作
- 可以通过搜索找到代码中的更改
关键字ETH_代码
发布说明
| 版本 | STM32CubeIDE版本 | STM32CubeH7版本 | 描述/更新 | 日期 |
|---|---|---|---|---|
| 1.2 | 1.9.0 | 1.10.0 | 已移植到新的IDE/库版本。以太网驱动程序在新的库版本中重新设计。添加了已调整的iperf测量和TCP/IP设置。在Github上发布 | 8月9日第2022 |
| 1.1 | 1.6.1 | 1.9.0 | 添加了Cortex-M4基础示例 | 7月19日第2021 |
| 1 | 1.6.1 | 1.9.0 | ST社区上的首次发布(在Github上做了一些小改动) | 6月21日st公司2021 |
使用GIT标记,应该很容易找到STM32CubeIDE和HAL库的特定版本的示例
LwIP中的TCP/IP配置
以下配置是实现良好TCP/IP性能所必需的
| 参数 | 价值 | 公式 | 需要在MX中更改 |
|---|---|---|---|
| TCP_MSS | 1460 | |
对 |
| TCP_SND_BUF程序 | 5840 | |
对 |
| TCP_WND | 5840 | |
不 |
| TCP_SND_QUEUELEN队列 | 16 | |
对 |
内存布局
在STM32H74x/H75x设备上,所有与以太网和LwIP相关的数据都放置在D2 SRAM存储器(288kB)中。该内存的前128kB是为双核设备上的Cortex-M4保留的。在单芯设备上,此部件可用于其他目的。
| 变量 | STM32H74x/H75x地址 | Cortex-M4别名 | 大小 | 源文件 |
|---|---|---|---|---|
| DMARxDscr选项卡 | 0x30040000 | 0x1004万 | 96(最大256) | 醚类化合物 |
| DMATxDscrTab(DMA发送数据控制选项卡) | 0x30041000 | 0x10040100 | 96(最大256) | 醚类化合物 |
| 内存_RX_POOL_base | 0x30040200 | 0x10040200 | 12*(1536+24) | 醚类化合物 |
| LwIP堆 | 0x30020000 | 0x1002万 | 131048(128kB-24) | lwipopts.h |
对于STM32H72x/H73x器件,D2 SRAM的限制更大(仅32kB)。RX缓冲区需要放在AXI SRAM中,因为它们不适合D2 RAM和LwIP堆。LwIP堆被缩减以将D2 RAM的其余部分与DMA描述符一起适配。
| 变量 | STM32H72x/H73x地址 | 大小 | 源文件 |
|---|---|---|---|
| DMARxDscr选项卡 | 3000万 | 96(最大256) | 醚类化合物 |
| DMATxDscrTab(DMA发送数据控制选项卡) | 0x30000100 | 96(最大256) | 醚类化合物 |
| 内存_RX_POOL_base | AXI SRAM(32字节对齐) | 12*(1536+24) | 醚类化合物 |
| LwIP堆 | 0x30000200 | 32232(32kB-512-24) | lwipopts.h |
这里提供的值是一个示例实现,为了优化内存使用,其他配置也是可能的。当更改内存布局时,需要相应地更新MPU配置。
许可证
库和中间件取自STM32Cube7包。所以同样的许可证也适用于这些例子。在STM32CubeMX和HAL库的顶部添加了最低限度的代码,该代码是按原样提供的。
如何从头开始创建项目
球门
此示例的目标是:
- 在STM32CubeMX中为STM32H750 Discovery配置项目
- 正确配置FreeRTOS和LwIP中间件
- 定期发送UDP消息(可选)
尽管该示例使用的是STM32H750 Discovery,但对于其他基于STM32H7的板,使用相同的步骤可能很容易。主要区别通常是引脚输出和时钟配置。您可能还需要检查板焊接桥,以确保以太网连接到MCU。
STM32CubeMX项目配置
- 在STM32CubeMX中创建新项目,选择STM32H850发现板,然后选择“否”以弹出“在默认模式下初始化所有外围设备?”。
- 这将有助于引脚分配。
基本配置
配置时钟树:
- 在引脚输出/RCC中,以旁路模式配置HSE
- 在时钟树中为核心配置400MHz

- 在引脚输出/SYS中配置与SysTick不同的时基(使用FreeRTOS时建议使用)
- TIM6通常是一个不错的选择,因为它是一个简单的定时器
以太网配置
- 在MII模式(板上使用的MII)下,在引脚输出视图中启用以太网外围设备。
- 启用以太网中断并将抢占优先级设置为5。这是FreeRTOS从中断处理程序调用其函数所必需的。
- 将以太网CRS和COL信号从PH2/PH3重新定位到PA0/PA3
- 这些信号在全双工模式下是可选的,在默认配置下不连接
- 这也将允许将PH2/PH3用于QSPI
- 其他引脚应该正确放置,因为我们从板选择器创建项目。

- 将GPIO引脚速度设置为“非常高”。
由于某种原因,ETH_MDC速度无法更改,但它不会影响应用程序,并且在新版本中已经修复。
Cortex-M7配置
使用Cortex-M4内核时可以跳过此步骤。
- 启用ICache和DCache。
- 在“仅后台区域特权访问+MPU禁用…”模式下启用内存保护单元(MPU)。根据下图配置区域:

以上示例适用于STM32H743设备。对于其他设备或双核设备上的Cortex-M4内核,可能需要不同的地址和大小。请参阅“内存布局”一节
当使用双核设备并在Cortex-M7内核上运行以太网时,必须确保以太网使用的内存不被Cortex-M4使用。还要注意,Cortex-M4可以为D2 RAM使用不同的地址别名
FreeRTOS配置
- 使用CMSIS_V1 API启用FreeRTOS。
- 将defaultTask堆栈的大小增加到512个单词1
- 较低的堆栈值会导致内存损坏
- 还请检查生成的代码是否正确,因为增加MINIMAL_STACK_SIZE时存在错误,并且代码中可能存在旧值(这应该在新版本中修复)

LwIP配置
- 在中间件中启用LwIP。
- 在“常规设置”选项卡中,禁用DHCP服务器并配置固定IP地址(除非您知道如何配置和使用DHCP)。

- 在所附的示例中,使用了192.168.1.10 IP地址(而不是屏幕截图上显示的192.168.0.10)。
- 在“平台设置”选项卡中,在两个选择框中选择“LAN8742”。LAN8742驱动程序也与LAN8740设备兼容,该设备实际上存在于STM32H750 Discovery板上。这些设备之间的主要区别在于支持MII接口。在其他板上使用LAN8742 PHY芯片。

在“关键选项”选项卡中:
- 将MEM_SIZE配置为16360。这指定了堆大小,我们将把它重新定位到D2SRAM(16kb减去分配器元数据的24字节)。
- 同时启用LWIP_NETIF_LINK_CALLBACK(电缆插拔检测所需)。
- 将LWIP_RAM_HEAP_POINTER设置为正确地址2
- 将MEM_SIZE参数设置为正确的大小2

- (应该由新的CubeMX自动完成)在“校验和”选项卡中启用Checksum_by_HARDWARE。其他选项应该自动重新配置,您可以将它们保持在这种状态。

生成项目
将项目保存到您选择的某个文件夹中。现在您可以为IDE生成项目了。在本例中,我们将使用STM32CubeIDE,但它应该与其他IDE一起使用。
修改代码(STM32CubeIDE)
有几个地方应该放置一些额外的代码,这也取决于所选的设备。在示例中,所有这些地方都用包含以下内容的注释进行标记
ETH_代码以及一些基本的解释。正在搜索
ETH_代码可以显示所有这些地方。
以下仅提及主要的有趣之处:
- 在ethernetif.c2中放置RX_POOL缓冲区(尽管我们在CubeMX中配置了地址):
/*用户代码开始2*/#如果已定义(__ICCARM__)/*!<IAR编译器*/#pragma location=0x300040200 extern u8_t memp_memory_RX_POOL_base[]#elif定义的(__CC_ARM)/*MDK ARM编译器*/__attribute__((位于(0x3040200))extern u8_t memp_memory_RX_POOL_base[]#elif定义的(__GNUC__)/*GNU编译器*/__attribute__((部分(“.Rx_PoolSection”))外部u8_t memp_memory_Rx_POOL_base[]#endif/*用户代码结束2*/
- 在新的STM32CubeIDE v1.9.0中,GCC更新到了版本10。这将更改COMMON部分的默认处理(https://gcc.gnu.org/gcc-10/changes.html)。这可能会导致多次定义errno变量时出现编译错误。启用FreeRTOS+LwIP时可能会发生这种情况。要解决此问题,请在lwipopts.h中放入以下代码(https://github.com/STMicroelectronics/stm32_mw_lwip/issues/1

/*用户代码开始1/#undef LWIP_PROVIDE_ERRNO#define LWIP_ERRNO_STDINCLUDE/用户代码结束1*/
- 将DATA_IN_D2_SRAM添加到项目中的宏定义中:

修改链接脚本(对Keil/IAR无效)2
对于Keil和IAR,应该跳过这一步骤,因为它们支持将变量放置在C代码中的特定地址。修改ETH描述符和缓冲区位于D2 SRAM中的链接脚本(*.ld)。此外,建议将所有RAM置于RAM_D1。在STM32CubeMX生成的项目中,应修改默认使用的“_FLASH”后缀链接脚本(例如:STM32H750XBHx_FLASH.ld)。“_RAM”后缀链接代码脚本是从内部RAM内存执行代码的模板。
}>RAM_D1/*修改开始*/.lwip_sec(NOLOAD):{.=绝对(0x3040000);*(.RxDecripSection).=绝对的(0x3040060
链接脚本开头的内存定义应该如下所示:
内存{FLASH(rx):ORIGIN=0x08000000,LENGTH=128K DTCMRAM(xrw):ORIGIN=0x20000000,LENGTH=128K RAM_D1(xrw
对于双核设备,最好限制RAM_D2部分,以避免与Cortex-M4发生冲突。请参阅示例中的链接脚本。
(可选)添加简单的Hello UDP消息
- 在main.c的开头添加以下包含文件:
#include“lwip/udp.h”#include<string.h>包含
- 使用以下代码修改main.c中的StartDefaultTask:
/*USER CODE BEGIN 5*/const char*message=“您好,UDP消息!\n\r”;osDelay(1000);ip_addr_t PC_IPADDR;IP_ADDR4(&PC_IPADDR,192168,1,1);结构udp_pcb*my_udp=udp_new();udp_connect(my_udp,&PC_IPADDR,55151);结构体pbuf*udp_buffer=NULL;/*无限循环*/for(;;){osDelay(1000);/*!!PBUF_RAM对正确操作至关重要!!*/udp_buffer=PBUF_alloc(PBUF_TRANSPORT,strlen(message),PBUF_RAM);if(udp_buffer!=NULL){memcpy(udp_buffer->payload,message,strlen(message));udp_send(my_udp,udp_buff);PBUF_free(udp_缓冲区);}/*用户代码END 5*/
现在您应该能够ping设备并接收UDP消息,假设您为接收设备配置了IP地址192.168.1.1(所附示例使用192.168.1.0/24网络)。在Linux机器上,您可以使用以下命令观察消息:
netcat公司–ul 55151
提示和常见错误
- 对于STM32H72x/H73x设备,以太网缓冲区不能放置在地址范围0x30040000-0x3004800内,因为该范围无效。这些设备上的D2 SRAM要小得多,因此缓冲区需要从0x3000000开始放置。这会影响RX和TX描述符以及用于TX缓冲区的RX缓冲区地址(CubeMX中的ETH配置)和LWIP_RAM_HEAP_POINTER(CubeMX中的LWIP>Key选项)。
- 在Cortex-M4上运行堆栈时,缓冲区可以放在同一地址(0x30040000),但最好放在0x10040000,这是同一地址的别名。此别名可通过Cortex-M4 D总线访问,并有助于利用哈佛架构。
- 当不使用FreeRTOS时,应禁用以太网中断,并应定期调用MX_LWIP_Process(在主循环中)。
- 在STM32H747发现板上,需要对默认的焊桥配置进行修改。SB8应该关闭,SB21应该打开,以太网才能工作,否则MDC信号没有正确连接。
当您自己的项目遇到问题时:
- 首先尝试这个例子,看看电脑端是否有正确的配置。有了公司防火墙和限制,可能很难对特定的IP地址执行简单的ping操作。在某些情况下,从个人电脑或禁用防火墙的电脑进行测试更容易。
- 检查是否调用了以太网中断以及是否调用了RX回调
- 如果没有,GPIO被设置为适当的速度(非常高)
- 并启用ETH全局中断(仅适用于FreeRTOS)
- 中断优先级应为5-抢占和0-次优先级。这是默认FreeRTOS配置所必需的。
- 如果调用Hardfault,则问题可能出现在MPU配置中
- 请仔细检查(例如,用“256KB”代替“256B”这样的轻微错误可能会产生很大的差异)调用了1个RX回调,但ping仍然不起作用
- 检查缓冲区是否正确放置在linkerscript(以“_FLASH.ld”结尾的那个)中
- 在STM32CubeIDE中,您可以使用“生成分析器”窗口
- 当通过pbuf_alloc(或类似)分配缓冲区时,pbuf_RAM必须用作第三个参数。这对于确保分配的缓冲区被放置在D2 SRAM中并与DMA同步是必要的
- 不同线程的堆栈大小不足可能会导致问题。
- 启用堆栈溢出检测有助于确定问题所在
问题和反馈
如果您看到这些示例有任何问题,请在此存储库中填写一个问题。如果您面临一些困难,但不确定这是否是一个问题,您可以在这个存储库中开始讨论,或者您可以在ST社区上启动线程
-
不同堆栈所需的确切大小可能取决于所使用的编译器和优化标志。FreeRTOS堆大小也是如此,因为线程堆栈是从这个堆中分配的。↩
-
某些地址和大小取决于所使用的设备或核心。请参阅“内存布局”一节。↩↩↩↩
