STM32H750配置LAN PHY芯片LAN8742

阶段一:MPU + Cache 初始化

MPU_Config()

├─ Region0: 0x30040000 / 256B / Device / Not Cacheable / Not Shareable ← ETH DMA 描述符

└─ Region1: 0x30044000 / 16KB / Cacheable Write-through / Not Shareable ← LwIP 堆

SCB_EnableICache() ← 指令缓存

SCB_EnableDCache() ← 数据缓存

SCB->CACR |= 1<<2 ← D-Cache 设为 Write-through 模式

阶段二:LwIP 协议栈内核初始化

lwip_init() // LwIP 内核

├─ mem_init / memp_init ← 内存池管理

├─ pbuf_init ← pbuf 链表管理

├─ tcp_init / udp_init / ip_init ← 各协议模块

└─ dhcp_init / arp_init / icmp_init ← 上层服务

阶段三:网络接口注册

Netif_Config()

│

├─ IP_ADDR4(&ipaddr, 192.168.3.122) ← 组装静态 IP

├─ IP_ADDR4(&netmask, 255.255.255.0) ← 子网掩码

├─ IP_ADDR4(&gw, 192.168.3.1) ← 网关

│

├─ netif_add(&gnetif, ..., ethernetif_init, ethernet_input)

│ │

│ └──► 触发 ethernetif_init() 回调 ← 进入阶段四

│

├─ netif_set_default(&gnetif) ← 设为默认网卡

│

└─ netif_set_up() / netif_set_down() ← 根据链路状态启停

阶段三:网络接口注册 (Netif_Config())

app_ethernet.c

```

Netif_Config()

│

├─ IP_ADDR4(&ipaddr, 192.168.3.122) ← 组装静态 IP

├─ IP_ADDR4(&netmask, 255.255.255.0) ← 子网掩码

├─ IP_ADDR4(&gw, 192.168.3.1) ← 网关

│

├─ netif_add(&gnetif, ..., ethernetif_init, ethernet_input)

│ │

│ └──► 触发 ethernetif_init() 回调 ← 进入阶段四

│

├─ netif_set_default(&gnetif) ← 设为默认网卡

│

└─ netif_set_up() / netif_set_down() ← 根据链路状态启停

```

阶段四:底层硬件驱动初始化

ethernetif_init(netif)

│

├─ mem_malloc(sizeof(ethernetif)) ← 分配私有结构体

├─ netif->name = "st" ← 接口名

├─ netif->output = etharp_output ← ★ LwIP 发包: 先 ARP 再发送

├─ netif->linkoutput = low_level_output ← ★ 最终硬件发送函数

│

└─ low_level_init(netif) ─────────────────────────────────────┐

│ │

├─ 填充 EthHandle 结构体: │

│ .Instance = ETH │

│ .MACAddr = {} │

│ .MediaInterface = HAL_ETH_RMII_MODE │

│ .RxDesc = DMARxDscrTab (0x30040000) │

│ .TxDesc = DMATxDscrTab (0x30040060) │

│ .RxBuffLen = 1536 │

│ │

├─ HAL_ETH_Init(&EthHandle) ────────────────────────────┐ │

│ │ │ │

│ └─ HAL_ETH_MspInit() ← 阶段五 │ │

│ │

├─ netif->hwaddr = MAC 地址 │

├─ netif->mtu = ETH_MAX_PAYLOAD (1500) │

├─ netif->flags |= BROADCAST | ETHARP │

│ │

├─ HAL_ETH_DescAssignMemory() × 4 │

│ ← 将 Rx_Buffidx 绑定到每个 DMA 描述符 │

│ │

├─ LWIP_MEMPOOL_INIT(RX_POOL) │

│ ← 初始化 Zero-Copy pbuf 池 (20个) │

│ │

├─ TxConfig 配置: │

│ .Attributes = CSUM | CRCPAD │

│ .ChecksumCtrl = IP+Payload 硬件校验和 │

│ .CRCPadCtrl = 自动 CRC 填充 │

│ │

└─ LAN8720_Init() ──► ethernet_link_check_state() ← 阶段六│

阶段五:ETH GPIO + 中断初始化

HAL_ETH_MspInit()

│

│

