尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
STM32裸机以太网实战:LAN8720A+LWIP ping通避坑指南
简介本资源面向使用STM32F429IGT6进行以太网开发的嵌入式工程师与学习者聚焦HAL库下LAN8720A PHY芯片的驱动与LWIP协议栈移植解决无操作系统环境下网络通信与PING测试的完整实现问题。压缩包共1300个文件约31.16MB以678个c源文件与450个h头文件为主体辅以78个s汇编启动文件、46个icf链接脚本及lib、a等库文件并包含ioc、uvprojx等工程配置覆盖从底层驱动到协议栈的完整代码结构。资源基于KEIL MDK_ARM_5.27、STM32CubeMX 6.6.1、HAL V1.27.1与LWIP 2.1.2搭建清晰展示了LAN8720A通过RMII接口与ETH外设通信、借助SMI接口读写PHY寄存器的实现方式。目前已有1989人学习下载适合希望掌握STM32以太网裸机开发、理解LWIP移植流程与网络调试排错思路的读者参考借鉴。1. 从一块“不亮灯”的板子说起STM32 裸机以太网到底难在哪手头有一块 STM32 的板子PHY 用的是 LAN8720A需求很朴素不跑操作系统用 HAL 库把 LWIP 协议栈跑起来能 ping 通就行。听起来像是入门级任务但真正动过手的人都知道从 PHY 地址、RMII 时钟到 LWIP 的ethernetif.c移植每一步都埋着雷。我见过太多人卡在“网口灯不亮”或者“能收不能发”上最后不了了之。这份资源就是围绕这个场景来的STM32 HAL 库 LAN8720A LWIP 无操作系统 PING 通。它解决的不是“以太网协议是什么”这种教科书问题而是“我照着 CubeMX 配完了为什么 ping 不通”这种一线问题。适合已经会点 C 语言、用过 STM32 HAL 库、想从串口/GPIO 跨到网络通信的嵌入式开发者。如果你正对着一个不响应的网口发愁下面的内容应该能帮你把链路一层层打通。2. 硬件链路与 CubeMX 配置先把 PHY 和 RMII 钉死2.1 LAN8720A 的地址与 RMII 时钟选错一个就全盘皆输LAN8720A 支持 RMII 接口硬件上最容易被忽略的是 PHY 地址。LAN8720A 的 PHY 地址由PHYAD0引脚在上电时决定该引脚悬空或拉高时地址为 1拉低时为 0。很多开发板为了省事直接悬空于是地址就是 1。如果你在代码里写PHY_ADDRESS 0MDIO 读出来的寄存器全是 0xFFFF链路永远起不来。另一个坑是 RMII 的 50MHz 时钟。LAN8720A 有两种时钟输出模式一种是从外部晶振输入 25MHz内部 PLL 倍频到 50MHz 从REF_CLK输出给 STM32另一种是 STM32 的 MCO 引脚输出 50MHz 给 PHY。常见做法是让 LAN8720A 自己产生 50MHzSTM32 端把ETH_RMII_REF_CLK配置为输入。CubeMX 里如果选错了时钟方向PHY 和 MAC 之间就没有同步时钟现象是网口灯亮但收不到任何包。配置时还要注意RMII 模式下ETH_MDC和ETH_MDIO必须接对且 MDC 时钟不能超过 2.5MHz。HAL 库默认分频是ETH_MDC_HCLK_DIV42在 168MHz 主频下约 4MHz有些 PHY 会不稳定改成ETH_MDC_HCLK_DIV62更稳妥。2.2 CubeMX 里 ETH 外设的参数怎么填在 CubeMX 的 Connectivity 里选 ETHMode 选 RMII下面几个参数需要手动确认参数项推荐值说明PHY Address1对应 PHYAD0 悬空Auto NegotiationEnable让 PHY 自动协商速率和双工Speed100Mbps协商失败时的默认值Duplex ModeFull Duplex同上Rx ModeInterrupt裸机下用中断收包TX/RX Descriptor4/4描述符数量够用即可Checksum OffloadDisable先关掉减少变量配置完成后生成代码HAL 会初始化 ETH 外设并调用HAL_ETH_Init。但此时 LWIP 还没接进来MX_LWIP_Init需要自己补。CubeMX 如果勾选了 LWIP 中间件会自动生成lwip.c和ethernetif.c但无操作系统模式下需要把NO_SYS设为 1并且把LWIP_NETCONN和LWIP_SOCKET关掉否则会引入一堆用不上的依赖。2.3 验证 PHY 是否活着的第一个测试在写任何 LWIP 代码之前先确认 MDIO 能读到 PHY 的 ID。LAN8720A 的 ID 寄存器地址是 2 和 3读出来应该是0x0007和0xC0F1。如果读不到后面全是白费。/* 在 main 里 HAL_ETH_Init 之后调用 */ uint32_t phy_id1 0, phy_id2 0; HAL_ETH_ReadPHYRegister(heth, 1, 2, phy_id1); HAL_ETH_ReadPHYRegister(heth, 1, 3, phy_id2); printf(PHY ID: 0x%04X 0x%04X\r\n, phy_id1, phy_id2); /* 正常应输出 0x0007 0xC0F1否则检查 PHY 地址和 MDIO 接线 */这段代码的逻辑很直接通过HAL_ETH_ReadPHYRegister读 PHY 的 ID 寄存器。参数依次是 ETH 句柄、PHY 地址、寄存器地址、存放结果的指针。如果输出不是0x0007 0xC0F1先别往下走回头查 PHYAD0 引脚和 MDC/MDIO 的 GPIO 配置。常见问题是 MDIO 没有上拉电阻或者 GPIO 复用选错了。3. LWIP 无操作系统移植ethernetif.c 是主战场3.1 NO_SYS1 时 LWIP 的初始化顺序无操作系统意味着 LWIP 的所有定时器和协议处理都要在主循环里手动调用。lwipopts.h里必须定义NO_SYS1同时把LWIP_NETCONN、LWIP_SOCKET、LWIP_NETIF_API都设为 0。初始化顺序不能乱HAL_Init()和时钟配置MX_ETH_Init()初始化 MAC 和 DMAMX_LWIP_Init()初始化 LWIP 和 netif主循环里调用MX_LWIP_Process()MX_LWIP_Init里会调用lwip_init()然后手动设置 netif 的 IP、掩码、网关最后netif_add并netif_set_up。如果顺序反了比如先netif_set_up再设 IPnetif 会以 0.0.0.0 启动ping 自然不通。/* lwip.c 中的 MX_LWIP_Init 关键片段 */ IP4_ADDR(ipaddr, 192, 168, 1, 100); IP4_ADDR(netmask, 255, 255, 255, 0); IP4_ADDR(gw, 192, 168, 1, 1); netif_add(gnetif, ipaddr, netmask, gw, NULL, ethernetif_init, ethernet_input); netif_set_default(gnetif); netif_set_up(gnetif); /* 注意netif_set_up 之后 PHY 才开始协商不要在这之前发 ping */参数说明netif_add的第五个参数是状态指针裸机下传 NULL 即可ethernetif_init是网卡初始化回调里面会调用low_level_init配置 MAC 地址和 DMA 描述符ethernet_input是 LWIP 自带的 IP 层输入函数。netif_set_up会触发 PHY 自动协商协商完成前网口是不通的所以 ping 测试要等几秒。3.2 low_level_output 和 low_level_input 的收发闭环ethernetif.c里最核心的是两个函数low_level_output负责把 LWIP 的 pbuf 通过 ETH DMA 发出去low_level_input负责从 DMA 接收缓冲区里取包并封装成 pbuf。无操作系统下接收通常用中断方式ETH 接收中断触发后在中断服务函数里调用ethernetif_input它会调用low_level_input然后交给netif-input。/* low_level_output 的关键逻辑 */ static err_t low_level_output(struct netif *netif, struct pbuf *p) { struct pbuf *q; uint8_t *buffer (uint8_t *)ETH_TX_BUF; uint32_t framelen 0; /* 把 pbuf 链里的数据拷贝到连续的 DMA 发送缓冲区 */ for (q p; q ! NULL; q q-next) { memcpy(buffer framelen, q-payload, q-len); framelen q-len; } /* 调用 HAL 发送注意超时时间别设太长 */ if (HAL_ETH_Transmit(heth, framelen, buffer, 1000) ! HAL_OK) { return ERR_IF; } return ERR_OK; }这里有个血泪经验DMA 发送缓冲区必须是 32 字节对齐的否则在某些 STM32 型号上会出现“发送完成但对方收不到”的玄学问题。另外HAL_ETH_Transmit的超时参数如果设成HAL_MAX_DELAY一旦 DMA 描述符耗尽就会死等主循环直接卡死。我一般设 1000ms超时后返回错误让上层重试。接收侧的中断处理要注意ETH 中断里不要做耗时操作只置个标志或者直接调用ethernetif_input。如果low_level_input里分配 pbuf 失败直接丢弃该包并重新挂接 DMA 描述符不要试图阻塞等待。3.3 主循环里的 MX_LWIP_Process 到底做了什么无操作系统下LWIP 的定时器ARP 超时、TCP 重传等需要周期性调用sys_check_timeouts()。CubeMX 生成的MX_LWIP_Process通常长这样void MX_LWIP_Process(void) { ethernetif_input(gnetif); /* 处理接收到的包 */ sys_check_timeouts(); /* 处理 LWIP 超时 */ }ethernetif_input会检查 DMA 接收描述符是否有新包有就调用low_level_input并递交给 LWIP。sys_check_timeouts依赖sys_now()提供毫秒计数所以sys_now必须正确实现通常直接返回HAL_GetTick()。如果sys_now返回 0 或者不递增ARP 永远超时ping 不通。主循环里调用MX_LWIP_Process的频率建议在 1ms 到 10ms 之间。太快浪费 CPU太慢影响响应。我一般放在while(1)里不加延时靠HAL_GetTick的节拍自然控制。4. 避坑与排查ping 不通时先看这五条4.1 网口灯不亮MDIO 读不到 PHY现象上电后网口指示灯不亮串口打印 PHY ID 为0xFFFF或0x0000。原因PHY 地址错误、MDIO 没上拉、或者 RMII 参考时钟没起来。解决先用示波器量REF_CLK引脚有没有 50MHz 波形。没有的话检查 LAN8720A 的晶振和nRST引脚是否被拉低。有波形但 MDIO 读不到换 PHY 地址 0 和 1 都试一遍同时确认 MDIO 线上有 1.5k 到 4.7k 的上拉电阻。4.2 能收 ARP 但 ping 不回现象用 Wireshark 抓包能看到开发板发出 ARP 请求对方也回了 ARP 应答但 ping 请求发过去没有 ICMP 应答。原因low_level_output里 pbuf 拷贝越界或者 DMA 发送描述符没有正确释放。解决检查framelen是否超过了 ETH_TX_BUF 的大小。另外确认HAL_ETH_Transmit返回HAL_OK后DMA 描述符的OWN位是否被硬件清掉。如果没清说明 DMA 没真正发出去通常是缓冲区地址不对齐导致的。4.3 ping 通几包后死机现象刚开始能 ping 通几十包之后串口无输出网口灯还在亮但完全没响应。原因pbuf 内存池耗尽或者 ETH 接收中断里发生了优先级反转。解决在lwipopts.h里把PBUF_POOL_SIZE从默认的 16 调到 32 或 64。另外检查 ETH 中断优先级是否高于SysTick如果 ETH 中断里调用了HAL_GetTick而 SysTick 优先级更低就会卡在中断里等 tick 递增。把 ETH 中断优先级设成比 SysTick 低一级即可。4.4 只有第一次 ping 通之后全超时现象上电后第一次 ping 能通后面无论怎么 ping 都不通。原因ARP 表项老化后没有重新解析或者 netif 的linkoutput指针被意外修改。解决检查ethernetif_init里是否正确设置了netif-linkoutput low_level_output。另外确认sys_check_timeouts在主循环里被周期性调用否则 ARP 表项永远不老化但新请求也进不来。4.5 换一台电脑就 ping 不通现象在自己的笔记本上能 ping 通换到同事的电脑上就不行。原因IP 地址冲突或者对方电脑的 ARP 缓存里有旧的 MAC 地址。解决先确认开发板的 IP 没有和局域网里其他设备冲突。然后在对方电脑上执行arp -d清除 ARP 缓存或者把开发板和电脑用网线直连排除交换机的影响。直连时注意有些网卡需要交叉线不过现在大多数网卡都支持自动翻转。5. 进阶验证用 Wireshark 看 ARP 和 ICMP 的完整交互把 ping 跑通只是第一步真正要确认协议栈工作正常得看包。在电脑上装 Wireshark选对网卡过滤条件写arp or icmp然后 ping 开发板。正常的交互应该是电脑发 ARP 请求“谁是 192.168.1.100”开发板回 ARP 应答“192.168.1.100 在 xx:xx:xx:xx:xx:xx”电脑发 ICMP Echo Request开发板回 ICMP Echo Reply如果第 2 步没有说明 LWIP 的 ARP 层没响应。检查ethernetif_input是否把包正确递交给netif-input以及etharp_input是否被调用。如果第 4 步没有说明 ICMP 应答没发出去回头查low_level_output的发送计数。我习惯在low_level_output里加一个全局计数器每发一包加一然后在主循环里打印。ping 的时候看计数器有没有涨涨了说明发送路径通没涨说明 LWIP 根本没调用发送函数。这个笨办法比单步调试快得多。还有一个验证技巧把开发板的 MAC 地址设成和电脑网卡一样的然后 ping 自己。如果能通说明协议栈的环回路径没问题问题出在物理层或 ARP。不过这个操作会干扰局域网测完记得改回来。从那以后我每次调以太网都强制先读 PHY ID再抓 ARP最后才看 ICMP。这三步走完基本没有调不通的网口。希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

