1.根目录对比情况
从解压出来的文件来看,在文件结构上来看,两个版本并没有很大的区别,2.1.2版本多了FEATURES和CMakeList.txt两个文件。(是因为后续的版本都采用git的缘故)。这篇文章,主要是针对两个版本的源文件的不同进行分析。
2.src源文件目录
从文件夹的结构来看,2.1.2版本的源文件比1.4.1的多了一个apps的文件夹。在2.1.2中,lwip增加了很多上层的应用,方便开发者直接使用。主要包括常见的应用程序,如 httpd、 mqtt、tftp、 sntp、 snmp 等。
2.1 netif文件夹比较
文件夹里面是与网卡移植有关的文件, 这些文件为我们移植网卡提供了模板,我们可以直接使用。在1.4.1中提供了ethernetif.c源文件,该文件是主要跟网卡底层驱动相关的,在2.1.2中该文件以及不提供在lwip源码里面了,它被挪到了contrib-2.1.0.zip包里,这个在官网也提供了,目前版本更新到了2.1.0。包里面装的主要是 LwIP 内核的源码文件,而 contrib 包里面装的是移植和应用 LwIP 的一些 demo,即应用示例。 contrib 包不属于 LwIP 内核的一部分,里面的很多内容来自开源社区的贡献。
2.2 include文件夹比较
该文件夹是LwIP 所有模块对应的头文件,1.4.2的ipv6和ipv4文件夹已经归并在include目录下的lwip了
而compat主要是跟不同的操作系统提供的一些兼容的头文件。
2.3 core文件夹比较
从本质上来说,这部分内核要实现的功能是大体一致的。
2.4 api文件夹比较
api 文件夹里面装的是 NETCONN API 和 Socket API 相关的源文件,只有在操作系统的环境中,才能被编译。如果跑lwip不用说操作系统的话,这个文件夹里面的内容其实是不用添加的。
3.从1.4.1升级到2.1.2,该怎么做?
这里的话,只是针对带操作系统的平台进行分析,仅以开发板的网络Demo进行讲解,这里的硬件平台为stm32f407+lan8720,操作系统为ucos。
1.准备工作,将开发板中的1.4.1工程拷贝出来。工程示意如下:
2.在工程中删除上述文件(注意保留跟操作系统和硬件接口相关的文件,sys_arch.c/.h,ethernetif.c,cc.h,pref.h),红色框框里全部是跟lwip相关的。现在我们要将它们替换成2.1.2版本的。
在工程里添加头文件
其中cc.h,perf.h是从contrib-2.1.0包拷贝而来的。
对于原始工程1.4.1的来说,cc.h主要是定义数据类型,在新版本中已经arch.h中进行定义
所以我们要注销这部分代码
1.4.1版本cc.h源代码
#ifndef __CC_H__
#define __CC_H__
#include <stdio.h> //使用printf函数,需包含stdio.h
#include "includes.h" //使用UCOS 要添加此头文件!
#define BYTE_ORDER LITTLE_ENDIAN //小端模式
#define LWIP_PROVIDE_ERRNO 1 //使用lwip/arch.h头文件来定义这些编码
//LwIP使用的数据类型定义――u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。
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;
//与编译器相关的LwIP结构体宏
#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函数
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0)
#endif
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif
#if OS_CRITICAL_METHOD == 3
#define SYS_ARCH_DECL_PROTECT(lev) u32_t lev
#define SYS_ARCH_PROTECT(lev) lev = OS_CPU_SR_Save() //UCOS II中进入临界区,关中断
#define SYS_ARCH_UNPROTECT(lev) OS_CPU_SR_Restore(lev) //UCOS II中退出A临界区,开中断
#endif
#endif /* __CC_H__ */
2.1.2中cc.h的源码,只需要在1.4.1中的代码基础上,注销掉以下代码即可
注销的代码段已经在arch.h文件中进行定义了
#ifndef __CC_H__
#define __CC_H__
#include <stdio.h> //使用printf函数,需包含stdio.h
#include "includes.h" //使用UCOS 要添加此头文件!
#define BYTE_ORDER LITTLE_ENDIAN //小端模式
#define LWIP_PROVIDE_ERRNO 1 //使用lwip/arch.h头文件来定义这些编码
// 2.1.2的库已经在arch.h中进行定义,这里的注销掉就好了
//LwIP使用的数据类型定义――u8_t, s8_t, u16_t,s16_t,u32_t,s32_t,mem_ptr_t。
//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;
//与编译器相关的LwIP结构体宏
#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函数
#ifndef LWIP_PLATFORM_ASSERT
#define LWIP_PLATFORM_ASSERT(x) do { if(!(x)) while(1); } while(0)
#endif
#ifndef LWIP_PLATFORM_DIAG
#define LWIP_PLATFORM_DIAG(x) do {printf x;} while(0)
#endif
#if OS_CRITICAL_METHOD == 3
#define SYS_ARCH_DECL_PROTECT(lev) u32_t lev
#define SYS_ARCH_PROTECT(lev) lev = OS_CPU_SR_Save() //UCOS II中进入临界区,关中断
#define SYS_ARCH_UNPROTECT(lev) OS_CPU_SR_Restore(lev) //UCOS II中退出A临界区,开中断
#endif
#endif /* __CC_H__ */
perf.h文件,实际上没有用到
ethernetif.c和sys_arch.c/sys_arch.h从本质上没有太大变化,只是在实际编译的过程中会出现一个错误
Error: L6218E: Undefined symbol sys_mbox_trypost_fromisr (referred from tcpip.o).
这个是因为在2.1.2.版本中新增加一个在中断中进行邮箱的发送,我们只需要重新编写一下这个函数即可
源码如下:
//向一个消息邮箱发送消息(发送一次,成功与否都不阻塞进程)
//*mbox:消息邮箱
//*msg:要发送的消息
//返回值:ERR_OK,发送OK
// ERR_MEM,发送失败
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg)
{
if((OSQPost((*mbox)->pst, msg))!=OS_ERR_NONE)return ERR_MEM;
return ERR_OK;
}
//新版本2.1.2增加在中断中发送邮箱的函数,我们需要对该函数进行实现
//直接跟 sys_mbox_trypost 函数一样实现即可。
err_t sys_mbox_trypost_fromisr(sys_mbox_t *mbox, void *msg)
{
if((OSQPost((*mbox)->pst, msg))!=OS_ERR_NONE)return ERR_MEM;
return ERR_OK;
}
整个过程大概就是这样了。顺便说一下移植过程中,编译出现的问题
1.V1.4.1中的struct ip_addr在新版中改为ip_addr_t,此处要作相应修改
2.这个是因为LWIP_RAND在V1.4.1中是常量宏,定义在lwipopts.h中,而到了V2.1.2,它是宏函数的形式LWIP_RAND(),定义在cc.h中,所以出现这么个提示。本例在sys_arch.c中定义了_LWIP_RAND(),使用系统的tick计数作为随机数,因此在cc.h中定义#define LWIP_RAND() _LWIP_RAND() 即可。另外一种解决办法就是不用DNS.
3.因为没有实现互斥锁带来的问题
4.Error: L6218E: Undefined symbol errno (referred from if_api.o).
在ethernetif.c中定义即可。
后续工作:
将操作ucos替换成rt-thread的。
参考文档:
《野火lwip应用开发指南》