├─ ETH_GPIO_Config() ← 逐个配置 9 个 RMII 引脚 (AF11)

│ ├─ PC2 → GPIO_OUTPUT (PHY NRST 复位脚)

│ ├─ PA2 → ETH_MDIO

│ ├─ PC1 → ETH_MDC

│ ├─ PA1 → ETH_RMII_REF_CLK

│ ├─ PA7 → ETH_RMII_CRS_DV

│ ├─ PC4 → ETH_RMII_RXD0

│ ├─ PC5 → ETH_RMII_RXD1

│ ├─ PB11 → ETH_RMII_TX_EN

│ ├─ PB12 → ETH_RMII_TXD0

│ └─ PB13 → ETH_RMII_TXD1

│

├─ HAL_NVIC_SetPriority(ETH_IRQn, 6, 0) ← 抢占优先级6,子优先级0

├─ HAL_NVIC_EnableIRQ(ETH_IRQn) ← 使能 ETH 中断

│

├─ __HAL_RCC_ETH1MAC_CLK_ENABLE() ← 使能 ETH MAC 时钟

├─ __HAL_RCC_ETH1TX_CLK_ENABLE() ← 使能 ETH TX 时钟

├─ __HAL_RCC_ETH1RX_CLK_ENABLE() ← 使能 ETH RX 时钟

│

└─ PHY 硬件复位时序:

PC2=1 → Delay(100ms) → PC2=0 → Delay(100ms) → PC0=1

阶段六:PHY 初始化 + 链路检测

LAN8720_Init()

│

├─ 写 PHY_BCR 寄存器: 软件复位 (PHY_RESET)

├─ Delay(PHY_RESET_DELAY)

├─ 写 PHY_BCR 寄存器: 使能自协商 (PHY_AUTONEGOTIATION)

├─ 轮询 PHY_BSR 寄存器,等待自协商完成 (超时 PHY_READ_TO)

│

└─ ethernet_link_check_state(netif)

│

├─ 读取 PHY SR 寄存器,获取链路状态和协商结果

├─ 根据自协商结果配置 MAC 双工模式和速率

└─ HAL_ETH_Start_IT() ← 启动 ETH 接收中断

DMACSR=0x00205504 → RXSTS=0 (RX DMA 已停止) 且有 TU (发送溢出错误)

各区域详解

  1. DMA 描述符区域 (0x30040000 ~ 0x300400DF)

项目 地址 大小

Rx 描述符 ×4 0x30040000 4 × ~32B = 128B

Tx 描述符 ×4 0x30040060 4 × ~32B = 128B

备注 : 代码中 RxDesc 地址 0x30040000 与 TxDesc 0x30040060 存在重叠区域 ( 0x30040060 ~ 0x3004007F ),建议将 RxDesc 改为 0x30040000 、TxDesc 改为 0x30040080 (或按 IOC 原值 RxDesc= 0x30040200 )以避免冲突。

MPU 保护: Region0 --- 起始 0x30040000 ,大小 256B ,属性 Device / Not Cacheable / Bufferable / Not Shareable ,确保 DMA 访问不经过 D-Cache。

  1. 接收缓冲区 (0x30040200 ~ 0x30041A00)

定义位置: ethernetif.c

缓冲区 地址 大小

Rx_Buff0 0x30040200 1536B

Rx_Buff1 0x30040800 1536B

Rx_Buff2 0x30040E00 1536B

Rx_Buff3 0x30041400 1536B

使用 Zero-Copy 机制:DMA 直接将收到的数据写入这里,LwIP 通过 pbuf_alloced_custom() 创建引用这些内存的 pbuf,不产生拷贝。释放时通过 pbuf_free_custom() 调用 SCB_InvalidateDCache_by_Addr() 刷新 D-Cache 后归还。

  1. LwIP RAM 堆 (0x30044000 ~ 0x30046800)

MPU 保护: Region1 --- 起始 0x30044000 ,大小 16KB ,属性 Cacheable / Write-through / Not Bufferable / Not Shareable 。使用 Write-through 确保写操作立即到达内存,DMA 可以读取到最新的 Tx 数据。

该堆由 LwIP 内部 mem_malloc/mem_free 管理,实际分配到(通过 LwIP 内核源码 mem.c ):

