先天后期lwip之无操作系统移植lwip协议栈

  • Post author:
  • Post category:其他


移植前必知必会:


1.移植分成以下两类:(本文是第一类)

第一类移植:只移植内核核心,此时用户应用程序编写只能基于Raw/Callback API进行;

==》完成几个头文件的定义,同时根据网卡的具体情况完成ethernetif.c中的函数(网卡驱动)的编写。

第二种移植:既移植内核核心,也移植上次API函数模块,此种方式三种API接口都可以使用;

==》除了实现第一种移植中的所有文件和函数外,还必须使用操作系统提供的邮箱和信号量机制,完成操作系统模拟层文件ssys_arch.c和sys_arch.h的编写。该种移植必须在操作系统的基础上进行。


2.网卡驱动的作用:

(1)控制网卡接收数据并将数据封装为lwip熟悉的格式pbuf,然后递交给内核。

(2)将上层发送的数据包解析为网卡熟悉的结构并控制网卡发送数据。


3.网卡功能函数:

(1)网卡初始化函数;

(2)网卡数据发送函数;

(3)网卡数据接收函数;


移植的步骤:


一、新建三个头文件cc.h、lwipopts.h、perf.h


1.新建cc.h文件添加如下代码:

// cc.h

该段代码对于不是OS的移植是通用的,直接复制使用

#ifndef __CC_H__
#define __CC_H__

#include "lwip_cpu.h"
#include "stdio.h"
#include "includes.h"  //OS需要

//定义与平台无关的数据类型
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;              


//OS需要的临界区保护,该处未定义这个宏
#if CPU_CFG_CRITICAL_METHOD == 1
#define SYS_ARCH_DECL_PROTECT(lev)
#define SYS_ARCH_PROTECT(lev)        CPU_INT_DIS()
#define SYS_ARCH_UNPROTECT(lev)        CPU_INT_EN()
#endif

#if CPU_CFG_CRITICAL_METHOD == 3  
#define SYS_ARCH_DECL_PROTECT(lev)    u32_t lev
#define SYS_ARCH_PROTECT(lev)        lev = CPU_SR_Save()     //UCOS IIIÖнøÈëÁÙ½çÇø,¹ØÖжÏ
#define SYS_ARCH_UNPROTECT(lev)        CPU_SR_Restore(lev)        //UCOS IIIÖÐÍ˳öAÁÙ½çÇø£¬¿ªÖжϠ
#endif

//根据不同编译器定义一些符号
#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

#elif defined (__TASKING__)

#define PACK_STRUCT_BEGIN
#define PACK_STRUCT_STRUCT
#define PACK_STRUCT_END
#define PACK_STRUCT_FIELD(x) x

#endif

//使用printf调试时候使用到的
#define U16_F "4d"
#define S16_F "4d"
#define X16_F "4x"
#define U32_F "8ld"
#define S32_F "8ld"
#define X32_F "8lx"

//一些需要的宏定义
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) //do { if(!(x)) while(1); } while(0)

//#define LWIP_PLATFORM_ASSERT(x) \
//    do \
//    {   printf("Assertion \"%s\" failed at line %d in %s\r\n", x, __LINE__, __FILE__); \
//    } while(0)
#endif

#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif

#endif /* __CC_H__ */


2.新建perf.h并添加代码

#ifndef __PERF_H__
#define __PERF_H__

#define PERF_START    /* null definition */
#define PERF_STOP(x)  /* null definition */


3.新建lwipopts.h,并添加代码,该文件的很多选项在opt.h中都有的,当该文件出现时候opt.h文件中的相同选项都不使用了。

#ifndef __LWIPOPTS_H__
#define __LWIPOPTS_H__

//线程优先级
#ifndef TCPIP_THREAD_PRIO
#define TCPIP_THREAD_PRIO            5    
#endif
#undef  DEFAULT_THREAD_PRIO
#define DEFAULT_THREAD_PRIO          2

