☰
STM32F407+LAN8720网口调试全攻略:RMII配置与排查链路
2026/10/5 12:50:40 网站建设 项目流程

先说结论:在STM32F407+LAN8720这套组合里,网口不通的锅,大半不在lwIP代码,而在时钟、复位、PHY地址这三样东西上。我在帮朋友排查和做自己项目时,遇到过代码拷来拷去、CubeMX配了半天、网口灯死活不亮的情况,也遇到过Link灯明明亮了但ping不通的怪事。这篇文章就把我这几年在F407+LAN8720上积累的RMII配置和调试经验完整写出来,包括CubeMX关键配置项的解释、硬件上容易忽略的几个坑、以及一套从底层硬件到上层协议栈的排查链路。适合刚入门STM32以太网、画过板子但没调通过网口、以及被网口调试折磨得头大的朋友,照着这个思路走,能省下不少时间。

1. 先别急着改代码:一条RMII链路上来就有六个环节

很多人一听到网口不通就打开网络调试助手,或者盯着lwIP的初始化代码不放。我的建议是,先停下来把整条链路在脑子里过一遍。RMII这个名字听着唬人,其实就是一个MAC和PHY之间的精简接口,F407内部自带MAC,LAN8720是PHY芯片,负责把MAC出来的数字信号转成差分信号送上网络变压器和RJ45。这条链路上任何一个环节断了,表象都是“网口不通”,但不同环节断掉以后的表现差别很大。

1.1 RMII到底在“传”什么:F407 MAC与LAN8720的接口信号

先看一下F407和LAN8720之间最常见的RMII信号连接,这也是CubeMX配置时对应的引脚:

功能STM32F407引脚说明
REF_CLKPA150MHz参考时钟,由PHY提供给MAC
MDIOPA2管理接口数据线,读写PHY寄存器
CRS_DVPA7载波侦听/数据有效
MDCPC1管理接口时钟线
RXD0PC4接收数据位0
RXD1PC5接收数据位1
TX_ENPB11发送使能
TXD0PB12发送数据位0
TXD1PB13发送数据位1

这九根线看起来不多,但每一根都有严格的时序要求。其中REF_CLK是最核心的,它决定了RMII所有信号的节奏。PA1这个引脚在CubeMX里必须被配置成ETH_RMII_REF_CLK复用功能,如果配置错误或者没有信号输入,MAC的收发逻辑根本没法跑起来。

1.2 “看灯诊断”的心得与边界:Link灯亮不代表网口通

LAN8720芯片上有两个LED指示引脚,分别可以接在RJ45座的Link/Activity指示上。很多人的调试习惯是看灯:灯不亮就怀疑PHY,灯亮了就怀疑代码。这个思路方向没问题,但边界要划清楚。

Link灯亮,只能说明PHY芯片和对端设备(路由器、交换机、电脑网口)通过网线完成了物理层协商,说明PHY的供电、晶振、复位这些基础条件基本是正常的。但灯亮完全不代表MAC已经拿到了数据,也不代表DMA把数据包交到了lwIP协议栈。反过来,如果灯都不亮,那多半还没到代码层面,先回去查PHY供电、晶振、复位、网线和对端设备。

我见过最典型的场景是:Link灯常亮,但PC端ping一直超时。这种时候很多人在lwIP里折腾半天,最后发现是MDC/MDIO配置问题导致读不到PHY状态,或者DMA描述符没有初始化好,数据压根没进MAC。所以看灯只能作为初步判断,不能作为定位依据,后面的排查链路才是重点。

2. CubeMX里最容易“看着对、实际错”的三处配置

CubeMX确实是好工具,但配置完生成的代码能不能直接跑起来,取决于你有没有把几个隐藏的关键点配对。下面这三点是我见过出问题的重灾区。

2.1 时钟源选型:为什么绝大多数板子选“25MHz晶振+LAN8720 CLKOUT”

RMII模式下,MAC需要50MHz的REF_CLK参考时钟。这个50MHz信号怎么来,直接决定了硬件的稳定性和调试难度。目前最常见的方案是在LAN8720旁边放一颗25MHz无源晶振,PHY内部PLL倍频到50MHz,然后从LAN8720的CLKOUT引脚输出给F407的PA1。

这个方案有一个很大的好处:REF_CLK的产生完全由PHY自己完成,不依赖MCU的时钟状态。只要PHY供电正常、晶振正常起振,CLKOUT就会稳定输出50MHz,MCU侧只管当作输入收下即可。调试的时候少了一个变量,出问题的概率低很多。