分配项 来源 用途

ethernetif 结构体 mem_malloc() 网络接口私有数据

Tx pbuf pbuf_alloc(PBUF_POOL) 发送缓冲区池 (PBUF_POOL_SIZE=4)

TCP PCB memp_alloc(MEMP_TCP_PCB) TCP 协议控制块 (×10)

UDP PCB memp_alloc(MEMP_UDP_PCB) UDP 协议控制块 (×6)

TCP 分段 memp_alloc(MEMP_TCP_SEG) TCP 重传队列 (×8) pbuf 结构体 memp_alloc(MEMP_PBUF)

pbuf 链表节点 (×10) 系统超时 memp_alloc(MEMP_SYS_TIMEOUT) 定时器节点 (×10)

DHCP 结构体 mem_malloc() DHCP 客户端 ARP 表项 memp_alloc(MEMP_ARP_TABLE) ARP 缓存

CDatex 总结

区域 D-Cache 策略 原因

DMA 描述符 (0x30040000) Not Cacheable 硬件 DMA 直接读写,绕过 Cache 防止数据不一致

DMA 接收缓冲 (0x30040200) 无专门 MPU 保护 接收时手动 SCB_InvalidateDCache_by_Addr() 刷新

LwIP 堆 (0x30044000) Write-through CPU 写入立即到 SRAM,DMA 发送时能读到最新数据

普通变量 (DTCM/AXI) Write-back (D-Cache 默认) 高速正常运行

相关注意点

1、硬件复位

// ★ PHY 复位时序

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET); // PC2 拉低

HAL_Delay(100);

HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET); // PC2 拉高

HAL_Delay(1000); // 等待 LAN8720 稳定

2、ld链接文件的修改

/* DMA 描述符 --- 与 MPU Region0 (0x30040000) 精确对齐 */

.RxDecripSection 0x30040000 (NOLOAD) :

{

. = ALIGN(32);

*(.RxDecripSection)

} >RAM_D2

/* TxDesc 偏移 0x80 避免与 RxDesc 重叠 (4×32B=128B) */

.TxDecripSection 0x30040080 (NOLOAD) :

{

. = ALIGN(32);

*(.TxDecripSection)

} >RAM_D2

/* Rx 缓冲区 --- 紧随描述符之后 */

.RxBuffSection 0x30040200 (NOLOAD) :

{

. = ALIGN(32);

*(.RxBuffSection)

} >RAM_D2

3、底层驱动

// ★ DMA 描述符 --- GCC section 属性

ETH_DMADescTypeDef DMARxDscrTabETH_RX_DESC_CNT

attribute((section(".RxDecripSection")));

ETH_DMADescTypeDef DMATxDscrTabETH_TX_DESC_CNT

attribute((section(".TxDecripSection")));

// ★ Rx_Buff --- 从 LWIP_MEMPOOL 改为固定地址数组

#define ETH_RX_BUFFER_CNT 12U

typedef struct {

struct pbuf_custom pbuf_custom;

uint8_t buffETH_RX_BUFFER_SIZE __ALIGNED(32);

} RxBuff_t;

static RxBuff_t Rx_BuffETH_RX_BUFFER_CNT

attribute((section(".RxBuffSection")));

static uint8_t rx_buff_usedETH_RX_BUFFER_CNT; // 位图分配器

// ★ RxAllocateCallback --- 简单遍历空闲位图

for (i = 0; i < ETH_RX_BUFFER_CNT; i++) {

if (!rx_buff_usedi) {

rx_buff_usedi = 1;

rxb->pbuf_custom.custom_free_function = pbuf_free_custom;

pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, ...);

*buff = rxb->buff;

return;

}

}

// ★ pbuf_free_custom --- 归还缓冲区

rxb = (RxBuff_t *)p;

rx_buff_usedrxb - Rx_Buff = 0;

SCB_InvalidateDCache_by_Addr(rxb->buff, ETH_RX_BUFFER_SIZE);

// ★ 中断模式启动

HAL_ETH_Start_IT(&heth); // 原 HAL_ETH_Start

4中断模式

D2 SRAM (Non-Cacheable)

╔═════════════════════════════════╗

║ 0x30040000 DMARxDscrTab4 ║ ← MPU Region0: Device, Not Cacheable