#define SYS_LIGHTWEIGHT_PROT    1       
#define NO_SYS                  0         
#define MEM_ALIGNMENT           4         
#define MEM_SIZE                16000     
#define MEMP_NUM_PBUF           20        
#define MEMP_NUM_UDP_PCB        6      
#define MEMP_NUM_TCP_PCB        10       
#define MEMP_NUM_TCP_PCB_LISTEN 6       
#define MEMP_NUM_TCP_SEG        15       
#define MEMP_NUM_SYS_TIMEOUT    8        

//pbuf选项
#define PBUF_POOL_SIZE          20        
#define PBUF_POOL_BUFSIZE       512      
#define LWIP_SUPPORT_CUSTOM_PBUF 1     

#define LWIP_TCP                1          //ʹÓÃTCP
#define TCP_TTL                 255        //Éú´æʱ¼ä

#undef TCP_QUEUE_OOSEQ
#define TCP_QUEUE_OOSEQ         0         //µ±TCPµÄÊý¾Ý¶Î³¬³ö¶ÓÁÐʱµÄ¿ØÖÆλ,µ±É豸µÄÄÚ´æ¹ýСµÄʱºò´ËÏîӦΪ0

#undef TCPIP_MBOX_SIZE
#define TCPIP_MBOX_SIZE         MAX_QUEUE_ENTRIES   //tcpip´´½¨Ö÷Ïß³ÌʱµÄÏûÏ¢ÓÊÏä´óС

#undef DEFAULT_TCP_RECVMBOX_SIZE
#define DEFAULT_TCP_RECVMBOX_SIZE       MAX_QUEUE_ENTRIES  

#undef DEFAULT_ACCEPTMBOX_SIZE
#define DEFAULT_ACCEPTMBOX_SIZE         MAX_QUEUE_ENTRIES  


#define TCP_MSS                 (1500 - 40)          //×î´óTCP·Ö¶Î,TCP_MSS = (MTU - IP±¨Í·´óС - TCP±¨Í·´óС
#define TCP_SND_BUF             (4*TCP_MSS)        //TCP·¢ËÍ»º³åÇø´óС(bytes).
#define TCP_SND_QUEUELEN        (2* TCP_SND_BUF/TCP_MSS)    //TCP_SND_QUEUELEN: TCP·¢ËÍ»º³åÇø´óС(pbuf).Õâ¸öÖµ×îСΪ(2 * TCP_SND_BUF/TCP_MSS)
#define TCP_WND                 (4*TCP_MSS)        //TCP·¢ËÍ´°¿Ú
#define LWIP_ICMP               1     //ʹÓÃICMPЭÒé
#define LWIP_DHCP               0    //ʹÓÃDHCP
#define LWIP_UDP                1     //ʹÓÃUDP·þÎñ
#define UDP_TTL                 255 //UDPÊý¾Ý°üÉú´æʱ¼ä
#define LWIP_STATS 0
#define LWIP_PROVIDE_ERRNO 1


//各种硬件帧校验和
#define CHECKSUM_BY_HARDWARE
#ifdef CHECKSUM_BY_HARDWARE
  //CHECKSUM_GEN_IP==0: Ó²¼þÉú³ÉIPÊý¾Ý°üµÄ֡УÑéºÍ
  #define CHECKSUM_GEN_IP                 0
  //CHECKSUM_GEN_UDP==0: Ó²¼þÉú³ÉUDPÊý¾Ý°üµÄ֡УÑéºÍ
  #define CHECKSUM_GEN_UDP                0
  //CHECKSUM_GEN_TCP==0: Ó²¼þÉú³ÉTCPÊý¾Ý°üµÄ֡УÑéºÍ
  #define CHECKSUM_GEN_TCP                0 
  //CHECKSUM_CHECK_IP==0: Ó²¼þ¼ì²éÊäÈëµÄIPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_IP               0
  //CHECKSUM_CHECK_UDP==0: Ó²¼þ¼ì²éÊäÈëµÄUDPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_UDP              0
  //CHECKSUM_CHECK_TCP==0: Ó²¼þ¼ì²éÊäÈëµÄTCPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_TCP              0
  //CHECKSUM_CHECK_ICMP==1:Ó²¼þ¼ì²éÊäÈëµÄICMPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_GEN_ICMP               0
