http://blog.csdn.net/ygrx/article/details/8020516

好久没有做过技术工作了,前几天因为一些需要,要在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)  //硬件抽象层初始化

这些函数里面已经实现了一部分代码,主要是硬件抽象层的代码,我们需要把具体的驱动程序添加进去,我们按调用的顺序来说一下:

当LWIP协议栈启动的时候,协议栈本身会首先调用ethernetif_init,然后在ethernetif_init函数中会调用low_level_init,在low_level_init中实际上就是你的网卡的初始化程序了,当然,为了更加结构化,你可以另外写一个函数比如DM9003_init,在low_level_init调用他,总之low_level_init中就要实现你的网卡初始化,参数就是netif,用来告诉你这个设备需要绑定一个句柄。

当协议栈要有数据发送出去的时候,协议栈会自动调用low_level_output,参数包括netif,这是的句柄,用来区分应该从哪个网卡发走,p就是数据包内容。

当有数据进来的时候,这时就和你的实现有关了,如果你底层获取数据是中断形式,那么就是中断来了以后在中断中调用ethernetif_input,然后在ethernetif_input会调用low_level_input去硬件获取数据,然后把数据通过消息队列的形式发给LWIP协议栈,所以这一部分中,low_level_input是需要实现的实际的硬件接收数据驱动程序,并且,需要驱动在适当的时候调用ethernetif_input来把数据传给上层协议栈,参数还是netif,实际上这个netif一直是个全局变量,稍后我们会说在哪里声明他。

数据接收这一部分有很大的灵活性,总的原则就是当数据确实的到达以后,由驱动在适当的时候调用ethernetif_input,而low_level_input是具体的获取数据函数,完了ethernetif_input会简单的判断一下数据类型然后决定是否传到上层协议栈。

由于OS20的速度有限,在中断中直接调用ethernetif_input时间消耗很长,我的实现是,在初始化时启动了中断模式,并且在初始化时同时启动了一个轮询线程,中断来了数据以后直接把网卡的数据存到内存中去,然后给轮询的线程发一个信号量,轮询线程先阻塞在信号量上,收到信号以后,查询该内存块,有数据的话就在线程中调用ethernetif_input把数据发给上层协议栈,然后重新阻塞住。

好了,至此,底层部分结构也说完了,现在让我们看看初始化,LWIP是如何工作的,其实上面两部分移植好了以后,要使用LWIP很简单,只有两个步骤。

1.找到netif.c,在前面声明一个全局变量: struct netif eth_netif;

然后找到void netif_init(void)函数,在该函数最后面加上(静态IP):

ip_addr_t loop_ipaddr, loop_netmask, loop_gw;

                    IP4_ADDR(&loop_gw, 192,168,3,1);  设置网关的IP    

                       IP4_ADDR(&loop_ipaddr, 192,168,3,100);设置本机IP

                       IP4_ADDR(&loop_netmask, 255,255,255,0);设置子网掩码

            /*将IP绑定到eth_netif上,并且告诉协议栈,初始化网卡的函数是ethernetif_init,网卡接收到数据以后调用tcpip_input来处理数据*/

                    netif_add(&eth_netif, &loop_ipaddr, &loop_netmask, &loop_gw, 0, ethernetif_init, tcpip_input);

                    netif_set_up(&eth_netif);  启动该网络设备

这里的eth_netif就是上一部分中的netif参数,设置成一个全局变量,驱动就能用了,具体是怎么对接的仔细看看netif_add就明白了,tcpip_input函数是协议栈自带的,不用管,填上就行。

2.在你的main函数里调用tcpip_init();记得include "tcpip.h"。

OK,结束。

如果你按这个步骤一步一步来的话,并且驱动程序正确,那么这时候你应该可以ping通你的设备了,接下来你可以试试网络上传下载的速度啦,后面的东西你就自己微调吧,怎么样,够简单吧?

