☰
STM32实战:YT8512 PHY芯片RMII接口配置与调试全攻略
2026/10/2 3:06:32 网站建设 项目流程

开头

做嵌入式网络设备这么多年,RMII接口和PHY芯片配置基本属于"家常便饭",但每次换一颗新的PHY厂商,还是会踩到一堆坑。最近项目里因为国产化选型,把原来用的LAN8720A换成了裕太微的YT8512,本来以为寄存器兼容性很好,结果调RMII模式、自协商、时钟树的时候折腾了两天。网上关于这颗芯片的中文资料确实少,大部分还是英文datasheet里能翻出来的零散信息,所以我想把这次实战的完整过程整理出来,从RMII接口原理、YT8512硬件设计,到MDIO驱动代码、常见问题排查,一步步写清楚,尤其给出可以直接抄的STM32工程代码。不管你是用STM32、国产MCU还是FPGA做百兆以太网,这篇文章都能帮你少走弯路。

1. 先摸清RMII接口:引脚、时序和时钟

1.1 RMII接口信号一览

RMII全称Reduced Media Independent Interface,是IEEE 802.3u里定义的一种MAC层与PHY层之间的标准接口。相比老一代MII,它最大的特点就是引脚少。一个完整的RMII接口,从MAC到PHY之间只需要这么几根信号线:

信号名方向作用
TXD[1:0]MAC → PHY发送数据线,2bit并行
TX_ENMAC → PHY发送使能,高电平表示TXD上的数据有效
RXD[1:0]PHY → MAC接收数据线,2bit并行
CRS_DVPHY → MAC载波侦听/数据有效,相当于MII里的CRS和RX_DV合并
REF_CLK双向/共用50MHz参考时钟,决定所有信号采样节奏
MDCMAC → PHY管理接口时钟,用于MDIO总线
MDIO双向管理接口数据线,用来读写PHY寄存器
RESET_NMAC → PHY硬件复位,低有效

这里有个很多人初学容易忽略的点:RMII模式下没有独立的RX_CLK,接收数据是靠REF_CLK来采样的。所以REF_CLK的稳定性和相位关系,直接决定了链路能不能正常工作。TXD和RXD都只有2bit,在100M速率下配合50MHz时钟,刚好做到2bit × 50MHz = 100Mbps。

1.2 MII与RMII,为什么二选一

既然MII是老标准,为什么后来RMII能普及?关键就在引脚数量上。MII需要TXD[3:0]、RXD[3:0]、TX_CLK、RX_CLK、TX_EN、RX_DV、CRS、COL、TX_ER、RX_ER等一系列信号,算下来20根左右。RMII砍到7根核心信号线,对于MCU这类引脚资源紧张、本身又没有专门以太网MAC外设的芯片来说,优势非常明显。

特性MIIRMII
数据线宽度4bit TX + 4bit RX2bit TX + 2bit RX
参考时钟25MHz(100M)/ 2.5MHz(10M)固定50MHz
信号线数量约20根约7根
是否独立RX_CLK是否
适合场景FPGA/高性能交换芯片MCU、低引脚数SoC

代价是RMII对时钟要求更严格,50MHz的REF_CLK必须始终存在,哪怕10M速率下也不能像MII那样降低时钟频率。另外由于数据位宽减小,信号变化频率提高,对PCB走线等长、时钟质量要求也略高。但对STM32这种芯片来说,RMII明显是更现实的选择,我项目里也基本只用RMII。

1.3 REF_CLK的50MHz到底谁来提供

这是新手高频踩坑点。RMII的REF_CLK可以由PHY芯片产生,也可以由MAC侧产生,两种方案各有前提。

如果PHY芯片接的是25MHz晶振,PHY内部PLL会把25MHz倍频到50MHz,然后从REF_CLK引脚输出给MAC,这种叫PHY提供时钟,MAC的RMII接口工作在从模式。如果MAC侧的RMII时钟由外部有源晶振或者主控芯片时钟模块直接产生50MHz,再同时送给MAC和PHY,这种叫外部时钟源模式,PHY的REF_CLK变成输入。对STM32来说,常用的CubeMX配置里,ETH外设的RMII时钟源一般选MCO引脚输出50MHz,或者由外部PHY来提供。