#else
  //CHECKSUM_GEN_IP==1: Èí¼þÉú³ÉIPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_GEN_IP                 1
  // CHECKSUM_GEN_UDP==1: Èí¼þÉú³ÉUDOPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_GEN_UDP                1
  //CHECKSUM_GEN_TCP==1: Èí¼þÉú³ÉTCPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_GEN_TCP                1
  // CHECKSUM_CHECK_IP==1: Èí¼þ¼ì²éÊäÈëµÄIPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_IP               1
  // CHECKSUM_CHECK_UDP==1: Èí¼þ¼ì²éÊäÈëµÄUDPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_UDP              1
  //CHECKSUM_CHECK_TCP==1: Èí¼þ¼ì²éÊäÈëµÄTCPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_CHECK_TCP              1
  //CHECKSUM_CHECK_ICMP==1:Èí¼þ¼ì²éÊäÈëµÄICMPÊý¾Ý°ü֡УÑéºÍ
  #define CHECKSUM_GEN_ICMP               1
#endif

//使能API接口功能

#define LWIP_NETCONN                    1    
#define LWIP_SOCKET                     1    
#define LWIP_COMPAT_MUTEX               1
#define LWIP_SO_RCVTIMEO                1     //防止线程阻塞

//系统有关的选项  ,在opt,h中默认为0的
#define TCPIP_THREAD_STACKSIZE             1000    //内核任务堆栈大小
#define DEFAULT_UDP_RECVMBOX_SIZE    2000
#define DEFAULT_THREAD_STACKSIZE        512

//LWIP调试相关选项
#define LWIP_DEBUG                         0     //¹Ø±ÕDEBUGÑ¡Ïî
#define ICMP_DEBUG                      LWIP_DBG_OFF //¿ªÆô/¹Ø±ÕICMPdebug

#endif /* __LWIPOPTS_H__ */

二、网卡驱动的编程框架(

五个函数组成的架构)


1.网卡驱动的实现准备材料和需预先知道的知识点


1.1 SPI接口的网卡(非硬件TCP/IP协议网卡,只是内部包含MAC和PHY的)

需要事先由厂商提供三个关于网卡的函数:

(1)XXX_init(char *macaddr);                             //网卡初始化函数完成网卡内部设置:发送缓冲区,接收缓冲区,中断允许,设置MAC地址,并完成其他的初始化工作。

(2)XXX_PacketSend(int len,char *packet);    //网卡发送函数将packet写入网卡RAM并启动发送

(3)XXX_PacketReceive(int len,char *packet);//网卡接收函数:接收数据


1.2  RMII接口的网卡(单片机内部含有MAC,网卡为PHY的,一般为STM32 的库,此处为HAL库),

(1)HAL_ETH_IsRxDataAvailable(&LAN8720_ETHHandle);                 //接收

(2)HAL_ETH_Transmit(&LAN8720_ETHHandle,&TxConfig,0);               //发送

(3)HAL_ETH_SetMACConfig(&LAN8720_ETHHandle,&MACConf);    // 初始化


1.3 网卡框架形式


1.3.1 主要函数列表

ethernetif.c(改动基本在这个文件中,还有sys_now())

——void low_level_init(struct netif *netif);

——err_t low_level_output(struct netif *netif, struct pbuf *p);

——struct pbuf * low_level_input(struct netif *netif);

——err_t ethernetif_input(struct netif *netif);

——err_t ethernetif_init(struct netif *netif);


1.3.2 函数之间的关系(以RMII的hal库来说的)


1.3.2.1


接收的框架



lwip_pkt_handle();



ethernetif_input(&lwip_netif);



low_level_input(netif);




HAL_ETH_IsRxDataAvailable(&LAN8720_ETHHandle)。


