环境是Attiny84A对接phy 88q2112,Attiny mdio访问phy时,读device1, register2的值老是不对,不知道怎么调试。
代码如下:
#if CODE_DESC("Clause45")
// 定义ATtiny84A的引脚 - 使用PORTB
#define MDIO_DIR_REG DDRB
#define MDIO_OUT_REG PORTB
#define MDIO_IN_REG PINB
#define MDIO_PIN PB1
#define MDC_DIR_REG DDRB
#define MDC_OUT_REG PORTB
#define MDC_PIN PB0
// MDIO线方向控制宏
#define MDIO_AS_OUTPUT() (MDIO_DIR_REG |= (1 << MDIO_PIN))
#define MDIO_AS_INPUT() (MDIO_DIR_REG &= ~(1 << MDIO_PIN))
#define MDIO_HIGH() (MDIO_OUT_REG |= (1 << MDIO_PIN))
#define MDIO_LOW() (MDIO_OUT_REG &= ~(1 << MDIO_PIN))
#define MDIO_READ() (MDIO_IN_REG & (1 << MDIO_PIN))
#define MDC_HIGH() (MDC_OUT_REG |= (1 << MDC_PIN))
#define MDC_LOW() (MDC_OUT_REG &= ~(1 << MDC_PIN))
// Clause 45 操作码定义
#define C45_ADDR_OPCODE 0x00 // 地址操作
#define C45_WRITE_OPCODE 0x01 // 写操作
#define C45_READ_OPCODE 0x03 // 读操作后递增地址
#define C45_READ_INC_OPCODE 0x02 // 读操作
// 函数声明
void mdio_send_preamble(void);
void mdio_send_bits(uint32_t data, uint8_t num_bits);
uint16_t mdio_read_bits(uint8_t num_bits);
void mdio_idle(void);
void mdio_c45_send_header(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr, uint8_t opcode);
//uint16_t mdio_c45_read(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr);
void mdio_c45_set_dev_addr(uint8_t phy_addr, uint8_t dev_addr);
// 发送32位前导码
void mdio_send_preamble(void) {
MDIO_AS_OUTPUT();
for (uint8_t i = 0; i < 32; i++) {
MDIO_HIGH();
MDC_HIGH();
_delay_us(0.4);
MDC_LOW();
_delay_us(0.4);
}
}
// 发送指定数量的比特
void mdio_send_bits(uint32_t data, uint8_t num_bits) {
MDIO_AS_OUTPUT();
for (int8_t i = num_bits - 1; i >= 0; i--) {
if (data & (1UL << i)) {
MDIO_HIGH();
} else {
MDIO_LOW();
}
MDC_LOW();
_delay_us(0.2);
MDC_HIGH();
_delay_us(0.4);
MDC_LOW();
_delay_us(0.2);
}
}
// 读取指定数量的比特
uint16_t mdio_read_bits(uint8_t num_bits) {
uint16_t result = 0;
MDIO_AS_INPUT();
for (uint8_t i = 0; i < num_bits; i++) {
result <<= 1;
MDC_LOW();
_delay_us(0.2);
MDC_HIGH();
#if 1
_delay_us(0.3);
#else
_delay_us(1);
#endif
if (MDIO_READ()) {
result |= 1;
}
#if 1
_delay_us(0.3);
#else
_delay_us(1);
#endif
MDC_LOW();
_delay_us(0.2);
}
return result;
}
// 将总线置于空闲状态
void mdio_idle(void) {
MDC_LOW();
MDIO_AS_INPUT();
}
// 发送Clause 45帧头
void mdio_c45_send_header(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr, uint8_t opcode) {
// Clause 45帧格式: 0b00 + PHY_ADDR(5) + OPCODE(2) + DEV_ADDR(5) + REG_ADDR(16)
// 发送起始符(0b00) + PHY地址(5位) + 操作码(2位)
uint8_t header_part1 = (0b00 << 6) | ((phy_addr & 0x1F) << 1) | (opcode & 0x03);
mdio_send_bits(header_part1, 8);
// 发送设备地址(5位) + 寄存器地址高3位
uint8_t header_part2 = ((dev_addr & 0x1F) << 3) | ((reg_addr >> 13) & 0x07);
mdio_send_bits(header_part2, 8);
// 发送寄存器地址剩余13位
uint16_t header_part3 = reg_addr & 0x1FFF;
mdio_send_bits(header_part3, 13);
}
// Clause 45 读取操作
uint16_t mdio_c45_read(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr) {
// 1. 发送前导码
mdio_send_preamble();
// 2. 发送地址帧头 (设置要访问的寄存器地址)
mdio_c45_send_header(phy_addr, dev_addr, reg_addr, C45_ADDR_OPCODE);
// 3. Turnaround阶段
MDIO_AS_INPUT();
MDC_HIGH();
_delay_us(1);
MDC_LOW();
_delay_us(1);
// 4. 发送读操作帧头
mdio_send_preamble(); // 需要新的前导码
mdio_c45_send_header(phy_addr, dev_addr, reg_addr, C45_READ_OPCODE);
// 5. Turnaround阶段
MDIO_AS_INPUT();
MDC_HIGH();
_delay_us(1);
MDC_LOW();
_delay_us(1);
MDC_HIGH();
_delay_us(1);
MDC_LOW();
_delay_us(1);
// 6. 读取16位数据
uint16_t data = mdio_read_bits(16);
// 7. 回到空闲状态
mdio_idle();
//g_walk_times to prove code run here
g_walk_times = 0x99;
return data;
}
// 设置设备地址(如果需要)
void mdio_c45_set_dev_addr(uint8_t phy_addr, uint8_t dev_addr) {
// 通过Clause 22写入MMD Access Control Register
// 这里使用Clause 22方式设置设备地址
// 13/14寄存器用于MMD访问控制
uint16_t mmd_ctrl = (dev_addr << 5) | 0x0000; // 设置设备地址
// 需要使用Clause 22写入13和14寄存器
}
// 初始化函数
void mdio_init(void) {
MDC_DIR_REG |= (1 << MDC_PIN);
MDC_LOW();
MDIO_AS_INPUT();
// 等待PHY上电稳定
_delay_ms(100);
}
#endif
int main(void)
{
mdio_init();
uint16_t dev_id = mdio_c45_read(0, 0x1, 0x02); //dev_id 读出来不对,现在是0xff01,是错的
}