这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » STM32H7S78-DK以太网实战:DHCP+LVGL

共1条 1/1 1 跳转至页

STM32H7S78-DK以太网实战:DHCP+LVGL

高工
2026-11-11 11:36:09     打赏
STM32H7S78-DK 以太网实战:DHCP + LVGL 

本文记录在 STM32H7S78-DK 上基于 Zephyr 4.5 实现以太网 DHCP 联网 + LVGL 状态栏显示的过程,重点是一次"裸跑正常、加上显示就挂"的疑难问题排查,根因是 MPU 内存属性区域覆盖破坏了 ETH DMA 的缓存一致性。

image.png

1. 目标与实现

屏幕顶部做一条状态栏:红圆点 = 断链,橙圆点 = 链路正常/DHCP 获取中,绿圆点 + IP 地址 = 联网成功。

prj.conf 关键配置:

CONFIG_DISPLAY=y
CONFIG_LVGL=y
CONFIG_LV_COLOR_DEPTH_24=y

CONFIG_NETWORKING=y
CONFIG_NET_L2_ETHERNET=y
CONFIG_NET_IPV4=y
CONFIG_NET_IPV6=n
CONFIG_NET_DHCPV4=y
CONFIG_NET_MGMT=y
CONFIG_NET_MGMT_EVENT=y

main.c 核心逻辑:订阅网络管理事件,同时每秒轮询兜底(防止启动早错过事件);链路正常但没地址时主动发起 DHCP:

static void refresh_status(void)
{
    struct net_if *iface = net_if_get_default();

    if (iface == NULL || !net_if_is_carrier_ok(iface)) {
        dhcp_started = false; /* 断链后重新插线时重新发起 */
        set_net_status(lv_color_hex(0xff4040), "Link down");
        return;
    }

    struct net_in_addr *in = net_if_ipv4_get_global_addr(iface, NET_ADDR_PREFERRED);

    if (in != NULL) {
        char addr[NET_IPV4_ADDR_LEN];

        net_addr_ntop(AF_INET, in, addr, sizeof(addr));
        snprintf(text, sizeof(text), "IP: %s", addr);
        set_net_status(lv_color_hex(0x20c040), text);
    } else {
        set_net_status(lv_color_hex(0xffa000), "Link up, DHCP...");
        if (!dhcp_started) {
            dhcp_started = true;
            net_dhcpv4_start(iface);   /* DHCP 要主动发起 */
        }
    }
}

注意两个新版本的 API 细节:事件名是 NET_EVENT_ETHERNET_CARRIER_ON/OFF(不是 ETH_);取地址用 net_if_ipv4_get_global_addr(),不要自己遍历 unicast[](4.5 里 net_if_addr 结构变了)。

2. 故障现象与排查过程

首次运行:PHY 识别正常(LAN8742,ID 7C131),链路 100Mb 全双工,但 DHCP 发出不去:

<err> eth_stm32_hal: HAL_ETH_TransmitIT tx_int_sem take timeout

TX 完成中断永远不来。排查采用了逐层隔离变量的方法:

实验结论
片内 Flash 裸跑(无 MCUboot、无显示),DHCP 结果用 LED 指示正常拿到地址
外部 Flash + MCUboot,无显示正常
外部 Flash + MEMC/PSRAM(无显示)正常
外部 Flash + LTDC 显示失败,TX 超时复现

(小技巧:没串口输出时用 LED 做结果指示;读 RAM 变量验证也可用 SWD,但低功耗下 hotplug 容易连不上。)

3. 根因:MPU 区域覆盖,DMA 缓冲区变成了 cacheable

H7RS 上 ETH 的 DMA 描述符和缓冲区通过 mac 节点的 memory-regions = <&sram1> 放在 SRAM1(0x30000000)。soc/st/stm32/stm32h7rsx/mpu_regions.c 会为它建两个 MPU 区域:NOCACHE 的数据缓冲区 + PPB 属性的描述符区。

但 SoC dtsi 里 sram1 节点同时带着 cacheable 属性:

sram1: memory@30000000 {
    zephyr,memory-region = "SRAM1";
    zephyr,memory-attr = <DT_MEM_ARM(ATTR_MPU_RAM)>;   /* ← cacheable */
};

MPU 区域序号大的优先级高:dts memory-attr 生成的区域排在 mpu_regions.c 的静态区域之后,于是 cacheable 区域把 ETH 的 NOCACHE 区域盖掉了。CPU 写的 TX 数据留在 D-Cache 里,ETH DMA 读到的是旧数据;DMA 出错后 TX 完成中断不来,表现为 tx_int_sem 超时。

这个坑只在显示/MEMC 加入后才暴露(区域布局变化导致覆盖关系改变),纯网络裸跑时恰好没事——这也解释了为什么社区里"同样的驱动,有人能通有人不通"。

4. 修复

应用层 overlay(app/eth_demo/boards/stm32h7s78_dk_stm32h7s7xx_ext_flash_app.overlay)把 sram1 显式改回 NOCACHE:

&sram1 {
    zephyr,memory-attr = <DT_MEM_ARM_MPU_RAM_NOCACHE>;
};

修复后串口日志:

image.png

PC 侧 ping 验证:4/4 回复,1ms,0% 丢包。屏幕状态栏绿点 + IP 显示正常。

5. 踩坑记录

  • DHCP 要主动发起:链路 up 后调 net_dhcpv4_start(iface),别假设全自动

  • MPU 区域覆盖优先级:mpu_regions.c 静态区域在前、dts zephyr,memory-attr 区域在后,后者覆盖前者——DMA 缓冲区的 NOCACHE 属性会被静默盖掉

  • dhcpv4.h 要在 net_if.h 之后包含:它自己不带 net_if 定义,顺序错了报 "incompatible pointer type"(同名不同类型)

  • -DCONF_FILE= 要用 Windows 路径:Git Bash 的 /tmp 路径原生 CMake 读不到

  • 烧过片内 Flash 测试程序会覆盖 MCUboot:之后跑 ext_flash_app 要重新完整 west flash





关键词: STM32H7S78-DK     以太网    

共1条 1/1 1 跳转至页

回复

匿名不能发帖!请先 [ 登陆 注册 ]