第2章 MDIO/MDC协议详解与读写时序

咱们做PHY调试,天天和寄存器打交道。要读写这些寄存器,就得通过MDIO/MDC接口。这玩意儿说白了就是两条线:一条时钟线(MDC),一条数据线(MDIO)。PHY芯片靠它们跟MAC层或者CPU通信。我第一次接触这个接口时,以为挺简单,结果被时序坑了好几次。今天我把这块说透了,你以后遇到寄存器读出来全是0xFF或0x00,大概率是MDIO没玩对。

2.1 协议基础:信号定义与帧结构

MDIO(Management Data Input/Output)是双向数据线,MDC(Management Data Clock)由主控产生。标准速率下MDC最高25MHz,但有些老PHY只能到12.5MHz。我建议你读手册时先看这个参数,否则时序超了,PHY根本不搭理你。

完整的MDIO帧长64个MDC周期,包含几个固定段。下面这个表列出每个字段,你对照着看就知道了。

字段 位宽 说明
Preamble 32 连续32个1(高电平),用于同步
Start of Frame 2 固定为01,标识帧开始
Opcode 2 读为10,写为01
PHY Address 5 目标PHY芯片地址(0~31)
Register Address 5 寄存器地址(0~31)
Turnaround 2 读操作时:Z0(高阻→0);写操作时:10
Data 16 读写的数据内容

你看,光是个帧结构就有这么多讲究。我实战时经常犯的错误是:Preamble少了几个时钟,或者PHY地址写错了。有一次我拿示波器抓MDIO波形,发现Preamble只有30个1,结果PHY根本没响应。后来我习惯在代码里把Preamble多发送几个时钟,反正没坏处。

2.2 读写时序详解

其实读和写的主要区别就在Opcode和Turnaround这两个地方。咱们分开说。

读操作:主控发完前导码后,Opcode为10(二进制),接着发PHY地址和寄存器地址。之后Turnaround是Z0——主控先释放MDIO(高阻),等一个时钟,让PHY接管数据线,这段时间是PHY把寄存器值放到MDIO上。注意!Z0那个时钟周期里,MDIO是高阻态,外部需要上拉电阻把它拉高,否则数据会飘。

写操作:Opcode为01,Turnaround直接用10。主控在Turnaround阶段输出1和0,然后紧接着输出16位要写的数据。写操作全程由主控驱动MDIO,PHY只管接收。

说得再直白一点:读操作需要交换数据线控制权,所以多了一个高阻过渡。写操作则全程主控当家。

下面这张SVG图把读和写操作的关键差异画出来了,你一看就明白。

MDIO读写帧时序对比 MDIO读写帧时序对比 读操作帧 Preamble 32个1 01 10 读Opcode PHY Addr Reg Addr Z0 高阻→0 Data(16bit) PHY驱动 写操作帧 Preamble 32个1 01 01 写Opcode PHY Addr Reg Addr 10 主控驱动 Data(16bit) 主控写入 注意 读操作TA是Z0,主控需释放总线 写操作TA是10,主控全程控制

2.3 实战中的常见坑与经验

纸上谈兵容易,真上手就发现有各种坑。我总结几个你大概率会碰到的。

时钟频率:MDC不是越快越好。我见过有人把MDC设到50MHz,结果PHY不理人。实际上标准规定最高25MHz,但有些老PHY降到12.5MHz甚至更低。我的习惯是:先2.5MHz跑起来,确认通信正常后再往上提。

上电时序:PHY复位后需要一段时间才能响应MDIO。我踩过坑:软件在PHY复位后立即发MDIO帧,结果读回的数据全乱。至少要等1ms,安全起见我一般等10ms。有些PHY手动复位引脚拉低后还要等更久,看手册最准。

  • PHY地址冲突:多颗PHY共一条MDIO总线时,地址不能重复。我遇到过硬件上两个PHY都设为0x1F,结果全挂。解决办法:每颗PHY单独用硬件管脚设置地址,程序里逐个扫。
  • 上拉电阻:MDIO线必须接上拉电阻到1.8V或3.3V,通常2.2kΩ~4.7kΩ。没有上拉电阻,读操作时高阻状态会让信号悬空,数据全乱。
  • 寄存器的影子拷贝:有些PHY在读取某些寄存器(如状态寄存器)后会自动清除某些位。你连续读两次,第二次可能看到不同的值。我自己的经验是:读一次后立刻保存,不要再读第二次。

嗯,还有一点很多人忽略:MDC在空闲时必须保持低电平。如果时钟线一直高或一直低,PHY可能误判帧边界。

接着说说我自己的一个小技巧。我用逻辑分析仪抓MDIO波形时,先抓一个已知的写操作,看帧头是不是对的。比如写控制寄存器0x00为0x1140,然后读回来验证。如果读回不对,先看PHY地址是否正确,再看TA的Z0部分主控是否释放了总线。有一次我发现是主控在TA阶段没释放,导致数据线一直冲突,读到的数据全是0xFF。

快速定位异常值的思路:当你发现读到的寄存器值出乎意料时,按这个顺序排查——①MDC频率是否超标;②PHY地址是否匹配硬件;③上拉电阻是否安装;④复位后的等待时间够不够;⑤是否是读清型寄存器;⑥用示波器看MDIO波形,确认帧结构完整。

好了,MDIO/MDC这块其实不复杂,但每个细节都可能让你卡半天。记住:读看Z0,写看Opcode。搞定这两点,大部分问题都能解。