lwip_pkt_handle()一般为ETH接收中断或者轮询中放置


1.3.2.2


初始化的的框架

lwip_comm_init



Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&tcpip_input);//向网卡列表中添加一网卡



ethernetif_init



low_level_init(netif);



HAL_ETH_SetMACConfig(&LAN8720_ETHHandle,&MACConf);  //设置MAC。


lwip_comm_init为初始化时候调用


1.3.2.3 输出的框架

static void etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)//用在回复ARP

static err_t etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst) 、

err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,

const struct eth_addr *ethdst_addr,

const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,

const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,

const u16_t opcode)、




low_level_output即netif->linkoutput(netif, p);



HAL_ETH_Transmit(&LAN8720_ETHHandle,&TxConfig,0);



该底层发送函数一般都是被内部回调使用。


1.4 网卡的函数详细解读


1.4.1.static void low_level_init(struct netif *netif) 函数:



设置netif以及网卡属性相关字段


  ETH_MACConfigTypeDef MACConf;       //有些变量没有列出

//==========设置netif网络管理接口四大项:MAC长度,MAC值,网络包大小,属性字段====
   //======MAC的位数与6位MAC值======
    netif->hwaddr_len=ETHARP_HWADDR_LEN;    
    netif->hwaddr[0]=lwipdev.mac[0]; 
    netif->hwaddr[1]=lwipdev.mac[1]; 
    netif->hwaddr[2]=lwipdev.mac[2];
    netif->hwaddr[3]=lwipdev.mac[3];   
    netif->hwaddr[4]=lwipdev.mac[4];
    netif->hwaddr[5]=lwipdev.mac[5];
    netif->mtu=ETH_MAX_PAYLOAD;//最大传输包大小
    netif->flags|=NETIF_FLAG_BROADCAST|NETIF_FLAG_ETHARP|NETIF_FLAG_LINK_UP;//设置网络接口的属性字段

                                       广播                                           ARP                             网络接口电路使能  
//====接收buf初始化============        
    for(idx=0;idx<ETH_RX_DESC_CNT;idx ++)
    {
        HAL_ETH_DescAssignMemory(&LAN8720_ETHHandle,idx,Rx_Buff[idx],NULL);
        rx_pbuf[idx].custom_free_function=pbuf_free_custom;
    }
//======发送buf初始化============ 
    memset(&TxConfig,0,sizeof(ETH_TxPacketConfig));
    TxConfig.Attributes=ETH_TX_PACKETS_FEATURES_CSUM|ETH_TX_PACKETS_FEATURES_CRCPAD;
    TxConfig.ChecksumCtrl=ETH_CHECKSUM_IPHDR_PAYLOAD_INSERT_PHDR_CALC;
    TxConfig.CRCPadCtrl=ETH_CRC_PAD_INSERT;
//===========网卡参数配置===============
    PHYLinkState=LAN8720_GetLinkState(); 
    switch (PHYLinkState)
    {
        case LAN8720_STATUS_100MBITS_FULLDUPLEX:    
            duplex=ETH_FULLDUPLEX_MODE;
            speed=ETH_SPEED_100M;
            break;
        case LAN8720_STATUS_100MBITS_HALFDUPLEX: 
            duplex=ETH_HALFDUPLEX_MODE;
            speed=ETH_SPEED_100M;
            break;
        case LAN8720_STATUS_10MBITS_FULLDUPLEX:     
            duplex=ETH_FULLDUPLEX_MODE;
            speed=ETH_SPEED_10M;
            break;
        case LAN8720_STATUS_10MBITS_HALFDUPLEX:   
            duplex=ETH_HALFDUPLEX_MODE;
            speed=ETH_SPEED_10M;
            break;
        default:
            break;      
    }
    
    HAL_ETH_GetMACConfig(&LAN8720_ETHHandle,&MACConf); 
    MACConf.DuplexMode=duplex;
    MACConf.Speed=speed;
    HAL_ETH_SetMACConfig(&LAN8720_ETHHandle,&MACConf);  //配置网卡信息参数:
    HAL_ETH_Start_IT(&LAN8720_ETHHandle);
    netif_set_up(netif);                      
    netif_set_link_up(netif);     


