前言
HC32F4A0是华大半导体出品的一款高性能芯片,支持10M/100M的ETH。官方提供了LWIP的移植方法,本文档是基于官方笔记 《AN_HC32F4A0系列的以太网LWIP协议栈移植_Rev1.1》,在HC32F4A0_DDL_Rev2.0.0 库上的移植笔记。

参数

MCU型号 HC32F4A0PIHB
库版本 HC32F4A0_DDL_Rev2.0.0
LWIP版本 lwip-STABLE-2_2_0_RC1
PHY型号 YT8512
MDK版本 5.37
PHY接口 RMII

LWIP文件引入
LWIP文件包含大同小异,这里不再赘述,可以参考官方文档,这里记录需要自己修改的一些文件

cc.h
主要定义平台相关类型和函数。

HC32F4A0支持TRNG,但是API不是很好使用,因此先采用rand()标准函数

#define LWIP_RAND() ((u32_t)rand())

同时需要在其他地方声明sys_now(void)

extern u32_t sys_now(void); //real define in hal.c

完整文件如下

#ifndef __CC_H__
#define __CC_H__
#include <stdlib.h>
#include <stdio.h>
#define LWIP_NO_STDINT_H  1 //if 0 typedef in arch.h
typedef unsigned   char    u8_t;
typedef signed     char    s8_t;
typedef unsigned   short   u16_t;
typedef signed     short   s16_t;
typedef unsigned   long    u32_t;
typedef signed     long    s32_t;
typedef u32_t mem_ptr_t;
typedef int sys_prot_t;
#define U16_F "hu"
#define S16_F "d"
#define X16_F "hx"
#define U32_F "u"
#define S32_F "d"
#define X32_F "x"
#define SZT_F "uz"
//
typedef int sys_prot_t;
#define LWIP_PROVIDE_ERRNO
#if defined (__GNUC__) & !defined (__CC_ARM)
#define LWIP_TIMEVAL_PRIVATE 0 
#include <sys/time.h>
#endif
#ifndef BYTE_ORDER
#define BYTE_ORDER LITTLE_ENDIAN
#endif
/* define compiler specific symbols */
#if defined (__ICCARM__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#define PACK_STRUCT_USE_INCLUDES
#elif defined (__CC_ARM)
#define PACK_STRUCT_BEGIN __packed
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#elif defined (__GNUC__)
#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT __attribute__ ((__packed__))
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x
#endif
// 
#define LWIP_PLATFORM_ASSERT(x) do {printf("Assertion \"%s\" failed at line %d in %s\n", \
                                     x, __LINE__, __FILE__); } while(0)
/* Define random number generator function */
//  
#define LWIP_RAND() ((u32_t)rand())
extern u32_t sys_now(void); //real define in hal.c
#endif /* __CC_H__ */

bpstruct.h

#if defined(__IAR_SYSTEMS_ICC__)
#pragma pack(1)
#endif

epstruct.h

#if defined(__IAR_SYSTEMS_ICC__)
#pragma pack()
#endif

由于不使用操作系统,无需引入sys_arch.c和sys_arch.h文件

配置文件
lwip相关配置在lwip_lwipopts.h中,主要设计内存大小分配,相关功能启用,无需太多修改

完整文件如下

#ifndef LWIP_LWIPOPTS_H
#define LWIP_LWIPOPTS_H

/*
 * Include user defined options first. Anything not defined in these files
 * will be set to standard values. Override anything you don't like!
 */

/*
   -----------------------------------------------
   ---------- Platform specific locking ----------
   -----------------------------------------------
*/

/**
 * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
 * critical regions during buffer allocation, deallocation and memory
 * allocation and deallocation.
 */
#define SYS_LIGHTWEIGHT_PROT            0

/**
 * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
 * use lwIP facilities.
 */
#define NO_SYS                          1

/*
   ------------------------------------
   ---------- Memory options ----------
   ------------------------------------
*/

/**
 * MEM_ALIGNMENT: should be set to the alignment of the CPU
 *    4 byte alignment -> #define MEM_ALIGNMENT 4
 *    2 byte alignment -> #define MEM_ALIGNMENT 2
 */
#define MEM_ALIGNMENT                   4U

/**
 * MEM_SIZE: the size of the heap memory. If the application will send
 * a lot of data that needs to be copied, this should be set high.
 */
#define MEM_SIZE                        (16*1024)

/*
   ------------------------------------------------
   ---------- Internal Memory Pool Sizes ----------
   ------------------------------------------------
*/
/**
 * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
 * If the application sends a lot of data out of ROM (or other static memory),
 * this should be set high.
 */
#define MEMP_NUM_PBUF                   16


/**
 * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
 * per active UDP "connection".
 * (requires the LWIP_UDP option)
 */
#define MEMP_NUM_UDP_PCB                6

/**
 * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections.
 * (requires the LWIP_TCP option)
 */
