4. MDIO 读操作时序详解归纳总结
MDIO 读操作,说穿了就是主设备(比如 CPU 或 MCU)通过两根线从 PHY 的寄存器里把数据“拉”出来。时序对了,数据就稳;时序错一点,读回来的值可能全是错的。我早年调一块板子时,就因为读时序里的空闲周期没留够,结果 PHY 的状态机一直没复位,读啥都是 0xFF……后来查了好久才找到原因。
4.1 帧结构速览
标准的 MDIO 读帧一共 64 个时钟周期,每个周期在 MDC 上升沿传输一位。咱们直接看结构:
| 字段 | 位宽 | 值 | 说明 |
|---|---|---|---|
| PREAMBLE | 32 | 全 1 | 前导码,让 PHY 同步 |
| START | 2 | 01 | 帧起始,不是 10! |
| OP | 2 | 10 | 读操作码(写是 01) |
| PHYAD | 5 | 0–31 | 目标 PHY 地址 |
| REGAD | 5 | 0–31 | 目标寄存器地址 |
| TA | 2 | Z0 | 转向位:第一拍高阻,第二拍 PHY 驱动 0 |
| DATA | 16 | 读回值 | PHY 在 MDC 上升沿输出数据 |
| IDLE | ≥1 | 高阻 | 总线释放,准备下一帧 |
关键点:TA 位的第二拍必须为 0,这是 PHY 在告诉主控“我准备好输出数据了”。如果你读到 TA 的第二拍不是 0,说明 PHY 根本没响应——地址可能写错了,或者 PHY 没上电。
4.2 读操作 vs 写操作的核心差异
很多人觉得读写差不多,其实就两个地方不一样:
- 操作码不同:读是
10,写是01。千万别搞反。 - 数据方向不同:写操作时主设备全程驱动 MDIO;读操作在 TA 位之后,主设备必须释放 MDIO(高阻),由 PHY 驱动数据线。很多新手在这块翻车——主设备没释放,两个输出打架,数据全乱。
我曾经踩过的坑:读帧的 TA 第一拍,主设备要主动释放总线,变成高阻输入。如果你用的 GPIO 模拟,一定要先把 GPIO 方向设为输入,等 2 个时钟后再切回输出。我有一版代码忘了切方向,结果 PHY 拉低 MDIO 时,主设备还在使劲输出 1,整个波形都扭曲了。
4.3 时序参数与容差
MDC 的最高频率标准是 2.5 MHz(有些 PHY 支持 12.5 MHz 快速模式),但实际项目中我一般跑 1–2 MHz,留足余量。几个关键时间:
| 参数 | 最小值 | 说明 |
|---|---|---|
| MDC 周期 | 400 ns | 2.5 MHz 对应周期 |
| MDC 高/低电平宽度 | 160 ns | 占空比接近 50% |
| MDIO 数据建立时间 | 10 ns | 相对 MDC 上升沿 |
| MDIO 数据保持时间 | 10 ns | 相对 MDC 上升沿 |
| 帧间空闲时间 | ≥1 MDC 周期 | 实际我习惯留 2–3 个周期 |
为什么强调空闲时间?因为 PHY 内部状态机需要时间回到 IDLE。如果你连续发帧,空闲时间太短,PHY 会把下一帧的前导码当成上一帧的尾巴——我遇到过这种情况,读回来的数据总差一位。加两个空闲周期就解决了。
4.4 读时序步骤拆解
你自己实现读操作时,可以照着下面这几步走:
- 拉高 MDC,释放 MDIO(高阻),准备开始。
- 输出 32 个 1:前导码,每个 1 在 MDC 上升沿之前准备好。
- 输出 START(01):注意是 0 然后 1,不是 10。
- 输出 OP(10):告诉 PHY 接下来是读。
- 输出 PHYAD(5 位):高位在前,比如地址 0x03 就输出 00011。
- 输出 REGAD(5 位):要读的寄存器地址。
- 进入 TA 阶段:第一拍将 MDIO 设为输入(高阻);第二拍读取 MDIO 引脚,检查是否为 0。如果为 0,说明 PHY 已响应;否则报错。
- 读取 16 位数据:在每个 MDC 上升沿前 10 ns 采样 MDIO 引脚,从高位到低位。
- 释放总线:数据读完立刻把 MDIO 设为输出高阻,再走至少 1 个空闲时钟。
嗯,有一个小技巧:采样数据时最好在 MDC 上升沿之后等 10–20 ns 再读 GPIO,因为 PHY 的输出可能会有一些延迟。如果你用 FPGA 实现,考虑用下降沿采样——这样数据在上升沿输出,下降沿采样,刚好错开半个周期,容错性好很多。我在项目里一直这么干。
4.5 读操作全流程图
下面这张图把整个帧结构串起来了。你注意看 TA 位那里,主设备方向的变化——之前是输出,之后是输入。这是读操作最精华的地方。
4.6 避坑指南
- 前导码不能少:虽然有些 PHY 支持“无前导码模式”,但默认大部分都要求 32 个 1。我建议永远老老实实发全,省得兼容性问题。
- TA 位一定要读取确认:如果 TA 第二拍不是 0,立即停止后续操作,检查 PHY 地址和电源。强行读数据只会得到垃圾。
- 空闲时间别吝啬:至少 1 个时钟,但我的习惯是 2–3 个时钟。连续读不同寄存器时,中间加一点空闲,PHY 状态机更稳定。
- 注意 MDIO 上拉电阻:标准要求 MDIO 有 1.5kΩ 上拉到 1.8V/2.5V/3.3V。如果你用 FPGA 直连,记得外部加电阻。没上拉的话,高阻状态电平会飘,TA 第一拍就可能读到错误值。
曾经有个教训:我在一块 4 层板上布 MDIO 和 MDC 时,两根线走了差不多 8 cm,还过了一个连接器。结果信号反射严重,读数据总是丢低位。后来在终端加了个 33Ω 串联电阻,并减小驱动强度,问题才消失。高频信号设计,走线越短越好,实在长了就要做匹配。
读操作是整个 MDIO 通信里最常用的操作。你只要把帧结构记牢,TA 位的方向切换处理好,再注意一点空闲和采样时机,基本上就不会出大问题。嗯,多看几次示波器波形,心里就有底了。