还有一种方案是用F407的PA8引脚的MCO1功能直接输出50MHz给PHY。这个方案看起来省了一颗晶振,实际用起来麻烦不少。F407要输出精确的50MHz并不是随便配一个分频系数就能做到的,和外部HSE频率、PLL配置都有关系,而且PA8在不少板子上还和Type-C的VBUS检测、SDIO、I2C3等外设复用。我见过一块板子PA8接了VBUS分压检测电路,MCO输出直接被拉成了直流电平,PHY完全没有REF_CLK,网口必然不通。所以如果你不是自己设计的板子,也没有仔细核算过时钟树,老老实实用25MHz晶振加CLKOUT方案就好。

还有一点容易被忽略:CubeMX时钟树配置中,ETH外设的AHB1时钟必须使能,生成代码后你会看到__HAL_RCC_ETH_CLK_ENABLE()这句。如果是从标准库手改到HAL库,经常有人漏掉时钟使能,结果HAL_ETH_Init()一直返回HAL_ERROR,等了半天才发现是时钟没开。

2.2 ETH外设参数的隐藏细节:PHY地址与DMA初始值

CubeMX在Connectivity分类下有ETH配置页,这里需要把工作模式选成RMII。很多人只改了模式就生成代码,结果MDIO读写一直失败,网口状态读不出来。这里有个关键参数:PHY Address。

LAN8720的PHY地址由PHYAD0引脚在复位时的电平决定。绝大多数开发板上这个引脚默认下拉,所以PHY地址是0x00。如果你在CubeMX里填的PHY Address和硬件上的实际地址不一致,比如填了0x01但硬件是0x00,那么MDIO读出来基本全是0xFFFF,网口必然不通。这里分享个经验:如果你不确定地址是几,可以先默认填0x00,然后用最下面的PHY ID读取函数验证,读出来如果厂商ID不对再改0x01试。

DMA描述符数量和缓冲区大小先用CubeMX的默认值即可,比如接收描述符4个、发送描述符4个,跑通之后再去调。接收和发送描述符必须使用32位对齐的内存地址,CubeMX自动生成的代码是没问题的,但如果你是自己移植的,比如把缓冲区定义在结构体里,就要小心编译器对齐问题。

2.3 GPIO复用与PA8“撞车”问题:MCO、Type-C VBUS、SDIO之间的引脚冲突

CubeMX生成代码后,引脚分配图看起来全是绿色,很多人就以为万事大吉了。实际上有几个RMII引脚如果没被正确配置成复用功能,会出现一个完全无法理解的“半通不通”状态。

逐个检查PA1、PA2、PA7、PC1、PC4、PC5、PB11、PB12、PB13这九个引脚,确认它们都显示为ETH的复用功能。尤其是PC4和PC5这两个RXD引脚,如果它们被配置成了普通GPIO输出或者处于悬空状态,接收路径就废了。症状是lwIP初始化正常、PHY也正常、Link灯也亮,但收不到任何包,因为数据根本没送到MAC里。

PA8的冲突问题在前面提过。如果你想用MCO方式给PHY提供REF_CLK,PA8必须复用为MCO1,同时不能有其他外设占用。但在实际项目里,PA8经常被连到Type-C的VBUS检测网络、SDIO数据线、或者其他用途。一旦发生这种复用冲突,MCO输出波形会被破坏,而且这种问题用万用表很难发现,必须用示波器看波形才能确认。

3. LAN8720硬件上三个不容易注意到的坑

软件配置正确但网口还是不通,那就要把眼睛移到原理图和PCB上。LAN8720有几个非常典型的硬件坑,网上讨论不多,但一旦踩中就是大半天时间没了。

3.1 PHY地址不是随便定的:PHYAD0引脚的上电采样

前面提到PHY地址在CubeMX里要填对,这里再往深挖一层。LAN8720的PHYAD0同时也是RXER引脚的复用脚,它的电平在PHY上电复位时被采样,决定芯片的PHY地址。如果这个引脚通过下拉电阻接地,地址是0x00;如果通过上拉电阻接VDD,地址是0x01。