#define MEMP_NUM_TCP_PCB                10

/**
 * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
 * (requires the LWIP_TCP option)
 */
#define MEMP_NUM_TCP_PCB_LISTEN         6

/**
 * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
 * (requires the LWIP_TCP option)
 */
#define MEMP_NUM_TCP_SEG                8

/**
 * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
 * (requires NO_SYS==0)
 */
#define MEMP_NUM_SYS_TIMEOUT            10


/* ---------- Pbuf options ---------- */
/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */

#define PBUF_POOL_SIZE                  8
/**
 * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
 * designed to accommodate single full size TCP frame in one pbuf, including
 * TCP_MSS, IP header, and link header.
 */
#define PBUF_POOL_BUFSIZE               LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)


/* ---------- TCP options ---------- */ 
#define LWIP_TCP                        0
#define TCP_TTL                         255
#define LWIP_TCP_KEEPALIVE              1

/* Controls if TCP should queue segments that arrive out of
   order. Define to 0 if your device is low on memory. */
#define TCP_QUEUE_OOSEQ                 0


/* TCP Maximum segment size. */
/* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */
#define TCP_MSS                         (1500 - 40)  


/* TCP sender buffer space (bytes). */
#define TCP_SND_BUF                     (4*TCP_MSS)

/*  TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
  as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */

#define TCP_SND_QUEUELEN                (2*TCP_SND_BUF/TCP_MSS)

/* TCP wirte window. */
#define TCP_WND                         (2*TCP_MSS)

/* ---------- IPv4 options ---------- */ 
#define LWIP_IPV4                       1

/* ---------- ICMP options ---------- */
#define LWIP_ICMP                       1

/* ---------- DHCP options ---------- */
#define LWIP_DHCP                       1


/* ---------- UDP options ---------- */
#define LWIP_UDP                        1
#define UDP_TTL                         255


/* ---------- Statistics options ---------- */
#define LWIP_STATS 0


/**
 * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
 */
#define LWIP_NETCONN                    0

/*
   ------------------------------------
   ---------- Socket options ----------
   ------------------------------------
*/
/**
 * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
 */
#define LWIP_SOCKET                     0

/*
   --------------------------------------
   ---------- Checksum options ----------
   --------------------------------------
*/

#define CHECKSUM_BY_HARDWARE

#ifdef CHECKSUM_BY_HARDWARE
/* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/
#define CHECKSUM_GEN_IP                 0
/* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP                0
/* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP                0
/* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/
#define CHECKSUM_CHECK_IP               0
/* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP              0
/* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP              0
/*CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP               0
#else
/* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/
#define CHECKSUM_GEN_IP                 1
/* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/
#define CHECKSUM_GEN_UDP                1
/* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/
#define CHECKSUM_GEN_TCP                1
/* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/
#define CHECKSUM_CHECK_IP               1
/* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/
#define CHECKSUM_CHECK_UDP              1
/* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/
#define CHECKSUM_CHECK_TCP              1
/*CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/
#define CHECKSUM_GEN_ICMP               1
#endif

//#define LWIP_DEBUG
//#define ETHARP_DEBUG                  LWIP_DBG_ON
//#define PBUF_DEBUG                    LWIP_DBG_ON
//#define NETIF_DEBUG                   LWIP_DBG_ON
#if !NO_SYS
void sys_check_core_locking(void);
#define LWIP_ASSERT_CORE_LOCKED()  sys_check_core_locking()
#endif

#endif /* LWIP_LWIPOPTS_H */

网卡驱动
配置完LWIP文件后,还需要自己对网卡进行配置。dll库里的ethernetif文件较为繁琐,但大部分不需要关注,移植LWIP时重点看

static void low_level_init(struct netif *netif)
static err_t low_level_output(struct netif *netif, struct pbuf *p)
static struct pbuf *low_level_input(struct netif *netif)

三个函数即可。

low_level_init
该函数主要进行ETH功能的初始化,在此之前要先配置GPIO功能。对于PHY,配置前需要先硬复位1次,拉高下IO即可。

/**
 * @brief  Initializes the Ethernet GPIO.
 * @param  None
 * @retval None
 */
