MyCollege1999 2025-08-29 15:58 采纳率: 65.8%
浏览 7
已结题

Attiny84A单片机mdio读phy不对

环境是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,是错的
}

  • 写回答

5条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-08-29 16:00
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案让您满意,请采纳、点赞、关注,非常感谢!
    .
    每日一句正能量,让它带给您好心情:每天进步一点点,就是最大的收获。
    .

    根据您提供的代码和问题描述,您在Attiny84A上通过MDIO接口读取PHY 88q2112的device 1 register 2时遇到值不正确的问题(读取到0xff01)。以下是可能的原因和调试建议:

    可能的问题原因

    1. 时序问题:MDIO/MDC时序要求严格,Attiny84A的延迟可能不足
    2. Clause 45帧格式错误:88q2112使用Clause 45但帧结构可能有特殊要求
    3. Turnaround时序不正确:读操作时的总线转向时序错误
    4. PHY地址或设备地址配置问题

    调试建议

    1. 首先检查硬件连接

    # 确认MDIO/MDC线路上拉电阻(通常需要2.2k-10k上拉)
    # 检查电源和时钟稳定性
    

    2. 添加调试输出(如果可用)

    // 在关键位置添加LED指示或串口输出
    #define DEBUG_LED_DIR DDRB
    #define DEBUG_LED_PORT PORTB  
    #define DEBUG_LED_PIN PB2
    
    
    void debug_led_blink(uint8_t times) {
        DEBUG_LED_DIR |= (1 << DEBUG_LED_PIN);
        for(uint8_t i=0; i<times; i++) {
            DEBUG_LED_PORT |= (1 << DEBUG_LED_PIN);
            _delay_ms(200);
            DEBUG_LED_PORT &= ~(1 << DEBUG_LED_PIN);
            _delay_ms(200);
        }
    }
    

    3. 优化时序延迟

    // 调整延迟时间,88q2112可能需要更长的时序
    #define MDIO_DELAY_US 1  // 增加到1us或更长
    
    
    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(MDIO_DELAY_US);
            MDC_HIGH();
            _delay_us(MDIO_DELAY_US * 2);  // 高电平时间稍长
            MDC_LOW();
            _delay_us(MDIO_DELAY_US);
        }
    }
    

    4. 检查Clause 45帧结构

    // 确保帧格式符合88q2112要求
    void mdio_c45_send_header(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr, uint8_t opcode) {
        // 验证88q2112的具体帧格式要求
        // 有些PHY可能对帧结构有特殊要求
        
        uint32_t frame = ((uint32_t)0b00 << 30) |
                        ((uint32_t)(phy_addr & 0x1F) << 25) |
                        ((uint32_t)(opcode & 0x03) << 23) |
                        ((uint32_t)(dev_addr & 0x1F) << 18) |
                        (reg_addr & 0xFFFF);
        
        mdio_send_bits(frame, 32);  // 发送完整的32位帧
    }
    

    5. 添加详细的调试函数

    uint16_t mdio_c45_read_debug(uint8_t phy_addr, uint8_t dev_addr, uint16_t reg_addr) {
        debug_led_blink(1);  // 开始读取指示
        
        mdio_send_preamble();
        debug_led_blink(2);  // 前导码发送完成
        
        mdio_c45_send_header(phy_addr, dev_addr, reg_addr, C45_ADDR_OPCODE);
        debug_led_blink(3);  // 地址帧头发送完成
        
        // Turnaround阶段
        MDIO_AS_INPUT();
        for(uint8_t i=0; i<4; i++) {
            MDC_HIGH();
            _delay_us(1);
            MDC_LOW();
            _delay_us(1);
        }
        
        mdio_send_preamble();
        mdio_c45_send_header(phy_addr, dev_addr, reg_addr, C45_READ_OPCODE);
        debug_led_blink(4);  // 读操作帧头发送完成
        
        // 更长的Turnaround等待
        for(uint8_t i=0; i<8; i++) {
            MDC_HIGH();
            _delay_us(1);
            MDC_LOW();
            _delay_us(1);
        }
        
        uint16_t data = mdio_read_bits(16);
        debug_led_blink(5);  // 数据读取完成
        
        mdio_idle();
        return data;
    }
    

    6. 验证PHY基本通信

    // 先尝试读取已知的寄存器验证基本通信
    void test_phy_basic(void) {
        // 尝试读取device 1, register 1 (可能包含厂商信息)
        uint16_t vendor_id = mdio_c45_read(0, 0x1, 0x01);
        
        // 或者尝试Clause 22读取(如果支持)
        // uint16_t basic_id = mdio_c22_read(0, 0x01);
    }
    

    7. 使用逻辑分析仪验证波形

    如果可能,使用逻辑分析仪捕获MDIO/MDC波形,检查:

    • 前导码是否正确(32个1)
    • 帧格式是否符合Clause 45
    • 时序是否满足PHY要求
    • Turnaround时间是否足够

    建议的修改步骤

    1. 首先增加时序延迟,特别是Turnaround时间
    2. 验证帧格式,确保符合88q2112文档要求
    3. 添加硬件调试指示,确认代码执行流程
    4. 尝试读取其他寄存器,确认是否是特定寄存器问题

    如果问题仍然存在,建议查阅88q2112的数据手册,确认其MDIO接口的具体时序要求和寄存器映射规范。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 1月7日
  • 已采纳回答 12月30日
  • 创建了问题 8月29日