freeRTOS系统GD32f470平台下网口驱动的问题
----------原创不易,欢迎关注+点赞收藏。广交嵌入式开发的朋友,讨论技术或者合作-------------
软件为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性能本来差,这样就导致网口吞吐性能更差了,有时间研究一下如何重构网口驱动,改成零拷贝。


更多推荐

所有评论(0)