1.4.2  static err_t low_level_output(struct netif *netif, struct pbuf *p):


网卡数据包发送函数将pbuf通过netif网卡发送出去

//pbuf为应用程序传下来的
static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
    uint32_t i=0, framelen = 0;
    struct pbuf *q;                  //链表的一个节点,用于轮询pbuf链表,处理
    err_t errval=ERR_OK;    
    ETH_BufferTypeDef Txbuffer[ETH_TX_DESC_CNT]; //宏为4,该数组组装成一个链表了
  
    
    for(q=p;q!=NULL;q=q->next)       //轮询链表
    {
        if(i>=ETH_TX_DESC_CNT)	
            return ERR_IF;
    
        Txbuffer[i].buffer=q->payload;  //数据
        Txbuffer[i].len=q->len;         //长度
        framelen+=q->len;               //帧长
    
        if(i>0)
        {
            Txbuffer[i-1].next=&Txbuffer[i];
        }
    
        if(q->next == NULL)
        {
            Txbuffer[i].next=NULL;
        }
        i++;
    }
    
    TxConfig.Length=framelen;
    TxConfig.TxBuffer=Txbuffer;

    SCB_CleanInvalidateDCache();    //
    HAL_ETH_Transmit(&LAN8720_ETHHandle,&TxConfig,0);
  
    return errval;
}

其中pbuf为:
struct pbuf {
  struct pbuf *next;          //指向下一包
  void *payload;              //有效数据
  u16_t tot_len;              //总长度
  u16_t len;                  //当前长度
  u8_t  type;                
  u8_t flags;
  u16_t ref;
};
其中Txbuffer
typedef struct __ETH_BufferTypeDef
{
  uint8_t *buffer;                  //数据     
  uint32_t len;                     //数据长度
  struct __ETH_BufferTypeDef *next; //指向下一个	
}ETH_BufferTypeDef;
其中TxConfig为发送的数据包,有些东西会在init中初始化
typedef struct
{
  uint32_t Attributes;             
  
  uint32_t Length;                 
  
  ETH_BufferTypeDef *TxBuffer;     
  
  uint32_t SrcAddrCtrl;            
  
  uint32_t CRCPadCtrl;             
  
  uint32_t ChecksumCtrl;          
  
  uint32_t MaxSegmentSize;         
  
  uint32_t PayloadLen;             
  
  uint32_t TCPHeaderLen;           

  uint32_t VlanTag;                
  
  uint32_t VlanCtrl;               
  
  uint32_t InnerVlanTag;          
  
  uint32_t InnerVlanCtrl;         
  
}ETH_TxPacketConfig;


1.4.3 static struct pbuf * low_level_input(struct netif *netif):


接收数据包转化成pbuf

static struct pbuf * low_level_input(struct netif *netif)
{
    struct pbuf *p = NULL;
    ETH_BufferTypeDef RxBuff;
    uint32_t framelength = 0;
  
    if(HAL_ETH_IsRxDataAvailable(&LAN8720_ETHHandle))//获取数据包
    {
        SCB_CleanInvalidateDCache();    
        HAL_ETH_GetRxDataBuffer(&LAN8720_ETHHandle,&RxBuff);//放入接收buff
        HAL_ETH_GetRxDataLength(&LAN8720_ETHHandle,&framelength);//得到接收到的数据长度
        //分配内存并且转存到rx_pbuf中,首节点地址为p
        p=pbuf_alloced_custom(PBUF_RAW,framelength,PBUF_POOL,
                              &rx_pbuf[current_pbuf_idx],RxBuff.buffer, framelength);

        if(current_pbuf_idx<(ETH_RX_DESC_CNT-1))
        {
            current_pbuf_idx++;
        }
        else
        {
            current_pbuf_idx=0;
        }
    
        return p;
    }
    else
    {
        return NULL;
    }
}