║ 0x30040080 DMATxDscrTab4 ║

║ 0x30040200 Rx_Buff0..11 ║ ← 固定地址 Zero-Copy

║ 0x30044B80 (end) ║

║ ║

║ 0x30044000 LwIP Heap (空) ║ ← MPU Region1: Write-through

║ 0x30046800 ║

╚═════════════════════════════════╝

DTCMRAM (CPU 独占,不缓存)

╔═════════════════════════════════╗

║ 0x20000000 .data / .bss ║

║ LwIP 内部结构体 (TCP/UDP PCB, ║

║ pbuf 节点, ARP 表, etc.) ║

╚═════════════════════════════════╝

类别 要点 时钟

STM32H750 最高 400MHz, 必须 VOS1 + FLASH_LATENCY=3 ;APB4 分频关系到 ETH 时钟

DMA 描述符 Cortex-M7 有 D-Cache,描述符必须放 non-cacheable 区域(D2 SRAM 或通过 MPU 标记)

MPU 对齐 链接脚本中 section 地址必须与 MPU Region 基地址 严格一致

PHY 复位 PC2 拉低 ≥100ms 再拉高,等待 ≥1s 让 LAN8720 内部 PLL 锁定

中断模式 必须 HAL_ETH_Start_IT ,不是 HAL_ETH_Start (后者轮询,不触发 IRQ)

Rx_Buff 数量 至少 2×ETH_RX_DESC_CNT(推荐 12),否则 LwIP 释放慢时 DMA 无缓冲可用

Cache 维护 接收后 SCB_InvalidateDCache_by_Addr ;发送前 SCB_CleanDCache_by_Addr

静态 IP 确保与 PC 同网段(192.168.7.xx),防火墙允许 ping

复制代码
/*
******************************************************************************
**

**  File        : LinkerScript.ld
**
**  Author		: STM32CubeMX
**
**  Abstract    : Linker script for STM32H750VBTx series
**                128Kbytes FLASH and 1056Kbytes RAM
**
**                Set heap size, stack size and stack location according
**                to application requirements.
**
**                Set memory bank area and size if external memory is used.
**
**  Target      : STMicroelectronics STM32
**
**  Distribution: The file is distributed "as is," without any warranty
**                of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>&copy; COPYRIGHT(c) 2025 STMicroelectronics</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
**   1. Redistributions of source code must retain the above copyright notice,
**      this list of conditions and the following disclaimer.
**   2. Redistributions in binary form must reproduce the above copyright notice,
**      this list of conditions and the following disclaimer in the documentation
**      and/or other materials provided with the distribution.
**   3. Neither the name of STMicroelectronics nor the names of its contributors
**      may be used to endorse or promote products derived from this software
**      without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/

/* Entry Point */
ENTRY(Reset_Handler)

/* Highest address of the user mode stack */
_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);    /* end of RAM */
_sstack = _estack - _Min_Stack_Size;
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */

/* Specify the memory areas */
MEMORY
{
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
RAM (xrw)      : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw)      : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 128K
}

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH

  .ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    *(.ARM.extab* .gnu.linkonce.armextab.*)
    . = ALIGN(4);
  } >FLASH

  .ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
    . = ALIGN(4);
  } >FLASH

  .preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
    . = ALIGN(4);
  } >FLASH

  .fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
  {
    . = ALIGN(4);
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
    . = ALIGN(4);
  } >FLASH

  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);

  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data :
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
    *(.RamFunc)        /* .RamFunc sections */
    *(.RamFunc*)       /* .RamFunc* sections */

    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >DTCMRAM AT> FLASH


  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)

    . = ALIGN(4);
  _ebss = .;         /* define a global symbol at bss end */
  __bss_end__ = _ebss;
  } >DTCMRAM

  /* ETH DMA descriptors at 0x30040000 (matches MPU Region0) */
  .RxDecripSection 0x30040000 (NOLOAD) :
  {
    . = ALIGN(32);
    *(.RxDecripSection)
  } >RAM_D2

  /* TxDesc at 0x30040080 avoids overlap with RxDesc (4x32B=128B) */
  .TxDecripSection 0x30040080 (NOLOAD) :
  {
    . = ALIGN(32);
    *(.TxDecripSection)
  } >RAM_D2

  /* ETH Rx buffers at 0x30040200 (4 x 1536B = 0x1800) */
  .RxBuffSection 0x30040200 (NOLOAD) :
  {
    . = ALIGN(32);
    *(.RxBuffSection)
  } >RAM_D2

  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(8);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(8);
  } >DTCMRAM



  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

}