关键是这个引脚上不应该有别的信号干扰复位瞬间的电平。有的设计图省事,把这个引脚直接连到了MCU的GPIO上,想通过GPIO控制地址。如果GPIO在上电瞬间输出高电平,而CubeMX里填的又是0x00,MDIO就会一直找不到PHY。更隐蔽的是,GPIO默认状态和PHY上电采样窗口并不总是一致,导致每次上电结果都不一样。我建议硬件上直接用固定电阻拉低或拉高,不要用MCU引脚控制PHYAD0,别有那种“想在软件里切地址”的想法,实际调试时只会增加不确定性。

3.2 REGOFF/nINT悬空还是一起拉低:寄存器关断模式的“假死”

这个坑我印象极深。LAN8720A的REGOFF/nINT引脚,REGOFF代表Regulator Off,低有效的中断引脚。当REGOFF被拉为高电平时,芯片内部稳压器会被关闭,数字核心需要由外部单独提供1.2V电源才能工作。如果你的板子没有外部1.2V供电,PHY就进入一个“假死”状态:有3.3V供电、晶振也在震荡,但MDIO读不到任何有效寄存器,Link灯也不亮。

很多照抄参考设计的板子在这里会出问题。参考设计里这个引脚通常接一个下拉电阻到地,但有些原理图为了保留中断功能,把这个引脚连到MCU的GPIO上。上电瞬间GPIO如果处于高电平或者浮空,PHY就可能被关断。处理方式很简单:如果你不需要PHY中断功能,直接把REGOFF/nINT用10k电阻下拉到地,一劳永逸。如果确实要用中断,在硬件上要保证GPIO复位期间输出低,并且注意上电时序。

3.3 复位时序和电源滤波:按下复位键偶尔活过来的原因

网口出现“冷启动第一次能通,断电马上重新上电就通不了”这种怪现象,大概率是复位时序不对。LAN8720的nRST复位引脚需要在上电后保持一段时间的低电平,给内部电源轨稳定和晶振起振留时间。很多开发板用RC电路实现复位,如果RC时间常数太小,3.3V还没完全稳定PHY就开始运行,内部逻辑容易进入一个不确定状态。

判断这个问题的方法很简单:如果每次按一下MCU的复位键,或者断电等几秒再上电,网口就正常了,那基本就是PHY复位时序的问题。解决办法是把复位RC电路的时间常数加大,比如把复位电容从0.1uF换成1uF,配合10k到47k的上拉电阻,让复位释放时间延后几十毫秒。

电源滤波方面,LAN8720的模拟电源和数字电源引脚建议都放置0.1uF陶瓷电容加10uF钽电容组合去耦,REF_CLK走线尽量短,远离晶振和电感。我遇到过一块板子,软件怎么查都没问题,就是网口偶尔断流,最后用示波器一看,PHY电源纹波快到50mV了,CLKOUT信号被纹波调制得乱七八糟。这种问题在软件层根本没法定位,只能回到硬件去解决。

4. 上电后先动手量这几个点,比瞎猜快半天

排查硬件问题,示波器比瞎猜靠谱得多。每次处理网口不通,我都是按固定的顺序去测量关键信号,哪个点不对一眼就能看出来。

4.1 示波器探针顺序:3.3V、25MHz晶振、CLKOUT、nRST、MDC/MDIO

下面是我常用的测量顺序和期望值,可以照着这个表格来:

测量点期望波形/数值异常表现对应的方向
PHY VDD3.3V,纹波尽量小于50mV电压偏低查电源电路,纹波大查去耦电容
晶振XI/XO25MHz正弦波,幅度约0.8到1.2V不起振查晶振负载电容和焊接
CLKOUT50MHz方波,0至3.3V摆幅无输出则PHY没工作或REGOFF拉高
nRST上电时低脉冲,然后拉高到3.3V一直低电平说明复位没释放
MDC有持续脉冲,频率几百kHz到2.5MHz无脉冲说明MAC没在对PHY操作
MDIO读写时有不规则数据波形只有高电平或只有低电平说明通信异常

测晶振有一点要特别注意:探头要用10倍衰减档,因为探头本身有电容,直接挂上去可能导致晶振停振或者幅度失真。如果看到晶振幅度明显偏低,不要急着下结论,先拿掉探头再确认一次。

CLKOUT是判断PHY是否真正工作的关键信号。如果你用的是25MHz晶振方案,PHY上电后CLKOUT应该立刻输出50MHz方波。如果这个信号没有,说明PHY的电源、晶振、REGOFF这些环节里有问题,连MAC都不用去查。

