LWIP的移植笔记

本文详细介绍了lwIP协议栈的移植步骤,包括配置头文件lwipopts.h、实现核心移植函数如ethernetif_init及low_level_input/output,并展示了具体的代码实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次发表博客,文章摘录于还不懂同学的专栏

  lwIp的作者做了大量的工作以方便像我这种懒人移植该协议栈,基本上只需修改一个配置头文件和改写3个函数即可完成lwIP的移植。要改写的函数位于lwIP-1.3.0/src/netif/ethernetif.c中,你也可以用自己更合适的网络接口名来代替“ethernetif”。另外还有一个配置头文件,叫做lwipopts.h文件,它要放在工程目录下。这个文件来源于lwIP-1.3.0/src/include/lwip/opt.h头文件,是对整个协议栈的一个配置,比如是否使用TCP/UDP/DHCP协议等等。

  第一个函数为ethernerif_init()函数,这个函数先是设置与协议栈有关的底层操作,指定底层接收回调函数等,接着对实际网络接口芯片进行初始化,设置硬件的工作方式,开放中断等。

  第二个函数为low_level_output函数,主要目的是将要发送的数据包分解成网络接口芯片能识别的书籍结构并将数据发送到链路上。

  第三个函数为low_level_input()函数,主要接收以太网数据并将数据打包成lwIP能识别的数据结构。

  第四个头文件lwipopts.h内容较多,按照具体应用进行配置。

  1、lwip协议栈的cc.h文件,该文件中定义了与处理器相关的数据类型。

  这个文件主要设置lwIP内部使用的数据类型,比如u8_t、u32_t等。lwIP可以移植到32位、16位甚至是8位构架的微控制器,由于移植的硬件平台以及编译器的不同,这些数据类型是要移植者根据自己的硬件和编译器特性来自行设置的。比如int类型变量,在8位和16位控制器中多表示2字节,但在32位微处理器中却表示4个字节,若是连这些基本数据类型都没有设置正确的话,就谈不上移植了。下面看cc.h的源代码:

  

复制代码
 1 #ifndef __CC_H__
 2 #define __CC_H__
 3  4 typedef unsigned char u8_t; //基本数据类型设置  5 typedef signed char s8_t;  6 typedef unsigned short u16_t;  7 typedef signed short s16_t;  8 typedef unsigned long u32_t;  9 typedef signed long s32_t; 10 typedef u32_t mem_ptr_t; 11 12 #ifndef BYTE_ORDER 13 #define BYTE_ORDER LITTLE_ENDIAN 14 #endif 15 16 #if defined(__arm__) && defined(__ARMCC_VERSION) //以下主要设置不同编译器的结构体数据的对齐,lwIP需要 17 // 18 // Setup PACKing macros for KEIL/RVMDK Tools 19 // 20 #define PACK_STRUCT_BEGIN __packed 21 #define PACK_STRUCT_STRUCT 22 #define PACK_STRUCT_END 23 #define PACK_STRUCT_FIELD(x) x 24 #elif defined (__IAR_SYSTEMS_ICC__) 25 // 26 // Setup PACKing macros for IAR Tools 27 // 28 #define PACK_STRUCT_BEGIN 29 #define PACK_STRUCT_STRUCT 30 #define PACK_STRUCT_END 31 #define PACK_STRUCT_FIELD(x) x 32 #define PACK_STRUCT_USE_INCLUDES 33 #else 34 // 35 // Setup PACKing macros for GCC Tools 36 // 37 #define PACK_STRUCT_BEGIN 38 #define PACK_STRUCT_STRUCT __attribute__ ((__packed__)) 39 #define PACK_STRUCT_END 40 #define PACK_STRUCT_FIELD(x) x 41 #endif 42 43 #ifdef DEBUG 44 extern void __error__(char *pcFilename, unsigned long ulLine); 45 #define LWIP_PLATFORM_ASSERT(expr) \ 46 { \ 47 if(!(expr)) \ 48  { \ 49  __error__(__FILE__, __LINE__); \ 50  } \ 51 } 52 #else 53 #define LWIP_PLATFORM_ASSERT(expr) 54 #endif 55 56 #endif /* __CC_H__ */
复制代码

  

  2、以太网硬件初始化、与硬件密切相关的数据接收、发送函数

        Stellais串口转以太网模块与硬件密切相关的移植代码位于stellarisif.c中。这里面的代码主要是三部分:lwIP协议栈和以太网硬件初始化函数、lwIP协议栈将数据发送到网络接口上的输出函数以及从Stellaris以太网硬件读取数据并送给lwIP协议栈的输入函数。

  2.1 lwIP协议栈和以太网硬件初始化

       在移植代码stellarisif.c中,对lwIP协议栈和以太网硬件初始化的函数是:

        err_t stellarisif_init(structnetif *netif)

  这个函数先是设置与协议栈有关的底层操作,指定底层接收回调函数等,接着对实际网络接口芯片进行初始化,设置硬件的工作方式,开放中断等。源代码如下所示:

