打印

GD32F4裸机移植LWIP

[复制链接]
1778|11
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
lmq0v9|  楼主 | 2024-4-30 22:36 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
参考资料
(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.创建一个裸机程序(包含串口输出和点灯的程序)

使用特权

评论回复
沙发
lmq0v9|  楼主 | 2024-4-30 22:37 | 只看该作者
添加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);


}

使用特权

评论回复
板凳
lmq0v9|  楼主 | 2024-4-30 22:37 | 只看该作者
按照正点原子裸机移植LWIP的操作步骤添加LWIP代码
(1)LWIP代码添加完毕之后,会出现26个错误,主要集中在ethernetif.c文件内。

使用特权

评论回复
地板
lmq0v9|  楼主 | 2024-4-30 22:38 | 只看该作者
修改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);
    }
               
#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, 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();
//}

使用特权

评论回复
5
lmq0v9|  楼主 | 2024-4-30 22:38 | 只看该作者
修改lwip_comm.c文件,这个错误修改比较简单,我就不细说了。

使用特权

评论回复
6
lmq0v9|  楼主 | 2024-4-30 22:38 | 只看该作者
.修改main.c内容

使用特权

评论回复
7
lmq0v9|  楼主 | 2024-4-30 22:38 | 只看该作者
总结
完成以上步骤。接下来就是验证lwip是否移植成功了,win+R,ping包测试了。ping通了,恭喜你移植成功了。

使用特权

评论回复
8
sfd123| | 2024-5-3 10:16 | 只看该作者
这个 我正好用!

使用特权

评论回复
9
chenjun89| | 2024-5-5 20:03 | 只看该作者
逻机跑lwip不是很方便。

使用特权

评论回复
10
weifeng90| | 2024-5-6 08:15 | 只看该作者
逻机跑LWIP,需要在定时器或者while循环中一直调用lwip的钩子函数。

使用特权

评论回复
11
埃娃| | 2024-5-15 23:31 | 只看该作者
还是上系统比较方便

使用特权

评论回复
12
kzlzqi| | 2024-7-31 21:55 | 只看该作者
ping通了,恭喜你移植成功了。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

15

主题

129

帖子

0

粉丝