Cubemx 生产的驱动只能选择LAN8742,无法适配RTL8201,这篇文字就分享一下如何逐步修改lan8742的代码去适配。
一、地址问题
入口函数:int32_t LAN8742_Init(lan8742_Object_t *pObj)
主要是做PHY 地址检测,但是我们查阅RTL8201手册得知:其PHY地址和2个LED引脚捆绑使用,地址为逻辑1时LED低电平有效。从其中至少得知两个信息:
1、2个引脚即2bit,即最多同时接入4个RTL8201
2、逻辑1时低电平,即上拉为0,下拉为1
查看自己的设备原理图中(自己的设备),两个LED上拉,则地址为0。
二、复位和开启自动协商
不同版本cubemx生成的驱动文件还有点差异,比如cubemx6.11+STM32H723生成的初始化函数就只有地址检测。早期的还有软件复位操作,见鬼。
我们这里主要要做的就是开启自动协商,和获取链接状态。
RTL8201和LAN8742的链接状态有差异,LAN8742在链接成功后需要读取0x1F寄存器,而RTL8201是其他寄存器。
我这里处理更简单,只需要判断协商是否完成,只要完成了就认为他是100Mbit/s。
如果有需要的话,还需要你自己实现:判断链接状态是否符合(比如目标是100M全双工,但是协商的是10M),需要进行二次协商或手动设置链接方式。
最后贴一下代码:
1、初始化 LAN8742_Init
#define PHY_ADDRESS 0
#define PHY_NAGO_TIME 1000
#define PHY_RST_TIMEOUT 5000
int32_t LAN8742_Init(lan8742_Object_t *pObj)
{
uint32_t tickstart = 0, regvalue = 0;
int32_t status = LAN8742_STATUS_OK;
if(pObj->Is_Initialized == 0)
{
if(pObj->IO.Init != 0)
{
/* GPIO and Clocks initialization */
pObj->IO.Init();
}
/* Fixed address */
pObj->DevAddr = PHY_ADDRESS;
/* if device address is matched */
if(status == LAN8742_STATUS_OK)
{
/* set a software reset */
if(pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_SOFT_RESET) >= 0)
{
/* get software reset status */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) >= 0)
{
tickstart = pObj->IO.GetTick();
/* wait until software reset is done or timeout occured */
while(regvalue & LAN8742_BCR_SOFT_RESET)
{
if((pObj->IO.GetTick() - tickstart) <= PHY_RST_TIMEOUT)
{
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, ®value) < 0)
{
status = LAN8742_STATUS_READ_ERROR;
break;
}
}
else
{
status = LAN8742_STATUS_RESET_TIMEOUT;
break;
}
}
}
else
{
status = LAN8742_STATUS_READ_ERROR;
}
}
else
{
status = LAN8742_STATUS_WRITE_ERROR;
}
}
}
if(status == LAN8742_STATUS_OK)
{
status = LAN8742_StartAutoNego(pObj);
if(status == LAN8742_STATUS_OK)
{
tickstart = pObj->IO.GetTick();
/* Wait for 2s to perform initialization */
while((pObj->IO.GetTick() - tickstart) <= PHY_NAGO_TIME)
{
}
pObj->Is_Initialized = 1;
}
}
return status;
}
2、获取状态 LAN8742_GetLinkState
int32_t LAN8742_GetLinkState(lan8742_Object_t *pObj)
{
uint32_t readval = 0;
/* Read Status register */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
{
return LAN8742_STATUS_READ_ERROR;
}
/* Read Status register again */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
{
return LAN8742_STATUS_READ_ERROR;
}
if((readval & LAN8742_BSR_LINK_STATUS) == 0)
{
/* Return Link Down status */
return LAN8742_STATUS_LINK_DOWN;
}
/* Check Auto negotiation */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &readval) < 0)
{
return LAN8742_STATUS_READ_ERROR;
}
if((readval & LAN8742_BCR_AUTONEGO_EN) != LAN8742_BCR_AUTONEGO_EN)
{
if(((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT) && ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE))
{
return LAN8742_STATUS_100MBITS_FULLDUPLEX;
}
else if ((readval & LAN8742_BCR_SPEED_SELECT) == LAN8742_BCR_SPEED_SELECT)
{
return LAN8742_STATUS_100MBITS_HALFDUPLEX;
}
else if ((readval & LAN8742_BCR_DUPLEX_MODE) == LAN8742_BCR_DUPLEX_MODE)
{
return LAN8742_STATUS_10MBITS_FULLDUPLEX;
}
else
{
return LAN8742_STATUS_10MBITS_HALFDUPLEX;
}
}
else /* Auto Nego enabled */
{
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BSR, &readval) < 0)
{
return LAN8742_STATUS_READ_ERROR;
}
/* Check if auto nego not done */
if((readval & LAN8742_BSR_AUTONEGO_CPLT) == 0)
{
return LAN8742_STATUS_AUTONEGO_NOTDONE;
}
else
{
return LAN8742_STATUS_100MBITS_FULLDUPLEX;
}
}
}