static void Ethernet_GpioInit(void)
{
  /* Ethernet RMII pins configuration */
  /*
    ETH_SMI_MDIO ----------------> PA2
    ETH_SMI_MDC -----------------> PC1
    ETH_RMII_TX_EN --------------> PB11
    ETH_RMII_TXD0 ---------------> PB12
    ETH_RMII_TXD1 ---------------> PB13
    ETH_RMII_REF_CLK ------------> PA1
    ETH_RMII_CRS_DV -------------> PA7
    ETH_RMII_RXD0 ---------------> PC4
    ETH_RMII_RXD1 ---------------> PC5
  */
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_02, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_01, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_11, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_12, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_13, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_01, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_07, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_04, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_05, GPIO_FUNC_11);
  /* ETH_RST */
  /*
      RMII_nRST---------------->PB14
  */
  stc_gpio_init_t stcGpioInit;
  GPIO_StructInit(&stcGpioInit);
  stcGpioInit.u16PinState = PIN_STAT_RST;
  stcGpioInit.u16PinDir = PIN_DIR_OUT;
  GPIO_Init(GPIO_PORT_B, GPIO_PIN_14, &stcGpioInit);
  /*RESET PHY*/
  GPIO_ResetPins(GPIO_PORT_B, GPIO_PIN_14);
  SysTick_Delay(PHY_HW_RST_DELAY);
  GPIO_SetPins(GPIO_PORT_B, GPIO_PIN_14);
}

在复位完phy后,最好等待phy的相关寄存器复位后在进行下一步操作。为了方便调试找问题,在初始化网卡失败时直接WHILE(1);

static void low_level_init(struct netif *netif)
{
  stc_eth_init_t stcEthInit;

  /* Enable ETH clock */
  FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_ETHMAC, ENABLE);

  /* Init Ethernet GPIO */
  Ethernet_GpioInit();

  uint16_t u16PhyReg = 0;
  while(1)
  {
    ETH_PHY_ReadReg(&EthHandle, PHY_BCR, &u16PhyReg);
    if(RESET == (u16PhyReg & PHY_SOFT_RESET))break;
  }

  /* Reset ETHERNET */
  (void)ETH_DeInit();
  /* Configure structure initialization */
  (void)ETH_CommStructInit(&EthHandle.stcCommInit);
  (void)ETH_StructInit(&stcEthInit);

  EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_RMII;
  stcEthInit.stcMacInit.u32ReceiveAll = ETH_MAC_RX_ALL_ENABLE;


  while(1) //等待网线连接后继续下一步
  {
    ETH_PHY_ReadReg(&EthHandle, PHY_BSR, &u16PhyReg);
    if(u16PhyReg & 0x0004)break;
  };

  int32_t _err_code = ETH_Init(&EthHandle, &stcEthInit);
  /* Configure ethernet peripheral */
  if(LL_OK != _err_code)
  {
    while(1)
    {};
  }

  /* Set netif link flag */
  netif->flags |= NETIF_FLAG_LINK_UP;

  /* Initialize TxRx Descriptors list: Chain Mode */
  (void)ETH_DMA_TxDescListInit(&EthHandle, EthDmaTxDscrTab, &EthTxBuff[0][0], ETH_TX_BUF_NUM);
  (void)ETH_DMA_RxDescListInit(&EthHandle, EthDmaRxDscrTab, &EthRxBuff[0][0], ETH_RX_BUF_NUM);

  /* set MAC */
  netif->hwaddr_len = 6U;
  netif->hwaddr[0] = (EthHandle.stcCommInit).au8MacAddr[0];
  netif->hwaddr[1] = (EthHandle.stcCommInit).au8MacAddr[1];
  netif->hwaddr[2] = (EthHandle.stcCommInit).au8MacAddr[2];
  netif->hwaddr[3] = (EthHandle.stcCommInit).au8MacAddr[3];
  netif->hwaddr[4] = (EthHandle.stcCommInit).au8MacAddr[4];
  netif->hwaddr[5] = (EthHandle.stcCommInit).au8MacAddr[5];
  /* maximum transfer unit */
  netif->mtu = 1500U;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

  (void)ETH_Start();

}

需要注意的是,在ETH_Init里,会软复位phy1次,然后会对PHY_LINK_STATUS和PHY_AUTONEGO_COMPLETE进行超时检测,库函数默认的时间较短,概率性会导致超时异常。最好在hc32_ll_eth.c中修改下超时时间定义。

/* Wait timeout(ms) */
#define ETH_WR_REG_TIMEOUT                          (50UL)
#define ETH_SW_RST_TIMEOUT                          (200UL)
#define ETH_LINK_STATUS_TIMEOUT                     (3000UL)
#define ETH_AUTO_NEGO_CPLT_TIMEOUT                  (3000UL)

