第2章:MDIO总线协议基础——时钟、数据、帧格式
做硬件调试这么多年,我接触过不少串行总线。MDIO 算是比较"温柔"的一种——只有两根线,时序也清晰。但温柔不代表不会出问题,很多通信故障的根源就藏在最基础的协议细节里。
这一章,咱们把 MDIO 的底子夯实。时钟、数据、帧格式,挨个捋一遍。
2.1 物理层信号:MDC 与 MDIO
MDIO 总线就两根线:管理数据时钟(MDC)和管理数据输入输出(MDIO)。
- MDC:时钟线,由主设备(通常是 MAC 或 CPU)驱动。频率最高能跑到多少?标准里写 2.5 MHz,但实际很多器件能跑到 12.5 MHz。我个人习惯先按 2.5 MHz 起步调试,稳定了再往上提。
- MDIO:双向数据线。主设备写的时候输出数据,读的时候由从设备(PHY)拉高或拉低。这就带来了一个问题:你想想看,两个设备共一根线,不做控制就会打架。所以协议规定了个严格的三态切换机制。
小经验:我在项目中遇到过 MDC 信号没加终端电阻,导致边缘抖动严重。排查了两天才发现——示波器探头一搭上去波形就正常,一拿走就乱码。嗯,后来在每个 PHY 的 MDC 脚旁边放了个 22 Ω 串阻,问题消失。
2.2 帧格式:一笔一笔拆开看
MDIO 的每一笔传输都是一个固定格式的帧。标准帧长度为 64 个时钟周期,支持 32 个 PHY 地址和 32 个寄存器地址。
时序概览(读帧):
预置空闲 → 起始 [2位] → 操作码 [2位] → PHY地址 [5位] → 寄存器地址 [5位] → TurnAround [2位] → 数据 [16位] → 空闲
下面这个表格把每个字段讲清楚:
| 字段 | 长度 | 描述 |
|---|---|---|
| 起始 (Preamble) | 32 个"1" | 同步用。主设备先发 32 个 MDC 周期,MDIO 一直为高。这是在告诉 PHY:"注意,准备说正事了。" |
| SFD (起始定界符) | 2 位 "01" | 标记帧开始。如果收到其他组合,PHY 会忽略这一帧。 |
| 操作码 (Op Code) | 2 位 | 01 = 写;10 = 读。00 和 11 保留。 |
| PHY 地址 | 5 位 | 选中哪个 PHY(0 ~ 31)。通常通过 PHYAD[4:0] 引脚配置。 |
| 寄存器地址 | 5 位 | PHY 内部寄存器的编号(0 ~ 31)。 |
| TurnAround (TA) | 2 位 | 控制权切换时间。写帧: 主设备发 "10";读帧: 主设备发 "Z",PHY 发 "0" 作为第一个 TA 位,再发第二位(数据量程控制,通常为 0)。 |
| 数据 (Data) | 16 位 | 读写的数据内容。 |
| 空闲 (Idle) | 至少 1 位高阻 | MDIO 释放为高阻,由外部上拉电阻拉高。 |
坑点提醒:我曾经遇到一个 PHY 在 TA 阶段不按套路出牌——读帧时第一个 TA 位不置 "0",直接输出数据。查了勘误手册才知道那个型号有 bug。所以排查通信失败时,一定要用示波器抓 TA 段的波形,确认主从设备谁在驱动。
2.3 读帧 vs 写帧:区别在哪儿?
说白了,读写帧的前半段一模一样——都从起始、操作码、地址、寄存器地址。区别就在 TA 和后面的数据驱动者。
- 写帧:主设备全程“控场”。TA 阶段发 "10",让 MDIO 仍然由主设备驱动,紧接着发 16 位数据。
- 读帧:主设备在 TA 第一个周期释放 MDIO(三态),PHY 接管。第二个 TA 周期 PHY 把驱动拉低(表示准备好),随后 PHY 在接下来的 16 个周期输出数据。
你可以把 TA 想象成一根接力棒——写帧不交棒,读帧必须交棒。如果 PHY 没接过这棒子,MDIO 会一直保持高阻状态,主设备读回来的全是 "1"(或者浮空电平乱跳)。
2.4 时序参数:别放松任何一个细节
协议对时序只有两个硬要求:MDC 占空比 40%~60%,MDIO 相对 MDC 建立/保持时间 > 10 ns。但实际硬件没这么宽容。
| 参数 | 最小值 | 典型值 | 最大值 |
|---|---|---|---|
| MDC 频率 | — | 2.5 MHz | 12.5 MHz |
| MDC 高电平时间 | 40 ns | 200 ns | — |
| MDC 低电平时间 | 40 ns | 200 ns | — |
| MDIO 建立时间(相对 MDC 上升沿) | 10 ns | 20 ns | — |
| MDIO 保持时间(相对 MDC 上升沿) | 10 ns | 20 ns | — |
关键:MDIO 在 MDC 的下降沿改变状态,PHY 在上升沿采样。这意味着主设备写数据时,要在 MDC 下降沿之前把 MDIO 设好;读数据时,PHY 在下降沿更新数据,主设备在下一个上升沿锁存。
2.5 帧结构总览图
下面这张 SVG 把整个帧流程串起来,读帧和写帧的 TA 差异用颜色区分开了。
2.6 常见陷阱与调试小贴士
最后聊几个我踩过的坑,希望能帮你少走弯路。
- 陷阱一:Preamble 长度被压缩。有些 PHY 支持高帧率模式(Preamble 只发 1 周期 "1"),但默认都要求 32 个。如果通信时好时坏,先看看初始化代码里 Preamble 是不是被偷懒了。
- 陷阱二:TA 时刻的电平冲突。读帧时主设备要释放总线,但释放延迟、上拉电阻强弱都会影响 PHY 能否正常接管。我曾经用 4.7 kΩ 上拉导致 TA 切换时间不够,换成 1 kΩ 就好了。
- 陷阱三:寄存器地址乱序。MDIO 帧里 PHY 地址和寄存器地址是 MSB-first。如果你习惯 LSB-first 编程,写寄存器 0x15 却发成了 0x2A,PHY 不会理你。
我的惯用调试法宝:逻辑分析仪抓 MDC+MDIO,再配合一个 Python 脚本把帧解析成字段。比盯着示波器手动数脉冲快十倍。
好了,这一章的基础就这些。MDIO 协议不复杂,但每个细节都可能变成故障的导火索。下一层咱们就要拿着这些知识,去排查实际总线上的各路妖孽了。