YT8512我实际用的方案是:外部25MHz晶振接在PHY的XI/XO引脚上,PHY内部倍频出50MHz,然后通过REF_CLK引脚送到STM32的ETH_CLK引脚。这样STM32只负责接收时钟,不用额外配置MCO输出,省了一个引脚,也减少了一路高频信号。需要注意,STM32的ETH外设RMII模式对REF_CLK是输入,所以这种情况下刚好像"MAC从模式"一样工作。

2. YT8512这颗PHY的选型与硬件设计

2.1 为什么选YT8512

YT8512是裕太微电子推出的一颗10/100M自适应以太网PHY芯片,支持RMII和MII两种MAC接口,内部集成LDO稳压,外围电路非常精简。国产化替代趋势下,这颗芯片在不少开发板和工控设备上出现得很频繁,我接触到的飞凌、正点原子部分板卡都开始用这颗替换LAN8720A。

从功能上看,YT8512支持IEEE 802.3/802.3u标准,支持自动协商、交叉网线自动检测、MDI/MDIX自适应,这些对实际调试很有帮助。它内部有完整的DSP均衡器,链路信号质量比老芯片稳定,极端温度下工作也算可靠,工业级场景能接受。关键的是,它在硬件设计上基本和LAN8720A引脚兼容,很多现成电路可以直接替换,对正在做替代方案的人来说省事不少。

2.2 YT8512关键引脚和启动配置

YT8512有几个关键引脚在硬件设计时就得定下来,软件再折腾也改不了硬件strap的状态:

  • PHYAD[2:0]:PHY地址配置脚,一般默认接成000,对应PHY地址0x00。如果I2C/SMI总线上挂了多个PHY,得用电阻上下拉改成不同地址。
  • MODE/RMII_MODE相关的strap脚:决定PHY工作在RMII还是MII模式,有些芯片手册叫"Interface Mode Select"。这个比寄存器优先级高,硬件上接对了,软件才谈得上后续配置。
  • CLK_OUT_EN:决定是否把50MHz参考时钟从REF_CLK引脚输出,我们项目里要让PHY给MAC供时钟,所以这个脚要让输出使能。
  • LED_0、LED_1:链路/活动指示灯,调试时接两个LED能直观看到Link状态变化,强烈建议预留。

YT8512的strap引脚通常通过外部上下拉电阻在复位释放时被锁存,所以硬件定型后就不能轻易改了。如果遇到板子layout已经固定但模式不对,只能靠飞线或者割线改strap,非常麻烦。我建议原理图阶段就把这些脚用0欧电阻连接到电源或地,方便调试期切换。

2.3 外围电路设计要点

PHY芯片的硬件设计不算复杂,但细节决定成败。网络变压器加RJ45是标准方案,变压器中心抽头、终端电阻、共模电感这些都要按datasheet参考电路来接。YT8512和LAN8720A的参考设计很接近,我一般是把官方demo的原理图导出来,然后按自己板子的电源域做微调。

电源方面,YT8512内部有LDO,通常3.3V单电源供电,模拟电源和数字电源引脚分别加RC滤波。PHY芯片对电源纹波比较敏感,我用LDO给PHY单供电,实测比DCDC直供更稳。滤波电容要尽量靠近电源引脚,每个电源引脚至少放一个0.1uF和一个1uF电容组合。

PCB走线里,TXD[1:0]、RXD[1:0]这些数字信号要控制等长,CRS_DV和REF_CLK这跟线尤其要注意,尽量短并且远离其他高速信号。如果REF_CLK走线过长,相位噪声会直接影响数据采样。网络变压器到RJ45之间的差分线要严格按照差分阻抗100欧来走,间距与线宽同时控制,走线离板边要留足够距离防止串扰。这些做好了,后面调试会省掉一半的麻烦。

3. 软件配置实战:从零驱动YT8512

3.1 先搞定MDIO总线读写

所有PHY寄存器操作都走MDIO管理总线,这是IEEE 802.3 Clause 22定义的标准协议。一条完整MDIO帧长64bit,包括32bit前导码、2bit起始码、2bit操作码、5bit PHY地址、5bit寄存器地址、2bit转发状态,以及16bit数据。读操作时最后2bit是高阻态,由PHY芯片驱动数据线把寄存器值返回给MAC;写操作时数据由MAC侧发出。

如果MCU自带MDC/MDIO控制器,直接用底层驱动就好。但很多场景下需要用GPIO模拟,比如国产MCU没有以太网MAC外设,只是用SPI接了W5500这类芯片,那就不涉及PHY寄存器。但在STM32裸机工程里,我习惯直接写GPIO模拟MDIO,一是方便移植,二是调试时可以随时在任意引脚上重现时序。