low_level_output
该函数主要是配置数据输出的方法,照着库里的用即可

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  err_t errval;
  struct pbuf *q;
  uint8_t *txBuffer;
  __IO stc_eth_dma_desc_t *DmaTxDesc;
  uint32_t byteCnt;
  uint32_t frameLength = 0UL;
  uint32_t bufferOffset;
  uint32_t payloadOffset;

  DmaTxDesc = EthHandle.stcTxDesc;
  txBuffer = (uint8_t *)((EthHandle.stcTxDesc)->u32Buf1Addr);
  bufferOffset = 0UL;

  /* Copy frame from pbufs to driver buffers */
  for(q = p; q != NULL; q = q->next)
  {
    /* If this buffer isn't available, goto error */
    if(0UL != (DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN))
    {
      errval = (err_t)ERR_USE;
      goto error;
    }

    /* Get bytes in LwIP buffer */
    byteCnt = q->len;
    payloadOffset = 0UL;
    /* Check if the length of data to copy is bigger than Tx buffer size */
    while((byteCnt + bufferOffset) > ETH_TX_BUF_SIZE)
    {
      /* Copy data to Tx buffer*/
      (void)memcpy((uint8_t *)&(txBuffer[bufferOffset]), (uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (ETH_TX_BUF_SIZE - bufferOffset));
      /* Point to next descriptor */
      DmaTxDesc = (stc_eth_dma_desc_t *)(DmaTxDesc->u32Buf2NextDescAddr);
      /* Check if the buffer is available */
      if(0UL != (DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN))
      {
        errval = (err_t)ERR_USE;
        goto error;
      }

      txBuffer = (uint8_t *)(DmaTxDesc->u32Buf1Addr);
      byteCnt = byteCnt - (ETH_TX_BUF_SIZE - bufferOffset);
      payloadOffset = payloadOffset + (ETH_TX_BUF_SIZE - bufferOffset);
      frameLength = frameLength + (ETH_TX_BUF_SIZE - bufferOffset);
      bufferOffset = 0UL;
    }
    /* Copy the remaining bytes */
    (void)memcpy((uint8_t *)&(txBuffer[bufferOffset]), (uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), byteCnt);
    bufferOffset = bufferOffset + byteCnt;
    frameLength = frameLength + byteCnt;
  }
  /* Prepare transmit descriptors to give to DMA */
  (void)ETH_DMA_SetTransFrame(&EthHandle, frameLength);
  errval = (err_t)ERR_OK;

error:
    /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
  if(RESET != ETH_DMA_GetStatus(ETH_DMA_FLAG_UNS))
  {
    /* Clear DMA UNS flag */
    ETH_DMA_ClearStatus(ETH_DMA_FLAG_UNS);
    /* Resume DMA transmission */
    WRITE_REG32(CM_ETH->DMA_TXPOLLR, 0UL);
  }

  return errval;
}

low_level_input
该文件主要将接收到的数据移动到pbuf中,进行后续操作,直接使用库函数即可。需要注意的是,库函数使用的是malloc申请的内存,不符合LWIP的规范,直接用虽然能正常编译,但是在测试时会报p->ref > 0错误,无法正常释放内存。需修改为pbuf的函数

  if(len > 0UL)
  {
    /* we allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, (uint16_t)len, PBUF_POOL);
  }

完整函数如下

static struct pbuf *low_level_input(struct netif *netif)
{
  struct pbuf *p = NULL;
  struct pbuf *q;
  uint32_t len;
  uint8_t *rxBuffer;
  __IO stc_eth_dma_desc_t *DmaRxDesc;
  uint32_t byteCnt;
  uint32_t bufferOffset;
  uint32_t payloadOffset;
  uint32_t i;

  /* Get received frame */
  if(LL_OK != ETH_DMA_GetReceiveFrame(&EthHandle))
  {
    return NULL;
  }

  /* Obtain the size of the packet */
  len = (EthHandle.stcRxFrame).u32Len;
  rxBuffer = (uint8_t *)(EthHandle.stcRxFrame).u32Buf;

  if(len > 0UL)
  {
    /* we allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, (uint16_t)len, PBUF_POOL);
  }
  if(p != NULL)
  {
    DmaRxDesc = (EthHandle.stcRxFrame).pstcFSDesc;
    bufferOffset = 0UL;
    for(q = p; q != NULL; q = q->next)
    {
      byteCnt = q->len;
      payloadOffset = 0UL;

      /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */
      while((byteCnt + bufferOffset) > ETH_RX_BUF_SIZE)
      {
        /* Copy data to pbuf */
        (void)memcpy((uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (uint8_t *)&(rxBuffer[bufferOffset]), (ETH_RX_BUF_SIZE - bufferOffset));
        /* Point to next descriptor */
        DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr);
        rxBuffer = (uint8_t *)(DmaRxDesc->u32Buf1Addr);
        byteCnt = byteCnt - (ETH_RX_BUF_SIZE - bufferOffset);
        payloadOffset = payloadOffset + (ETH_RX_BUF_SIZE - bufferOffset);
        bufferOffset = 0UL;
      }

      /* Copy remaining data in pbuf */
      (void)memcpy((uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (uint8_t *)&(rxBuffer[bufferOffset]), byteCnt);
      bufferOffset = bufferOffset + byteCnt;
    }
  }

  /* Release descriptors to DMA */
  DmaRxDesc = (EthHandle.stcRxFrame).pstcFSDesc;
  for(i = 0UL; i < (EthHandle.stcRxFrame).u32SegCount; i++)
  {
    DmaRxDesc->u32ControlStatus |= ETH_DMA_RXDESC_OWN;
    DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr);
  }
  /* Clear Segment_Count */
  (EthHandle.stcRxFrame).u32SegCount = 0UL;

  /* When Rx Buffer unavailable flag is set, clear it and resume reception */
  if(RESET != ETH_DMA_GetStatus(ETH_DMA_FLAG_RUS))
  {
    /* Clear DMA RUS flag */
    ETH_DMA_ClearStatus(ETH_DMA_FLAG_RUS);
    /* Resume DMA reception */
    WRITE_REG32(CM_ETH->DMA_RXPOLLR, 0UL);
  }

  return p;
}

