----------原创不易,欢迎关注+点赞收藏。广交嵌入式开发的朋友,讨论技术或者合作-------------

软件为freeRTOS,硬件是兆易创新的GD32f470的单片机。单板出一个100M/10M双速网口,物理层芯片PHY为裕泰的YT8512C,RMII2连接方式。

问题现象是:通过RJ45网口,网线连接1000M的交换机或者电脑网卡,无法正常通讯。只有将电脑固定为100M全双工才可以通讯,电脑图标显示网卡断链。看样子是PHY芯片的问题,这得从硬件开始,到驱动一层层排查。

YT8512C的原理图部分:
1、PIN26必须拉低,用示波器或者万用表量了是低电平,但是厂家建议要下拉;
2、RXDV(PIN-8 上拉)和RXD3(PIN-11下拉)确定其工作在RMII2;
3、外部晶振提供25M时钟;
4、MIDO地址为0x01;

在这里插入图片描述
在这里插入图片描述
YT8512C资料显示,在RMII2模式下面,不需要配置特殊的寄存器。其提供的linux驱动源码,仅设置了禁止休眠和LED的配置,不会影响link结果
在这里插入图片描述
查看freeRTOS下面的网口驱动和配置:
在enet_mac_dma_config函数中调用网口驱动初始化接口的时候,下发的就是固定速率模式,将其修改成自协商
enet_init_status = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS)

static void enet_mac_dma_config(void)
{

    printf("enet_mac_dma_config entry\n");
    ErrStatus reval_state = ERROR;

    /* enable ethernet clock  */
    rcu_periph_clock_enable(RCU_ENET);
    rcu_periph_clock_enable(RCU_ENETTX);
    rcu_periph_clock_enable(RCU_ENETRX);
    rcu_periph_clock_enable(RCU_ENETPTP);

    /* reset ethernet on AHB bus */
    enet_deinit(); // ¸´Î»ENETÄ£¿é¼°Ïà¹ØÈí¼þ³õʼ»¯ËùÐè½á¹¹Ìå

    reval_state = enet_software_reset(); // ¸´Î»ENET¼Ä´æÆ÷£¬²¢¼ì²âCLK_TX/CLK_RXÐźÅ
    if (ERROR == reval_state)
    {
        printf("Ó²¼þÍøÂç³õʼ»¯Ê§°Ü£¬Çë¼ì²éÊÇ·ñ²åÈëÍøÏߣ¡\r\n");
    }
    else
    {
        printf("Ó²¼þÍøÂç³õʼ»¯³É¹¦£¬ÒѲåÈëÍøÏߣ¡\r\n");
        //enet_init_status = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS);//ENETÄ£¿é³õʼ»¯£¬ÅäÖÃÓû§×î¹ØÐĵŦÄÜ

        enet_init_status = enet_init(ENET_100M_FULLDUPLEX, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS); // ENETÄ£¿é³õʼ»¯£¬ÅäÖÃÓû§×î¹ØÐĵŦÄÜ
    }
    
    printf("enet_mac_dma_config exit\n");
}

修改后测试,仍然不通。进入enet_init接口,里面有对PHY芯片的配置:
1、软复位PHY芯片,并等待复位结束;
2、等待PHY状态为link up,否则超时退出
3、使能自协商,并等待其正常完成,否则超时退出;
4、读取PHY寄存器16或者31号寄存器,确定PHY的link状态,速率,双工模式;
5、将速率和双工结果设置到MAC寄存器中;

