如何在以太网和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
1500-40年
TCP_SND_BUF程序 5840
4个TCP_MSS
TCP_WND 5840
4个TCP_MSS
TCP_SND_QUEUELEN队列 16
上限((4*TCP_SND_BUF)/TCP_MSS)

内存布局

在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

提示和常见错误

  1. 对于STM32H72x/H73x设备,以太网缓冲区不能放置在地址范围0x30040000-0x3004800内,因为该范围无效。这些设备上的D2 SRAM要小得多,因此缓冲区需要从0x3000000开始放置。这会影响RX和TX描述符以及用于TX缓冲区的RX缓冲区地址(CubeMX中的ETH配置)和LWIP_RAM_HEAP_POINTER(CubeMX中的LWIP>Key选项)。
  2. 在Cortex-M4上运行堆栈时,缓冲区可以放在同一地址(0x30040000),但最好放在0x10040000,这是同一地址的别名。此别名可通过Cortex-M4 D总线访问,并有助于利用哈佛架构。
  3. 当不使用FreeRTOS时,应禁用以太网中断,并应定期调用MX_LWIP_Process(在主循环中)。
  4. 在STM32H747发现板上,需要对默认的焊桥配置进行修改。SB8应该关闭,SB21应该打开,以太网才能工作,否则MDC信号没有正确连接。

当您自己的项目遇到问题时:

  1. 首先尝试这个例子,看看电脑端是否有正确的配置。有了公司防火墙和限制,可能很难对特定的IP地址执行简单的ping操作。在某些情况下,从个人电脑或禁用防火墙的电脑进行测试更容易。
  2. 检查是否调用了以太网中断以及是否调用了RX回调
    • 如果没有,GPIO被设置为适当的速度(非常高)
    • 并启用ETH全局中断(仅适用于FreeRTOS)
      • 中断优先级应为5-抢占和0-次优先级。这是默认FreeRTOS配置所必需的。
  3. 如果调用Hardfault,则问题可能出现在MPU配置中
    • 请仔细检查(例如,用“256KB”代替“256B”这样的轻微错误可能会产生很大的差异)调用了1个RX回调,但ping仍然不起作用
    • 检查缓冲区是否正确放置在linkerscript(以“_FLASH.ld”结尾的那个)中
    • 在STM32CubeIDE中,您可以使用“生成分析器”窗口
  4. 当通过pbuf_alloc(或类似)分配缓冲区时,pbuf_RAM必须用作第三个参数。这对于确保分配的缓冲区被放置在D2 SRAM中并与DMA同步是必要的
  5. 不同线程的堆栈大小不足可能会导致问题。
    • 启用堆栈溢出检测有助于确定问题所在

问题和反馈

如果您看到这些示例有任何问题,请在此存储库中填写一个问题。如果您面临一些困难,但不确定这是否是一个问题,您可以在这个存储库中开始讨论,或者您可以在ST社区上启动线程


  1. 不同堆栈所需的确切大小可能取决于所使用的编译器和优化标志。FreeRTOS堆大小也是如此,因为线程堆栈是从这个堆中分配的。↩

  2. 某些地址和大小取决于所使用的设备或核心。请参阅“内存布局”一节。↩↩↩↩