MDIO 总线协议详解:帧格式、读写操作指令、起始/停止条件
做嵌入式这些年,跟 MDIO 打交道的次数多得数不清。说实话,MDIO 协议不算复杂,但坑不少。我记得刚入行那会儿,第一次调 D-PHY 链路,光看 MDIO 的时序就把我折腾够呛。后来摸透了帧格式和指令细节,才真正体会到——MDIO 就是管理层的串行握手。
这一节,我把 MDIO 协议的细节拆开来讲。从帧结构到读写指令,再到起始/停止条件。我会结合自己踩过的坑,尽量让你一次搞明白。
2.1 MDIO 帧结构——到底长什么样?
MDIO 总线上每帧数据固定是 64 位(或者说 32 个 MDC 时钟周期,每个周期传输 2 位?别急,后面说)。
嗯,实际上 MDIO 是串行同步协议,每个 MDC 时钟沿采样 1 位数据。一帧共 64 个 MDC 周期,传输 64 位数据。这 64 位有固定的分段:
| 字段 | 位宽 | 描述 |
|---|---|---|
| PREAMBLE | 32 位 | 前导码,全 1,用于同步 |
| ST (Start) | 2 位 | 帧起始标志 |
| OP (Operation) | 2 位 | 读 / 写操作码 |
| PHYAD | 5 位 | PHY 设备地址 (0~31) |
| DEVAD (REGAD) | 5 位 | 寄存器地址 (0~31) |
| TA (Turn Around) | 2 位 | 地址/数据切换周期 |
| DATA | 16 位 | 读回或写入的数据 |
记得:前导码的 32 个 1 不是摆设。我遇到过一些 PHY 芯片要求前导码严格 32 位,少了或者多了都不认。尤其某些国产 PHY,前导码的时序余量特别紧。我曾因为 MCU 的 MDC 时钟抖动导致前导码少了 1 位,结果怎么读都是 0xFFFF。查了三天才发现是前导码问题。
2.2 起始条件(ST)与停止条件
MDIO 的起始条件通过 ST 字段来标识。ST 固定为 2 位:
- ST = 01:表示一帧新数据的起始(标准帧)
- ST = 00:保留,不能用于数据帧(有些 PHY 用它标识管理帧,但非标准)
停止条件呢?MDIO 没有单独的总线释放信号。一帧结束后,MDIO 数据线变成高阻态(三态),由 PHY 或 MAC 的上下拉电阻维持高电平。下次传输前,主设备先发出前导码,再拉低数据线形成真正的“起始”。
说白了,MDIO 的停止就是帧结束后的空闲高电平。
避坑:我曾经在切换主从设备时,忘记让总线空闲至少一个 MDC 周期。结果连续帧之间没有足够的 Turn Around,导致下一帧的起始被误判。我的经验是:每帧结束后,主设备必须释放 MDIO 总线(输出高阻),然后等待至少 2 个 MDC 高电平周期再发起下一帧。
2.3 读写操作指令(OP 字段)
OP 码告诉 PHY 你要干嘛:
- OP = 01:写操作(Write)
- OP = 10:读操作(Read)
- OP = 00 或 11:保留或用于地址扩展
写操作流程:主设备在 DATA 段输出 16 位数据,TA 段由主设备驱动 2 位(Z0),然后 PHY 在接下来的 DATA 段采样。
读操作流程:主设备在 TA 段释放总线(Z态),由 PHY 驱动 2 位(Z0),然后 PHY 在 DATA 段输出 16 位数据。
这里有个关键点——TA 段的第一个位是“Z”高阻,第二个位是“0”。为什么?我猜是为了确认从设备已准备好。实际上,很多 PHY 对 TA 段要求很严格:主设备必须在第一个 MDC 上升沿前释放总线,否则 PHY 不会输出数据。
个人习惯:我写 MDIO 驱动时,会在 TA 段插入一个空操作(NOP),确保总线切换有足够的余量。尤其当 MDC 频率超过 10MHz 时,信号反射和边沿抖动可能在 TA 段造成毛刺。我曾经在 25MHz 的 MDC 下做实验,发现 TA 段如果太短,PHY 会误将主设备的释放动作当成数据,导致读回的结果错位。
2.4 读写完整时序示例
下面是一个读寄存器的完整帧(假设 PHY 地址 0x1E,寄存器 0x0A):
MDC 时钟周期: 0..31 32-33 34-35 36-40 41-45 46-47 48-63
MDIO 内容: 32个1 01 10 11110 01010 Z0 (16位读数据)
写操作类似,只是 TA 段变成主设备驱动 Z0,然后 DATA 段由主设备输出数据。
2.5 用 SVG 梳理帧结构
下面这张图把 64 位 MDIO 帧的完整结构画了出来。我建议你仔细看节点间的衔接——尤其 TA 段和 DATA 段的方向变化。
2.6 常见问题与我的调试建议
写到这里,回忆一下我以前在 MDIO 上栽过的跟头:
- 前导码长度不对:有的 PHY 对前导码要求 32 位,有的能识别更少。保险做法:固定 32 位。
- TA 段方向切换失败:主设备释放总线后,PHY 可能延迟 1~2 个 MDC 才接管。我的建议:TA 段的 Z0 之后,再插入一个 dummy 时钟让总线稳定。
- 地址匹配问题:PHYAD 和 DEVAD 的 bit 顺序?MDIO 采用 MSB 优先(高位在前)。比如地址 0x1E 的二进制是 11110,发送时先发 MSB(1)。我曾经搞反过,结果一直读不到数据。
- 时钟频率过高:MDC 理论上最高 2.5MHz(802.3 规范),但现代 PHY 通常支持更高。不过我建议不要超过 12.5MHz,否则从机可能采样不稳。
经验分享:调试 MDIO 时,一定要用逻辑分析仪或示波器抓 MDC 和 MDIO 两根线。我曾经仅靠打印日志调试,完全看不出问题。后来发现是 MCU 的 GPIO 配置成开漏输出,上升沿太慢导致采样错位。嗯,从那以后,我调任何串行协议都先看波形。
2.7 本章小结
MDIO 协议其实不复杂——64 位一帧,分 7 个段。只要抓住起始标志(ST=01)、操作码(OP)、地址段、TA 切换、数据段这五个核心,基本就掌握了。实际项目中遇到的大部分问题都在 TA 段和时钟匹配上。记住:调试先看波形,不要靠猜。