Spring Boot+Vue校园食堂供应链系统开发:采购库存全链路实战

Spring Boot+Vue校园食堂供应链系统开发:采购库存全链路实战

1. 项目整体设计思路与需求拆解1.1 校园食堂供应链业务的核心痛点大学校园食堂和后厨供应链,看似只是“买菜、做饭、卖饭”三个环节,实际跑过一遍流程的人都知道,这里面藏着大量琐碎且容易出错的细节。我刚接触到这个“校园食堂供应链管理系统…

📅 2026/10/21 21:45:08
实验步骤翻译官:用AI把操作手册写成学术文本

实验步骤翻译官:用AI把操作手册写成学术文本

理工科的实验记录,写得像操作手册;论文要的,却是学术叙事。这个“好写作AI”干的事,就是把后者从前者的流水账里“翻译”出来。说白了,它就像一个实验步骤翻译官:你给它一段“我今天把样品放进烘箱&#xf…

📅 2026/10/21 21:45:08
SFT监督微调实战:从数据构建到LoRA训练踩坑全解

SFT监督微调实战:从数据构建到LoRA训练踩坑全解

在本地部署过几个开源基座模型之后,很多程序员的感受其实高度一致:模型确实能跑起来,但回答总不如官方Demo里那么“听话”。你问它东,它给你续写西;你要它按JSON格式输出,它偏要自由发挥加一段解释。这个差…