/* USER CODE BEGIN Header */
/**
 ******************************************************************************
 * File Name          : ethernetif.c
 * Description        : This file provides code for the configuration
 *                      of the ethernetif.c MiddleWare.
 ******************************************************************************
 * @attention
 *
 * Copyright (c) 2026 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>

/* Within 'USER CODE' section, code will be kept by default at each generation */
/* USER CODE BEGIN 0 */
/*
 * ===== CubeMX 再生保护 (Regeneration Protection) =====
 * 当 CubeMX 重新生成代码后, 以下项目需手动恢复:
 *   1. RxBuff_t.buff 需要添加 __ALIGNED(32) 对齐修饰符
 *   2. 见下方 USER CODE BEGIN 2 / 4 / 6 中的自定义代码
 * ======================================================
 */

/* 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)
/* ETH_RX_BUFFER_SIZE parameter is defined in lwipopts.h */

/* 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 memory heap,
          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_BUFFER_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_BUFFER_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;

#if defined(__ICCARM__) /*!< IAR Compiler */

#pragma location = 0x30040000
ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
#pragma location = 0x30040080
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */

#elif defined(__CC_ARM) /* MDK ARM Compiler */

__attribute__((at(0x30040000))) ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
__attribute__((at(0x30040080))) ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */

#elif defined(__GNUC__) /* GNU Compiler */

ETH_DMADescTypeDef DMARxDscrTab[ETH_RX_DESC_CNT] __attribute__((section(".RxDecripSection"))); /* Ethernet Rx DMA Descriptors */
ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".TxDecripSection"))); /* Ethernet Tx DMA Descriptors */

#endif

/* USER CODE BEGIN 2 */
/*
 * ===== 自定义以太网缓冲区 (CubeMX 再生后保留) =====
 */
#define ETH_RX_BUFFER_CNT 12U
static RxBuff_t Rx_Buff[ETH_RX_BUFFER_CNT] __attribute__((section(".RxBuffSection")));
static uint8_t rx_buff_used[ETH_RX_BUFFER_CNT];
/* 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);

lan8742_Object_t LAN8742;
lan8742_IOCtx_t LAN8742_IOCtx = {ETH_PHY_IO_Init,
                                 ETH_PHY_IO_DeInit,
                                 ETH_PHY_IO_WriteReg,
                                 ETH_PHY_IO_ReadReg,
                                 ETH_PHY_IO_GetTick};

/* USER CODE BEGIN 3 */

/* 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 */

  /* 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 */
  /* Hardware reset PHY via PC2 */
  printf("[PHY] Hardware reset via PC2...\r\n");
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_RESET);
  HAL_Delay(100);
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_2, GPIO_PIN_SET);
  HAL_Delay(500);
  printf("[PHY] Reset done\r\n");
  /* USER CODE END PHY_PRE_CONFIG */
  /* Set PHY IO functions */
  LAN8742_RegisterBusIO(&LAN8742, &LAN8742_IOCtx);

  /* Initialize the LAN8742 ETH PHY */
  LAN8742_Init(&LAN8742);

  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)
{
  RxBuff_t *rxb = (RxBuff_t *)p;
  uint32_t idx = rxb - Rx_Buff;

  if (idx < ETH_RX_BUFFER_CNT)
  {
    rx_buff_used[idx] = 0;
    SCB_InvalidateDCache_by_Addr((uint32_t *)rxb->buff, ETH_RX_BUFFER_SIZE);
  }

  if (RxAllocStatus == RX_ALLOC_ERROR)
  {
    RxAllocStatus = RX_ALLOC_OK;
  }
}

/* USER CODE BEGIN 6 */

/**
 * @brief  Custom Rx pbuf free callback
 * @param  pbuf: pbuf to be freed
 * @retval None
 */
