最近做的一块板子需要在STM32F407上扩展一个百兆网口,用来做远程参数配置和数据上报。刚开始我并没太当回事,觉得选一颗PHY芯片、把MAC侧的引脚连上、初始化一下以太网外设再挂个LWIP就能收工。结果从画原理图到真正调通,前前后后折腾了将近两周。回头复盘,问题几乎都出在一些非常基础但没人给我提前打预防针的细节上。
这篇文章就把我这次用RMII接口连接外部PHY芯片的完整过程、选型逻辑和踩坑记录整理出来。文章以STM32F407 + LAN8720A这套非常经典的组合为主线,只要你的芯片也带内部以太网MAC(比如F4系列、G4系列、H7系列),很多结论都是通用的。内容会比较偏向硬件连线、时钟方案和调试方法,适合第一次碰以太网、想用STM32内部ETH外设而不是外挂W5500这类协议栈芯片的朋友参考。
1. 为什么我最终选了RMII,而不是MII
很多人第一次接触STM32以太网时,都会遇到两个词:MII和RMII。MII是标准介质无关接口,RMII是精简版。名字里多了一个R(Reduced),但实际差别不只是“少几根线”这么简单。
1.1 两种接口的直观差异
从硬件设计角度,MII接口在100M以太网下需要4位发送数据线TXD[3:0]、4位接收数据线RXD[3:0],加上TX_EN、TX_CLK、RX_DV、RX_CLK以及CRS、COL这些控制信号,再算上MDC/MDIO管理接口,一共要占掉MCU十八个左右的引脚。
RMII的思路是把数据线从4位砍到2位,把收发时钟合并成一个50MHz的参考时钟REF_CLK,同时把载波侦听和数据有效信号合并成CRS_DV。这样一来,数据通道只需要TXD0、TXD1、TX_EN、RXD0、RXD1、CRS_DV、REF_CLK这7根线,算上MDIO和MDC也才9根。
我当时用的是STM32F407VET6,LQFP100封装,引脚本来就很紧张,还要兼顾ADC采集、串口、SPI Flash等一堆外设,MII那种十几个引脚的接口实在吃不消。所以第一轮选型就直接把RMII定下来了。
1.2 RMII为什么能用一半的线跑同样的速度
MII在100M模式下的工作时钟是25MHz,一次传输4位数据,25MHz乘以4位正好是100Mbps。RMII把位宽降到了2位,如果还想保持100Mbps,就必须把时钟频率抬高到50MHz,用2位乘以50MHz来凑够100Mbps。
这里有个容易误解的点,RMII并不是靠上下沿双沿采样实现翻倍的,数据还是和时钟的上升沿对齐。之所以用50MHz而不是25MHz,单纯是因为位宽减半后需要频率翻倍来补吞吐量。到了10M模式,RMII的REF_CLK依然是50MHz,不能直接降频,而是通过发送端重复发送同一份数据来适配10M速率。这个逻辑PHY芯片内部会自动处理,不需要用户干预。
由于RMII的数据线位宽更窄,对时钟的依赖就更加突出。MII模式下MAC和PHY各自有独立的发送时钟和接收时钟,容错空间相对大一点。RMII则要求MAC和PHY必须使用同一个50MHz参考时钟来同步所有收发逻辑,只要这个时钟出问题,整个链路就完全瘫痪。这也是很多RMII方案第一次上电死活不通的最常见原因。
1.3 什么时候不该用RMII
虽然RMII省引脚,但它不是万能的。如果你的PHY芯片不支持RMII模式,那自然只能选MII。另外,如果你对时钟质量有极高要求,或者MCU和PHY之间的距离比较远,MII那种并行接口反而更“耐造”。不过对于绝大多数百兆以太网应用场景,RMII都是更务实的选择,尤其是F407这种引脚不多、又要挂不少外设的情况下。
2. RMII接口信号与时钟方案:先看懂再连线
这部分是整篇文章的重点,也是我认为最应该反复确认的地方。很多RMII接线的坑,本质上都是没有把信号关系吃透。
2.1 每一根信号线到底是干什么的
RMII接口的核心信号不多,但每一根都有明确的职责。
TXD0和TXD1是发送数据线,发送时以2位为单位并行传出。TX_EN是发送使能,表示当前总线上有有效数据。RXD0和RXD1对应接收数据线,同样以2位为单位进入MAC。CRS_DV是一个复合信号,它把MII标准的CRS(载波侦听)和RX_DV(接收数据有效)合并成了一根线,用来告诉MAC“现在介质上有数据正在传”。REF_CLK就是50MHz参考时钟,所有数据收发都围绕这个时钟展开。
MDC和MDIO属于管理通道,走的是类似I2C那种串行协议。配置PHY的工作模式、读取链路状态、做自协商,全靠这两根线完成。需要注意的是,MDIO是开漏输出,硬件上必须加上拉电阻,一般取4.7kΩ到10kΩ之间。
2.2 50MHz参考时钟到底该由谁提供
这个问题的答案直接决定你的电路能不能通。不同MCU支持的模式不一样,不同PHY芯片的时钟电路也不一样,千万不能想当然。
STM32F407的内部以太网MAC在RMII模式下,PA1或者PH2是REF_CLK引脚,而且角色是输入。也就是说,MCU不会自己产生这个50MHz时钟,必须靠外部电路给它提供。很多第一次做的人会误以为把MAC配置成RMII模式后,MCU会自动从某个引脚输出50MHz时钟给PHY,这个理解在F407上是不成立的。
那么50MHz信号通常从哪里来呢?三种常见接法:
- PHY芯片自己产生REF_CLK,然后输出给MCU。像LAN8720A这类PHY,使用外部时钟源之后,可以把50MHz时钟从REF_CLKO引脚送出来,直接接到STM32的PA1。
- 用一颗独立的有源晶振,同时给PHY和MCU提供50MHz参考时钟。这种方案时钟最独立,但要多花一颗有源晶振,而且布线时要更加小心。
- 部分MCU型号支持从MAC侧输出50MHz参考时钟给PHY。但F407的RMII模式下没有这个能力,具体要查参考手册“RMII clock”相关章节。
我当时手头有一颗25MHz的无源晶振,想当然地把它接到了LAN8720A的时钟引脚上,结果PHY完全不工作。后来仔细看了数据手册才发现,这颗PHY在RMII模式下更需要正确的参考时钟来源,不是随便给个25MHz晶振就能打发的。不同PHY的时钟要求差异非常大,比如有些PHY用25MHz晶振加内部PLL倍频到50MHz输出,有些则是直接吃50MHz外部时钟。选型之后第一件事就是把数据手册里“Clock Generation”那一节反复读三遍。
2.3 为什么时钟布线不能随便拉
RMII的50MHz参考时钟是所有收发逻辑的基准,如果它出现抖动、过冲或者相位不对,MAC侧采到的数据就会偶发出错。表现就是抓包时偶尔有乱帧,ping测试时大包不通或者延迟忽高忽低。
我现在的习惯是:REF_CLK走线尽量短,优先走内层或铺地包起来,避免和TXD、RXD以及其它高速数字信号并行走长线。如果MCU和PHY距离比较远,还应在靠近接收端的位置加一个小阻值串联电阻,比如22Ω或者33Ω,用来抑制过冲。虽然这些属于信号完整性范畴,对低速板子来说很多工程师不太在意,但RMII这个接口确实值得稍微认真对待。
3. 电路设计实战:F407 + LAN8720A 网口连接
有了信号层面的概念,接下来就可以落到具体原理图上了。我这里用LAN8720A做例子,因为它便宜、外围少、很多开发板都在用,网上可以参考的资料也最多。
3.1 关于PHY选型的一点建议
市面上常见的百兆PHY还有DP83848、KSZ8081、IP101GRI等。选型时重点看几个维度:时钟方案是否匹配、供电复杂度、封装、价格和资料齐全度。
| 维度 | LAN8720A | DP83848 | KSZ8081 |
|---|---|---|---|
| 参考时钟方案 | 外接时钟后输出REF_CLK供MAC使用 | 常用25MHz晶振,配置灵活 | 支持25MHz晶振或外部时钟,输出50MHz给MAC |
| 内部稳压 | 内置1.2V稳压,外围简单 | 需要额外关注供电拓扑 | 内置稳压 |
| 封装与体积 | QFN小封装,适合紧凑布局 | QFP封装,容易焊接 | QFN封装 |
| 市场资料 | 极多,F407板卡常用 | 老牌稳定,资料丰富 | 工业级常用 |
如果你是第一次做,我建议直接照抄成熟组合:STM32F407 + LAN8720A + 集成变压器的RJ45座子。先跑通再考虑换PHY,不然很容易同时踩进“时钟不对”“模式不对”“地址不对”好几个坑里,排查起来非常痛苦。
3.2 核心引脚连接关系
下面这张连接表基本就是我做原理图时依据的映射关系。不同MCU封装下引脚复用可能略有差异,但RMII信号到PHY的对应关系是一致的。
| STM32F407引脚 | 信号名 | LAN8720A引脚 | 说明 |
|---|---|---|---|
| PA1 | RMII_REF_CLK | REF_CLKO | 50MHz参考时钟输入 |
| PB11 | RMII_TX_EN | TX_EN | 发送使能 |
| PB12 | RMII_TXD0 | TXD0 | 发送数据位0 |
| PB13 | RMII_TXD1 | TXD1 | 发送数据位1 |
| PC4 | RMII_RXD0 | RXD0 | 接收数据位0 |
| PC5 | RMII_RXD1 | RXD1 | 接收数据位1 |
| PA7 | RMII_CRS_DV | CRS_DV | 载波侦听与接收有效复合信号 |
| PC1 | RMII_MDC | MDC | 管理接口时钟 |
| PA2 | RMII_MDIO | MDIO | 管理接口数据,需上拉电阻 |
| 任意GPIO | PHY_nRST | nRST | PHY复位信号,低有效 |
| 任意GPIO或固定电平 | PHYAD0配置脚 | PHYAD0 | 决定PHY地址,一般下拉到地 |
这张表里最容易忽略的是PHYAD0。LAN8720A在复位时会锁存PHYAD0引脚的电平来确认自身地址,最常见配置是用一个10kΩ电阻下拉到地,让PHY地址变成0x00。如果你忘了接这个引脚,或者上下拉完全悬空,PHY地址可能不是预期值,MDIO通信自然对不上。软件里配置的PHY地址和硬件上锁存的地址必须一致。
另外,PHY复位信号也不能随便处理。如果只是简单接一个RC复位,上电后PHY可能早于MCU初始化完毕,也可能在MCU还没准备好时进入错误自举状态。比较好的做法是用MCU的一个GPIO单独控制PHY复位,软件上先拉低一段时间,再拉高并延时等待PHY稳定。这种方式最可控,后续调试时还可以用软复位操作PHY,非常方便。
3.3 变压器、RJ45和电源处理
RMII的TXD0、TXD1、TX_EN这些是数字信号,最终要经过PHY转换成差分信号送往网口。LAN8720A的TXP/TXN和RXP/RXN两对差分线,需要接到带隔离变压器的RJ45座子上。比较省心的选择是HR911105A这类集成网络变压器的RJ45连接器,直接按数据手册把差分对接上去就行。注意中心抽头的接法,LAN8720A方案里一般接到3.3V,并且要放退耦电容,具体电压和电容位置以数据手册为准。
电源方面,LAN8720A内部集成了稳压电路,外围只加了各种容值的去耦电容,把3.3V电源管脚附近布置好0.1uF和10uF电容基本就够了。MDIO、MDC以及其它控制引脚如果有相连的上拉电阻,也不要省,尤其是MDIO的开漏输出结构,没有上拉是读不到任何PHY寄存器的。
原理图层面,如果你不想自己从零画,直接在立创EDA或GitHub上搜“F407 LAN8720核心板原理图”,找一份看得顺眼、元器件齐的参考设计,核对一下这十几个引脚的连接之后照着画,比自己闷头搭要稳得多。我当时就是把官方开发板的网口部分抄过来,再配合LAN8720A数据手册逐脚核对,一次打板回来硬件链路基本没有大问题。
4. 软件侧的三件套:CubeMX、PHY寄存器、LWIP
硬件连完,接下来要解决的就是怎么让STM32把这些信号用起来。这里我不会把LWIP整个移植过程从头到尾展开,因为那又是另外一大篇文章了,重点讲最容易出错、也最影响排查方向的几个软件配置点。
4.1 STM32CubeMX里的RMII配置
在STM32CubeMX里新建工程后,先把ETH外设打开。以太网模式要选择RMII,而不是MII。PHY Address一般填0,对应硬件上PHYAD0下拉后的地址。如果硬件接的是上拉,那这里就要填1,不然后面用HAL库自带的MDIO工具读PHY寄存器时会找不到设备。
配置ETH时钟时,要留意MDC时钟频率不能超过2.5MHz。CubeMX会根据你设置的HCLK给出一个自动分频结果,尽量保证MDC落在合理的范围里。以前有人把MDC分频设得太高,导致MDIO偶尔读写失败,现象非常诡异。
如果你的工程里还要用LWIP,直接在中间件里把LwIP勾上。在配置页面选好静态IP或DHCP,IP地址、子网掩码、网关先按局域网默认值填一个。MAC地址不要填全0,也不要填成组播地址,否则发包会被网卡直接丢弃。系统自动生成的那个MAC地址一般可以先用着。
4.2 初始化顺序为什么很关键
我调试的时候发现一个规律,很多RMII链接不上的问题其实不是硬件坏了,而是软件初始化顺序不对。正确顺序应该是:
- 配置PHY复位引脚为GPIO输出,先给PHY一个有效的复位信号,然后等待PHY完成自举。这个等待时间不能太短,手册上通常给的是几十毫秒量级。
- 使能ETH外设和GPIO复用时钟,确保所有RMII引脚都处于正确的复用功能状态。
- 调用HAL_ETH_Init初始化MAC和DMA,这一步还会读写PHY的寄存器来配置自协商等选项。
- 配置接收和发送DMA描述符,并调用HAL_ETH_Start启动MAC。
- 最后再初始化LWIP协议栈,把netif接口上线。
如果PHY还没完全就绪就急着去读寄存器,MDIO通信大概率失败,读回来的数据要么是0xFFFF要么是乱值。很多人遇到这种情况第一反应是怀疑电路问题,其实只要把复位时序延长,问题就消失了。
4.3 先用MDIO验证硬件,再谈协议栈
在我的调试流程里,所有跟以太网有关的问题,第一步永远是MDIO读寄存器。只要MDIO能正确读回PHY的厂商ID和型号ID,就说明供电、时钟、复位、PHY地址、MDC/MDIO这几条硬件链路全部正常,后面的问题基本是软件配置层面的。
HAL库里已经有HAL_ETH_ReadPHYRegister这个函数,直接调它读PHY寄存器2和寄存器3就能拿到ID值,再和PHY数据手册上的ID值比对,一致就说明管理通道已经打通。如果不一致,优先去查PHY地址、MDC时钟、上拉电阻和复位引脚。
之后还要看一下PHY的基本控制寄存器和基本状态寄存器,确认自协商是否完成、链路是否已经连上。这一步能直接把问题定位在“PHY没建立链路”还是“MAC侧没收到数据”。如果PHY状态寄存器显示link是up的,但MCU就是收不到包,那就该怀疑RMII数据信号和参考时钟了。
4.4 关于Keil5和芯片包的提醒
如果你用的芯片是F407这类老型号,但本地Keil5里完全没有对应芯片选项,第一件事是去安装对应的设备支持包,也就是在包管理器里搜STM32F4系列并安装。这个问题和以太网关系不大,但我在调试时遇到过一种很低级的卡壳:代码在另一个电脑上编译正常,换到自己的电脑后Keil5找不到芯片,结果连工程都打不开。先装好芯片包、确认编译器版本和CMSIS版本一致,再开始调网络,能省很多无意义的折腾。
5. 调试排错实录:网口不通时,应按什么顺序排查
这部分应该是最多朋友关心的。因为RMII一旦不工作,现象千奇百怪:有时候网口指示灯亮但ping不通,有时候网线插上电脑根本识别不到,有时候能ping通两三包然后就断掉。下面是我这次实际调试中遇到的三个典型问题,以及整套排查思路。
5.1 坑一:到处都有电,唯独REF_CLK量不到50MHz
板子第一次上电的时候,我拿着万用表量了一圈,3.3V正常,PHY芯片摸上去也温温的,应该是供电了。RJ45座子的Link指示灯也亮了,但电脑那边就是显示“网络电缆被拔出”。
我下意识觉得是不是变压器那边接错了,后来用示波器去点PA1引脚的REF_CLK,发现电压恒定在1.6V左右,完全不是50MHz方波的样子。问题源头在PHY侧,它并没有向MAC输出参考时钟。
根因是我最初给PHY配的时钟源不对,而且时钟输出使能也没有配置对。这颗PHY虽然内部有振荡电路,但RMII模式要求你先给它提供正确的时钟基准,然后它才会把REF_CLKO引脚变成50MHz输出,驱动MCU。后来我把外部的时钟源方式纠正过来,并按照数据手册的要求配置好模式选择,再量PA1,立刻就能看到50MHz方波,ping也通了。
经验是,RMII调不通时,第一件事不要急着翻软件,去量REF_CLK。如果这个50MHz时钟没有,后面所有数据收发都是空谈。没有示波器的话,最低限度也要保证PA1引脚在PHY启动后出现明显的电平翻转,而不能是死板的低电平或者高电平。
5.2 坑二:MDIO能通,但Link始终Up不起来
还有一次遇到的现象是MDIO能正确读到PHY ID,但PHY的状态寄存器里Link状态一直显示没连接。我用的是网线直连开发板和电脑,理论上应该自动协商上百兆,但就是不起来。
这种问题的排查方向很集中:先换网线,确认网线本身没问题。然后用测线器或者按照RMII信号表逐根检查RXD0、RXD1、CRS_DV这些引脚有没有虚焊、连错。我那次最后发现是PHY地址虽然对了,但PHY的模式引脚没有按RMII要求接,导致PHY内部认为自己是MII模式,RXD数据相关信号的行为跟MAC的RMII预期完全不匹配。
这类问题软件层看不出任何异常,因为MDIO管理通道是独立的。所以排查时一定要回头核对PHY数据手册里“Strapping Options”和“Mode Select”相关引脚,确认每一个引脚在复位时被拉到正确的电平。
5.3 坑三:RMII为什么会出现“接收数据错误”
很多人在网上搜“rmii接口网卡接收数据错误”,其实就是我上面说的现象:PHY把数据送上来了,MAC也能识别到部分数据,但收到的包CRC全错或者内容莫名其妙。
我当时用抓包工具看到网口一直在报告FCS错误,检查了PHY寄存器,自动协商结果是100M全双工,看起来都正常。最后定位到还是参考时钟的质量问题。因为我在布局时让REF_CLK走线从PHY绕了很长一段才到MCU,附近还经过了一颗开关电源的电感,时钟边沿变得很差,MAC用这个时钟采样RXD0、RXD1的数据时,经常采样到不稳定电平。
解决方式说起来很简单,就是调整布线,把REF_CLK走短、远离干扰源,并且在源头靠近REF_CLKO引脚串联了一个22Ω电阻,用来匹配阻抗和抑制振铃。改版之后同样的软件配置就不再报CRC错了。
如果你的板子已经做出来了,没法改布线,也可以尝试把MDC分频调慢、降低以太网速率从100M降到10M测试。虽然10M情况下RMII依然用50MHz参考时钟,但实际数据流的建立时间裕量会好不少,能临时验证是不是信号完整性导致的问题。
5.4 一套通用的排查链路
把上面这些经验提炼成一套顺序,以后无论换哪个PHY、哪块STM32都可以套用:
- 量电源:PHY所有供电引脚电压正常,纹波不要太大。
- 量时钟:确认REF_CLK引脚上有50MHz信号,频率准确、幅值足够。
- 读ID:通过MDIO读PHY寄存器,确认管理通道是通的,且PHY地址正确。
- 看Link:检查PHY状态寄存器,确认网线连接后链路能自协商成功。
- 抓数据:如果链路Up但没有网络业务,检查MAC侧DMA配置、RMII引脚复用和信号质量。
- 查软件:最后再回头看LWIP配置、MAC地址、DMA描述符数量和中断处理。
这套顺序的核心思路是从物理层到链路层再到协议层,不要一上来就怀疑LWIP配置错了,那样很容易把时间浪费在根本无关的地方。
如果上来就自己画网口部分,建议直接从F407 + LAN8720A + 集成变压器RJ45这套成熟组合起步,原理图照着官方开发板抄,软件先不看协议栈、只看MDIO是否能把PHY ID读出来。做通之后再换其他PHY,你会慢慢理解不同PHY在时钟方案、模式选择、地址锁存上的差异到底在哪里。RMII接口看起来省了十几根引脚,实际并没有省掉对时钟和时序的把控,反而比MII更考验对信号关系的理解。希望这次的记录能帮你少走一些我走过的弯路。