1.4.4  err_t ethernetif_input(struct netif *netif):


调用low_level_input()接收数据包,然后解析该包的类型,区分ARP/IP递交给相应的上层。调用一次完成一个数据包的接收与递交


err_t ethernetif_input(struct netif *netif)
{
    err_t err;
    struct pbuf *p;
  
    p=low_level_input(netif);
    if(p==NULL) return ERR_MEM;
    err=netif->input(p, netif); //调用网络接口交给内核
    if (err!=ERR_OK)
    {
        LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
        pbuf_free(p);
        p=NULL;
    }
    HAL_ETH_BuildRxDescriptors(&LAN8720_ETHHandle);
    return err;
}


1.4.5.err_t ethernetif_init(struct netif *netif);

err_t ethernetif_init(struct netif *netif)
{
    LWIP_ASSERT("netif != NULL", (netif != NULL));
  
#if LWIP_NETIF_HOSTNAME
    netif->hostname="lwip"; //Ö÷»úÃû×Ö
#endif

    netif->name[0]=IFNAME0;
    netif->name[1]=IFNAME1;
    netif->output=etharp_output;
    netif->linkoutput=low_level_output;
    low_level_init(netif);
    return ERR_OK;
}

三、最后需要完善的小点


1.sys_now函数:

系统中引入sys_check_timeouts来处理内核的各种定时事件。该函数要求移植者两次sys_now返回时间差来判断事件是否达到。所以只需将某变量放在一个20ms中断的定时器到中断就+20,然后sys_now返回它就可以了。


2.协议栈初始化:

u8 lwip_comm_init(void)
{
    u8 retry=0;
    struct netif *Netif_Init_Flag;		
	struct ip_addr ipaddr;  		
	struct ip_addr netmask; 		
	struct ip_addr gw;      			
    
    if(lwip_comm_mem_malloc())return 2;	
    lwip_comm_default_ip_set(&lwipdev);	
	while(LAN8720_Init())		        
    {
        retry++;
        if(retry>5) {retry=0;return 3;} 
    }
	tcpip_init(NULL,NULL);				

#if LWIP_DHCP	 //DHCP 获取动态IP	
	ipaddr.addr = 0;
	netmask.addr = 0;
	gw.addr = 0;
#else	//静态IP等网络参数			
	IP4_ADDR(&ipaddr,lwipdev.ip[0],lwipdev.ip[1],lwipdev.ip[2],lwipdev.ip[3]);
	IP4_ADDR(&netmask,lwipdev.netmask[0],lwipdev.netmask[1] ,lwipdev.netmask[2],lwipdev.netmask[3]);
	IP4_ADDR(&gw,lwipdev.gateway[0],lwipdev.gateway[1],lwipdev.gateway[2],lwipdev.gateway[3]);
	
	Netif_Init_Flag=netif_add(&lwip_netif,&ipaddr,&netmask,&gw,NULL,&ethernetif_init,&tcpip_input);
	if(Netif_Init_Flag==NULL)return 4;
	else
	{
		netif_set_default(&lwip_netif); //添加网卡
		netif_set_up(&lwip_netif);		//启动网卡
	}
	return 0;
}   


3.交互方式:

(1)中断方式(响应快,但是数据量大的话,容易一直打断别的程序)


void ETH_IRQHandler(void)
{
    lwip_pkt_handle();

    __HAL_ETH_DMA_CLEAR_IT(&LAN8720_ETHHandle,ETH_DMA_NORMAL_IT);  
    __HAL_ETH_DMA_CLEAR_IT(&LAN8720_ETHHandle,ETH_DMA_RX_IT);     
    __HAL_ETH_DMA_CLEAR_IT(&LAN8720_ETHHandle,ETH_DMA_TX_IT);     
}

(2)轮询方式(可能丢包,可控制时间)

while(1)
{

    lwip_pkt_handle();
   
}



版权声明:本文为alingbo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。