ethernetf.c
以上三个函数移植完后,基本就可以直接使用了。库中的其他代码主要是用于link检测和自动重连,可以先不使用,调通LWIP后再去处理。

完整文件如下

#include "hc32_ll.h"
#include "netif/etharp.h"

#include "ethernetif.h"
#include <stdlib.h>
#include <string.h>
#include "hal_uart.h"

/* Define those to better describe your network interface. */
#define IFNAME0                         'h'
#define IFNAME1                         'd'

/* PHY hardware reset time */
#define PHY_HW_RST_DELAY                (0x40U)

/* Global Ethernet handle*/
static stc_eth_handle_t EthHandle;
/* Ethernet Tx DMA Descriptor */
__ALIGN_BEGIN static stc_eth_dma_desc_t EthDmaTxDscrTab[ETH_TX_BUF_NUM];
/* Ethernet Rx DMA Descriptor */
__ALIGN_BEGIN static stc_eth_dma_desc_t EthDmaRxDscrTab[ETH_RX_BUF_NUM];
/* Ethernet Transmit Buffer */
__ALIGN_BEGIN static uint8_t EthTxBuff[ETH_TX_BUF_NUM][ETH_TX_BUF_SIZE];
/* Ethernet Receive Buffer */
__ALIGN_BEGIN static uint8_t EthRxBuff[ETH_RX_BUF_NUM][ETH_RX_BUF_SIZE];


/**
 * @brief  Initializes the Ethernet GPIO.
 * @param  None
 * @retval None
 */
static void Ethernet_GpioInit(void)
{

  /* Ethernet RMII pins configuration */
  /*
    ETH_SMI_MDIO ----------------> PA2
    ETH_SMI_MDC -----------------> PC1
    ETH_RMII_TX_EN --------------> PB11
    ETH_RMII_TXD0 ---------------> PB12
    ETH_RMII_TXD1 ---------------> PB13
    ETH_RMII_REF_CLK ------------> PA1
    ETH_RMII_CRS_DV -------------> PA7
    ETH_RMII_RXD0 ---------------> PC4
    ETH_RMII_RXD1 ---------------> PC5
  */
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_02, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_01, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_11, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_12, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_B, GPIO_PIN_13, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_01, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_A, GPIO_PIN_07, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_04, GPIO_FUNC_11);
  GPIO_SetFunc(GPIO_PORT_C, GPIO_PIN_05, GPIO_FUNC_11);


  /* ETH_RST */
  /*
      RMII_nRST---------------->PB14
  */
  stc_gpio_init_t stcGpioInit;
  GPIO_StructInit(&stcGpioInit);
  stcGpioInit.u16PinState = PIN_STAT_RST;
  stcGpioInit.u16PinDir = PIN_DIR_OUT;
  GPIO_Init(GPIO_PORT_B, GPIO_PIN_14, &stcGpioInit);

  /*RESET PHY*/
  GPIO_ResetPins(GPIO_PORT_B, GPIO_PIN_14);
  SysTick_Delay(PHY_HW_RST_DELAY);
  GPIO_SetPins(GPIO_PORT_B, GPIO_PIN_14);
}