下面是GPIO模拟MDIO读写的核心代码,参考了常见开源驱动,我整理成了可以直接用的版本:

#define MDIO_DELAY() for(volatile int _i=0;_i<20;_i++) // MDIO读 uint16_t mdio_read(uint8_t phyaddr, uint8_t regaddr) { uint16_t val = 0; // 前导码:至少32个连续1 for (int i = 0; i < 32; i++) { MDC_L(); MDIO_W(1); MDC_H(); } // 起始码 + 读操作码 (01 + 10) // 实际封装的逻辑是逐bit发送 write_bits(0x01, 2); // 起始码 write_bits(0x02, 2); // 读操作码 write_bits(phyaddr, 5); write_bits(regaddr, 5); // 2bit TA,读时第1个clk高阻,第2个clk为0 MDC_L(); MDIO_W(1); MDC_H(); // 高阻时间 MDC_L(); MDIO_W(0); MDC_H(); // turn around // 读16bit数据 for (int i = 0; i < 16; i++) { MDC_L(); val <<= 1; if (MDIO_R()) val |= 1; MDC_H(); } // 释放总线 MDIO_W(1); return val; }

写操作结构和读类似,只是操作码是01,TA里两个bit直接输出01,然后跟16bit数据。这里要注意,MDIO时序里电平切换必须在MDC低电平期间做好,MDC上升到高电平后PHY/MAC才采样数据,这个顺序反了很容易读到随机值。

3.2 PHY复位与ID识别

硬件上电会自动复位,但软件更稳妥的做法是控制RESET_N引脚做一次完整复位,然后延时等待PHY内部稳定。我一般给PHY芯片至少留100ms的上电稳定时间,软件复位后再延时20ms左右,等内部DSP和模拟电路完全就绪。

复位完成后第一步就是读PHY ID,确认SMI总线上挂的确实是YT8512,且地址正确。标准寄存器0x02和0x03存放PHY ID,其中0x02高16位、0x03低16位。我实际读取YT8512时,有些批次寄存器2读到0x0000,寄存器3读到类似0x0A13这类型号ID,具体数值建议以你手里的手册为准。关键是先判断读回来的值不是0xFFFF,也不是全0,否则先不要往下走,优先排查硬件。