📅 2026/10/21 21:45:08
MORE NEWS

更多资讯

📰

C++ STL反向迭代器的实现

前言 反向迭代器(reverse iterator)让算法可以倒着遍历容器。rbegin() 指向最后一个元素,rend() 指向第一个元素之前的位置, 在反向迭代器上表示「向前一个元素移动」。它最容易被误解的地方是内部保存的「基迭代器」(…

📰

CRISPE框架详解:让大语言模型输出高质量结果的提示词工程指南

写提示词这件事,入门只要十分钟,但要写好往往得反复磨。我见过太多人——包括从前的我自己——把大语言模型当成一个“有问必答的同事”,想到什么就直接问什么:“帮我写一份市场推广方案”,然后对着平庸的输出叹气。后…

📰

视频配乐生成中的三类对齐:语义、时间与节奏的联合约束

去年我们在做一个内部代号为“某视频配乐实验”的项目时,遇到一个让我印象深刻的现象:模型生成的音乐单独听,旋律、配器、情绪都很像回事,可一旦配上画面,就总觉得哪里不对劲。说不清是音乐来得晚了一拍,还…

📰

RANSAC直线拟合C#源码:测量粗差下的鲁棒估计实现

简介:针对2025年测绘程序设计大赛国赛选题一,这份以RANSAC算法为核心的实战指南非常适合正在备赛的测绘、GIS及相关专业学生。资源提供一套完整的C#工程源码,涵盖点云数据读取、直线模型构建、RANSAC迭代拟合与结果可视化等关键环节&#xff…

📰

Gipps模型混合驾驶交通流仿真系统:从安全距离约束到CAV渗透率分析

最近在推进一个路网级混合驾驶仿真项目的功能开发,核心跟驰逻辑用的是 Gipps 模型。说句实话,项目刚立项时我也纠结过:Gipps 是上世纪八十年代提出的老模型,放在今天做自动驾驶与人工驾驶混行的仿真,会不会显得过时&am…

📰

C++ STL中的常用遍历算法分享

前言 提到「遍历容器」&#xff0c;很多人第一反应是范围 for 循环或者下标循环。这当然没错&#xff0c;但 STL 在 <algorithm> 和 <numeric> 里提供了一组表达力更强的算法&#xff1a;std::for_each、std::transform、std::copy_if、std::count_if、std::any_of…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