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. SLD 官方实例

    基于xml标准的sld格式: <?xml version="1.0" encoding="UTF-8"?> <StyledLayerDescr ...

  2. curd 插件

    1. Django项目启动 自动加载文件 制作启动文件 . 注册strak 在apps.py 类里面增加如下 def ready(self): from django.utils.module_loa ...

  3. Codefroces 919D Substring(拓扑排序+DP)

    题目链接:http://codeforces.com/problemset/problem/919/D 题目大意:给你一张有向图,给你每个顶点上的字母和一些边,让你找出一条路径,路径上的相同字母数最多 ...

  4. HDU 2181 哈密顿绕行世界问题 (DFS)

    题目链接:https://vjudge.net/contest/185350#problem/C 题目大意:一个规则的实心十二面体,它的 20个顶点标出世界著名的20个城市,你从一个城市出发经过每个城 ...

  5. 下划线css

    /* <div class="text">header</div> */ .text { /* 作用元素 */ display: inline-block; ...

  6. 安卓使用WebView清除缓存

    原文:https://blog.csdn.net/liwei123liwei123/article/details/52624826 Android 清除WebView缓存 最近项目中需要用WebVi ...

  7. [实战]MVC5+EF6+MySql企业网盘实战(24)——视频列表

    写在前面 上篇文章实现了文档列表,所以实现视频列表就依葫芦画瓢就行了. 系列文章 [EF]vs15+ef6+mysql code first方式 [实战]MVC5+EF6+MySql企业网盘实战(1) ...

  8. jQuery 最简化实现

    window.jQuery = (selector) => { let nodes = {}; if (typeof selector === 'string') { //是字符串 let te ...

  9. Python全栈开发之7、模块和几种常见模块以及format知识补充

    一.模块的分类 Python流行的一个原因就是因为它的第三方模块数量巨大,我们编写代码不必从零开始重新造轮子,许多要用的功能都已经写好封装成库了,我们只要直接调用即可,模块分为内建模块.自定义的模块. ...

  10. loadrunner中Run-time-Setting设置