ErrStatus enet_init(enet_mediamode_enum mediamode, enet_chksumconf_enum checksum, enet_frmrecept_enum recept)
{
    uint32_t reg_value = 0U, reg_temp = 0U, temp = 0U;
    uint32_t media_temp = 0U;
    uint32_t timeout = 0U;
    uint16_t phy_value = 0U;
    ErrStatus phy_state = ERROR, enet_state = ERROR;

    /* PHY interface configuration, configure SMI clock and reset PHY chip */
    if(ERROR == enet_phy_config()) {
        _ENET_DELAY_(PHY_RESETDELAY);
        if(ERROR == enet_phy_config()) {
            return enet_state;
        }
    }
    /* initialize ENET peripheral with generally concerned parameters */
    enet_default_init();

    /* 1st, configure mediamode */
    media_temp = (uint32_t)mediamode;
    /* if is PHY auto negotiation */
    if((uint32_t)ENET_AUTO_NEGOTIATION == media_temp) {
        /* wait for PHY_LINKED_STATUS bit be set */
        do {
            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
            phy_value &= PHY_LINKED_STATUS;
            timeout++;
        } while((RESET == phy_value) && (timeout < PHY_READ_TO));
        /* return ERROR due to timeout */
        if(PHY_READ_TO == timeout) {
            return enet_state;
        }
        /* reset timeout counter */
        timeout = 0U;

        /* enable auto-negotiation */
        phy_value = PHY_AUTONEGOTIATION;
        phy_state = enet_phy_write_read(ENET_PHY_WRITE, PHY_ADDRESS, PHY_REG_BCR, &phy_value);
        if(!phy_state) {
            /* return ERROR due to write timeout */
            return enet_state;
        }

        /* wait for the PHY_AUTONEGO_COMPLETE bit be set */
        do {
            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
            phy_value &= PHY_AUTONEGO_COMPLETE;
            timeout++;
        } while((RESET == phy_value) && (timeout < (uint32_t)PHY_READ_TO));
        /* return ERROR due to timeout */
        if(PHY_READ_TO == timeout) {
            return enet_state;
        }
        /* reset timeout counter */
        timeout = 0U;

        /* read the result of the auto-negotiation */
        enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_SR, &phy_value);
        /* configure the duplex mode of MAC following the auto-negotiation result */
        if((uint16_t)RESET != (phy_value & PHY_DUPLEX_STATUS)) {
            media_temp = ENET_MODE_FULLDUPLEX;
        } else {
            media_temp = ENET_MODE_HALFDUPLEX;
        }
        /* configure the communication speed of MAC following the auto-negotiation result */
        if((uint16_t)RESET != (phy_value & PHY_SPEED_STATUS)) {
            media_temp |= ENET_SPEEDMODE_10M;
        } else {
            media_temp |= ENET_SPEEDMODE_100M;
        }
    } else {
        phy_value = (uint16_t)((media_temp & ENET_MAC_CFG_DPM) >> 3);
        phy_value |= (uint16_t)((media_temp & ENET_MAC_CFG_SPD) >> 1);
        phy_state = enet_phy_write_read(ENET_PHY_WRITE, PHY_ADDRESS, PHY_REG_BCR, &phy_value);
        if(!phy_state) {
            /* return ERROR due to write timeout */
            return enet_state;
        }
        /* PHY configuration need some time */
        _ENET_DELAY_(PHY_CONFIGDELAY);
    }
    /* after configuring the PHY, use mediamode to configure registers */
    reg_value = ENET_MAC_CFG;
    /* configure ENET_MAC_CFG register */
    reg_value &= (~(ENET_MAC_CFG_SPD | ENET_MAC_CFG_DPM | ENET_MAC_CFG_LBM));
    reg_value |= media_temp;
    ENET_MAC_CFG = reg_value;


    ....................
    enet_state = SUCCESS;
    return enet_state;
}

查看YT8512C的寄存器,16和31号寄存器并不能代表速率和双工模式。原来这套代码是针对TI DP83848 或者micochip LAN8700两种芯片。我下载了LAN8700的手册,也对不上。不知道怎么回事,我就不去深究了。
在这里插入图片描述
从uboot或者linux移植一下标准的PHY初始化流程,修改为
1、软复位PHY芯片,并等待复位结束;
2、等待PHY状态为link up,否则超时退出
3、使能自协商并发起自协商,并等待其正常完成,否则超时退出;
4、读取PHY寄存器4和5号寄存器,确定PHY的link状态,速率,双工模式;
5、将速率和双工结果设置到MAC寄存器中;