static void low_level_init(struct netif *netif)
{
  stc_eth_init_t stcEthInit;

  /* Enable ETH clock */
  FCG_Fcg1PeriphClockCmd(FCG1_PERIPH_ETHMAC, ENABLE);

  /* Init Ethernet GPIO */
  Ethernet_GpioInit();

  uint16_t u16PhyReg = 0;
  while(1)
  {
    ETH_PHY_ReadReg(&EthHandle, PHY_BCR, &u16PhyReg);
    if(RESET == (u16PhyReg & PHY_SOFT_RESET))break;
  }

  /* Reset ETHERNET */
  (void)ETH_DeInit();
  /* Configure structure initialization */
  (void)ETH_CommStructInit(&EthHandle.stcCommInit);
  (void)ETH_StructInit(&stcEthInit);

  EthHandle.stcCommInit.u32Interface = ETH_MAC_IF_RMII;
  stcEthInit.stcMacInit.u32ReceiveAll = ETH_MAC_RX_ALL_ENABLE;


  while(1) //等待网线连接后继续下一步
  {
    ETH_PHY_ReadReg(&EthHandle, PHY_BSR, &u16PhyReg);
    if(u16PhyReg & 0x0004)break;
  };

  int32_t _err_code = ETH_Init(&EthHandle, &stcEthInit);
  /* Configure ethernet peripheral */
  if(LL_OK != _err_code)
  {
    while(1)
    {};
  }

  /* Set netif link flag */
  netif->flags |= NETIF_FLAG_LINK_UP;

  /* Initialize TxRx Descriptors list: Chain Mode */
  (void)ETH_DMA_TxDescListInit(&EthHandle, EthDmaTxDscrTab, &EthTxBuff[0][0], ETH_TX_BUF_NUM);
  (void)ETH_DMA_RxDescListInit(&EthHandle, EthDmaRxDscrTab, &EthRxBuff[0][0], ETH_RX_BUF_NUM);

  /* set MAC */
  netif->hwaddr_len = 6U;
  netif->hwaddr[0] = (EthHandle.stcCommInit).au8MacAddr[0];
  netif->hwaddr[1] = (EthHandle.stcCommInit).au8MacAddr[1];
  netif->hwaddr[2] = (EthHandle.stcCommInit).au8MacAddr[2];
  netif->hwaddr[3] = (EthHandle.stcCommInit).au8MacAddr[3];
  netif->hwaddr[4] = (EthHandle.stcCommInit).au8MacAddr[4];
  netif->hwaddr[5] = (EthHandle.stcCommInit).au8MacAddr[5];
  /* maximum transfer unit */
  netif->mtu = 1500U;

  /* device capabilities */
  /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
  netif->flags |= NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;

  (void)ETH_Start();

}

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
  err_t errval;
  struct pbuf *q;
  uint8_t *txBuffer;
  __IO stc_eth_dma_desc_t *DmaTxDesc;
  uint32_t byteCnt;
  uint32_t frameLength = 0UL;
  uint32_t bufferOffset;
  uint32_t payloadOffset;

  DmaTxDesc = EthHandle.stcTxDesc;
  txBuffer = (uint8_t *)((EthHandle.stcTxDesc)->u32Buf1Addr);
  bufferOffset = 0UL;

  /* Copy frame from pbufs to driver buffers */
  for(q = p; q != NULL; q = q->next)
  {
    /* If this buffer isn't available, goto error */
    if(0UL != (DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN))
    {
      errval = (err_t)ERR_USE;
      goto error;
    }

    /* Get bytes in LwIP buffer */
    byteCnt = q->len;
    payloadOffset = 0UL;
    /* Check if the length of data to copy is bigger than Tx buffer size */
    while((byteCnt + bufferOffset) > ETH_TX_BUF_SIZE)
    {
      /* Copy data to Tx buffer*/
      (void)memcpy((uint8_t *)&(txBuffer[bufferOffset]), (uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (ETH_TX_BUF_SIZE - bufferOffset));
      /* Point to next descriptor */
      DmaTxDesc = (stc_eth_dma_desc_t *)(DmaTxDesc->u32Buf2NextDescAddr);
      /* Check if the buffer is available */
      if(0UL != (DmaTxDesc->u32ControlStatus & ETH_DMA_TXDESC_OWN))
      {
        errval = (err_t)ERR_USE;
        goto error;
      }

      txBuffer = (uint8_t *)(DmaTxDesc->u32Buf1Addr);
      byteCnt = byteCnt - (ETH_TX_BUF_SIZE - bufferOffset);
      payloadOffset = payloadOffset + (ETH_TX_BUF_SIZE - bufferOffset);
      frameLength = frameLength + (ETH_TX_BUF_SIZE - bufferOffset);
      bufferOffset = 0UL;
    }
    /* Copy the remaining bytes */
    (void)memcpy((uint8_t *)&(txBuffer[bufferOffset]), (uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), byteCnt);
    bufferOffset = bufferOffset + byteCnt;
    frameLength = frameLength + byteCnt;
  }
  /* Prepare transmit descriptors to give to DMA */
  (void)ETH_DMA_SetTransFrame(&EthHandle, frameLength);
  errval = (err_t)ERR_OK;

error:
    /* When Transmit Underflow flag is set, clear it and issue a Transmit Poll Demand to resume transmission */
  if(RESET != ETH_DMA_GetStatus(ETH_DMA_FLAG_UNS))
  {
    /* Clear DMA UNS flag */
    ETH_DMA_ClearStatus(ETH_DMA_FLAG_UNS);
    /* Resume DMA transmission */
    WRITE_REG32(CM_ETH->DMA_TXPOLLR, 0UL);
  }

  return errval;
}

static struct pbuf *low_level_input(struct netif *netif)
{
  struct pbuf *p = NULL;
  struct pbuf *q;
  uint32_t len;
  uint8_t *rxBuffer;
  __IO stc_eth_dma_desc_t *DmaRxDesc;
  uint32_t byteCnt;
  uint32_t bufferOffset;
  uint32_t payloadOffset;
  uint32_t i;

