PHY——LAN8720A 代码解析 (三)

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, &regvalue) < 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 寄存器

其他函数类似,这里就不一一解析了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tyustli

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值