第一章 MDIO 协议概述与背景

做嵌入式开发的朋友,十有八九都跟 PHY 芯片打过交道。插上网线、网口灯亮、数据收发…… 这些背后其实藏着一条不起眼的串行总线——MDIO。你可能用过它,但未必真正把它搞清楚。

我个人习惯把 MDIO 叫做“PHY 芯片的后门”。为什么这么讲?你想想看,PHY 芯片正常工作时只管收发数据,但它的状态、配置、错误信息,全靠 MDIO 才能读出来。没有 MDIO,PHY 就像一个黑盒子。

1.1 什么是 MDIO?

MDIO,全称 Management Data Input/Output,一种管理数据输入输出总线。它专门用来访问以太网 PHY 芯片的内部寄存器。千兆以太网标准 IEEE 802.3 第 22 条和第 45 条详细定义了它的电气特性和协议帧格式。

MDIO 总共两条线:

  • MDC(Management Data Clock):管理时钟,由 MAC 或 CPU 侧主动提供。
  • MDIO(Management Data Input/Output):双向数据线,用于传输读写命令、地址和数据。

说白了,就是主设备(MAC/CPU)通过这两根线,跟 PHY 芯片对话:告诉它要读哪个寄存器,或者写什么值进去。PHY 再乖乖回复数据。

信号 方向 描述
MDC 主 → 从 时钟,频率最高 2.5 MHz(IEEE 802.3 规定)
MDIO 双向 数据线,时分复用发送命令与接收响应
一个小细节:有些 MCU 的 ETH MAC 模块内部自带 MDIO 控制器,通过配置寄存器就能自动收发,不用自己写 GPIO 模拟时序。但我遇到的很多低成本方案还是得手撸时序,后面章节我会手撕代码。

1.2 为什么需要 MDIO?

PHY 芯片的本质是物理层收发器,处理信号编码、解码、电平转换、自动协商这些脏活累活。但问题来了——我们怎么知道 PHY 的工作状态?自动协商结果是什么?链路是否建立?有没有远端错误?这些信息都存在 PHY 内部的寄存器里。

没有 MDIO,就无法:

  • 读取状态:比如链接状态、速度、双工模式。
  • 动态配置:比如强制设为百兆全双工,而不是靠自动协商。
  • 调试排错:比如读出中断状态寄存器,定位链路闪断原因。

我记得有一次调试一款工业交换机,现场链路频繁闪断。拿示波器量 TX 输出波形正常,但 MDIO 接口读出的 Link Status 寄存器一直在 0/1 之间跳变。后来发现是 PHY 的电源纹波太大,导致芯片内部锁相环失锁。要不是 MDIO 能实时读寄存器,这种间歇故障根本没办法定位。

1.3 MDIO 协议帧结构(IEEE 802.3 Clause 22)

这是最常用的 MDIO 帧格式,用来读写 32 个地址(0~31)、每个地址 16 位 的标准寄存器。PHY 芯片最少有 0~15 共 16 个基本寄存器,扩展寄存器走 Clause 45。

一个完整的 Clause 22 读写帧总长度固定:

  • 前导码:32 个连续 "1"
  • 起始码:01
  • 操作码:读 10 / 写 01
  • PHY 地址:5 位
  • 寄存器地址:5 位
  • TA 转换:2 位(读操作为 Z0,写操作为 10)
  • 数据:16 位
  • 闲置:至少 1 位高阻

帧结构表格就像这样:

段名 位宽 读 写
Preamble 32 全 1 全 1
Start 2 01 01
Opcode 2 10 01
PHYAD 5 PHY 地址 PHY 地址
REGAD 5 寄存器地址 寄存器地址
TA 2 Z0 10
Data 16 从 PHY 读取 写入 PHY
Idle ≥1 高阻 高阻

其中 TA 段的 Z0 比较恶心人:主设备在 TA 的第一位释放总线(高阻),同时 PHY 接管总线,然后在第二位驱动 0 作为应答。这一步时序如果没处理好,就可能读到错误数据。我曾经因为 GPIO 输入输出切换慢了半拍,导致数据位错位,查了一天才发现。

避坑:很多国产 PHY 芯片对 TA 时序要求更严格,尤其是高阻切换的空窗期,建议至少空出 200 ns 的保持时间。如果你用 GPIO 模拟,确认 GPIO 的输入输出延迟。最好先写一个简单的读 ID 寄存器测试(寄存器 2),验证时序没问题再往下写。

1.4 PHY 的标准寄存器简表

IEEE 802.3 规定了 0~15 共 16 个基本寄存器,这里列几个最常用的:

地址 名称 功能
0 BMCR 基本模式控制寄存器(复位、速度、环回、自动协商开关等)
1 BMSR 基本模式状态寄存器(链接状态、自动协商完成、远端故障等)
2 PHY IDR1 PHY 标识符高 16 位
3 PHY IDR2 PHY 标识符低 16 位
4 ANAR 自动协商通告能力
5 ANLPAR 自动协商链路伙伴能力

你想想看,只要读寄存器 0~3,就能拿到 PHY 的型号、复位状态、链接情况。很多驱动初始化流程就是:
读 ID → 软复位 → 配置速度/双工 → 开启自动协商 → 轮询链接状态。

1.5 一句心里话

MDIO 协议本身并不复杂,就是一条串行总线读写 16 位寄存器。但实际项目中翻车的点却不少:上拉电阻没加、MDC 频率超标、GPIO 模拟时序被中断打断、PHY 地址设错了(很多芯片默认 0x00 但需要拨码配置)……

我最早接触 MDIO 是在一个路由器项目上,MCU 的 MAC 内部没有 MDIO 控制器,只能用两个 GPIO 脚模拟。写了一个下午,连 PHY ID 都读不对。后来拿逻辑分析仪抓波形,发现是 MDC 空闲时没拉低,PHY 分不清帧边界。加了一条 MDC 空闲低保证,问题瞬间解决。

所以本章先把 MDIO 的来龙去脉讲清,下一节开始就是真正的实战——手把手写代码读写 PHY 寄存器,你跟着一起写一遍,一定会有感觉。

个人建议:初次接触 PHY 时,找个开发板,用 MCU 先读 PHY IDR 寄存器(地址 2 和 3),确认能读到正确值。这是最简单也是最关键的测试点。如果这步都错,后面全白费。
MDIO 协议体系框架 主设备(MAC/CPU) 从设备(PHY 芯片) CL1: 提供 MDC 时钟 CL2: 响应 MDIO 命令 MDC MDIO(双向) Clause 22 帧结构(64 位固定长度) 前导 32bit 起始 2bit Opcode 2bit PHYAD 5bit REGAD 5bit TA 2bit Data 16bit 读操作 写操作 自动协商 主发地址,从回数据 主发地址+数据 读取 BMCR/BMSR

1.6 本章小结

MDIO 总线是连接 MAC 与 PHY 的管理通道,只靠两根线就能完成 16 位寄存器的读写。它的帧结构虽然固定,但实际编码时要注意时钟极性、TA 时序和 PHY 地址。掌握 MDIO 是调试 PHY 的基础技能,不管你是做交换机、路由器,还是工业以太网,都绕不开它。