4.2 静态检查:网络变压器与RJ45的端接

软件和信号都查完了还是找不到问题,就得回头看看网络变压器和RJ45部分的电路。LAN8720的发送输出是TXP/TXN差分对,经过网络变压器后再接到RJ45。变压器中心抽头的处理方式、共模电阻和端接电容取值,不同PHY芯片的要求不完全一样。

经常有人把原来给DP83848设计的电路直接改成LAN8720,结果出现Link能协商上但数据全错的情况。这通常就是变压器中心抽头和端接参数不匹配导致的。这种问题示波器在RMII侧很难看出来,因为MAC和PHY之间的信号都是正常的,问题出在PHY到RJ45的模拟链路上。这种问题只能靠对照LAN8720数据手册的参考电路,重新核对原理图。

5. 软硬联调的排查链路:从PHY ID到lwIP回包

硬件信号都测过了还是不通,或者Link灯正常但ping不通,这时候就要沿着软件链路一层一层往上查。我的习惯是先把PHY寄存器读通,再查MAC和DMA,最后才看lwIP。

5.1 先读PHY寄存器:验证MDIO链路是否活着

不要一上来就初始化lwIP,先写一个简单的函数去读PHY芯片的ID寄存器,确认MDIO管理链路是通的。HAL库提供了现成的读寄存器函数,代码很简单:

#include "main.h" extern ETH_HandleTypeDef heth; static int lan8720_check_phy(void) { uint32_t id1 = 0, id2 = 0, bsr = 0; if (HAL_ETH_ReadPHYRegister(&heth, 0x00, 2, &id1) != HAL_OK) { printf("read PHY ID1 failed\r\n"); return -1; } if (HAL_ETH_ReadPHYRegister(&heth, 0x00, 3, &id2) != HAL_OK) { printf("read PHY ID2 failed\r\n"); return -2; } printf("PHY ID1 = 0x%04X, ID2 = 0x%04X\r\n", (unsigned)id1, (unsigned)id2); if (HAL_ETH_ReadPHYRegister(&heth, 0x00, 1, &bsr) == HAL_OK) { printf("PHY BSR = 0x%04X\r\n", (unsigned)bsr); if (bsr & (1UL << 2)) { printf("Link is UP\r\n"); } else { printf("Link is DOWN\r\n"); } } return 0; }

注意这段代码必须在HAL_ETH_Init()成功之后才能调用,因为MDC/MDIO引脚的硬件初始化是在Init里面完成的。正常情况读出来的ID1是0x0007,ID2是0x13F4左右,不同批次可能有细微差异。如果读出来是全0或者全F,先查PHY地址、MDIO引脚配置、REGOFF是否被拉高。如果打印都没有,先确认串口通没通,别笑,我曾见过一个人调了半天PHY,最后发现串口波特率配错了,输出全是乱码,他以为程序死掉了。

BSR寄存器第2位是Link Status,读出来为1表示物理链路已经建立。如果ID读得出来但Link Status为0,说明MDIO链路是好的,问题出在网线、对端设备、网络变压器或者PHY的协商配置上。

5.2 Link正常却ping不通:MAC/DMA/lwIP的检查顺序

ID正常、Link也正常、但ping依然不通,这时候问题集中在MAC初始化、DMA接收、lwIP收包这几层。我的排查顺序很固定:

  1. 先确认ETH中断已经使能,并且在中断服务函数ETH_IRQHandler里调用了HAL_ETH_IRQHandler(&heth)。如果中断没进来,收到的数据包就永远没有人处理。
  2. 确认lwIP初始化时,ethernetif_input被正确调用。用CubeMX加FreeRTOS的组合时,常见的做法是在HAL_ETH_RxCpltCallback回调里把收到的数据包投递到线程消息队列。
  3. 检查MAC地址。给lwIP使用的网卡MAC地址不要全0,有些网卡驱动对全0的MAC会有问题。虽然理论上全0也能发,但实际调试时各种奇怪问题都有。
  4. 如果有串口调试信息,打开lwIP的DEBUG选项,在PC端ping的同时启动Wireshark抓包,看PC发出去的ARP请求有没有进到MCU侧。

一个特别隐蔽的问题:如果你的板子跑到了低功耗模式,比如进入了STOP模式,ETH的AHB1时钟可能被关掉。恢复后如果没重新使能ETH时钟和重新初始化DMA,网口就会一直“假死”。嵌入式开发里常遇到“程序跑飞断电重启就正常”的情况,排查手段就是把所有可能被低功耗关闭的外设查一遍。

