一、MDIO协议原理
1.硬件连接(控制引脚只有两个,一路MDC,一路MDIO)类似模拟iic 一路时钟线一路数据线
2.协议帧
(1)双向传输
(2)先传高位,再传低位
(3)MDIO相关管理寄存器 clause-22 总共32个寄存器 ,每个寄存器16bit
IDLE: 空闲状态下MDIO总线是高阻态(中间态),外部上拉至高电平状态
PRE:preamble前导码,32个连续的高电平bit位
ST : 固定发送两bit 01表示数据传输开始
OP:操作码,表示不同的操作类型 读(10) 写(01)
PHYAD:5个bit位组成的32个地址,先传高位
REGAD: 5个bit位组成的32个地址,先传高位
TA :TurnAround 。介于寄存器地址和寄存器数据之间的两个bit位用来转换数据方向
写操作:TA 由MAC芯片驱动成 10 ,写操作都是由MAC芯片驱动
读操作:TA第一个bit mac和phy都必须将mdio驱动成高阻态 外部上拉高电平
TA第二个bit 由PHY驱动MDIO 为低电平,驱动转移给PHY芯片
DATA:16bit数据位
写操作:MAC芯片驱动
读操作:phy芯片驱动
二、代码
hd_mdio.c
#include "hd_mdio.h"
#include "gpio.h"
#include "hd_gpt_timer.h"
// 1us延时
void delay_us(uint32_t us) {
volatile uint32_t i;
for (i = 0; i < us * (48 / 2); i++);
}
/**
* @brief Initialize GPIO pins for MDIO interface
* @note Configures MDC as output and MDIO as bidirectional
*/
void mdio_gpio_init(void) {
stc_gpio_cfg_t stcGpioCfg;
// Enable GPIO peripheral clock
Sysctrl_SetPeripheralGate(SysctrlPeripheralGpio, TRUE);
// Configure MDC pin (always output)
stcGpioCfg.enDir = GpioDirOut;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enDrv = GpioDrvH; // High drive capability for MDC
stcGpioCfg.enOD = GpioOdDisable;
stcGpioCfg.enCtrlMode = GpioAHB; // AHB control mode
Gpio_Init(MDC_GPIO_PORT, MDC_PIN, &stcGpioCfg);
// Configure MDIO pin (initial direction set to output)
Gpio_Init(MDIO_GPIO_PORT, MDIO_PIN, &stcGpioCfg);
}
/**
* @brief Set MDIO pin as output
* @note Re-initializes the pin with output direction
*/
void mdio_set_output(void) {
stc_gpio_cfg_t stcGpioCfg;
stcGpioCfg.enDir = GpioDirOut;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enDrv = GpioDrvH;
stcGpioCfg.enOD = GpioOdDisable;
stcGpioCfg.enCtrlMode = GpioAHB;
Gpio_Init(MDIO_GPIO_PORT, MDIO_PIN, &stcGpioCfg);
}
/**
* @brief Set MDIO pin as input
* @note Re-initializes the pin with input direction
*/
void mdio_set_input(void) {
stc_gpio_cfg_t stcGpioCfg;
stcGpioCfg.enDir = GpioDirIn;
stcGpioCfg.enPu = GpioPuDisable;
stcGpioCfg.enPd = GpioPdDisable;
stcGpioCfg.enDrv = GpioDrvH;
stcGpioCfg.enOD = GpioOdDisable;
stcGpioCfg.enCtrlMode = GpioAHB;
Gpio_Init(MDIO_GPIO_PORT, MDIO_PIN, &stcGpioCfg);
}
void mdio_write_bit(uint8_t bit) {
mdio_set_output();
if (bit) MDIO_HIGH(); else MDIO_LOW();
delay_us(1);
MDC_HIGH(); delay_us(1);
MDC_LOW(); delay_us(1);
}
uint8_t mdio_read_bit(void) {
uint8_t bit;
mdio_set_input();
delay_us(1);
MDC_HIGH(); delay_us(1);
bit = MDIO_READ();
MDC_LOW(); delay_us(1);
return bit;
}
void mdio_send_bits(uint16_t data, uint8_t len) {
for (int i = len - 1; i >= 0; i--) {
mdio_write_bit((data >> i) & 1);
}
}
uint16_t mdio_read_bits(uint8_t len) {
uint16_t val = 0;
for (int i = len - 1; i >= 0; i--) {
if (mdio_read_bit()) val |= (1 << i);
}
return val;
}
uint16_t mdio_read(uint8_t phy_addr, uint8_t reg_addr) {
uint16_t val = 0;
// 发送前导码(32个1)
mdio_set_output();
for(int i=0; i<32; i++) {
mdio_write_bit(1);
}
// 发送读命令
mdio_send_bits(0x06, 4); // 01(start) + 10(read)
mdio_send_bits(phy_addr, 5);
mdio_send_bits(reg_addr, 5);
// 读取数据
mdio_set_input();
mdio_read_bit(); // 跳过turnaround
val = mdio_read_bits(16);
mdio_set_output();
MDIO_HIGH(); // 释放总线
return val;
}
void mdio_write(uint8_t phy_addr, uint8_t reg_addr, uint16_t data) {
// 发送前导码(32个1)
mdio_set_output();
for(int i=0; i<32; i++) {
mdio_write_bit(1);
}
// 发送写命令
mdio_send_bits(0x05, 4); // 01(start) + 01(write)
mdio_send_bits(phy_addr, 5);
mdio_send_bits(reg_addr, 5);
mdio_send_bits(0x02, 2); // turnaround
mdio_send_bits(data, 16);
MDIO_HIGH(); // 释放总线
}
hd_mdio.h
#ifndef HD_MDIO_H
#define HD_MDIO_H
#include <stdbool.h>
#include "gpio.h"
#define MDC_GPIO_PORT GpioPortA
#define MDC_PIN GpioPin0
#define MDIO_GPIO_PORT GpioPortA
#define MDIO_PIN GpioPin1
#define MDC_HIGH() Gpio_SetIO(MDC_GPIO_PORT, MDC_PIN)
#define MDC_LOW() Gpio_ClrIO(MDC_GPIO_PORT, MDC_PIN)
#define MDIO_HIGH() Gpio_SetIO(MDIO_GPIO_PORT, MDIO_PIN)
#define MDIO_LOW() Gpio_ClrIO(MDIO_GPIO_PORT, MDIO_PIN)
#define MDIO_READ() Gpio_GetInputIO(MDIO_GPIO_PORT, MDIO_PIN)
// PHY地址定义(根据硬件连接设置)
#define PHY_ADDR_YT8531 0x01
#define PHY_ADDR_YT8011 0x02
void mdio_gpio_init(void);
uint16_t mdio_read(uint8_t phy_addr, uint8_t reg_addr);
void mdio_write(uint8_t phy_addr, uint8_t reg_addr, uint16_t data);
#endif
三、效果
写时序 mdio_write(0x01, 0x03, 0x5555);