  /* Get received frame */
  if(LL_OK != ETH_DMA_GetReceiveFrame(&EthHandle))
  {
    return NULL;
  }

  /* Obtain the size of the packet */
  len = (EthHandle.stcRxFrame).u32Len;
  rxBuffer = (uint8_t *)(EthHandle.stcRxFrame).u32Buf;

  if(len > 0UL)
  {
    /* we allocate a pbuf chain of pbufs from the Lwip buffer pool */
    p = pbuf_alloc(PBUF_RAW, (uint16_t)len, PBUF_POOL);
  }
  if(p != NULL)
  {
    DmaRxDesc = (EthHandle.stcRxFrame).pstcFSDesc;
    bufferOffset = 0UL;
    for(q = p; q != NULL; q = q->next)
    {
      byteCnt = q->len;
      payloadOffset = 0UL;

      /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size */
      while((byteCnt + bufferOffset) > ETH_RX_BUF_SIZE)
      {
        /* Copy data to pbuf */
        (void)memcpy((uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (uint8_t *)&(rxBuffer[bufferOffset]), (ETH_RX_BUF_SIZE - bufferOffset));
        /* Point to next descriptor */
        DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr);
        rxBuffer = (uint8_t *)(DmaRxDesc->u32Buf1Addr);
        byteCnt = byteCnt - (ETH_RX_BUF_SIZE - bufferOffset);
        payloadOffset = payloadOffset + (ETH_RX_BUF_SIZE - bufferOffset);
        bufferOffset = 0UL;
      }

      /* Copy remaining data in pbuf */
      (void)memcpy((uint8_t *)&(((uint8_t *)q->payload)[payloadOffset]), (uint8_t *)&(rxBuffer[bufferOffset]), byteCnt);
      bufferOffset = bufferOffset + byteCnt;
    }
  }

  /* Release descriptors to DMA */
  DmaRxDesc = (EthHandle.stcRxFrame).pstcFSDesc;
  for(i = 0UL; i < (EthHandle.stcRxFrame).u32SegCount; i++)
  {
    DmaRxDesc->u32ControlStatus |= ETH_DMA_RXDESC_OWN;
    DmaRxDesc = (stc_eth_dma_desc_t *)(DmaRxDesc->u32Buf2NextDescAddr);
  }
  /* Clear Segment_Count */
  (EthHandle.stcRxFrame).u32SegCount = 0UL;

  /* When Rx Buffer unavailable flag is set, clear it and resume reception */
  if(RESET != ETH_DMA_GetStatus(ETH_DMA_FLAG_RUS))
  {
    /* Clear DMA RUS flag */
    ETH_DMA_ClearStatus(ETH_DMA_FLAG_RUS);
    /* Resume DMA reception */
    WRITE_REG32(CM_ETH->DMA_RXPOLLR, 0UL);
  }

  return p;
}


/**
 * @brief  This function should be called when a packet is ready to be read from the interface.
 * @param  netif                        The network interface structure for this ethernetif.
 * @retval None
 */

void ethernetif_input(struct netif *netif)
{
  err_t err;
  struct pbuf *p;
  /* Move received packet into a new pbuf */
  p = low_level_input(netif);
  /* No packet could be read, silently ignore this */
  if(p == NULL)
  {
    return;
  }
  /* Entry point to the LwIP stack */
  err = netif->input(p, netif);
  if(err != (err_t)ERR_OK)
  {
    LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
    (void)pbuf_free(p);
    p = NULL;
  }
}


err_t ethernetif_init(struct netif *netif)
{
#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_t)ERR_OK;
}

phy_conf
搞定网卡驱动前,还要针对不同的PHY芯片修改寄存器地址定义。由于hc32_ll_eth.c中也需要用到这些定义,因此最好放在hc32f4xx_conf.h中

/**
 * @brief Ethernet and PHY Configuration.
 */
/* MAC ADDRESS */
#define ETH_MAC_ADDR0                               (0x02U)
#define ETH_MAC_ADDR1                               (0x00U)
#define ETH_MAC_ADDR2                               (0x00U)
#define ETH_MAC_ADDR3                               (0x00U)
#define ETH_MAC_ADDR4                               (0x00U)
#define ETH_MAC_ADDR5                               (0x00U)

/* PHY(RTL8201F) Address*/
#define ETH_PHY_ADDR                                (0x00U)

/* PHY Configuration delay(ms) */
#define ETH_PHY_RST_DELAY                           (0x0080UL)
#define ETH_PHY_CONFIG_DELAY                        (0x0040UL)
#define ETH_PHY_RD_TIMEOUT                          (0x0005UL)
#define ETH_PHY_WR_TIMEOUT                          (0x0005UL)

/* Common PHY Registers */
#define PHY_BCR                                     (0x00U)     /*!< Basic Control Register               */
#define PHY_BSR                                     (0x01U)     /*!< Basic Status Register                */

