PHY——LAN8720A 代码解析 (三)
文章目录
- PHY——LAN8720A 代码解析 (三)
- PHY 源码解析
- ETH_PHY_IO_Init
- ETH_PHY_IO_DeInit
- ETH_PHY_IO_WriteReg
- ETH_PHY_IO_ReadReg
- ETH_PHY_IO_GetTick
- LAN8720 源码解析
- LAN8720_RegisterBusIO
- LAN8720_Init
- LAN8720_DisablePowerDownMode
- LAN8720_EnablePowerDownMode
PHY——LAN8720A 代码解析 (三)
PHY 源码解析
这里 PHY 的操作是通过函数指针实现的。
PHY 结构体定义如下:
typedef int32_t (*lan8720_Init_Func) (void);
typedef int32_t (*lan8720_DeInit_Func) (void);
typedef int32_t (*lan8720_ReadReg_Func) (uint32_t, uint32_t, uint32_t *);
typedef int32_t (*lan8720_WriteReg_Func) (uint32_t, uint32_t, uint32_t);
typedef int32_t (*lan8720_GetTick_Func) (void);
typedef struct
{
lan8720_Init_Func Init;
lan8720_DeInit_Func DeInit;
lan8720_WriteReg_Func WriteReg;
lan8720_ReadReg_Func ReadReg;
lan8720_GetTick_Func GetTick;
} lan8720_IOCtx_t;
typedef struct
{
uint32_t DevAddr;
uint32_t Is_Initialized;
lan8720_IOCtx_t IO;
void *pData;
}lan8720_Object_t;
lan8720_IOCtx_t
结构体实现 PHY 操作的方法lan8720_Object_t
结构体是 PHY 操作的对象
PHY 操作方法的实现
int32_t ETH_PHY_IO_Init(void);
int32_t ETH_PHY_IO_DeInit (void);
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal);
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal);
int32_t ETH_PHY_IO_GetTick(void);
lan8720_Object_t LAN8720;
lan8720_IOCtx_t LAN8720_IOCtx = {ETH_PHY_IO_Init,
ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg,
ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick};
ETH_PHY_IO_Init
ETH_PHY_IO_Init
函数用于初始化 PHY 接口。这里主要用于初始化 MDIO 的时钟。
int32_t ETH_PHY_IO_Init(void)
{
/* We assume that MDIO GPIO configuration is already done
in the ETH_MspInit() else it should be done here
*/
/* Configure the MDIO Clock */
HAL_ETH_SetMDIOClockRange(&heth);
return 0;
}
ETH_PHY_IO_DeInit
ETH_PHY_IO_DeInit
函数用于反初始化 PHY 接口。这里暂未实现。
ETH_PHY_IO_WriteReg
ETH_PHY_IO_WriteReg
函数用于向 PHY 写入寄存器。这里其实就是封装了 HAL_ETH_WritePHYRegister
函数,并对返回值进行判断。
int32_t ETH_PHY_IO_WriteReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t RegVal)
{
if(HAL_ETH_WritePHYRegister(&heth, DevAddr, RegAddr, RegVal) != HAL_OK)
{
return -1;
}
return 0;
}
ETH_PHY_IO_ReadReg
ETH_PHY_IO_ReadReg
函数用于从 PHY 读取寄存器。这里其实也是封装了 ETH_PHY_IO_ReadReg
函数,并对返回值进行判断。
int32_t ETH_PHY_IO_ReadReg(uint32_t DevAddr, uint32_t RegAddr, uint32_t *pRegVal)
{
if(HAL_ETH_ReadPHYRegister(&heth, DevAddr, RegAddr, pRegVal) != HAL_OK)
{
return -1;
}
return 0;
}
ETH_PHY_IO_GetTick
ETH_PHY_IO_GetTick
函数用于获取当前的 tick 值。对 HAL_GetTick
进行了封装。
int32_t ETH_PHY_IO_GetTick(void)
{
return HAL_GetTick();
}
最终将函数指针赋值给 LAN8720_IOCtx
这个变量
LAN8720 源码解析
LAN8720 的接口其实就是调用 PHY 的一系列接口来控制 LAN8720,或者获取 LAN8720 的状态。
LAN8720_RegisterBusIO
LAN8720_RegisterBusIO
函数用于注册 PHY 接口。这里主要是对 lan8720_Object_t
结构体中的 IO
成员进行赋值。
int32_t LAN8720_RegisterBusIO(lan8720_Object_t *pObj, lan8720_IOCtx_t *ioctx)
{
if(!pObj || !ioctx->ReadReg || !ioctx->WriteReg || !ioctx->GetTick)
{
return LAN8720_STATUS_ERROR;
}
pObj->IO.Init = ioctx->Init;
pObj->IO.DeInit = ioctx->DeInit;
pObj->IO.ReadReg = ioctx->ReadReg;
pObj->IO.WriteReg = ioctx->WriteReg;
pObj->IO.GetTick = ioctx->GetTick;
return LAN8720_STATUS_OK;
}
函数调用
/* Set PHY IO functions */
LAN8720_RegisterBusIO(&LAN8720, &LAN8720_IOCtx);
/* Initialize the LAN8742 ETH PHY */
if(LAN8720_Init(&LAN8720) != LAN8720_STATUS_OK)
{
printf("init lan8720 error\r\n");
while(1);
}
这样 LAN8720
这个变量的结构体成员就被赋值了。操作 PHY 的使用调用 LAN8720
对应的函数指针即可。
LAN8720_Init
这个函数其实就是从 0 遍历 PHY 的地址,找到一个可用的 PHY 地址。
int32_t LAN8720_Init(lan8720_Object_t *pObj)
{
uint32_t regvalue = 0, addr = 0;
int32_t status = LAN8720_STATUS_OK;
if(pObj->Is_Initialized == 0)
{
if(pObj->IO.Init != 0)
{
/* GPIO and Clocks initialization */
pObj->IO.Init();
}
/* for later check */
pObj->DevAddr = LAN8720_MAX_DEV_ADDR + 1;
/* Get the device address from special mode register */
for(addr = 0; addr <= LAN8720_MAX_DEV_ADDR; addr ++)
{
if(pObj->IO.ReadReg(addr, LAN8720_SMR, ®value) < 0)
{
status = LAN8720_STATUS_READ_ERROR;
/* Can't read from this device address
continue with next address */
continue;
}
if((regvalue & LAN8720_SMR_PHY_ADDR) == addr)
{
pObj->DevAddr = addr;
status = LAN8720_STATUS_OK;
break;
}
}
if(pObj->DevAddr > LAN8720_MAX_DEV_ADDR)
{
status = LAN8720_STATUS_ADDRESS_ERROR;
}
/* if device address is matched */
if(status == LAN8720_STATUS_OK)
{
pObj->Is_Initialized = 1;
}
}
return status;
}
这个函数主要是读取 LAN8720_SMR
寄存器,并获取 PHY 的地址
- LAN8720_SMR 寄存器偏移是 0x12
- bit4:0 是 PHY 的地址。
LAN8720_DisablePowerDownMode
这个函数用于禁用 PHY 的 Power Down 模式。
int32_t LAN8720_DisablePowerDownMode(lan8720_Object_t *pObj)
{
uint32_t readval = 0;
int32_t status = LAN8720_STATUS_OK;
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8720_BCR, &readval) >= 0)
{
readval &= ~LAN8720_BCR_POWER_DOWN;
/* Apply configuration */
if(pObj->IO.WriteReg(pObj->DevAddr, LAN8720_BCR, readval) < 0)
{
status = LAN8720_STATUS_WRITE_ERROR;
}
}
else
{
status = LAN8720_STATUS_READ_ERROR;
}
return status;
}
这个函数其实就是实现 读-改-写
- 读 LAN8720_BCR 寄存器,也就是偏移地址是 0
- 将 bit11 清零,也就是禁用 Power Down 即 normal operation 模式
- 写入 LAN8720_BCR 寄存器
LAN8720_EnablePowerDownMode
这个函数用于启用 PHY 的 Power Down 模式。
int32_t LAN8720_EnablePowerDownMode(lan8720_Object_t *pObj)
{
uint32_t readval = 0;
int32_t status = LAN8720_STATUS_OK;
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8720_BCR, &readval) >= 0)
{
readval |= LAN8720_BCR_POWER_DOWN;
/* Apply configuration */
if(pObj->IO.WriteReg(pObj->DevAddr, LAN8720_BCR, readval) < 0)
{
status = LAN8720_STATUS_WRITE_ERROR;
}
}
else
{
status = LAN8720_STATUS_READ_ERROR;
}
return status;
}
这个函数其实就是实现 读-改-写
- 读 LAN8720_BCR 寄存器,也就是偏移地址是 0
- 将 bit11 设置为 1,也就是 General power down mode 模式
- 写入 LAN8720_BCR 寄存器
其他函数类似,这里就不一一解析了。