前言
- 本次使用的正点原子的探索者开发板,版本为V3.4
- 使用的是stm32cubemx6.15版本生成的代码
- 本次提供关键的操作步骤,开发工具选用clion+arm-gcc+openocd
- 此次YT8512C驱动参考的链接为gitee
- 为了防止
stm32cubemx创建项目工程
开启时钟和调试



开启ETH外设


配置ETH_RESET复位引脚

使用LWIP组件


代码生成


使用clion打开生成项目

添加yt8512c驱动

头文件
c
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef YT8512C_H
#define YT8512C_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include <stdint.h>
#include "stm32f4xx_hal.h"
/* PHY芯片寄存器映射表 */
#define YT8512C_BCR ((uint16_t)0x0000U)
#define YT8512C_BSR ((uint16_t)0x0001U)
#define PHY_REGISTER2 ((uint16_t)0x0002U)
#define PHY_REGISTER3 ((uint16_t)0x0003U)
/* 操作SCR寄存器的值(一般不需要修改) */
#define YT8512C_BCR_SOFT_RESET ((uint16_t)0x8000U)
#define YT8512C_BCR_LOOPBACK ((uint16_t)0x4000U)
#define YT8512C_BCR_SPEED_SELECT ((uint16_t)0x2000U)
#define YT8512C_BCR_AUTONEGO_EN ((uint16_t)0x1000U)
#define YT8512C_BCR_POWER_DOWN ((uint16_t)0x0800U)
#define YT8512C_BCR_ISOLATE ((uint16_t)0x0400U)
#define YT8512C_BCR_RESTART_AUTONEGO ((uint16_t)0x0200U)
#define YT8512C_BCR_DUPLEX_MODE ((uint16_t)0x0100U)
/* 操作BSR寄存器的值(一般不需要修改) */
#define YT8512C_BSR_100BASE_T4 ((uint16_t)0x8000U)
#define YT8512C_BSR_100BASE_TX_FD ((uint16_t)0x4000U)
#define YT8512C_BSR_100BASE_TX_HD ((uint16_t)0x2000U)
#define YT8512C_BSR_10BASE_T_FD ((uint16_t)0x1000U)
#define YT8512C_BSR_10BASE_T_HD ((uint16_t)0x0800U)
#define YT8512C_BSR_100BASE_T2_FD ((uint16_t)0x0400U)
#define YT8512C_BSR_100BASE_T2_HD ((uint16_t)0x0200U)
#define YT8512C_BSR_EXTENDED_STATUS ((uint16_t)0x0100U)
#define YT8512C_BSR_AUTONEGO_CPLT ((uint16_t)0x0020U)
#define YT8512C_BSR_REMOTE_FAULT ((uint16_t)0x0010U)
#define YT8512C_BSR_AUTONEGO_ABILITY ((uint16_t)0x0008U)
#define YT8512C_BSR_LINK_STATUS ((uint16_t)0x0004U)
#define YT8512C_BSR_JABBER_DETECT ((uint16_t)0x0002U)
#define YT8512C_BSR_EXTENDED_CAP ((uint16_t)0x0001U)
/* PHY芯片进程状态 */
#define YT8512C_STATUS_READ_ERROR ((int32_t)-5)
#define YT8512C_STATUS_WRITE_ERROR ((int32_t)-4)
#define YT8512C_STATUS_ADDRESS_ERROR ((int32_t)-3)
#define YT8512C_STATUS_RESET_TIMEOUT ((int32_t)-2)
#define YT8512C_STATUS_ERROR ((int32_t)-1)
#define YT8512C_STATUS_OK ((int32_t) 0)
#define YT8512C_STATUS_LINK_DOWN ((int32_t) 1)
#define YT8512C_STATUS_100MBITS_FULLDUPLEX ((int32_t) 2)
#define YT8512C_STATUS_100MBITS_HALFDUPLEX ((int32_t) 3)
#define YT8512C_STATUS_10MBITS_FULLDUPLEX ((int32_t) 4)
#define YT8512C_STATUS_10MBITS_HALFDUPLEX ((int32_t) 5)
#define YT8512C_STATUS_AUTONEGO_NOTDONE ((int32_t) 6)
/* PHY地址 ---- 由用户设置 */
#define YT8512C_ADDR ((uint16_t)0x0000U)
/* PHY寄存器的数量 */
#define YT8512C_PHY_COUNT ((uint16_t)0x001FU)
#define YT8512C_PHYSCSR ((uint16_t)0x11) /*!< tranceiver status register */
#define YT8512C_SPEED_STATUS ((uint16_t)0x4010) /*!< configured information of speed: 100Mbit/s */
#define YT8512C_DUPLEX_STATUS ((uint16_t)0x2000) /*!< configured information of duplex: full-duplex */
/* 定义函数指针 */
typedef int32_t (*yt8512c_init_func) (void);
typedef int32_t (*yt8512c_deinit_func) (void);
typedef int32_t (*yt8512c_readreg_func) (uint32_t, uint32_t, uint32_t *);
typedef int32_t (*yt8512c_writereg_func) (uint32_t, uint32_t, uint32_t);
typedef int32_t (*yt8512c_gettick_func) (void);
/* PHY共用函数结构体 */
typedef struct
{
yt8512c_init_func init; /* 指向PHY初始化函数 */
yt8512c_deinit_func deinit; /* 指向PHY反初始化函数 */
yt8512c_writereg_func writereg; /* 指向PHY写寄存器函数 */
yt8512c_readreg_func readreg; /* 指向PHY读寄存器函数 */
yt8512c_gettick_func gettick; /* 指向节拍函数 */
} yt8512c_ioc_tx_t;
/* 注册到组件对象结构体 */
typedef struct
{
uint32_t devaddr; /* PHY地址 */
uint32_t is_initialized; /* 描述该设备是否初始化 */
yt8512c_ioc_tx_t io; /* 设备调用的函数入口 */
void *pdata; /* 传入的形参 */
}yt8512c_object_t;
int32_t yt8512c_regster_bus_io(yt8512c_object_t *pobj, yt8512c_ioc_tx_t *ioctx); /* 将IO函数注册到组件对象 */
int32_t yt8512c_init(yt8512c_object_t *pobj); /* 初始化YT8512C并配置所需的硬件资源 */
int32_t yt8512c_deinit(yt8512c_object_t *pobj); /* 反初始化YT8512C及其硬件资源 */
int32_t yt8512c_disable_power_down_mode(yt8512c_object_t *pobj); /* 关闭YT8512C的下电模式 */
int32_t yt8512c_enable_power_down_mode(yt8512c_object_t *pobj); /* 使能YT8512C的下电模式 */
int32_t yt8512c_start_auto_nego(yt8512c_object_t *pobj); /* 启动自动协商过程 */
int32_t yt8512c_get_link_state(yt8512c_object_t *pobj); /* 获取YT8512C设备的链路状态 */
int32_t yt8512c_set_link_state(yt8512c_object_t *pobj, uint32_t linkstate); /* 设置YT8512C设备的链路状态 */
int32_t yt8512c_enable_loop_back_mode(yt8512c_object_t *pobj); /* 启用环回模式 */
int32_t yt8512c_disable_loop_back_mode(yt8512c_object_t *pobj); /* 禁用环回模式 */
#ifdef __cplusplus
}
#endif
#endif /* YT8512C_H */
源文件
c
/* Includes ------------------------------------------------------------------*/
#include "yt8512c.h"
#define YT8512C_SW_RESET_TO ((uint32_t)500U) /* 软件复位等待时间 */
#define YT8512C_INIT_TO ((uint32_t)2000U) /* 初始化等待时间 */
#define YT8512C_MAX_DEV_ADDR ((uint32_t)31U) /* PHY地址的最大值 */
#define YT8512C_AND_RTL8201BL_PHYREGISTER2 0x0000
/**
* @brief 将IO函数注册到组件对象
* @param pobj:设备对象
* @param ioctx:保存设备IO功能
* @retval YT8512C_STATUS_OK:OK
* YT8512C_STATUS_ERROR:缺少功能
*/
int32_t yt8512c_regster_bus_io(yt8512c_object_t *pobj, yt8512c_ioc_tx_t *ioctx)
{
if (!pobj || !ioctx->readreg || !ioctx->writereg || !ioctx->gettick)
{
return YT8512C_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 YT8512C_STATUS_OK;
}
/**
* @brief 初始化YT8512C并配置所需的硬件资源
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:初始化YT8512C并配置所需的硬件资源成功
YT8512C_STATUS_ADDRESS_ERROR:找不到设备地址
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写入寄存器
YT8512C_STATUS_RESET_TIMEOUT:无法执行软件复位
*/
int32_t yt8512c_init(yt8512c_object_t *pobj)
{
uint32_t tickstart = 0, regvalue = 0, addr = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->is_initialized == 0)
{
if (pobj->io.init != 0)
{
/* MDC时钟 */
pobj->io.init();
}
/* 设置PHY地址为32 */
pobj->devaddr = YT8512C_MAX_DEV_ADDR + 1;
/* 主要为了查找PHY地址 */
for (addr = 0; addr <= YT8512C_MAX_DEV_ADDR; addr ++)
{
if (pobj->io.readreg(addr, YT8512C_PHYSCSR, ®value) < 0)
{
status = YT8512C_STATUS_READ_ERROR;
/* 无法读取这个设备地址继续下一个地址 */
continue;
}
/* 已经找到PHY地址了 */
if ((regvalue & YT8512C_PHY_COUNT) == addr)
{
pobj->devaddr = addr;
status = YT8512C_STATUS_OK;
break;
}
}
/* 判断这个PHY地址是否大于32(2^5)*/
if (pobj->devaddr > YT8512C_MAX_DEV_ADDR)
{
status = YT8512C_STATUS_ADDRESS_ERROR;
}
/* 如果PHY地址有效 */
if (status == YT8512C_STATUS_OK)
{
/* 设置软件复位 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, YT8512C_BCR_SOFT_RESET) >= 0)
{
/* 获取软件重置状态 */
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, ®value) >= 0)
{
tickstart = pobj->io.gettick();
/* 等待软件复位完成或超时 */
while (regvalue & YT8512C_BCR_SOFT_RESET)
{
if ((pobj->io.gettick() - tickstart) <= YT8512C_SW_RESET_TO)
{
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, ®value) < 0)
{
status = YT8512C_STATUS_READ_ERROR;
break;
}
}
else
{
status = YT8512C_STATUS_RESET_TIMEOUT;
break;
}
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
}
else
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
}
/* 到了这里,初始化完成!!! */
if (status == YT8512C_STATUS_OK)
{
tickstart = pobj->io.gettick();
/* 等待2s进行初始化 */
while ((pobj->io.gettick() - tickstart) <= YT8512C_INIT_TO)
{
}
pobj->is_initialized = 1;
}
return status;
}
/**
* @brief 反初始化YT8512C及其硬件资源
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:反初始化失败成功
YT8512C_STATUS_ERROR:反初始化失败
*/
int32_t yt8512c_deinit(yt8512c_object_t *pobj)
{
if (pobj->is_initialized)
{
if (pobj->io.deinit != 0)
{
if (pobj->io.deinit() < 0)
{
return YT8512C_STATUS_ERROR;
}
}
pobj->is_initialized = 0;
}
return YT8512C_STATUS_OK;
}
/**
* @brief 关闭YT8512C的下电模式
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_disable_power_down_mode(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0)
{
readval &= ~YT8512C_BCR_POWER_DOWN;
/* 清除下电模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 使能YT8512C的下电模式
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_enable_power_down_mode(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0)
{
readval |= YT8512C_BCR_POWER_DOWN;
/* 使能下电模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 启动自动协商过程
* @param pobj: 设备对象
* @retval YT8512C_STATUS_OK:关闭成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR:不能写寄存器
*/
int32_t yt8512c_start_auto_nego(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0)
{
readval |= YT8512C_BCR_AUTONEGO_EN;
/* 启动自动协商 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 获取YT8512C设备的链路状态
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_100MBITS_FULLDUPLEX:100M,全双工
YT8512C_STATUS_100MBITS_HALFDUPLEX :100M,半双工
YT8512C_STATUS_10MBITS_FULLDUPLEX:10M,全双工
YT8512C_STATUS_10MBITS_HALFDUPLEX :10M,半双工
YT8512C_STATUS_READ_ERROR:不能读取寄存器
*/
int32_t yt8512c_get_link_state(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
/* 检测特殊功能寄存器链接值 */
if (pobj->io.readreg(pobj->devaddr, YT8512C_PHYSCSR, &readval) < 0)
{
return YT8512C_STATUS_READ_ERROR;
}
if (((readval & YT8512C_SPEED_STATUS) != YT8512C_SPEED_STATUS) && ((readval & YT8512C_DUPLEX_STATUS) != 0))
{
return YT8512C_STATUS_100MBITS_FULLDUPLEX;
}
else if (((readval & YT8512C_SPEED_STATUS) != YT8512C_SPEED_STATUS))
{
return YT8512C_STATUS_100MBITS_HALFDUPLEX;
}
else if (((readval & YT8512C_BCR_DUPLEX_MODE) != YT8512C_BCR_DUPLEX_MODE))
{
return YT8512C_STATUS_10MBITS_FULLDUPLEX;
}
else
{
return YT8512C_STATUS_10MBITS_HALFDUPLEX;
}
}
/**
* @brief 设置YT8512C设备的链路状态
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_ERROR :设置失败
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_set_link_state(yt8512c_object_t *pobj, uint32_t linkstate)
{
uint32_t bcrvalue = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &bcrvalue) >= 0)
{
/* 禁用链路配置(自动协商,速度和双工) */
bcrvalue &= ~(YT8512C_BCR_AUTONEGO_EN | YT8512C_BCR_SPEED_SELECT | YT8512C_BCR_DUPLEX_MODE);
if (linkstate == YT8512C_STATUS_100MBITS_FULLDUPLEX)
{
bcrvalue |= (YT8512C_BCR_SPEED_SELECT | YT8512C_BCR_DUPLEX_MODE);
}
else if (linkstate == YT8512C_STATUS_100MBITS_HALFDUPLEX)
{
bcrvalue |= YT8512C_BCR_SPEED_SELECT;
}
else if (linkstate == YT8512C_STATUS_10MBITS_FULLDUPLEX)
{
bcrvalue |= YT8512C_BCR_DUPLEX_MODE;
}
else
{
/* 错误的链路状态参数 */
status = YT8512C_STATUS_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
if(status == YT8512C_STATUS_OK)
{
/* 写入链路状态 */
if(pobj->io.writereg(pobj->devaddr, YT8512C_BCR, bcrvalue) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
return status;
}
/**
* @brief 启用环回模式
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_enable_loop_back_mode(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0)
{
readval |= YT8512C_BCR_LOOPBACK;
/* 启用环回模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
/**
* @brief 禁用环回模式
* @param pobj: 设备对象
* @param pLinkState: 指向链路状态的指针
* @retval YT8512C_STATUS_OK:设置成功
YT8512C_STATUS_READ_ERROR:不能读取寄存器
YT8512C_STATUS_WRITE_ERROR :不能写入寄存器
*/
int32_t yt8512c_disable_loop_back_mode(yt8512c_object_t *pobj)
{
uint32_t readval = 0;
int32_t status = YT8512C_STATUS_OK;
if (pobj->io.readreg(pobj->devaddr, YT8512C_BCR, &readval) >= 0)
{
readval &= ~YT8512C_BCR_LOOPBACK;
/* 禁用环回模式 */
if (pobj->io.writereg(pobj->devaddr, YT8512C_BCR, readval) < 0)
{
status = YT8512C_STATUS_WRITE_ERROR;
}
}
else
{
status = YT8512C_STATUS_READ_ERROR;
}
return status;
}
复制一个ethernetif.c的源文件

内容修改如下
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* File Name : ethernetif.c
* Description : This file provides code for the configuration
* of the ethernetif.c MiddleWare.
******************************************************************************
* @attention
*
* Copyright (c) 2025 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "lwip/opt.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
#include "lwip/timeouts.h"
#include "netif/ethernet.h"
#include "netif/etharp.h"
#include "lwip/ethip6.h"
#include "ethernetif.h"
#include "lan8742.h"
#include <string.h>
#include "yt8512c.h"
/* Within 'USER CODE' section, code will be kept by default at each generation */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* Private define ------------------------------------------------------------*/
/* Network interface name */
#define IFNAME0 's'
#define IFNAME1 't'
/* ETH Setting */
#define ETH_DMA_TRANSMIT_TIMEOUT ( 20U )
#define ETH_TX_BUFFER_MAX ((ETH_TX_DESC_CNT) * 2U)
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* Private variables ---------------------------------------------------------*/
/*
@Note: This interface is implemented to operate in zero-copy mode only:
- Rx Buffers will be allocated from LwIP stack Rx memory pool,
then passed to ETH HAL driver.
- Tx Buffers will be allocated from LwIP stack memory heap,
then passed to ETH HAL driver.
@Notes:
1.a. ETH DMA Rx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_RX_DESC_CNT in ETH GUI (Rx Descriptor Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
1.b. ETH DMA Tx descriptors must be contiguous, the default count is 4,
to customize it please redefine ETH_TX_DESC_CNT in ETH GUI (Tx Descriptor Length)
so that updated value will be generated in stm32xxxx_hal_conf.h
2.a. Rx Buffers number must be between ETH_RX_DESC_CNT and 2*ETH_RX_DESC_CNT
2.b. Rx Buffers must have the same size: ETH_RX_BUF_SIZE, this value must
passed to ETH DMA in the init field (heth.Init.RxBuffLen)
2.c The RX Ruffers addresses and sizes must be properly defined to be aligned
to L1-CACHE line size (32 bytes).
*/
/* Data Type Definitions */
typedef enum
{
RX_ALLOC_OK = 0x00,
RX_ALLOC_ERROR = 0x01
} RxAllocStatusTypeDef;
typedef struct
{
struct pbuf_custom pbuf_custom;
uint8_t buff[(ETH_RX_BUF_SIZE + 31) & ~31] __ALIGNED(32);
} RxBuff_t;
/* Memory Pool Declaration */
#define ETH_RX_BUFFER_CNT 12U
LWIP_MEMPOOL_DECLARE(RX_POOL, ETH_RX_BUFFER_CNT, sizeof(RxBuff_t), "Zero-copy RX PBUF pool");
/* Variable Definitions */
static uint8_t RxAllocStatus;
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Global Ethernet handle */
ETH_HandleTypeDef heth;
ETH_TxPacketConfig TxConfig;
/* Private function prototypes -----------------------------------------------*/
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);
/* USER CODE BEGIN 3 */
yt8512c_object_t YT8512C;
yt8512c_ioc_tx_t YT8512C_IOCtx = {
ETH_PHY_IO_Init,
ETH_PHY_IO_DeInit,
ETH_PHY_IO_WriteReg,
ETH_PHY_IO_ReadReg,
ETH_PHY_IO_GetTick
};
/* USER CODE END 3 */
/* Private functions ---------------------------------------------------------*/
void pbuf_free_custom(struct pbuf* p);
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/*******************************************************************************
LL Driver Interface ( LwIP stack --> ETH)
*******************************************************************************/
/**
* @brief In this function, the hardware should be initialized.
* Called from ethernetif_init().
*
* @param netif the already initialized lwip network interface structure
* for this ethernetif
*/
static void low_level_init(struct netif* netif)
{
HAL_StatusTypeDef hal_eth_init_status = HAL_OK;
/* Start ETH HAL Init */
uint8_t MACAddr[6];
heth.Instance = ETH;
MACAddr[0] = 0x00;
MACAddr[1] = 0x80;
MACAddr[2] = 0xE1;
MACAddr[3] = 0x00;
MACAddr[4] = 0x00;
MACAddr[5] = 0x00;
heth.Init.MACAddr = &MACAddr[0];
heth.Init.MediaInterface = HAL_ETH_RMII_MODE;
heth.Init.TxDesc = DMATxDscrTab;
heth.Init.RxDesc = DMARxDscrTab;
heth.Init.RxBuffLen = 1536;
/* USER CODE BEGIN MACADDRESS */
HAL_GPIO_WritePin(ETH_RESET_GPIO_Port, ETH_RESET_Pin, 0); /* 硬件复位 */
HAL_Delay(100);
HAL_GPIO_WritePin(ETH_RESET_GPIO_Port, ETH_RESET_Pin, 1); /* 复位结束 */
HAL_Delay(100);
/* USER CODE END MACADDRESS */
hal_eth_init_status = HAL_ETH_Init(&heth);
memset(&TxConfig, 0, sizeof(ETH_TxPacketConfig));
TxConfig.Attributes = ETH_TX_PACKETS_FEATURES_CSUM | ETH_TX_PACKETS_FEATURES_CRCPAD;
TxConfig.ChecksumCtrl = ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
TxConfig.CRCPadCtrl = ETH_CRC_PAD_INSERT;
/* End ETH HAL Init */
/* Initialize the RX POOL */
LWIP_MEMPOOL_INIT(RX_POOL);
#if LWIP_ARP || LWIP_ETHERNET
/* set MAC hardware address length */
netif->hwaddr_len = ETH_HWADDR_LEN;
/* set MAC hardware address */
netif->hwaddr[0] = heth.Init.MACAddr[0];
netif->hwaddr[1] = heth.Init.MACAddr[1];
netif->hwaddr[2] = heth.Init.MACAddr[2];
netif->hwaddr[3] = heth.Init.MACAddr[3];
netif->hwaddr[4] = heth.Init.MACAddr[4];
netif->hwaddr[5] = heth.Init.MACAddr[5];
/* maximum transfer unit */
netif->mtu = ETH_MAX_PAYLOAD;
/* Accept broadcast address and ARP traffic */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
#if LWIP_ARP
netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
#else
netif->flags |= NETIF_FLAG_BROADCAST;
#endif /* LWIP_ARP */
/* USER CODE BEGIN PHY_PRE_CONFIG */
/* USER CODE END PHY_PRE_CONFIG */
/* Set PHY IO functions */
yt8512c_regster_bus_io(&YT8512C, &YT8512C_IOCtx);
// 初始化PHY
/* Initialize the LAN8742 ETH PHY */
if (yt8512c_init(&YT8512C) != LAN8742_STATUS_OK)
{
netif_set_link_down(netif);
netif_set_down(netif);
return;
}
/* 必须开启自动协商功能 */
yt8512c_start_auto_nego(&YT8512C);
if (hal_eth_init_status == HAL_OK)
{
/* Get link state */
ethernet_link_check_state(netif);
}
else
{
Error_Handler();
}
#endif /* LWIP_ARP || LWIP_ETHERNET */
/* USER CODE BEGIN LOW_LEVEL_INIT */
/* USER CODE END LOW_LEVEL_INIT */
}
/**
* @brief This function should do the actual transmission of the packet. The packet is
* contained in the pbuf that is passed to the function. This pbuf
* might be chained.
*
* @param netif the lwip network interface structure for this ethernetif
* @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
* @return ERR_OK if the packet could be sent
* an err_t value if the packet couldn't be sent
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
* to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
static err_t low_level_output(struct netif* netif, struct pbuf* p)
{
uint32_t i = 0U;
struct pbuf* q = NULL;
err_t errval = ERR_OK;
ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT] = {0};
memset(Txbuffer, 0, ETH_TX_DESC_CNT * sizeof(ETH_BufferTypeDef));
for (q = p; q != NULL; q = q->next)
{
if (i >= ETH_TX_DESC_CNT)
return ERR_IF;
Txbuffer[i].buffer = q->payload;
Txbuffer[i].len = q->len;
if (i > 0)
{
Txbuffer[i - 1].next = &Txbuffer[i];
}
if (q->next == NULL)
{
Txbuffer[i].next = NULL;
}
i++;
}
TxConfig.Length = p->tot_len;
TxConfig.TxBuffer = Txbuffer;
TxConfig.pData = p;
HAL_ETH_Transmit(&heth, &TxConfig, ETH_DMA_TRANSMIT_TIMEOUT);
return errval;
}
/**
* @brief Should allocate a pbuf and transfer the bytes of the incoming
* packet from the interface into the pbuf.
*
* @param netif the lwip network interface structure for this ethernetif
* @return a pbuf filled with the received packet (including MAC header)
* NULL on memory error
*/
static struct pbuf* low_level_input(struct netif* netif)
{
struct pbuf* p = NULL;
if (RxAllocStatus == RX_ALLOC_OK)
{
HAL_ETH_ReadData(&heth, (void**)&p);
}
return p;
}
/**
* @brief This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void ethernetif_input(struct netif* netif)
{
struct pbuf* p = NULL;
do
{
p = low_level_input(netif);
if (p != NULL)
{
if (netif->input(p, netif) != ERR_OK)
{
pbuf_free(p);
}
}
}
while (p != NULL);
}
#if !LWIP_ARP
/**
* This function has to be completed by user in case of ARP OFF.
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if ...
*/
static err_t low_level_output_arp_off(struct netif* netif, struct pbuf* q, const ip4_addr_t* ipaddr)
{
err_t errval;
errval = ERR_OK;
/* USER CODE BEGIN 5 */
/* USER CODE END 5 */
return errval;
}
#endif /* LWIP_ARP */
/**
* @brief Should be called at the beginning of the program to set up the
* network interface. It calls the function low_level_init() to do the
* actual setup of the hardware.
*
* This function should be passed as a parameter to netif_add().
*
* @param netif the lwip network interface structure for this ethernetif
* @return ERR_OK if the loopif is initialized
* ERR_MEM if private data couldn't be allocated
* any other err_t on error
*/
err_t ethernetif_init(struct netif* netif)
{
LWIP_ASSERT("netif != NULL", (netif != NULL));
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
// MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
#if LWIP_ARP || LWIP_ETHERNET
#if LWIP_ARP
netif->output = etharp_output;
#else
/* The user should write its own code in low_level_output_arp_off function */
netif->output = low_level_output_arp_off;
#endif /* LWIP_ARP */
#endif /* LWIP_ARP || LWIP_ETHERNET */
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = low_level_output;
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
/**
* @brief Custom Rx pbuf free callback
* @param pbuf: pbuf to be freed
* @retval None
*/
void pbuf_free_custom(struct pbuf* p)
{
struct pbuf_custom* custom_pbuf = (struct pbuf_custom*)p;
LWIP_MEMPOOL_FREE(RX_POOL, custom_pbuf);
/* If the Rx Buffer Pool was exhausted, signal the ethernetif_input task to
* call HAL_ETH_GetRxDataBuffer to rebuild the Rx descriptors. */
if (RxAllocStatus == RX_ALLOC_ERROR)
{
RxAllocStatus = RX_ALLOC_OK;
}
}
/* USER CODE BEGIN 6 */
/**
* @brief Returns the current time in milliseconds
* when LWIP_TIMERS == 1 and NO_SYS == 1
* @param None
* @retval Current Time value
*/
u32_t sys_now(void)
{
return HAL_GetTick();
}
/* USER CODE END 6 */
/**
* @brief Initializes the ETH MSP.
* @param ethHandle: ETH handle
* @retval None
*/
void HAL_ETH_MspInit(ETH_HandleTypeDef* ethHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (ethHandle->Instance == ETH)
{
/* USER CODE BEGIN ETH_MspInit 0 */
/* USER CODE END ETH_MspInit 0 */
/* Enable Peripheral clock */
__HAL_RCC_ETH_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOG_CLK_ENABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PG11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF11_ETH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
/* USER CODE BEGIN ETH_MspInit 1 */
/* USER CODE END ETH_MspInit 1 */
}
}
void HAL_ETH_MspDeInit(ETH_HandleTypeDef* ethHandle)
{
if (ethHandle->Instance == ETH)
{
/* USER CODE BEGIN ETH_MspDeInit 0 */
/* USER CODE END ETH_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_ETH_CLK_DISABLE();
/**ETH GPIO Configuration
PC1 ------> ETH_MDC
PA1 ------> ETH_REF_CLK
PA2 ------> ETH_MDIO
PA7 ------> ETH_CRS_DV
PC4 ------> ETH_RXD0
PC5 ------> ETH_RXD1
PG11 ------> ETH_TX_EN
PG13 ------> ETH_TXD0
PG14 ------> ETH_TXD1
*/
HAL_GPIO_DeInit(GPIOC, GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5);
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7);
HAL_GPIO_DeInit(GPIOG, GPIO_PIN_11 | GPIO_PIN_13 | GPIO_PIN_14);
/* USER CODE BEGIN ETH_MspDeInit 1 */
/* USER CODE END ETH_MspDeInit 1 */
}
}
/*******************************************************************************
PHI IO Functions
*******************************************************************************/
/**
* @brief Initializes the MDIO interface GPIO and clocks.
* @param None
* @retval 0 if OK, -1 if ERROR
*/
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;
}
/**
* @brief De-Initializes the MDIO interface .
* @param None
* @retval 0 if OK, -1 if ERROR
*/
int32_t ETH_PHY_IO_DeInit(void)
{
return 0;
}
/**
* @brief Read a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param pRegVal: pointer to hold the register value
* @retval 0 if OK -1 if Error
*/
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;
}
/**
* @brief Write a value to a PHY register through the MDIO interface.
* @param DevAddr: PHY port address
* @param RegAddr: PHY register address
* @param RegVal: Value to be written
* @retval 0 if OK -1 if Error
*/
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;
}
/**
* @brief Get the time in millisecons used for internal PHY driver process.
* @retval Time value
*/
int32_t ETH_PHY_IO_GetTick(void)
{
return HAL_GetTick();
}
/**
* @brief Check the ETH link state then update ETH driver and netif link accordingly.
* @retval None
*/
void ethernet_link_check_state(struct netif* netif)
{
ETH_MACConfigTypeDef MACConf = {0};
int32_t PHYLinkState = 0;
uint32_t linkchanged = 0U, speed = 0U, duplex = 0U;
PHYLinkState = yt8512c_get_link_state(&YT8512C);
if (netif_is_link_up(netif) && (PHYLinkState <= LAN8742_STATUS_LINK_DOWN))
{
HAL_ETH_Stop(&heth);
netif_set_down(netif);
netif_set_link_down(netif);
}
else if (!netif_is_link_up(netif) && (PHYLinkState > LAN8742_STATUS_LINK_DOWN))
{
switch (PHYLinkState)
{
case LAN8742_STATUS_100MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_100MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_100M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_FULLDUPLEX:
duplex = ETH_FULLDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
case LAN8742_STATUS_10MBITS_HALFDUPLEX:
duplex = ETH_HALFDUPLEX_MODE;
speed = ETH_SPEED_10M;
linkchanged = 1;
break;
default:
break;
}
if (linkchanged)
{
/* Get MAC Config MAC */
HAL_ETH_GetMACConfig(&heth, &MACConf);
MACConf.DuplexMode = duplex;
MACConf.Speed = speed;
HAL_ETH_SetMACConfig(&heth, &MACConf);
HAL_ETH_Start(&heth);
netif_set_up(netif);
netif_set_link_up(netif);
}
}
}
void HAL_ETH_RxAllocateCallback(uint8_t** buff)
{
/* USER CODE BEGIN HAL ETH RxAllocateCallback */
struct pbuf_custom* p = LWIP_MEMPOOL_ALLOC(RX_POOL);
if (p)
{
/* Get the buff from the struct pbuf address. */
*buff = (uint8_t*)p + offsetof(RxBuff_t, buff);
p->custom_free_function = pbuf_free_custom;
/* Initialize the struct pbuf.
* This must be performed whenever a buffer's allocated because it may be
* changed by lwIP or the app, e.g., pbuf_free decrements ref. */
pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, p, *buff, ETH_RX_BUF_SIZE);
}
else
{
RxAllocStatus = RX_ALLOC_ERROR;
*buff = NULL;
}
/* USER CODE END HAL ETH RxAllocateCallback */
}
void HAL_ETH_RxLinkCallback(void** pStart, void** pEnd, uint8_t* buff, uint16_t Length)
{
/* USER CODE BEGIN HAL ETH RxLinkCallback */
struct pbuf** ppStart = (struct pbuf**)pStart;
struct pbuf** ppEnd = (struct pbuf**)pEnd;
struct pbuf* p = NULL;
/* Get the struct pbuf from the buff address. */
p = (struct pbuf*)(buff - offsetof(RxBuff_t, buff));
p->next = NULL;
p->tot_len = 0;
p->len = Length;
/* Chain the buffer. */
if (!*ppStart)
{
/* The first buffer of the packet. */
*ppStart = p;
}
else
{
/* Chain the buffer to the end of the packet. */
(*ppEnd)->next = p;
}
*ppEnd = p;
/* Update the total length of all the buffers of the chain. Each pbuf in the chain should have its tot_len
* set to its own length, plus the length of all the following pbufs in the chain. */
for (p = *ppStart; p != NULL; p = p->next)
{
p->tot_len += Length;
}
/* USER CODE END HAL ETH RxLinkCallback */
}
void HAL_ETH_TxFreeCallback(uint32_t* buff)
{
/* USER CODE BEGIN HAL ETH TxFreeCallback */
pbuf_free((struct pbuf*)buff);
/* USER CODE END HAL ETH TxFreeCallback */
}
/* USER CODE BEGIN 8 */
/* USER CODE END 8 */
将自定义的文件添加到项目中

在main函数添加Lwip处理逻辑

编译下载并测试(这里不再说明如何配置下载步骤)


命令行测试(测试ok)

总结
- 太久没有用lwip了,准备这段时间开始复习一下。
- 记录一下YT8512C结合stm32cubemx的方式,更加的便利。