#define PHY_SOFT_RESET                              (0x8000U)   /*!< PHY Soft Reset                       */
#define PHY_LOOPBACK                                (0x4000U)   /*!< Select loop-back mode                */
#define PHY_FULLDUPLEX_100M                         (0x2100U)   /*!< Set the full-duplex mode at 100 Mb/s */
#define PHY_HALFDUPLEX_100M                         (0x2000U)   /*!< Set the half-duplex mode at 100 Mb/s */
#define PHY_FULLDUPLEX_10M                          (0x0100U)   /*!< Set the full-duplex mode at 10 Mb/s  */
#define PHY_HALFDUPLEX_10M                          (0x0000U)   /*!< Set the half-duplex mode at 10 Mb/s  */
#define PHY_AUTONEGOTIATION                         (0x1000U)   /*!< Enable auto-negotiation function     */
#define PHY_POWERDOWN                               (0x0800U)   /*!< Select the power down mode           */
#define PHY_ISOLATE                                 (0x0400U)   /*!< Isolate PHY from MII                 */
#define PHY_RESTART_AUTONEGOTIATION                 (0x0200U)   /*!< Restart auto-negotiation function    */

#define PHY_100BASE_TX_FD                           (0x4000U)   /*!< 100Base-TX full duplex support       */
#define PHY_100BASE_TX_HD                           (0x2000U)   /*!< 100Base-TX half duplex support       */
#define PHY_10BASE_T_FD                             (0x1000U)   /*!< 10Base-T full duplex support         */
#define PHY_10BASE_T_HD                             (0x0800U)   /*!< 10Base-T half duplex support         */
#define PHY_AUTONEGO_COMPLETE                       (0x0020U)   /*!< Auto-Negotiation process completed   */
#define PHY_LINK_STATUS                             (0x0004U)   /*!< Valid link established               */
#define PHY_JABBER_DETECTION                        (0x0002U)   /*!< Jabber condition detected            */

NETIF配置
移植完网卡驱动后,就要配置IP地址等信息了,配置函数可以自己找个地方放,比如main.c

/* Static IP Address */
#define IP_ADDR0 (192U)
#define IP_ADDR1 (168U)
#define IP_ADDR2 (1U)
#define IP_ADDR3 (20U)
/* Static Netmask */
#define NETMASK_ADDR0 (255U)
#define NETMASK_ADDR1 (255U)
#define NETMASK_ADDR2 (255U)
#define NETMASK_ADDR3 (0U)
/* Static Gateway Address*/
#define GW_ADDR0 (192U)
#define GW_ADDR1 (168U)
#define GW_ADDR2 (1U)
#define GW_ADDR3 (1U)

/**
* @brief Configurate the network interface
* @param None
* @retval None
*/
static void Netif_Config(void)
{
  ip_addr_t ipaddr;
  ip_addr_t netmask;
  ip_addr_t gw;
  IP_ADDR4(&ipaddr, IP_ADDR0, IP_ADDR1, IP_ADDR2, IP_ADDR3);
  IP_ADDR4(&netmask, NETMASK_ADDR0, NETMASK_ADDR1, NETMASK_ADDR2, NETMASK_ADDR3);
  IP_ADDR4(&gw, GW_ADDR0, GW_ADDR1, GW_ADDR2, GW_ADDR3);
  /* Add the network interface */
  (void)netif_add(&gnetif, &ipaddr, &netmask, &gw, NULL, &ethernetif_init, &ethernet_input);
  /* Registers the default network interface */
  netif_set_default(&gnetif);
  if(netif_is_link_up(&gnetif))
  {
     /* When the netif is fully configured this function must be called */
    netif_set_up(&gnetif);
  }
  else
  {
     /* When the netif link is down this function must be called */
    int cnt = 0;
    netif_set_down(&gnetif);
    while(1);
  }
}

测试
在main函数里初始化LWIP和ETH后,即可尝试ping测试了。需要注意下,LWIP需要提供时间信息使用,在这里使用systick。

u32_t sys_now(void)
{
  return SysTick_GetTick();
}

void SysTick_Handler(void)
{
  SysTick_IncTick();
}


int main(void)
{
  SysTick_Init(1000U);
  lwip_init();
  Netif_Config();
  while(1)
  {
    ethernetif_input(&gnetif);
    sys_check_timeouts();
  };
}

测试结果如下:

Pinging 192.168.1.20 with 32 bytes of data:
Reply from 192.168.1.20: bytes=32 time<1ms TTL=255
Reply from 192.168.1.20: bytes=32 time<1ms TTL=255
Reply from 192.168.1.20: bytes=32 time<1ms TTL=255
Reply from 192.168.1.20: bytes=32 time<1ms TTL=255

Ping statistics for 192.168.1.20:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 0ms, Maximum = 0ms, Average = 0ms