uint16_t id1 = mdio_read(PHY_ADDR, 0x02); uint16_t id2 = mdio_read(PHY_ADDR, 0x03); printf("PHY ID: 0x%04X 0x%04X\r\n", id1, id2); if (id1 == 0xFFFF || id1 == 0x0000) { // 检查PHY地址和MDIO上拉电阻 }

3.3 软复位与RMII模式确认

PHY ID正常后,下一步是软复位。寄存器0x00的bit15是软复位位,写1触发复位,复位完成后硬件自动清零。软复位期间不要读写其他寄存器,等待100ms比较稳定。

mdio_write(PHY_ADDR, 0x00, 0x8000); HAL_Delay(100);

关键的RMII接口模式配置,得看YT8512的手册。在我拿到的版本里,YT8512默认根据strap引脚决定接口模式,软件可以通过扩展寄存器再次确认或切换。很多YT8512资料里提到厂商特定寄存器0x1F等位置有接口选择位,我这边工程里是通过硬件strap把模式锁死为RMII,所以软件里只做了读取确认,没有强行改写。如果你手头的板子没有正确strap,软件里就需要找到对应的interface mode寄存器去设置,这个一定要以datasheet里的寄存器定义为准,不同批次定义可能存在差异。

这里建议加一个自检逻辑:初始化完成后,把寄存器0x00的值读出来打印,看看bit8(全双工使能)、bit13(速度选择)是否符合预期。如果寄存器0x00的读写不正常,后面的自动协商配置根本没有意义。

3.4 自动协商与链路状态读取

YT8512上电默认开启自动协商,一般不需要改寄存器就能和交换机协商出100M全双工。但为了稳定性,我还是会在代码里显式开启ANE(Auto-Negotiation Enable)。

// 标准控制寄存器0x00 // bit12: 自动协商使能 // bit13: 速度选择(1=100M) // bit8: 全双工 uint16_t ctrl = mdio_read(PHY_ADDR, 0x00); ctrl &= ~(1 << 12); ctrl |= (1 << 12) | (1 << 13) | (1 << 8); // 100M全双工 + 自协商 mdio_write(PHY_ADDR, 0x00, ctrl); // 读取状态寄存器0x01,bit2是Link Status uint16_t status = 0; for (int i = 0; i < 100; i++) { status = mdio_read(PHY_ADDR, 0x01); if (status & (1 << 2)) { // 链路已建立 break; } HAL_Delay(10); }

Link Status这个bit有个特点:它是锁存性的,只要发生过一次断链,会一直保持0,必须读一次寄存器或者重新读才能反映当前真实状态。所以我判断链路时习惯连续读两次,第一次清锁存位,第二次才是当前状态。这也是很多人明明插着网线,却每次都读到链路down的原因。

STM32HAL库工程里,如果你的MCU自带MAC,也可以用HAL_ETH_ReadPHYRegister这类现成函数,但底层逻辑和上面GPIO模拟是一样的,寄存器地址和PHY地址匹配就行。

3.5 完整初始化代码串起来

把上面的逻辑合并成一个初始化函数,便于工程直接引用,核心流程包括:硬件复位、延时、读ID、软复位、确认RMII模式、开启自动协商、等待Link Up、最后配置MAC的RMII模式并启用收发。下面是简化但可用的版本:

uint8_t yt8512_init(void) { uint16_t reg; // 1. 硬件复位 PHY_RESET_GPIO = 0; HAL_Delay(50); PHY_RESET_GPIO = 1; HAL_Delay(100); // 2. 读取ID,确认挂在正确地址 uint16_t phy_id_high = mdio_read(PHY_ADDR, 0x02); uint16_t phy_id_low = mdio_read(PHY_ADDR, 0x03); if (phy_id_high == 0xFFFF || phy_id_high == 0x0000) { return 1; } // 3. 软复位 mdio_write(PHY_ADDR, 0x00, 0x8000); HAL_Delay(100); // 4. 设置100M全双工 + 自动协商 reg = mdio_read(PHY_ADDR, 0x00); reg |= (1 << 12) | (1 << 13) | (1 << 8); mdio_write(PHY_ADDR, 0x00, reg); // 5. 等待Link Up for (int i = 0; i < 200; i++) { reg = mdio_read(PHY_ADDR, 0x01); // 第一次读清锁存 HAL_Delay(1); reg = mdio_read(PHY_ADDR, 0x01); // 第二次读当前状态 if (reg & (1 << 2)) { // 链路OK return 0; } HAL_Delay(10); } return 2; }

实际工程中,初始化函数返回值要区分"没有检测到PHY""Link超时"等不同错误,方便串口定位。我不建议把初始化搞得特别多分支,嵌入式里越简单越可靠,出错时靠串口日志和硬件测量来定位比较快。

4. 实战中常见问题与排查技巧

4.1 现象:PHY ID读不到或读到0xFFFF

这是最基础也是最常见的现象。排查顺序我一般固定为:先量PHY的供电电压,确认3.3V正常;再用示波器看25MHz晶振有没有起振,幅度够不够;然后查PHYAD strap引脚的电平,确认SMI地址没配错;最后看MDC/MDIO这两根线上有没有上拉电阻,MDIO是开漏输出,必须有上拉才能被拉高,一般用10k上拉到3.3V。

如果上面都正常,还要考虑RESET引脚是否一直被拉低。我曾经遇到板子因为RC复位电路时间常数太大,导致PHY迟迟没有释放复位,MDIO自然读不到任何响应。后来直接把RESET_N改由单片机GPIO控制,复位时序完全可控,问题就消失了。

4.2 现象:Link能够Up但Ping不通

链路指示灯都亮了,自动协商也完成了,但ping却不通。这种问题多半在MAC侧RMII配置和时钟相位上,而不是PHY芯片本身。

先确认STM32或MCU的ETH外设是不是真的配置成了RMII模式,有些代码从别的项目拷过来,MII和RMII配置混在一起,初始化时自带矛盾。其次确认REF_CLK的频率,用示波器量50MHz是否稳定,如果PHY输出给MAC,波形幅度和抖动是否在可接受范围内。还有一个容易被忽略的点是CRS_DV信号,在RMII模式下这个信号由PHY产生,如果MAC的GPIO复用没配好,或者板子飞线接错,数据路径就不通。

这时候抓包Wireshark很有效。把电脑网卡和板子直连或者经过交换机抓包,看看板子上电后有没有发出ARP广播。如果VLAN/网络抓包看到ARP但PC不回包,多半是MAC地址问题或者IP配置在同一个子网里;如果板子根本没发包,问题大概率集中在RMII数据通路上。也可以先在板子上做自环测试:MAC发送模式改成内部回环,看收发计数是否有变化,快速定位是MAC侧还是PHY侧故障。

4.3 现象:速率协商成10M而不是100M

有时候插上交换机后速率只有10M。这种情况先确认网线是超五类以上且线序正常,然后用ethtool看协商结果。YT8512的自动协商能力要看两个地方:一个是0x04寄存器(Auto-Negotiation Advertisement),要确保bit7和bit6都置1,分别表示100Base-TX全双工和半双工能力被通告;另一个是0x00控制寄存器,不能手动强制成10M模式。

如果芯片默认通告能力存在问题,可以尝试先关闭自动协商,强制写100M全双工模式工作,看链路是否变为100M。我之前遇到过一次,是PHY的strap脚把速率上限限制在了10M,硬件电路上的下拉电阻和软件愿望不一致,改软件没用,最后用烙铁挪了0欧电阻才解决。所以排查这类问题要有耐心,先从硬件层面下手。

4.4 现象:Link偶尔Down、Ping延迟忽高忽低

表象是网络不稳定,实际多半是电源纹波大、PCB信号质量差或者网线接触不良。我遇到过YT8512布局离DCDC电感太近,开关噪声耦合到REF_CLK上,导致数据采样随机出错,表现为ping延迟波动大、偶尔丢包。解决办法是把PHY供电和REF_CLK走线远离DCDC,加磁珠和电容隔离。

另外晶振精度也会导致长时间传输后出现CRC错误。RMII的REF_CLK通常要求50ppm以内的精度,普通晶振尽量选±30ppm的,不要为了省钱用廉价谐振器。如果发现发出去的报文在交换机侧抓包看到CRC错误很多,时钟抖动基本是头号嫌疑。软件层面还可以降低MDC时钟频率,MDC一般不超过2.5MHz,时钟太快容易误采样。

4.5 Linux下快速验证PHY状态和回环测试

如果你的平台跑的是Linux,调试百兆PHY会比裸机方便得多。用ethtool直接看寄存器:

# 查看协商速率、双工、Link状态 ethtool eth0 # 查看PHY寄存器原始值 ethtool -d eth0 # 查看PHY ID ethtool -e eth0

自环测试的话,Linux内核自带PHY回环测试接口可以用ethtool触发,不过不同驱动支持程度不一样。我常用的土办法是:先用ifconfig把IP配上,然后板子上ping自己,确认协议栈基本通。再用PC和板子互ping,配不同IP方便路由区分,看丢包和延迟。需要抓包分析时用tcpdump在板子上抓,tcpdump -i eth0 -XX能看到完整帧头和MAC地址,比盲猜要快得多。

4.6 排查技巧速查表

现象检查方向快速定位手段
读不到PHY ID供电、晶振、复位、MDIO上拉、PHY地址万用表量电压、示波器看晶振波形
Link Up但Ping不通RMII模式配置、REF_CLK、CRS_DV、MAC侧复用示波器量REF_CLK、Wireshark抓PC端数据
协商成10M网线、通告寄存器、strap限速ethtool、读取0x04寄存器
丢包延迟高电源噪声、晶振精度、线缆质量抓包看CRC、换独立电源测试
一插网线就复位网口隔离、防雷器件、地弹断开RJ45测试、电源加磁珠隔离

结尾

回头再看这次YT8512的实战调试,很多问题其实不是芯片本身复杂,而是RMII接口的时钟关系、寄存器配置和硬件设计之间的耦合。

我个人操作中的体会是:拿到一块新PHY板子,先花半小时把硬件原理图、strap脚状态、PHY地址和时钟来源理顺,再写软件,比直接闷头敲代码要快得多。另外调试时严格"由底向上"走:供电、晶振、MDIO读写、PHY ID、Link状态、数据通路,每层都有可观测的证据再往上层走。最后补一句,YT8512这类国产芯片的资料对比境外芯片确实少一些,但遇到问题时多翻datasheet的寄存器表,多量波形,基本都能解决,用熟了反而觉得它性价比很高。

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

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

立即咨询