复制代码
 1 /**
 2  * Should be called at the beginning of the program to set up the  3  * network interface. It calls the function stellarisif_hwinit() to do the  4  * actual setup of the hardware.  5  * 此在程序开始的时候被调用,用来设置网络接口.他调用stellarisif_hwinit()函数  6  * 来完成以太网硬件的设置.  7  * This function should be passed as a parameter to netif_add().  8  * 这个函数作为一个参数传递给netif_add()函数.  9  * @param netif the lwip network interface structure for this ethernetif 10  * @return ERR_OK if the loopif is initialized 11  * ERR_MEM if private data couldn't be allocated 12  * any other err_t on error 13 */ 14 err_t 15 stellarisif_init(struct netif *netif) 16 { 17 LWIP_ASSERT("netif != NULL", (netif != NULL)); 18 19 #if LWIP_NETIF_HOSTNAME 20 /* Initialize interface hostname */ 21 netif->hostname = "lwip"; //初始化接口主机名字 22 #endif /* LWIP_NETIF_HOSTNAME */ 23 24 /* 25  * Initialize the snmp variables and counters inside the struct netif. 26  * The last argument should be replaced with your link speed, in units 27  * of bits per second. 28 */ 29 NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, 1000000); //初始化snmp变量 30 31 netif->state = &stellarisif_data; //指向以太网接口的私有数据,包括pbuf数据链和MAC地址 32 netif->name[0] = IFNAME0; 33 netif->name[1] = IFNAME1; 34 /* We directly use etharp_output() here to save a function call. 35  * You can instead declare your own function an call etharp_output() 36  * from it if you have to do some checks before sending (e.g. if link 37  * is available...) */ 38 netif->output = etharp_output; //IP层将一包数据发往网络接口时调用此函数 39 netif->linkoutput = stellarisif_output; //ARP模块将一包数据发往网络接口时调用此函数 40 41 stellarisif_data.ethaddr = (struct eth_addr *)&(netif->hwaddr[0]); //初始化MAC地址 42 stellarisif_data.txq.qread = stellarisif_data.txq.qwrite = 0; //初始化pbuf数据链 43 stellarisif_data.txq.overflow = 0; 44 45 /* initialize the hardware */ 46 stellarisif_hwinit(netif); //初始化Stellaris以太网硬件 47 48 return ERR_OK; 49 }
复制代码

  1. netif->output = etharp_output;用于将一包数据发送到网络接口,由IP层调用。这个函数最终会调用netif->linkoutput来将数据发送到网络接口。

  2. netif->linkoutput = stellarisif_output;用于将一包数据发送到网络接口,有ARP模块调用。程序员需根据自己的硬件平台来编写该函数。后面会讲到该函数。

  3.stellarisif_hwinit(netif):初始化以太网硬件,还是有必要看看该函数的,代码如下所示,不再单独解释,可以看注释。

  

复制代码
 1 /**
 2  * In this function, the hardware should be initialized.  3  * Called from stellarisif_init().  4  *  5  * @param netif the already initialized lwip network interface structure  6  * for this ethernetif  7 */  8 static void  9 stellarisif_hwinit(struct netif *netif) 10 { 11  u32_t temp; 12 //struct stellarisif *stellarisif = netif->state; 13 14 /* 设置以太网硬件MAC地址长度 */ 15 netif->hwaddr_len = ETHARP_HWADDR_LEN; 16 17 /* 设置以太网硬件地址 */ 18 EthernetMACAddrGet(ETH_BASE, &(netif->hwaddr[0])); 19 20 /* 最大发送单元 */ 21 netif->mtu = 1500; 22 23 /* 使能网卡的功能,允许网卡广播、ARP功能和允许硬件链路连接该网卡*/ 24 /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */ 25 netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP; 26 27 /* Do whatever else is needed to initialize interface. */ 28 /* 禁止所有以太网中断 */ 29 EthernetIntDisable(ETH_BASE, (ETH_INT_PHY | ETH_INT_MDIO | ETH_INT_RXER | 30 ETH_INT_RXOF | ETH_INT_TX | ETH_INT_TXER | ETH_INT_RX)); 31 temp = EthernetIntStatus(ETH_BASE, false); 32  EthernetIntClear(ETH_BASE, temp); 33 34 /* 初始化以太网控制器 */ 35  EthernetInitExpClk(ETH_BASE, SysCtlClockGet()); 36 37 /* 38  * 配置以太网控制器正常运行. 39  * - 使能 TX 全双工模式 40  * - 使能 TX 填充 41  * - 使能 TX CRC 生成 42  * - 使能 RX 组播接收 43 */ 44 EthernetConfigSet(ETH_BASE, (ETH_CFG_TX_DPLXEN |ETH_CFG_TX_CRCEN | 45 ETH_CFG_TX_PADEN | ETH_CFG_RX_AMULEN)); 46 47 /* 使能以太网控制器的发送器和接收器 */ 48  EthernetEnable(ETH_BASE); 49 50 /* 使能以太网中断 */ 51  IntEnable(INT_ETH); 52 53 /* 使能以太网TX和RX数据包中断 */ 54 EthernetIntEnable(ETH_BASE, ETH_INT_RX | ETH_INT_TX); 55 }
复制代码

  2.2 Stellaris以太网硬件底层数据发送函数
        上面也已经讲到了,ARP模块发送数据到网络接口会调用netif->linkoutput函数;IP层发送数据到网络接口会调用netif->output函数,但IP层实际的数据发送函数任然是netif->linkoutput,在以太网初始化中,已经为linkoutput指针赋值:netif->linkoutput=stellarisif_output;因此,移植lwIP时程序员需要编写的Stellaris以太网硬件底层数据发送函数正是stellarisif_output()函数。先来看以下它的源码:

复制代码
 1 /**
 2  * This function with either place the packet into the Stellaris transmit fifo,  3  * or will place the packet in the interface PBUF Queue for subsequent  4  * transmission when the transmitter becomes idle.  5  * 这个函数要么将数据包放到Stellaris发送缓存FIFO中,要么把数据包放到PBUF队列,等待  6  * 发送器变空之后载发送。  7  *  8  * @param netif the lwip network interface structure for this ethernetif  9  * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type) 10  * @return ERR_OK if the packet could be sent 11  * an err_t value if the packet couldn't be sent 12  * 13 */ 14 static err_t 15 stellarisif_output(struct netif *netif, struct pbuf *p) 16 { 17 struct stellarisif *stellarisif = netif->state; 18  SYS_ARCH_DECL_PROTECT(lev); 19 20 /** 21  * This entire function must run within a "critical section" to preserve 22  * the integrity of the transmit pbuf queue. 23  * 24 */ 25  SYS_ARCH_PROTECT(lev); 26 27 /** 28  * Bump the reference count on the pbuf to prevent it from being 29  * freed till we are done with it. 30  * 31 */ 32  pbuf_ref(p); 33 34 /** 35  * If the transmitter is idle, and there is nothing on the queue, 36  * send the pbuf now. 37  * 38 */ 39 if(PBUF_QUEUE_EMPTY(&stellarisif->txq) && 40 ((HWREG(ETH_BASE + MAC_O_TR) & MAC_TR_NEWTX) == 0)) { 41  stellarisif_transmit(netif, p); 42  } 43 44 /* Otherwise place the pbuf on the transmit queue. */ 45 else { 46 /* Add to transmit packet queue */ 47 if(!enqueue_packet(p, &(stellarisif->txq))) { 48 /* if no room on the queue, free the pbuf reference and return error. */ 49  pbuf_free(p); 50  SYS_ARCH_UNPROTECT(lev); 51 return (ERR_MEM); 52  } 53  } 54 55 /* Return to prior interrupt state and return. */ 56  SYS_ARCH_UNPROTECT(lev); 57 return ERR_OK; 58 }
复制代码

  1、 SYS_ARCH_DECL_PROTECT(lev);、SYS_ARCH_PROTECT(lev);、SYS_ARCH_UNPROTECT(lev);由于Stellaris以太网在发送数据的时候必须处在临界区,既不能被中断打扰,因此,上面几个宏是用来关闭和打开总中断的。SYS_ARCH_DECL_PROTECT(lev)用来定义一个32位变量lev,这个变量用来保存当前中断使能信息;SYS_ARCH_PROTECT(lev)用来关闭中断;SYS_ARCH_UNPROTECT(lev)用来打开中断。

  2、pbuf_ref(p);将参数结构体pbuf的ref域加一。这个域统计有多少个指针指向这个pbuf,这些指针可能是应用程序的指针、协议栈的指针或者数据链中的pbuf-      >next指针,只有ref为0时,才可以释放这个pbuf。关于结构体pbuf的详细介绍参见博客:http://blog.youkuaiyun.com/zhzht19861011/article/details/6591252

  3、stellarisif_transmit(netif, p):发送数据。

 

  2.3  Stellaris以太网硬件底层数据过程
  当网络上一包数据到达以太网控制器后,以太网控制器会置为接收中断,在中断服务函数中,调用

  stellarisif_receive函数(需程序员根据具体硬件自行编写)来接收一个数据包,再通过

  ethernet_input函数分析接收到的数据包的类型,比如类型为0x8000,则为IP帧,会调用ip_input

  函数来将收到的这个IP数据包送往lwIP协议栈的高层。Stellaris以太网硬件具体中断函数分析见博客http://blog.youkuaiyun.com/zhzht19861011/article/details/6221699

 

转载于:https://www.cnblogs.com/wanghuaijun/p/7535434.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值