// void pbuf_free_custom(struct pbuf *p)
// {
//   RxBuff_t *rxb = (RxBuff_t *)p; /* pbuf_custom is at start of RxBuff_t */
//   uint32_t idx = rxb - Rx_Buff;

//   if (idx < ETH_RX_BUFFER_CNT)
//   {
//     rx_buff_used[idx] = 0;
//     SCB_InvalidateDCache_by_Addr((uint32_t *)rxb->buff, ETH_RX_BUFFER_SIZE);
//   }

//   if (RxAllocStatus == RX_ALLOC_ERROR)
//   {
//     RxAllocStatus = RX_ALLOC_OK;
//   }
// }

/**
 * @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_ETH1MAC_CLK_ENABLE();
    __HAL_RCC_ETH1TX_CLK_ENABLE();
    __HAL_RCC_ETH1RX_CLK_ENABLE();

    __HAL_RCC_GPIOC_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOB_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
    PB11     ------> ETH_TX_EN
    PB12     ------> ETH_TXD0
    PB13     ------> 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_12 | GPIO_PIN_13;
    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(GPIOB, &GPIO_InitStruct);

    /* Peripheral interrupt init */
    HAL_NVIC_SetPriority(ETH_IRQn, 6, 0);
    HAL_NVIC_EnableIRQ(ETH_IRQn);
    /* 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 */
    /* Disable Peripheral clock */
    __HAL_RCC_ETH1MAC_CLK_DISABLE();
    __HAL_RCC_ETH1TX_CLK_DISABLE();
    __HAL_RCC_ETH1RX_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
    PB11     ------> ETH_TX_EN
    PB12     ------> ETH_TXD0
    PB13     ------> 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(GPIOB, GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13);

    /* Peripheral interrupt Deinit*/
    HAL_NVIC_DisableIRQ(ETH_IRQn);

    /* 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 = LAN8742_GetLinkState(&LAN8742);

  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_IT(&heth); // 中断模式 → 描述符正常构建(&heth);
      netif_set_up(netif);
      netif_set_link_up(netif);
    }
  }
}

void HAL_ETH_RxAllocateCallback(uint8_t **buff)
{
  /* USER CODE BEGIN HAL ETH RxAllocateCallback */
  int i;

  for (i = 0; i < ETH_RX_BUFFER_CNT; i++)
  {
    if (!rx_buff_used[i])
    {
      rx_buff_used[i] = 1;
      RxBuff_t *rxb = &Rx_Buff[i];
      rxb->pbuf_custom.custom_free_function = pbuf_free_custom;
      pbuf_alloced_custom(PBUF_RAW, 0, PBUF_REF, &rxb->pbuf_custom, rxb->buff, ETH_RX_BUFFER_SIZE);
      *buff = rxb->buff;
      return;
    }
  }

  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;
  }

  /* Invalidate data cache because Rx DMA's writing to physical memory makes it stale. */
  SCB_InvalidateDCache_by_Addr((uint32_t *)buff, 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 */
相关推荐
Cry丶7 小时前
ubuntu安装vsftpd
服务器·网络·ubuntu
SHENGWEI胜为10 小时前
交换机端口显示正常,终端却获取不到 IP 地址?从 DHCP 流程定位故障
服务器·网络·测试工具·缓存·通信工程
HQB202610 小时前
EG1198|屹晶微电子 120V 内置 MOS 异步降压 DC‑DC(高性价比)
嵌入式硬件·硬件工程
果壳science12 小时前
张祥前实验如何获取正电荷
笔记·科技·嵌入式硬件·硬件工程·制造
IMPYLH12 小时前
Linux 系统备份与镜像
linux·服务器·网络
沉淀的.晴天13 小时前
can总线
stm32
桀人13 小时前
认识AT24C02
stm32·单片机·嵌入式硬件
淘气包海鸟13 小时前
水声网络框架
网络·ns3·水
国科安芯14 小时前
星间激光通信终端CANFD控制网络的时延确定性与辐射可靠性
网络·嵌入式硬件·信息与通信·canfd·低轨卫星·商业航天·抗辐射
一直C14 小时前
嵌入式 Linux 驱动开发学习笔记:内核定时器、platform 总线与 I²C 驱动
linux·驱动开发·嵌入式硬件·学习·imx6ull