HC32F4A0 lwip移植
前言
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 = ðarp_output;
netif->linkoutput = &low_level_output;
/* initialize the hardware */
low_level_init(netif);
return (err_t)ERR_OK;
}
ethernetf.h
#ifndef __ETHERNETIF_H__
#define __ETHERNETIF_H__
/* C binding of definitions if building with C++ compiler */
#ifdef __cplusplus
extern "C"
{
#endif
#include "lwip/err.h"
#include "lwip/netif.h"
err_t ethernetif_init(struct netif *netif);
void ethernetif_input(struct netif *netif);
#ifdef __cplusplus
}
#endif
#endif /* __ETHERNETIF_H__ */
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, ðernetif_init, ðernet_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