GD32F4裸机移植LWIP
文章目录
前言
以下是GD32F407裸机移植LWIP的详细操作步骤。
一、参考资料
(1)正点原子的《lwIP开发指南_V1.3》
(2)《GD32F4xx_User_Manual_CN_V2.2》
二、材料准备
(1)GD32官网下载最新的firmware库文件;
(2)LWIP官网下载最新的lwip-2.1.3及contrib-2.1.0包;
(3)GD32F407开发板(PHY为LAN8720A);
三、操作步骤
1.创建一个裸机程序(包含串口输出和点灯的程序)
2.添加PHY芯片驱动代码
我用的板子的PHY为LAN8720A,需要注意:我的PHY芯片是额外加的25MHZ的晶振,当然你也可以用芯片PA8引脚输出25MHZ的时钟,但需要添加PA8引脚作为MC0时钟输出。这个代码下面添加以太网中断的处理函数,驱动代码如下图所示:
#include "drv_ethernet.h"
#include "gd32f4xx_enet.h"
#include "sysytic.h"
static __IO uint32_t enet_init_status = 0;
enum
{
ETH_SUCCES=0,
ETH_ERROR
};
/* ENET RxDMA/TxDMA descriptor */
extern enet_descriptors_struct rxdesc_tab[ENET_RXBUF_NUM], txdesc_tab[ENET_TXBUF_NUM];
/* ENET receive buffer */
extern uint8_t *rx_buff; //rx_buff[ENET_RXBUF_NUM][ENET_RXBUF_SIZE];
/* ENET transmit buffer */
extern uint8_t *tx_buff; //tx_buff[ENET_TXBUF_NUM][ENET_TXBUF_SIZE];
/*global transmit and receive descriptors pointers */
extern enet_descriptors_struct *dma_current_txdesc;
extern enet_descriptors_struct *dma_current_rxdesc;
uint8_t ETH_MACDMA_Config(void);
static void nvic_configuration(void);
/* 初始化ETH MAC层及DMA配置
返回值:ETH_ERROR 发送失败(0);ETH_SUCCESS,发送成功(1) */
uint8_t ETH_MACDMA_Config(void)
{
rcu_periph_clock_enable(RCU_ENET);
rcu_periph_clock_enable(RCU_ENETTX);
rcu_periph_clock_enable(RCU_ENETRX);
enet_deinit();/*AHB总线重启以太网*/
enet_software_reset(); /*软件重启网络*/
/*改为自动、强制模式切换*/
/*通过硬件计算和验证 IP、UDP、TCP 和 ICMP 校验和*/
enet_init_status = enet_init(ENET_AUTO_NEGOTIATION, ENET_AUTOCHECKSUM_DROP_FAILFRAMES, ENET_BROADCAST_FRAMES_PASS); /*不使用MAC地址过滤*/
if(SUCCESS == enet_init_status)
{
enet_interrupt_enable(ENET_DMA_INT_NIE);
enet_interrupt_enable(ENET_DMA_INT_RIE);
return ETH_SUCCES;
}
else
{
return ETH_ERROR;
}
}
/*!
\brief configures the nested vectored interrupt controller
\param[in] none
\param[out] none
\retval none
*/
static void nvic_configuration(void)
{
nvic_vector_table_set(NVIC_VECTTAB_FLASH, 0x0);
nvic_irq_enable(ENET_IRQn, 2, 0);
}
/* LAN8720初始化
返回值:0,成功;
其他,失败 */
uint8_t LAN8720_Init(void)
{
nvic_configuration();
uint8_t rval = ETH_ERROR;
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
rcu_periph_clock_enable(RCU_GPIOC);
rcu_periph_clock_enable(RCU_GPIOD);
rcu_periph_clock_enable(RCU_GPIOG);
rcu_periph_clock_enable(RCU_GPIOH);
rcu_periph_clock_enable(RCU_GPIOI);
rcu_periph_clock_enable(RCU_SYSCFG);
/* choose DIV2 to get 50MHz from 200MHz on CKOUT0 pin (PA8) to clock the PHY */
// rcu_ckout0_config(RCU_CKOUT0SRC_PLLP, RCU_CKOUT0_DIV4);
syscfg_enet_phy_interface_config(SYSCFG_ENET_PHY_RMII);
/*复位引脚*/
gpio_af_set(GPIOC, GPIO_AF_0, GPIO_PIN_0);
gpio_mode_set(GPIOC, GPIO_MODE_OUTPUT, GPIO_PUPD_PULLUP, GPIO_PIN_0);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_0);
/* PA1: ETH_RMII_REF_CLK */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_1);
/* PA2: ETH_MDIO */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_2);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_2);
/* PA7: ETH_RMII_CRS_DV */
gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_7);
gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_7);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_2);
gpio_af_set(GPIOA, GPIO_AF_11, GPIO_PIN_7);
/* PB11: ETH_RMII_TX_EN */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_11);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_11);
/* PB12: ETH_RMII_TXD0 */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_12);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_12);
/* PB13: ETH_RMII_TXD1 */
gpio_mode_set(GPIOB, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_13);
gpio_output_options_set(GPIOB, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_13);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_11);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_12);
gpio_af_set(GPIOB, GPIO_AF_11, GPIO_PIN_13);
/* PC1: ETH_MDC */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_1);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_1);
/* PC4: ETH_RMII_RXD0 */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_4);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_4);
/* PC5: ETH_RMII_RXD1 */
gpio_mode_set(GPIOC, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_5);
gpio_output_options_set(GPIOC, GPIO_OTYPE_PP, GPIO_OSPEED_MAX, GPIO_PIN_5);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_1);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_4);
gpio_af_set(GPIOC, GPIO_AF_11, GPIO_PIN_5);
LAN8720_RST(0); /*硬件复位LAN8720*/
delay_ms(50);
LAN8720_RST(1); /*硬件复位结束*/
rval = ETH_MACDMA_Config();/*配置MAC及DMA*/
return rval;
}
extern void lwip_pkt_handle(void); //在lwip_comm.c里面定义
/*!
\brief this function handles ethernet interrupt request
\param[in] none
\param[out] none
\retval none
*/
void ENET_IRQHandler(void)
{
uint32_t reval;
/* clear the enet DMA Rx interrupt pending bits */
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_RS_CLR);
enet_interrupt_flag_clear(ENET_DMA_INT_FLAG_NI_CLR);
/* handles all the received frames */
do {
reval = enet_rxframe_size_get();
if(reval > 1) {
lwip_pkt_handle();
}
} while(reval != 0);
}
3.按照正点原子裸机移植LWIP的操作步骤添加LWIP代码
(1)LWIP代码添加完毕之后,会出现26个错误,主要集中在ethernetif.c文件内。
(2)修改ethernetif.c文件,注意:这里有个lwip获取时间函数sys_now();我这个工程采用的定时器的方式实现,修改完成的代码如下:
/*
* This file is a skeleton for developing Ethernet network interface
* drivers for lwIP. Add code to the low_level functions and do a
* search-and-replace for the word "ethernetif" to replace it with
* something that better describes your network interface.
*/
#include "lwip/opt.h"
#include "netif/etharp.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
#include "lwip/etharp.h"
#include "netif/ppp/pppoe.h"
#include "ethernetif.h"
#include "lwip_comm.h"
#include "string.h"
/* Define those to better describe your network interface. */
#define IFNAME0 'G'
#define IFNAME1 'D'
/* ENET RxDMA/TxDMA descriptor */
extern enet_descriptors_struct rxdesc_tab[ENET_RXBUF_NUM], txdesc_tab[ENET_TXBUF_NUM];
/* ENET receive buffer */
extern uint8_t rx_buff[ENET_RXBUF_NUM][ENET_RXBUF_SIZE];
/* ENET transmit buffer */
extern uint8_t tx_buff[ENET_TXBUF_NUM][ENET_TXBUF_SIZE];
/*global transmit and receive descriptors pointers */
extern enet_descriptors_struct *dma_current_txdesc;
extern enet_descriptors_struct *dma_current_rxdesc;
/* preserve another ENET RxDMA/TxDMA ptp descriptor for normal mode */
enet_descriptors_struct ptp_txstructure[ENET_TXBUF_NUM];
enet_descriptors_struct ptp_rxstructure[ENET_RXBUF_NUM];
/**
* Helper struct to hold private data used to operate your ethernet interface.
* Keeping the ethernet address of the MAC in this struct is not necessary
* as it is already kept in the struct netif.
* But this is only an example, anyway...
*/
//struct ethernetif {
// struct eth_addr *ethaddr;
// /* Add whatever per-interface state that is needed here. */
//};
/* Forward declarations. */
//void ethernetif_input(struct netif *netif);
/**
* 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)
{
netif->hwaddr_len = ETHARP_HWADDR_LEN; /* 设置MAC地址长度,为6个字节 */
/* 初始化MAC地址,设置什么地址由用户自己设置,但是不能与网络中其他设备MAC地址重复 */
netif->hwaddr[0] = g_lwipdev.mac[0];
netif->hwaddr[1] = g_lwipdev.mac[1];
netif->hwaddr[2] = g_lwipdev.mac[2];
netif->hwaddr[3] = g_lwipdev.mac[3];
netif->hwaddr[4] = g_lwipdev.mac[4];
netif->hwaddr[5] = g_lwipdev.mac[5];
netif->mtu=1500; /* 最大允许传输单元,允许该网卡广播和ARP功能 */
/* 网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播 */
/* 使能、 ARP 使能等等重要控制位 */
netif->flags = NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP| NETIF_FLAG_IGMP; /* 广播 ARP协议 链接检测 */
enet_mac_address_set(ENET_MAC_ADDRESS0, netif->hwaddr);
enet_descriptors_chain_init(ENET_DMA_TX);
enet_descriptors_chain_init(ENET_DMA_RX);
int i;
for(i = 0; i < ENET_RXBUF_NUM; i ++)
{
enet_rx_desc_immediate_receive_complete_interrupt(&rxdesc_tab[i]);
}
#ifdef CHECKSUM_BY_HARDWARE
/* enable the TCP, UDP and ICMP checksum insertion for the Tx frames */
for(i=0; i < ENET_TXBUF_NUM; i++){
enet_transmit_checksum_config(&txdesc_tab[i], ENET_CHECKSUM_TCPUDPICMP_FULL);
}
#endif /* CHECKSUM_BY_HARDWARE */
enet_enable(); /* 开启MAC和DMA */
}
/**
* 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)
{
struct pbuf *q;
int framelength = 0;
uint8_t *buffer;
while((uint32_t)RESET != (dma_current_txdesc->status & ENET_TDES0_DAV)){
}
buffer = (uint8_t *)(enet_desc_information_get(dma_current_txdesc, TXDESC_BUFFER_1_ADDR));
/* copy frame from pbufs to driver buffers */
for(q = p; q != NULL; q = q->next){
memcpy((uint8_t *)&buffer[framelength], q->payload, q->len);
framelength = framelength + q->len;
}
/* note: padding and CRC for transmitted frame
are automatically inserted by DMA */
/* transmit descriptors to give to DMA */
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_TRANSMIT_ENHANCED_MODE(framelength, NULL);
#else
ENET_NOCOPY_FRAME_TRANSMIT(framelength);
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
return ERR_OK;
}
/**
* 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, *q;
u16_t len;
int l =0;
uint8_t *buffer;
p = NULL;
/* obtain the size of the packet and put it into the "len" variable. */
len = enet_desc_information_get(dma_current_rxdesc, RXDESC_FRAME_LENGTH);
buffer = (uint8_t *)(enet_desc_information_get(dma_current_rxdesc, RXDESC_BUFFER_1_ADDR));
/* we allocate a pbuf chain of pbufs from the Lwip buffer pool */
p = pbuf_alloc(PBUF_RAW, len + 5, PBUF_POOL);
/* copy received frame to pbuf chain */
if (p != NULL){
for (q = p; q != NULL; q = q->next){
memcpy((uint8_t *)q->payload, (u8_t*)&buffer[l], q->len);
l = l + q->len;
}
}
#ifdef SELECT_DESCRIPTORS_ENHANCED_MODE
ENET_NOCOPY_PTPFRAME_RECEIVE_ENHANCED_MODE(NULL);
#else
ENET_NOCOPY_FRAME_RECEIVE();
#endif /* SELECT_DESCRIPTORS_ENHANCED_MODE */
return p;
}
/**
* This function should be called when a packet is ready to be read
* from the interface. It uses the function low_level_input() that
* should handle the actual reception of bytes from the network
* interface. Then the type of the received packet is determined and
* the appropriate input function is called.
*
* @param netif the lwip network interface structure for this ethernetif
*/
void
ethernetif_input(struct netif *netif)
{
struct pbuf *p;
/* move received packet into a new pbuf */
p = low_level_input(netif);
/* if no packet could be read, silently ignore this */
if (p != NULL)
{
/* pass all packets to ethernet_input, which decides what packets it supports */
if (netif->input(p, netif) != ERR_OK)
{
LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
pbuf_free(p);
p = NULL;
}
}
}
/**
* 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)
{
struct ethernetif *ethernetif;
LWIP_ASSERT("netif != NULL", (netif != NULL));
// ethernetif = mem_malloc(sizeof(struct ethernetif));
//
// if (ethernetif == NULL)
// {
// LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
// return ERR_MEM;
// }
#if LWIP_NETIF_HOSTNAME
/* Initialize interface hostname */
netif->hostname = "lwip";
#endif /* LWIP_NETIF_HOSTNAME */
/*
* Initialize the snmp variables and counters inside the struct netif.
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->state = ethernetif;
netif->name[0] = IFNAME0;
netif->name[1] = IFNAME1;
/* We directly use etharp_output() here to save a function call.
* You can instead declare your own function an call etharp_output()
* from it if you have to do some checks before sending (e.g. if link
* is available...) */
#if LWIP_IPV4
netif->output = etharp_output;
#endif /* LWIP_IPV4 */
#if LWIP_IPV6
netif->output_ip6 = ethip6_output;
#endif /* LWIP_IPV6 */
netif->linkoutput = low_level_output;
// ethernetif->ethaddr = (struct eth_addr *) & (netif->hwaddr[0]);
/* initialize the hardware */
low_level_init(netif);
return ERR_OK;
}
/**
* @ingroup sys_time
* Returns the current time in milliseconds,
* may be the same as sys_jiffies or at least based on it.
* Don't care for wraparound, this is only used for time diffs.
* Not implementing this function means you cannot use some modules (e.g. TCP
* timestamps, internal timeouts for NO_SYS==1).
*/
//u32_t sys_now(void)
//{
// // return lwip_localtime;这个为lwip的时间基准,我这里采用定时器的方式实现
// //return HAL_GetTick();
//}
(3)修改lwip_comm.c文件,这个错误修改比较简单,我就不细说了。
4.修改main.c内容
总结
完成以上步骤。接下来就是验证lwip是否移植成功了,win+R,ping包测试了。ping通了,恭喜你移植成功了。