DP83822HF: DP83822 Driver File

Part Number: DP83822HF

I am currently conducting the development of network communication based on the STM32H7 and DP83822HF chips. 


Previously, I had already implemented communication based on the driver files of LAN8742 (lan8742.c/lan8742.h), but now the PHY chip has been replaced with DP83822HF. I even failed to successfully initialize this chip because after initialization, I couldn't read any correct register data. 


Where should I obtain the driver file for this chip? On the official website, only DP83848 is mentioned as being related. Can I directly use it?

  • Hello, we have received your case and the investigation will take some time. Thank you for your patience.

  • Hi,

    Thanks for your reply information. I'v got and use driver files(dp83822.c/dp83822.h/dp83822_priv.h/phy_common.h/phy_common_priv.c/phy_common_priv.h) in this GitHub link.

    Now I can read the register information of the PHY, but I have encountered new problems. Communication still cannot be achieved and ping failed.

    1. Firstly, I found that there was no external resistor connected to pin 26 (RX_DV) on the circuit board. So, I tried to achieve the software setting of the PHY to the Master mode of RMII by setting bit 5 of the RCSR register to 1 and bit 7 to 0. Then, I set bit 14 of the PHYRCR register (0x001F) to 1 to perform a digital restart.

    However, after this, my ETH initialization always failed because the Ethernet Software reset caused the SWR bit to be set to 1, but the SWR bit could not be automatically cleared, resulting in a timeout.

    I don't understand why this affects the initialization of ETH. Since the initialization of ETH is done before the initialization of PHY, and when using LAN8742, the initialization of ETH can succeed.

    2. I found that in the dp83822.c driver, the configuration of the PHY only enables Auto-MDIX and Robust Auto-MDIX, and there are no other configurations. I want to know what the correct initialization and configuration process is? It seems to be different from that of LAN8742? Could you please offer a correct example? If possible, here is my ethernet.c code(Only changes are provided as compared with using LAN8742). Could you please help confirm if my process is correct?

  • /**
      ******************************************************************************
      * @file    LwIP/LwIP_UDP_Echo_Server/Src/ethernetif.c
      * @author  MCD Application Team
      * @brief   This file implements Ethernet network interface drivers for lwIP
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2017 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.
      *
      ******************************************************************************
      */
    
    /* Includes ------------------------------------------------------------------*/
    #include "stm32h7xx_hal.h"
    #include "lwip/opt.h"
    #include "lwip/timeouts.h"
    #include "lwip/netif.h"
    #include "netif/etharp.h"
    #include "ethernetif.h"
    // #include "../Components/lan8742/lan8742.h"
    #include "../../Drivers/BSP/Components/dp83822/dp83822_stm32_adapter.h"
    #include <string.h>
    
    /* Private typedef -----------------------------------------------------------*/
    /* Private define ------------------------------------------------------------*/
    /* Network interface name */
    #define IFNAME0 's'
    #define IFNAME1 't'
    
    #define ETH_DMA_TRANSMIT_TIMEOUT                (20U)
    
    #define ETH_RX_BUFFER_SIZE            1000U
    #define ETH_RX_BUFFER_CNT             12U
    #define ETH_TX_BUFFER_MAX             ((ETH_TX_DESC_CNT) * 2U)
    
    /* Private macro -------------------------------------------------------------*/
    /* 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: ETH_RX_BUFFER_CNT must be greater than 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)
    */
    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;
    
    #if defined ( __ICCARM__ ) /*!< IAR Compiler */
    
    #pragma location=0x30000000
    ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
    #pragma location=0x30000080
    ETH_DMADescTypeDef  DMATxDscrTab[ETH_TX_DESC_CNT]; /* Ethernet Tx DMA Descriptors */
    
    
    #elif defined ( __CC_ARM )  /* MDK ARM Compiler */
    
    __attribute__((section(".RxDescripSection"))) ETH_DMADescTypeDef  DMARxDscrTab[ETH_RX_DESC_CNT]; /* Ethernet Rx DMA Descriptors */
    __attribute__((section(".TxDescripSection"))) 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(".RxDescripSection"))); /* Ethernet Rx DMA Descriptors */
    ETH_DMADescTypeDef DMATxDscrTab[ETH_TX_DESC_CNT] __attribute__((section(".TxDescripSection")));   /* Ethernet Tx DMA Descriptors */
    
    #endif
    
    /* Memory Pool Declaration */
    LWIP_MEMPOOL_DECLARE(RX_POOL, ETH_RX_BUFFER_CNT, sizeof(RxBuff_t), "Zero-copy RX PBUF pool");
    
    #if defined ( __ICCARM__ ) /*!< IAR Compiler */
    #pragma location = 0x30000100
    extern u8_t memp_memory_RX_POOL_base[];
    
    #elif defined ( __CC_ARM )  /* MDK ARM Compiler */
    __attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
    
    #elif defined ( __GNUC__ ) /* GNU Compiler */
    __attribute__((section(".Rx_PoolSection"))) extern u8_t memp_memory_RX_POOL_base[];
    
    #endif
    
    /* Variable Definitions */
    static RxAllocStatusTypeDef RxAllocStatus;
    
    /* Global Ethernet handle*/
    ETH_HandleTypeDef EthHandle;
    ETH_TxPacketConfig TxConfig;
    
    
    /* Private function prototypes -----------------------------------------------*/
    u32_t sys_now(void);
    
    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};
    // dp83822_Object_t DP83822;
    // dp83822_IOCtx_t  DP83822_IOCtx = {ETH_PHY_IO_Init,
    //                                ETH_PHY_IO_DeInit,
    //                                ETH_PHY_IO_WriteReg,
    //                                ETH_PHY_IO_ReadReg,
    //                                ETH_PHY_IO_GetTick};
    DP83822_STM32_Handle DP83822_Handle;
    bool phy_link_state = false;
    uint32_t phy_speed = ETH_SPEED_100M;
    uint32_t phy_duplex = ETH_FULLDUPLEX_MODE;
    
    /* Private functions ---------------------------------------------------------*/
    void pbuf_free_custom(struct pbuf *p);
    /*******************************************************************************
                           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)
    {
    
      uint8_t macaddress[6]= {ETH_MAC_ADDR0, ETH_MAC_ADDR1, ETH_MAC_ADDR2, 
                              ETH_MAC_ADDR3, ETH_MAC_ADDR4, ETH_MAC_ADDR5};
    
      EthHandle.Instance = ETH;
      EthHandle.Init.MACAddr = macaddress;
      EthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE;
      EthHandle.Init.RxDesc = DMARxDscrTab;
      EthHandle.Init.TxDesc = DMATxDscrTab;
      EthHandle.Init.RxBuffLen = ETH_RX_BUFFER_SIZE;
      printf("[ETH] 1 配置参数完成\r\n");
    
      /* configure ethernet peripheral (GPIOs, clocks, MAC, DMA) */
      HAL_StatusTypeDef status = HAL_ETH_Init(&EthHandle);
      if (status != HAL_OK)
      {
        printf("--[ERROR] HAL_ETH_Init失败! 返回值: %d, ErrorCode: 0x%lx, gState: 0x%lx\r\n", 
          status, EthHandle.ErrorCode, EthHandle.gState);
      }else
      {
        printf("[ETH] 2 HAL_ETH_Init成功\r\n");
      }
    
      /* set MAC hardware address length */
      netif->hwaddr_len = ETH_HWADDR_LEN;
    
      /* set MAC hardware address */
      netif->hwaddr[0] =  ETH_MAC_ADDR0;
      netif->hwaddr[1] =  ETH_MAC_ADDR1;
      netif->hwaddr[2] =  ETH_MAC_ADDR2;
      netif->hwaddr[3] =  ETH_MAC_ADDR3;
      netif->hwaddr[4] =  ETH_MAC_ADDR4;
      netif->hwaddr[5] =  ETH_MAC_ADDR5;
    
      /* maximum transfer unit */
      netif->mtu = ETH_MAX_PAYLOAD;
    
      /* device capabilities */
      /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
      netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
    
      /* Initialize the RX POOL */
      LWIP_MEMPOOL_INIT(RX_POOL);
    
      /* Set Tx packet config common parameters */
      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;
    
      /* =========== DP83822 PHY 初始化 =========== */
      DP83822_Handle.heth = &EthHandle;
      DP83822_Handle.phy_addr = 0x01;  // 通常是0或1
      DP83822_Handle.reset_port = GPIOA;              // 根据实际硬件
      DP83822_Handle.reset_pin = GPIO_PIN_3;
      
      /* 初始化 DP83822 PHY */
      if (DP83822_STM32_Init(&DP83822_Handle) != PHY_SOK)
      {
        netif_set_link_down(netif);
        netif_set_down(netif);
        printf("[EER] DP83822 PHY初始化失败!\r\n");
        return;
      }
      
      printf("[PHY] DP83822 PHY初始化成功!\r\n");
    
      /* 检查链路状态 */
      if (status == HAL_OK)
      {
        ethernet_link_check_state(netif);
      }
    
      uint8_t rmii = DP83822_CheckRMIIMode();
      printf("rmii mode (bit5 of 0x17): %d \r\n", rmii);
      
    }
    
    /**
      * @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(&EthHandle, &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(&EthHandle, (void **)&p);
      }
      return p;
    
    }
    
    /**
      * @brief This function is the ethernetif_input task, it is processed 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);
    
    }
    
    /**
      * @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 */
    
      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...) */
      netif->output = etharp_output;
      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;
      }
    }
    
    /**
      * @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();
    }
    /*******************************************************************************
                           Ethernet MSP Routines
    *******************************************************************************/
    /**
      * @brief  Initializes the ETH MSP.
      * @param  heth: ETH handle
      * @retval None
      */
    void HAL_ETH_MspInit(ETH_HandleTypeDef *heth)
    {
      GPIO_InitTypeDef GPIO_InitStructure = {0};
    
      /* Ethernett MSP init: RMII Mode */
      /* Ethernet pins configuration */
       /*
            RMII_REF_CLK ----------------------> PA1
            RMII_MDIO -------------------------> PA2
            RMII_MDC --------------------------> PC1
            RMII_MII_CRS_DV -------------------> PA7
            RMII_MII_RXD0 ---------------------> PC4
            RMII_MII_RXD1 ---------------------> PC5
            RMII_MII_RXER ---------------------> PB10
            RMII_MII_TX_EN --------------------> PB11
            RMII_MII_TXD0 ---------------------> PB12
            RMII_MII_TXD1 ---------------------> PB13
            LAN8742改为DP83822, 多了两个引脚定义
            RMII_RESET_N-----------------------> PA3
            RMII_IRQ---------------------------> PG4
      */
    
      /* Enable GPIOs clocks */
      __HAL_RCC_GPIOA_CLK_ENABLE();
      __HAL_RCC_GPIOB_CLK_ENABLE();
      __HAL_RCC_GPIOC_CLK_ENABLE();
      // __HAL_RCC_GPIOG_CLK_ENABLE();
    
      /* Configure PA1, PA2 , PA7 */
      GPIO_InitStructure.Pin =  GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_7;
      GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
      GPIO_InitStructure.Mode = GPIO_MODE_AF_PP;
      GPIO_InitStructure.Pull = GPIO_NOPULL;
      GPIO_InitStructure.Alternate = GPIO_AF11_ETH;
      HAL_GPIO_Init(GPIOA, &GPIO_InitStructure);
    
      /* Configure PB10, PB11, PB12 and PB13 */
      GPIO_InitStructure.Pin = GPIO_PIN_10 | 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_InitStructure);
    
      /* Configure PC1, PC4 and PC5 */
      GPIO_InitStructure.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_InitStructure);
    
      /* Enable Ethernet clocks */
      __HAL_RCC_ETH1MAC_CLK_ENABLE();
      __HAL_RCC_ETH1TX_CLK_ENABLE();
      __HAL_RCC_ETH1RX_CLK_ENABLE();
      
    }
    
    /*******************************************************************************
                           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(&EthHandle);
    
    //   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(&EthHandle, 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(&EthHandle, 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 检查以太网链路状态
      */
    // 修改 ethernet_link_check_state 函数
    void ethernet_link_check_state(struct netif *netif)
    {
        ETH_MACConfigTypeDef MACConf = {0};
        static uint32_t last_link_state = 0;
        uint32_t current_link_state;
        
        /* 获取 DP83822 链路状态 */
        current_link_state = DP83822_STM32_IsLinkUp(&DP83822_Handle) ? 1 : 0;
        
        /* 链路状态发生变化 */
        if (current_link_state != last_link_state)
        {
            printf(" [ETH] 链路状态变化: %s -> %s\r\n", 
                   last_link_state ? "Up" : "Down",
                   current_link_state ? "Up" : "Down");
            
            if (current_link_state)
            {
                HAL_ETH_DeInit(&EthHandle);
                
                /* 2. 重新配置ETH初始化参数(MAC地址、描述符等) */
                uint8_t macaddress[6] = {ETH_MAC_ADDR0, ETH_MAC_ADDR1, ETH_MAC_ADDR2, 
                                          ETH_MAC_ADDR3, ETH_MAC_ADDR4, ETH_MAC_ADDR5};
                EthHandle.Instance = ETH;
                EthHandle.Init.MACAddr = macaddress;
                EthHandle.Init.MediaInterface = HAL_ETH_RMII_MODE;
                EthHandle.Init.RxDesc = DMARxDscrTab;
                EthHandle.Init.TxDesc = DMATxDscrTab;
                EthHandle.Init.RxBuffLen = ETH_RX_BUFFER_SIZE;
                
                if (HAL_ETH_Init(&EthHandle) != HAL_OK) {
                    printf("[ERR] HAL_ETH_Init 失败\n");
                    return;
                } // 解决HAL_ETH_Init容易失败的问题后,这里不应该每次都重新配置。
    
                /* 链路建立 */
                uint32_t speed, duplex;
                if (DP83822_STM32_GetLinkStatus(&DP83822_Handle, &speed, &duplex) == PHY_SOK)
                {
                    /* 配置 MAC */
                    HAL_ETH_GetMACConfig(&EthHandle, &MACConf);
                    MACConf.Speed = speed;
                    MACConf.DuplexMode = duplex;
                    HAL_ETH_SetMACConfig(&EthHandle, &MACConf);
                    
                    /* 启动 ETH */
                    if (HAL_ETH_Start(&EthHandle) != HAL_OK) {
                        printf("[EER] ETH启动失败\r\n");
                        return;
                    }
                    
                    /* 更新网络接口状态 */
                    netif_set_up(netif);
                    netif_set_link_up(netif);
                    
                    printf("[ETH] 以太网链路建立: %s, %s\r\n", 
                           (speed == ETH_SPEED_100M) ? "100M" : "10M",
                           (duplex == ETH_FULLDUPLEX_MODE) ? "全双工" : "半双工");
                }
                else
                {
                    printf("[EER] 获取链路状态失败\r\n");
                }
            }
            else
            {
                /* 链路断开 */
                HAL_ETH_Stop(&EthHandle);
                netif_set_down(netif);
                netif_set_link_down(netif);
                printf("[ETH] 以太网链路断开\r\n");
            }
            
            last_link_state = current_link_state;
        }
    }
    
    void HAL_ETH_RxAllocateCallback(uint8_t **buff)
    {
      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_BUFFER_SIZE);
      }
      else
      {
        RxAllocStatus = RX_ALLOC_ERROR;
        *buff = NULL;
      }
    }
    
    void HAL_ETH_RxLinkCallback(void **pStart, void **pEnd, uint8_t *buff, uint16_t Length)
    {
      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);
    }
    
    void HAL_ETH_TxFreeCallback(uint32_t * buff)
    {
      pbuf_free((struct pbuf *)buff);
    }
    
    
    #define LEDCR_RMII_MODE   (1U << 5)  /* bit5 = RMII Mode Enable/Status */
    /**
      * @brief 简单直接地读取DP83822的0x17寄存器,检查RMII模式
      * @retval 1 = RMII模式, 0 = MII模式, 0xFF = 读取失败
      */
    uint8_t DP83822_CheckRMIIMode(void)
    {
        uint32_t regval;
        if (HAL_ETH_ReadPHYRegister(&EthHandle, DP83822_Handle.phy_addr, 0x17, &regval) == HAL_OK) {
            return (regval & LEDCR_RMII_MODE) ? 1 : 0;
        }
        return 0xFF;   // 通信失败
    }
    
    // dp83822_stm32_adapter.c
    #include "dp83822_stm32_adapter.h"
    #include "phy_common.h"
    #include <stdio.h>
    
    
    int32_t stm32_read_reg(void* pArgs, uint32_t reg, uint16_t *val)
    {
        DP83822_STM32_Handle *hphy = (DP83822_STM32_Handle*)pArgs;
        HAL_StatusTypeDef status;
        uint32_t temp_val;
        
        if (hphy == NULL || hphy->heth == NULL) {
            printf("[READ ERR] 参数错误\r\n");
            return PHY_EFAIL;
        }
        
        // 添加超时保护
        uint32_t timeout = 1000000; // 超时计数器
        uint32_t start_time = DWT->CYCCNT; // 使用DWT计数器
        
        do {
            status = HAL_ETH_ReadPHYRegister(hphy->heth, hphy->phy_addr, reg, &temp_val);
            if (status == HAL_OK) {
                *val = (uint16_t)temp_val;
                return PHY_SOK;
            }
            
            // 检查超时
            if ((DWT->CYCCNT - start_time) > timeout) {
                printf("[READ ERR] 读取超时: phy=0x%02X, reg=0x%02X\r\n", 
                       hphy->phy_addr, (uint16_t)reg);
                return PHY_ETIMEOUT;
            }
            
        } while(1);
        
        // 不会执行到这里
        return PHY_EFAIL;
    }
    
    int32_t stm32_write_reg(void* pArgs, uint32_t reg, uint16_t val)
    {
        DP83822_STM32_Handle *hphy = (DP83822_STM32_Handle*)pArgs;
        HAL_StatusTypeDef status;
        
        status = HAL_ETH_WritePHYRegister(hphy->heth, hphy->phy_addr, reg, (uint32_t)val);
        if (status != HAL_OK) {
            printf("PHY写寄存器失败: addr=0x%02X, reg=0x%02X, val=0x%04X\r\n", 
                   hphy->phy_addr, (uint16_t)reg, val);
            return PHY_EFAIL;
        }
        return PHY_SOK;
    }
    
    int32_t stm32_rmw_reg(void* pArgs, uint32_t reg, uint16_t mask, uint16_t val)
    {
        uint16_t reg_val;
        int32_t ret;
        
        ret = stm32_read_reg(pArgs, reg, &reg_val);
        if (ret != PHY_SOK) {
            return ret;
        }
        
        reg_val = (reg_val & ~mask) | (val & mask);
        return stm32_write_reg(pArgs, reg, reg_val);
    }
    
    int32_t DP83822_STM32_Init(DP83822_STM32_Handle *hphy)
    {
        // 复位 PHY
        DP83822_STM32_Reset(hphy);
        
        // PHY的引脚26RX_DV无上拉电阻,默认模式为MII,所以需要强制修改寄存器配置为RMII模式,并数字重启
        // HAL_StatusTypeDef rmii_result = DP83822_Force_RMII_Master_Mode(hphy);
        // printf("[DP83822] RMII模式配置结果: ");
        // switch(rmii_result) {
        //     case HAL_OK:
        //         printf("成功 (HAL_OK)\r\n");
        //         break;
        //     case HAL_ERROR:
        //         printf("失败 (HAL_ERROR) - 寄存器读写错误\r\n");
        //         break;
        //     case HAL_TIMEOUT:
        //         printf("超时 (HAL_TIMEOUT) - 数字重启超时\r\n");
        //         break;
        //     default:
        //         printf("未知错误 (0x%08X)\r\n", rmii_result);
        //         break;
        // }
    
        
        // 检查 PHY ID
        uint16_t id1, id2;
        if (stm32_read_reg(hphy, PHY_PHYIDR1, &id1) != PHY_SOK) {
            printf("读取PHYIDR1失败\r\n");
            return PHY_EFAIL;
        }
        if (stm32_read_reg(hphy, PHY_PHYIDR2, &id2) != PHY_SOK) {
            printf("读取PHYIDR2失败\r\n");
            return PHY_EFAIL;
        }
        printf("PHY ID: ID1=0x%04X, ID2=0x%04X\r\n", id1, id2);
        
        // 检查是否为DP83822
        Phy_Version version = {
            .oui = ((id1 << 6) | (id2 >> 10)) & 0x3FFFFF,
            .model = (id2 >> 4) & 0x3F,
            .revision = id2 & 0xF
        };
        
        // printf("PHY: OUI=0x%06lX, Model=0x%02lX, Rev=%lu\r\n", 
        //        version.oui, version.model, version.revision);
        
        // 创建驱动句柄和回调
        Phy_RegAccessCb_t reg_access;
        EthPhyDrv_Handle hDrv;
        
        // 配置寄存器访问回调
        reg_access.EnetPhy_readReg = stm32_read_reg;
        reg_access.EnetPhy_writeReg = stm32_write_reg;
        reg_access.EnetPhy_rmwReg = stm32_rmw_reg;
        reg_access.EnetPhy_readExtReg = NULL;
        reg_access.EnetPhy_writeExtReg = NULL;
        reg_access.pArgs = (void*)hphy;
        
        // 绑定驱动
        Dp83822_bind(&hDrv, hphy->phy_addr, &reg_access);
        
        // 检查是否支持该PHY
        if (!Dp83822_isPhyDevSupported(hDrv, &version)) {
            printf("不支持的PHY设备\r\n");
            return PHY_EFAIL;
        }
        printf("[DP83822] - PHY识别成功");
        printf(" OUI=0x%06lX, Model=0x%02lX, Rev=%lu\r\n", 
            version.oui, version.model, version.revision);
        
        /* 4. 禁用中断功能 */
        DP83822_Disable_Interrupt(hphy->heth, hphy->phy_addr);
    
        Dp83822_Cfg cfg;
        Dp83822_initCfg(&cfg);
        
        int32_t status = Dp83822_config(hDrv, &cfg, sizeof(cfg), PHY_MAC_MII_RMII, false);
        if (status != PHY_SOK) {
            printf("[DP83822] PHY配置失败: status=%ld\r\n", status);
            return status;
        }
        printf("[DP83822] PHY配置完成\r\n");
        return PHY_SOK;
    }
    
    bool DP83822_STM32_IsLinkUp(DP83822_STM32_Handle *hphy)
    {
        uint16_t bmsr;
        if (stm32_read_reg(hphy, PHY_BMSR, &bmsr) != PHY_SOK) {
            return false;
        }
        
        // BMSR bit 2: Link Status
        return (bmsr & (1 << 2)) ? true : false;
    }
    
    int32_t DP83822_STM32_GetLinkStatus(DP83822_STM32_Handle *hphy, 
                                       uint32_t *speed, 
                                       uint32_t *duplex)
    {
        uint16_t bmsr, bmcr;
        
        if (stm32_read_reg(hphy, PHY_BMSR, &bmsr) != PHY_SOK) {
            return PHY_EFAIL;
        }
        
        if (stm32_read_reg(hphy, PHY_BMCR, &bmcr) != PHY_SOK) {
            return PHY_EFAIL;
        }
        
        // 默认值
        *speed = ETH_SPEED_100M;
        *duplex = ETH_FULLDUPLEX_MODE;
        
        // 检查是否自动协商
        if (bmcr & PHY_BMCR_ANEN) {
            // 自动协商模式
            uint16_t anlpar;
            if (stm32_read_reg(hphy, PHY_ANLPAR, &anlpar) == PHY_SOK) {
                // 检查对方能力
                if (anlpar & (1 << 13)) { // 100Base-TX full duplex
                    *speed = ETH_SPEED_100M;
                    *duplex = ETH_FULLDUPLEX_MODE;
                } else if (anlpar & (1 << 12)) { // 100Base-TX half duplex
                    *speed = ETH_SPEED_100M;
                    *duplex = ETH_HALFDUPLEX_MODE;
                } else if (anlpar & (1 << 8)) { // 10Base-T full duplex
                    *speed = ETH_SPEED_10M;
                    *duplex = ETH_FULLDUPLEX_MODE;
                } else if (anlpar & (1 << 7)) { // 10Base-T half duplex
                    *speed = ETH_SPEED_10M;
                    *duplex = ETH_HALFDUPLEX_MODE;
                }
            }
        } else {
            // 强制模式
            if (bmcr & PHY_BMCR_SPEED100) {
                *speed = ETH_SPEED_100M;
            } else {
                *speed = ETH_SPEED_10M;
            }
            
            if (bmcr & PHY_BMCR_FD) {
                *duplex = ETH_FULLDUPLEX_MODE;
            } else {
                *duplex = ETH_HALFDUPLEX_MODE;
            }
        }
        
        return PHY_SOK;
    }
    
    void DP83822_STM32_Reset(DP83822_STM32_Handle *hphy)
    {
        // 硬件复位
        if (hphy->reset_port) {
            HAL_GPIO_WritePin(hphy->reset_port, hphy->reset_pin, GPIO_PIN_RESET);
            HAL_Delay(1);
            HAL_GPIO_WritePin(hphy->reset_port, hphy->reset_pin, GPIO_PIN_SET);
            HAL_Delay(10);
            printf("DP83822硬件复位完成\r\n");
        } else {
            // 软件复位
            uint16_t bmcr;
            stm32_read_reg(hphy, PHY_BMCR, &bmcr);
            bmcr |= PHY_BMCR_RESET;
            stm32_write_reg(hphy, PHY_BMCR, bmcr);
            
            // 等待复位完成
            do {
                HAL_Delay(10);
                stm32_read_reg(hphy, PHY_BMCR, &bmcr);
            } while (bmcr & PHY_BMCR_RESET);
            
            printf("DP83822软件复位完成\r\n");
        }
    }
    
    
    void DP83822_Disable_Interrupt(ETH_HandleTypeDef *heth, uint8_t phy_addr)
    {
        uint32_t reg_value;
        HAL_StatusTypeDef status;
        
        // 1. 读取 PHY Specific Control Register (0x0011)
        status = HAL_ETH_ReadPHYRegister(heth, phy_addr, 0x0011, &reg_value);
        
        // 2. 清除中断相关位
        reg_value &= ~(1 << 0);  // 位 0: Interrupt Output Enable = 0(禁用中断输出)
        reg_value &= ~(1 << 1);  // 位 1: Interrupt Enable = 0(禁用中断)
        reg_value &= ~(1 << 2);  // 位 2: Test Interrupt = 0(禁用测试中断)
        
        // 3. 确保掉电功能不被误触发
        // 位 11: IEEE Power Down,确保为 0
        reg_value &= ~(1 << 11);
        
        // 4. 写回寄存器
        status = HAL_ETH_WritePHYRegister(heth, phy_addr, 0x0011, reg_value);
        if (status != HAL_OK) {
            printf("PHY写寄存器失败: addr=0x%02X, reg=0x0011, val=0x%04lX\r\n", 
                   phy_addr, reg_value);
            // return PHY_EFAIL;
        }
    }
    
    
    /**
      * @brief  强制 DP83822 进入 RMII Master 模式(25MHz 时钟参考),覆盖硬件 strap 设置
      * @param  hphy    DP83822处理器句柄
      * @retval HAL_OK     操作成功
      * @retval HAL_ERROR  寄存器读写失败
      * @retval HAL_TIMEOUT 数字重启超时
      */
    HAL_StatusTypeDef DP83822_Force_RMII_Master_Mode(DP83822_STM32_Handle *hphy)
    {
        uint16_t reg_val;
        uint32_t timeout = 1000;   // 1s 超时(实际数字重启很快)
    
        /* 1. 读取当前 RCSR 寄存器(0x0017) */
        if (stm32_read_reg(hphy, 0x0017, &reg_val) != PHY_SOK)
            return HAL_ERROR;
    
        /* 2. 强制配置为 RMII Master 模式 */
        reg_val &= ~(1 << 9);      // 清除 RGMII 使能位(bit9),防止冲突
        reg_val |=  (1 << 5);      // 置位 RMII 使能位(bit5)
        reg_val &= ~(1 << 7);      // 时钟选择:0 = 25MHz(RMII Master),1 = 50MHz(RMII Slave)
    
        /* 3. 写回配置 */
        if (stm32_write_reg(hphy, 0x0017, reg_val) != PHY_SOK)
            return HAL_ERROR;
    
        /* 4. 触发数字重启(寄存器 0x001F bit14 = 1)使配置生效 */
        if (stm32_write_reg(hphy, 0x001F, 0x4000) != PHY_SOK)
            return HAL_ERROR;
    
        /* 5. 等待数字重启完成(bit14 硬件自清除) */
        do {
            HAL_Delay(1);   // 避免忙等待过频
            if (stm32_read_reg(hphy, 0x001F, &reg_val) != PHY_SOK)
                return HAL_ERROR;
            timeout--;
        } while ((reg_val & 0x4000) && timeout);
    
        if (timeout == 0)
            return HAL_TIMEOUT;
    
        /* 6. 等待 PHY 模拟部分稳定(建议 10ms 以上) */
        HAL_Delay(10);
    
        return HAL_OK;
    }