ErrStatus enet_init(enet_mediamode_enum mediamode, enet_chksumconf_enum checksum, enet_frmrecept_enum recept)
{
    uint32_t reg_value = 0U, reg_temp = 0U, temp = 0U;
    uint32_t media_temp = 0U;
    uint32_t timeout = 0U;
    uint32_t media, speed, duplex;
    uint16_t phy_value = 0U;
    uint16_t adv, lpa;
    
    ErrStatus phy_state = ERROR, enet_state = ERROR;

    /* PHY interface configuration, configure SMI clock and reset PHY chip */
    if (ERROR == enet_phy_config()) 
    {
        printf("%s-%d,  enet_phy_config ERROR \r\n", __FUNCTION__, __LINE__);
        _ENET_DELAY_(PHY_RESETDELAY);
        if(ERROR == enet_phy_config()) 
        {
            printf("%s-%d,  enet_phy_config ERROR \r\n", __FUNCTION__, __LINE__);
            return enet_state;
        }
    }
    
    /* initialize ENET peripheral with generally concerned parameters */
    enet_default_init();
    //yt8512_led_init(PHY_ADDRESS);
    //yt8512_disable_sleep(PHY_ADDRESS);
    enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BCR, &phy_value);
    printf("%s-%d, reg0, phy_value = 0x%x \r\n", __FUNCTION__, __LINE__, phy_value);

    enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
    printf("%s-%d, reg1, phy_value = 0x%x \r\n", __FUNCTION__, __LINE__, phy_value);

    enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, 3, &phy_value);
    printf("%s-%d, reg2, phy_value = 0x%x \r\n", __FUNCTION__, __LINE__, phy_value);

    /* 1st, configure mediamode */
    media_temp = (uint32_t)mediamode;

    printf("%s-%d, media_temp = 0x%x \r\n", __FUNCTION__, __LINE__, media_temp);
    /* if is PHY auto negotiation */
    if((uint32_t)ENET_AUTO_NEGOTIATION == media_temp) 
    {
            /* wait for PHY_LINKED_STATUS bit be set */
        do 
        {
            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
            phy_value &= PHY_LINKED_STATUS;
            timeout++;
        }
        
        while((RESET == phy_value) && (timeout < PHY_READ_TO));

        /* return ERROR due to timeout */
        if (PHY_READ_TO == timeout) 
        {
            printf("%s-%d, timeout = 0x%x \r\n", __FUNCTION__, __LINE__, timeout);
            return enet_state;
        }
        
        /* reset timeout counter */
        timeout = 0U;

        /* enable auto-negotiation */
        phy_value = PHY_AUTONEGOTIATION | PHY_RESTART_AUTONEGOTIATION;
        phy_state = enet_phy_write_read(ENET_PHY_WRITE, PHY_ADDRESS, PHY_REG_BCR, &phy_value);
        
        if (!phy_state) 
        {
            /* return ERROR due to write timeout */
            return enet_state;
        }

        /* wait for the PHY_AUTONEGO_COMPLETE bit be set */
        do 
        {
            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
            phy_value &= PHY_AUTONEGO_COMPLETE;
            timeout++;
        } 
        while((RESET == phy_value) && (timeout < (uint32_t)PHY_READ_TO));
        
        /* return ERROR due to timeout */
        if (PHY_READ_TO == timeout) 
        {
            printf("%s-%d, timeout = 0x%x \r\n", __FUNCTION__, __LINE__, timeout);
            return enet_state;
        }
        /* reset timeout counter */
        timeout = 0U;

        enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
        printf("%s-%d, reg1, phy_value = 0x%x \r\n", __FUNCTION__, __LINE__, phy_value);

        if (!(phy_value & PHY_LINKED_STATUS)) 
        {
            printf("%s: link down (status: 0x%04x)\n", __FUNCTION__, phy_value);
        }

        else
        {

            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, MII_ADVERTISE, &adv);
            enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, MII_LPA, &lpa);
            
            media = enet_mii_nway_result(lpa & adv);
            speed = (media & (ADVERTISE_100FULL | ADVERTISE_100HALF) ? 1 : 0);
            duplex = (media & ADVERTISE_FULL) ? 1 : 0;
            printf("%s: link up, %sMbps %s-duplex (lpa: 0x%04x) \r\n",
                   __FUNCTION__,
                   speed ? "100" : "10",
                   duplex ? "full" : "half",
                   lpa);
                   
            /* read the result of the auto-negotiation */
            //enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_SR, &phy_value);
            /* configure the duplex mode of MAC following the auto-negotiation result */
            //if((uint16_t)RESET != (phy_value & PHY_DUPLEX_STATUS)) 
            if (duplex)
            {
                media_temp = ENET_MODE_FULLDUPLEX;
            } 
            else 
            {
                media_temp = ENET_MODE_HALFDUPLEX;
            }
            
            /* configure the communication speed of MAC following the auto-negotiation result */
            //if((uint16_t)RESET != (phy_value & PHY_SPEED_STATUS)) 
            if (speed)
            {
                media_temp |= ENET_SPEEDMODE_100M;
            } 
            else 
            {
                media_temp |= ENET_SPEEDMODE_10M;
            }
                   
       }

        enet_phy_write_read(ENET_PHY_READ, PHY_ADDRESS, PHY_REG_BSR, &phy_value);
        printf("%s-%d, reg1, phy_value = 0x%x \r\n", __FUNCTION__, __LINE__, phy_value);

        printf("%s-%d, media_temp = 0x%x \r\n", __FUNCTION__, __LINE__, media_temp);
    } 
    
    else 
    {
        printf("%s-%d, media_temp = 0x%x \r\n", __FUNCTION__, __LINE__, media_temp);
        
        phy_value = (uint16_t)((media_temp & ENET_MAC_CFG_DPM) >> 3);
        phy_value |= (uint16_t)((media_temp & ENET_MAC_CFG_SPD) >> 1);
        phy_state = enet_phy_write_read(ENET_PHY_WRITE, PHY_ADDRESS, PHY_REG_BCR, &phy_value);
        if(!phy_state) 
        {
            /* return ERROR due to write timeout */
            return enet_state;
        }
        /* PHY configuration need some time */
        _ENET_DELAY_(PHY_CONFIGDELAY);
    }
    /* after configuring the PHY, use mediamode to configure registers */
    reg_value = ENET_MAC_CFG;
    /* configure ENET_MAC_CFG register */
    reg_value &= (~(ENET_MAC_CFG_SPD | ENET_MAC_CFG_DPM | ENET_MAC_CFG_LBM));
    reg_value |= media_temp;
    ENET_MAC_CFG = reg_value;

    .........................
    enet_state = SUCCESS;
    return enet_state;
}

编译烧录后,对接千兆网卡能正常通讯。不过发现每ping17个报文就超时一次,原来是驱动到协议栈的报文都经过了一次拷贝,MCU性能本来差,这样就导致网口吞吐性能更差了,有时间研究一下如何重构网口驱动,改成零拷贝。
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