LWIP轻量级TCPIP协议栈的移植的更多相关文章

  1. LwIP学习笔记——STM32 ENC28J60移植与入门

    0.前言     去年(2013年)的整理了LwIP相关代码,并在STM32上"裸奔"成功.一直没有时间深入整理,在这里借博文整理总结.LwIP的移植过程细节很多,博文也不可能一一 ...

  2. LWIP在STM32上的移植

    本文做记录摘抄,加上自己的体会: 文章标题:STM32使用LWIP实现DHCP客户端 http://www.cnblogs.com/dengxiaojun/p/4379545.html 该文章介绍了几 ...

  3. LWIP network interface 即 LWIP 的 硬件 数据 接口 移植 首先 详解 STM32 以太网数据 到达 的第一站: ETH DMA 中断函数

    要 运行  LWIP  不光 要实现  OS  的 一些 接口  ,还要 有 硬件 数据 接口 移植 ,即 网线上 来的 数据 怎么个形式 传递给  LWIP ,去解析 做出相应的 应答  ,2017 ...

  4. LWIP network interface 即 LWIP 的 硬件 数据 接口 移植 详解 STM32 以太网数据 到达 的第二站: void ethernetif_input( void * pvParameters )

    根据 上一篇 文章 , ETH  DMA 数据中断 会 发送 一个信号量 ,我使用 全局 搜索 这个信号量 s_xSemaphore 得到 一下 几个 值 根据 这个 分析  我们找到了   数据 的 ...

  5. 【lwip】04-网络数据包流向

    目录 前言 4.1 TCPIP分层与lwip数据共享 4.2 协议栈线程模型 4.3 pbuf 结构体 4.3.1 pbuf的标志位flags 4.4 pbuf的类型 4.4.1 PBUF_RAM类型 ...

  6. lwip移植到stm32上-enc28j60,103mcu(2)

    前面小玩了一下ucos和lwip,但是都还不是真正的网络多任务,真正的网络多任务应该是什么样子的呢?应该是有一个专门的任务负责网络的通讯,他负责将数据发送出去,将数据接收回来,而其他的需要用到网络的任 ...

  7. Lwip:原生态的Linux socket应用如何移植到Lwip上

    lwIP - A Lightweight TCP/IP stack 在上一篇中,我们了解到在OpenFastPath上如何移植原生态的Linux Socket应用程序,那么,对于另外一个老牌的小型TC ...

  8. LWIP移植文件介绍

    在介绍文件之前首先介绍一下DMA描述符 stm32以太网模块接收/发送FIFO和内存之间的以太网传输是通过以太网DMA使用DMA描述符完成的,一共有两个描述符列表:一个用于接收,一个用于发送, 两个列 ...

  9. LwIP应用开发笔记之一:LwIP无操作系统基本移植

    现在,TCP/IP协议的应用无处不在.随着物联网的火爆,嵌入式领域使用TCP/IP协议进行通讯也越来越广泛.在我们的相关产品中,也都有应用,所以我们结合应用实际对相关应用作相应的总结. 1.技术准备 ...

随机推荐

  1. 用js实现图片连播和联级菜单的实现

    <!DOCTYPE html> <html> <head> <title>图片轮播</title> <style> div{ b ...

  2. hdu 5001(概率DP)

    Walk Time Limit: 30000/15000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Subm ...

  3. linux中使用vim编译C++程序

    Vi三种模式详解 命令行模式 (command mode/一般模式) 任何时候,不管用户处于何种模式,只要按一下“ESC”键,即可使Vi进入命令行模式:我们在shell环境(提示符为$)下输入启动Vi ...

  4. Linux 基础——权限管理命令chown、chgrp

    一.chown命令与chgrp命令的作用 有时你需要改变文件或目录的属主,比如有人离职或开发人员创建了一个在测试或生产环境中需要归属在系统账户下的应用.Linux提供了两个命令来实现这个功能:chow ...

  5. MINIBASE源代码阅读笔记之heapfile

    Heapfile 用来管理heap file里的dir page们 成员 _firstDirPageId:这个文件的第一个dir page _ftype:文件类型 _file_deleted:删除的时 ...

  6. <<Javascript Patterns>>阅读笔记 -- 第2章 基本技巧(二)

    关于for-in循环 循环数据时, 强烈不推荐使用for-in循环.因为当Array对象被扩展后, 再用for-in循环遍历数据会导致逻辑上的错误, 举例说明: var arr = ['a', 'b' ...

  7. Vuex总结

    Vuex官网链接:https://vuex.vuejs.org/zh-cn/strict.html Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式. 它采用集中式存储管理应用的所有组件 ...

  8. 转:vs无法调试解决方案

    转:http://blog.csdn.net/jean7155/article/details/48223739 vs中无法加入断点进行调试的解决方案 [ 1] 以前也遇到过同样的问题,但没有问个为什 ...

  9. hihocoder 1500 EL SUENO

    树上背包. 简单的树形$dp$,计算出摧毁每一个节点所需的最小费用,背包即可. #include<bits/stdc++.h> using namespace std; struct X ...

  10. Python并发编程-一个简单的爬虫

    一个简单的爬虫 #网页状态码 #200 正常 #404 网页找不到 #502 504 import requests from multiprocessing import Pool def get( ...