5.3 实在不行就上逻辑分析仪抓RMII

当整个代码链路看起来都没问题但数据就是不通,逻辑分析仪是最好的工具。RMII信号也就九根线,用逻辑分析仪把TXD0、TXD1、TX_EN这三根信号挂上,然后在PC端ping一个包。如果看到TX_EN拉高并且TXD上有电平变化,说明MAC确实把数据发出来了。如果TX_EN一直保持低电平,说明MAC侧没有数据在发送,问题大概率还在初始化或者上层没触发发送。

查接收方向就抓CRS_DV、RXD0、RXD1。如果CRS_DV拉高并且RXD上有电平翻转,说明PHY把接收数据送出来了。如果RXD始终是0,那数据根本没有从PHY到MAC这条线上,问题在PHY或物理链路。

逻辑分析仪采样率不用太高,100MHz以上就行,抓几十毫秒就够判断问题方向了。这个方法看着土,但定位效率非常高,省得反复猜测是MAC问题还是PHY问题。

6. 量产/自绘板常见的“看似正常但网口抽风”的延展排查

如果你的板子已经能ping通了,但时不时的出点小毛病,或者从开发板搬到自绘板上之后出现各种奇葩现象,那恭喜你进入了下半场,这些问题的根因往往在PCB和信号完整性上。

6.1 能通但不稳定、掉包,先查REF_CLK抖动

最典型的就是网口能通,但Ping大包或者长时间通信会掉包。先用示波器看PA1上的50MHz REF_CLK波形,如果能看到明显的抖动或者毛刺,很大概率是时钟被干扰了。检查一下25MHz晶振附近有没有走开关电源的线,晶振的负载电容是不是和数据手册一致,LAN8720的模拟电源引脚有没有加磁珠和电容滤波。

如果是两层板,REF_CLK走线旁边尽量不要有长距离的电源平行走线。如果是四层板,晶振正下方的地层要完整,不要被信号线割裂。这些PCB层面的问题,在开发板上通常不明显,因为开发板布线质量普遍比较讲究,一到自己的板子上问题就暴露了。

6.2 只有10M能Link、100M起不来,多半是差分信号质量问题

10M能协商上,100M起不来,这是RMII接口最常见的高速问题。100M以太网的UI只有10ns,每一个REF_CLK周期要采两个数据位,任何一根TXD或RXD走线过长、阻抗不连续、串联电阻过大,都会让100M模式下误码率飙升。

自查思路:检查PC4、PC5、PB12、PB13这几根线上的串联电阻,有的人为加泪滴或走线修补导致走线长度差异过大。虽然RMII不像差分对那样严格,但TXD0和TXD1之间、RXD0和RXD1之间的走线长度最好控制在一个厘米量级以内。

软件上有一个缓解手段:把CubeMX里ETH接收DMA描述符的数量从默认的4个加大到8个或16个,压力大时能减少因为描述符耗尽导致的丢包。但话说回来,这只是治标,信号质量的问题不解决,数据量一上来照样出问题。

6.3 温度、静电和线缆对RMII信号的影响

最后说两个平时很少注意、但量产最容易爆雷的场景。低温环境下网口偶尔握手失败,可以先查晶振起振。晶振和负载电容匹配不好时,低温下起振时间会变长,甚至完全起不来。如果产品要过-20度低温,建议低温摸底测试时把网口上电握手这部分重点盯一下。

ESD静电测试把网口打挂,先检查LAN8720的复位脚和REGOFF/nINT脚有没有加保护。这两个脚一旦被静电打进去,芯片可能就锁死了。软件上可以在主循环里定时读PHY的Link状态,发现异常时自动重新初始化PHY,算是一个兜底方案。我自己的项目里就有类似的看门狗逻辑,读PHY寄存器连续失败几次就整个重新初始化ETH和lwIP,虽然不能解决硬件问题,但至少设备不用断电重启。

调试网口这套东西,最忌讳的就是没有章法地瞎试。先量硬件信号,再读PHY寄存器,然后查MAC和DMA,最后才动协议栈,按这个顺序走下来,你会发现大部分问题都能定位到具体那一层。拿我个人经验来说,只要把REF_CLK、复位时序和PHY地址这三个东西确认好,F407加LAN8720的组合基本就能稳了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询