超简单stm32cubemx+lwip+yt8512c工程搭建记录

前言

  1. 本次使用的正点原子的探索者开发板,版本为V3.4
  2. 使用的是stm32cubemx6.15版本生成的代码
  3. 本次提供关键的操作步骤,开发工具选用clion+arm-gcc+openocd
  4. 此次YT8512C驱动参考的链接为gitee
  5. 为了防止

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, &regvalue) < 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, &regvalue) >= 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, &regvalue) < 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)

总结

  1. 太久没有用lwip了,准备这段时间开始复习一下。
  2. 记录一下YT8512C结合stm32cubemx的方式,更加的便利。
相关推荐
亿道电子Emdoor1 小时前
【Arm】MCU和SOC的区别
arm开发·单片机·嵌入式硬件
Vizio<2 小时前
STM32HAL库开发笔记-系统定时器与中断闪灯
笔记·stm32·单片机
纳米软件12 小时前
IGBT模块测试项目集合——纳米软件
单片机·嵌入式硬件·模块测试
单片机系统设计16 小时前
基于STM32的智能台灯系统/智能书桌
stm32·单片机·嵌入式硬件·毕业设计·智能家居
F1331689295716 小时前
5030A 芯片 24V 转 5V 15A 大电流快充选型
网络·单片机·嵌入式硬件·物联网·汽车
恒锐丰小吕16 小时前
无锡黑锋 HF4004 低噪声电荷泵DC-DC转换器技术解析
嵌入式硬件·硬件工程
星一工作室17 小时前
STM32项目分享:基于单片机的智能宠物玩具的设计
stm32·单片机·嵌入式硬件
up向上up17 小时前
基于51单片机数字频率计仿真设计
单片机·嵌入式硬件·51单片机
SystickInt18 小时前
32 RTC实时时钟-独立定时器
stm32·单片机·嵌入式硬件