好久没有做过技术工作了,前几天因为一些需要,要在ST的OS20平台上进行了LWIP的移植,有一些心得,写出来供大家参考。
LWIP的背景我就不介绍了,相信能看到这篇**的人都对其背景有过了解了。 LWIP的模块化还是很强的,所以移植起来没有想象的那么多困难,一个协议栈在某个平台上移植,其实主要来说包括两个大的部分接口,注意一下,我使用的是最新的LWIP1.4.0。 1.与系统相关的接口,比如多线程,信号量,互斥锁,系统时间等,当然,LWIP还有一种无操作系统模式,那这些接口就不用实现了。 2.与硬件相关的接口,实际上就是网卡驱动啦,当然,如果你的物理层不是网卡而是别的什么,那就需要的是另外那个硬件设备的驱动啦,比如Docsis设备之类的。
好了,开始移植吧。 首先是操作系统部分: 1.opt.h 首先看这个文件,这个文件里面包含了LWIP的模块选项,可以在这里选择哪些模块需要编译,那些模块不编译,分成几个部分,mem,arp,icmp,igmp,ppp,dhcp等,这里可以根据自己的需要修改编译选项,如果是带操作系统的,还要修改栈空间,优先级之类的选项。 2.cc.h 这个文件是没有的,你需要建立一个目录arch,然后在下面添加cc.h,这里里面主要是一些定义,包括数据类型,大小头端之类的,我的cc.h比较简单
/*数据类型宏定义*/ typedef unsigned char u8_t; typedef unsigned short u16_t; typedef unsigned int u32_t; typedef char s8_t; typedef short s16_t; typedef int s32_t; typedef int mem_ptr_t;
typedef semaphore_t sys_sem_t; typedef mutex_t sys_mutex_t; typedef message_queue_t sys_mbox_t; typedef u8_t sys_thread_t; #define BYTE_ORDER LITTLE_ENDIAN //大小头端选择 /*调试信息宏定义*/ #define U16_F "hu" #define S16_F "d" #define X16_F "hx" #define U32_F "u" #define S32_F "d" #define X32_F "x" #define SZT_F "uz"
就这些。
3.arch.h 这个地方需要注意就是字节对齐,字节对齐在某些编译器上是用的#pragam pack(n)来实现的,那需要把相应的宏定义修改:
#ifndef PACK_STRUCT_BEGIN #define PACK_STRUCT_BEGIN #pragma pack(1) #endif /* PACK_STRUCT_BEGIN */ #ifndef PACK_STRUCT_END #pragma pack() #define PACK_STRUCT_END //#
而ST的st2cc编译器是在编译的时候加了个参数来进行字节对齐的,所以这里不用修改
4.sys.h 这个文件就比较关键了,这是你移植过程中要写最多代码的地方,在LWIP中,如果是有操作系统的编译版本,那么需要实现线程和与线程通信的相关代码,其中包括:线程建立,互斥锁,信号量,消息队列(一般叫MailBox),系统当前时间,需要实现的东西都在sys.h中,一般情况下,我们为了文件清晰,会在arch目录下新建一个文件sys_arch.c,当然,你也可以直接在根目录下建立这个文件,注意的是要在该文件中include sys.h,然后你就开始挨个实现sys.h中的系统函数吧,实际上也挺简单,就是把系统的API调用封装成LWIP的模式。 线程部分:
sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio) 线程建立 互斥锁:
err_t sys_mutex_new(sys_mutex_t *mutex) 新建锁 void sys_mutex_lock(sys_mutex_t *mutex) 获取锁 void sys_mutex_unlock(sys_mutex_t *mutex) 释放锁 void sys_mutex_free(sys_mutex_t *mutex) 删除锁 int sys_mutex_valid(sys_mutex_t *mutex) 查询锁是否可用 void sys_mutex_set_invalid(sys_mutex_t *mutex) 设置锁为失效 信号量:
err_t sys_sem_new(sys_sem_t *sem, u8_t count) 新建信号量 void sys_sem_signal(sys_sem_t *sem) 发送信号量 u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout) 等待信号量 void sys_sem_free(sys_sem_t *sem) 删除信号量 int sys_sem_valid(sys_sem_t *sem) 查询是否可用 void sys_sem_set_invalid(sys_sem_t *sem) 设置为失效 消息队列(MailBox):
这个东西稍微复杂一点,如果操作系统的API中没有现成的消息队列机制,那么你需要自己实现,其实无非就是个链表的带锁操作,在OS20中是自己带有MailBox的API的,所以我也没自己实现,直接使用了,需要实现的函数在sys.h中也都有,我就不列举了。
系统时间: 这个我返回的是系统调用的系统时间,就是系统启动后多少秒
u32_t sys_now(void) 好了,这几个文件都实现完了,那基本上操作系统部分就差不多了,我的建议是,在opt.h和cc.h修改完以后,你就开始编译,如果有错误几乎都应该是没有include某个文件造成的,及时修改错误,然后直到编译得没有错误了,再来添加sys_arch.c,这样可以减少一些麻烦。 总得来说就是,首先,你要修改opt.h,把你需要的模块添加上去,把栈空间,mailbox大小之类的定义好,然后编译,因为LWIP是标准的C写的,理论上应该很容易编译成功,我只是有两个地方没有.h文件,添加一下就编译过了。 当然,编译过了只是第一步,完了以后就要手写sys_arch.c了,sys.h里面的所有函数都要实现,当然,你要偷懒的话有些也可以直接返回。这个文件是和你系统API相关的,写好以后记得加入到Makefile里面,然后再编译,找错误,编译,直到成功。 其实还有临界保护,主要是
#define SYS_ARCH_DECL_PROTECT(lev) #define SYS_ARCH_PROTECT(lev) #define SYS_ARCH_UNPROTECT(lev) 这三个宏,定义成你的系统的临界保护调用就行了,我偷懒定义的空的,暂时没发现问题。
好了,操作系统部分大概就这么一些,接下来就是底层的硬件驱动了,驱动的具体实现我就不说了,各自的平台不一样,网卡硬件也不一样,但是基本的三个东西一定要有,1.初始化网卡,2.接收数据,3.发送数据。 在LWIP1.4.0中,其实已经跟我们写好驱动的框架,我们所需要做的就是填充这个框架。框架对应的文件是:ethernetif.c 打开这个文件后你会发现这个文件被#if 0包起来了,首先改成#if 1,这样,你会发现这个文件中有如下几个函数:
static void low_level_init(struct netif *netif) //底层硬件初始化
static err_t low_level_output(struct netif *netif, struct pbuf *p) //底层硬件发送数据函数
static struct pbuf * low_level_input(struct netif *netif) //底层硬件接收数据函数
err_t ethernetif_input(struct pbuf *pp,struct netif *netif) //硬件抽象层接收数据
err_t ethernetif_init(struct netif *netif) //硬件抽象层初始化
|