STM32F407扩展百兆网口:RMII接口与LAN8720A实战调试
最近做的一块板子需要在STM32F407上扩展一个百兆网口用来做远程参数配置和数据上报。刚开始我并没太当回事觉得选一颗PHY芯片、把MAC侧的引脚连上、初始化一下以太网外设再挂个LWIP就能收工。结果从画原理图到真正调通前前后后折腾了将近两周。回头复盘问题几乎都出在一些非常基础但没人给我提前打预防针的细节上。这篇文章就把我这次用RMII接口连接外部PHY芯片的完整过程、选型逻辑和踩坑记录整理出来。文章以STM32F407 LAN8720A这套非常经典的组合为主线只要你的芯片也带内部以太网MAC比如F4系列、G4系列、H7系列很多结论都是通用的。内容会比较偏向硬件连线、时钟方案和调试方法适合第一次碰以太网、想用STM32内部ETH外设而不是外挂W5500这类协议栈芯片的朋友参考。1. 为什么我最终选了RMII而不是MII很多人第一次接触STM32以太网时都会遇到两个词MII和RMII。MII是标准介质无关接口RMII是精简版。名字里多了一个RReduced但实际差别不只是“少几根线”这么简单。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根。我当时用的是STM32F407VET6LQFP100封装引脚本来就很紧张还要兼顾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等。选型时重点看几个维度时钟方案是否匹配、供电复杂度、封装、价格和资料齐全度。维度LAN8720ADP83848KSZ8081参考时钟方案外接时钟后输出REF_CLK供MAC使用常用25MHz晶振配置灵活支持25MHz晶振或外部时钟输出50MHz给MAC内部稳压内置1.2V稳压外围简单需要额外关注供电拓扑内置稳压封装与体积QFN小封装适合紧凑布局QFP封装容易焊接QFN封装市场资料极多F407板卡常用老牌稳定资料丰富工业级常用如果你是第一次做我建议直接照抄成熟组合STM32F407 LAN8720A 集成变压器的RJ45座子。先跑通再考虑换PHY不然很容易同时踩进“时钟不对”“模式不对”“地址不对”好几个坑里排查起来非常痛苦。3.2 核心引脚连接关系下面这张连接表基本就是我做原理图时依据的映射关系。不同MCU封装下引脚复用可能略有差异但RMII信号到PHY的对应关系是一致的。STM32F407引脚信号名LAN8720A引脚说明PA1RMII_REF_CLKREF_CLKO50MHz参考时钟输入PB11RMII_TX_ENTX_EN发送使能PB12RMII_TXD0TXD0发送数据位0PB13RMII_TXD1TXD1发送数据位1PC4RMII_RXD0RXD0接收数据位0PC5RMII_RXD1RXD1接收数据位1PA7RMII_CRS_DVCRS_DV载波侦听与接收有效复合信号PC1RMII_MDCMDC管理接口时钟PA2RMII_MDIOMDIO管理接口数据需上拉电阻任意GPIOPHY_nRSTnRSTPHY复位信号低有效任意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或DHCPIP地址、子网掩码、网关先按局域网默认值填一个。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更考验对信号关系的理解。希望这次的记录能帮你少走一些我走过的弯路。