最近在工作中需要使用RTL8201F,在网上找了很多帖子,没有找到合适的,自己翻资料移植了一个。
模板工程使用的是正点原子的f407探索版的例程,原子使用的是LAN8720,需要把他的驱动修改成为我们自己用的RTL8201F。
1.将PHY_TYPE改成我们自己需要的RTL8201
图片
2.根据RTL8201芯片手册修改SR寄存器速度和模式
我们可以从手册看到,SR寄存器地址和speed和DUPLEX的比特位
根据手册修改成我们需要的值,
定义PHY_ID寄存器地址
通过读取该地址查找我们的PHY地址和判断PHY是否启动
定义页选择寄存器、RMII模式设置寄存器、省电模式寄存器
相关定义如下
3.在PHY初始化设置位置修改成我们自己的代码
//查找PHY_addr
for(int i = 0 ;i <31;i++)
{
phyreg = 0;
heth->Init.PhyAddress = i;
HAL_ETH_ReadPHYRegister(heth, PHY_ID, &phyreg);
if(phyreg == 0x1c)
{
heth->Init.PhyAddress = i;
break;
}
HAL_Delay(PHY_RESET_DELAY);
}
//设置页寄第七页准备配置RMII模式
if((HAL_ETH_WritePHYRegister(heth,PHY_PAGE_ADDR,PHY_PAGE_7)) != HAL_OK)
{
/* In case of write timeout */
err = ETH_ERROR;
/* Config MAC and DMA */
ETH_MACDMAConfig(heth, err);
/* Set the ETH peripheral state to READY */
heth->State = HAL_ETH_STATE_READY;
/* Return HAL_ERROR */
return HAL_ERROR;
}
我的板子需要PHY提供REF_CLK,大家如果有外部时钟,可以直接填手册上写的7FFB
//设置RMII模式
if((HAL_ETH_WritePHYRegister(heth,PHY_PAGE_7_MODE,0x6ffb)) != HAL_OK)
{
/* In case of write timeout */
err = ETH_ERROR;
/* Config MAC and DMA */
ETH_MACDMAConfig(heth, err);
/* Set the ETH peripheral state to READY */
heth->State = HAL_ETH_STATE_READY;
/* Return HAL_ERROR */
return HAL_ERROR;
}
回到第0页
if((HAL_ETH_WritePHYRegister(heth,PHY_PAGE_ADDR,0)) != HAL_OK)
{
/* In case of write timeout */
err = ETH_ERROR;
/* Config MAC and DMA */
ETH_MACDMAConfig(heth, err);
/* Set the ETH peripheral state to READY */
heth->State = HAL_ETH_STATE_READY;
printf("ETH reset timeout!! \r\n");
/* Return HAL_ERROR */
return HAL_ERROR;
}
配置PMSR寄存器
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/3e5120700be04172a24269fb93c76cae.webp#pic_center)
我这里的REF_CLK是OUTPUT模式需要关闭省电模式,如果大家不是则不需要配置该项
//
```c
if((HAL_ETH_WritePHYRegister(heth,PHY_PSMR,0x8000)) != HAL_OK)
{
/* In case of write timeout */
err = ETH_ERROR;
/* Config MAC and DMA */
ETH_MACDMAConfig(heth, err);
/* Set the ETH peripheral state to READY */
heth->State = HAL_ETH_STATE_READY;
printf("ETH reset timeout!! \r\n");
/* Return HAL_ERROR */
return HAL_ERROR;
}
4.配置ETH模式
在lwip_comm.c里的lwip_link_thread
这个函数是检测网线连接情况的
通过读取link status来判断网线是否插入
当有网线插入时,启动PHY自协模式,等待自协完成,读取PHY速度和双工模式,将对应的模式配置到ETH中,启动ETH
//
void lwip_link_thread( void * argument )
{
uint32_t regval = 0;
struct netif *netif = (struct netif *) argument;
int link_again_num = 0;
while(1)
{
/* 读取PHY状态寄存器,获取链接信息 */
HAL_ETH_ReadPHYRegister(&g_eth_handler,PHY_BSR, ®val);
/* 判断链接状态 */
if((regval & PHY_LINKED_STATUS) == 0)
{
g_lwipdev.link_status = LWIP_LINK_OFF;
link_again_num ++ ;
if (link_again_num >= 2) /* 网线一段时间没有插入 */
{
continue;
}
else /* 关闭虚拟网卡及以太网中断 */
{
#if LWIP_DHCP /* 如果使用DHCP的话 */
g_lwip_dhcp_state = LWIP_DHCP_LINK_DOWN;
dhcp_stop(netif);
#endif
HAL_ETH_Stop(&g_eth_handler);
netif_set_down(netif);
netif_set_link_down(netif);
}
}
else /* 网线插入检测 */
{
link_again_num = 0;
// printf("检测到有网线插入 \r\n");
if (g_lwipdev.link_status == LWIP_LINK_OFF)/* 开启以太网及虚拟网卡 */
{
HAL_ETH_WritePHYRegister(&g_eth_handler, PHY_BCR, PHY_AUTONEGOTIATION);
uint8_t timeout = 100;
do{
HAL_ETH_ReadPHYRegister(&g_eth_handler,PHY_BSR, ®val);
vTaskDelay(1);
timeout--;
if(timeout==0)
{
g_lwipdev.link_status = LWIP_LINK_OFF;
goto exit;
}
}while(((regval & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE));
HAL_ETH_ReadPHYRegister(&g_eth_handler, PHY_SR, ®val);
if(regval & PHY_SPEED_STATUS)
{
g_eth_handler.Init.Speed = ETH_SPEED_100M;
}
else
{
g_eth_handler.Init.Speed = ETH_SPEED_10M;
}
if(regval & PHY_DUPLEX_STATUS)
{
g_eth_handler.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
}
else
{
g_eth_handler.Init.DuplexMode = ETH_MODE_HALFDUPLEX;
}
g_lwipdev.link_status = LWIP_LINK_ON;
HAL_ETH_Start(&g_eth_handler);
netif_set_up(netif);
netif_set_link_up(netif);
exit:;
}
}
vTaskDelay(100);
}
}
5.设置IP
正点原子的工程默认打开DHCP,如果想使用静态IP需要在,lwipopts.h关闭
在lwip_comm.c的lwip_comm_default_ip_set函数设置成自己需要的ip
6.测试
以上步骤做完之后,编译程序下载到板子里面,我们通过串口助手可以查看到初始化信息
打开命令行PING 一下看看
可以ping通的
我们继续使用网络助手
可以连接上
串口助手页提示有ip连接到
我们的移植工作就完成了。