LwIP协议栈源码详解

LwIP协议栈源码详解

——TCP/IP协议的实现

Created by.. 老衲五木

at.. UESTC

Contact me.. for_rest@https://www.360docs.net/doc/ab14372109.html,

540535649@https://www.360docs.net/doc/ab14372109.html,

前言

最近一个项目用到LwIP,恰好看到网上讨论的人比较多,所以有了写这篇学习笔记的冲动,一是为了打发点发呆的时间,二是为了吹过的那些NB。往往决定做一件事是简单的,而坚持做完这件事却是漫长曲折的,但终究还是写完了,时间开销大概为四个月,内存开销无法估计。。

这篇文章覆盖了LwIP协议大部分的内容,但是并不全面。它主要讲解了LwIP协议最重要也是最常被用到的部分,包括内存管理,底层网络接口管理,ARP层,IP层,TCP层,API 层等,这些部分是LwIP的典型应用中经常涉及到的。而LwIP协议的其他部分,包括UDP,DHCP,DNS,IGMP,SNMP,PPP等不具有使用共性的部分,这篇文档暂时未涉及。

原来文章是发在空间中的,每节每节依次更新,后来又改发为博客,再后来就干脆懒得发了。现在终于搞定,于是将所有文章汇总。绞尽脑汁的想写一段空前绝后,人见人爱的序言,但越写越觉得像是猫儿抓的一样。就这样,PS:由于本人文笔有限,情商又低,下里巴人一枚,所以文中的很多语句可能让您很纠结,您可以通过邮箱与我联系。共同探讨才是进步的关键。

最后,欢迎读者以任何方式使用与转载,但请保留作者相关信息,酱紫!码字。。。世界上最痛苦的事情莫过于此。。。

——老衲五木

目录

1 移植综述------------------------------------------------------------------------------------------------------4

2 动态内存管理------------------------------------------------------------------------------------------------6

3 数据包pbuf--------------------------------------------------------------------------------------------------9

4 pbuf释放---------------------------------------------------------------------------------------------------13

5 网络接口结构-----------------------------------------------------------------------------------------------16

6 以太网数据接收--------------------------------------------------------------------------------------------20

7 ARP表-----------------------------------------------------------------------------------------------------23

8 ARP表查询-----------------------------------------------------------------------------------------------26

9 ARP层流程-----------------------------------------------------------------------------------------------28

10 IP层输入-------------------------------------------------------------------------------------------------31

11 IP分片重装1--------------------------------------------------------------------------------------------34

12 IP分片重装2--------------------------------------------------------------------------------------------37

13 ICMP处理-----------------------------------------------------------------------------------------------40

14 TCP建立与断开----------------------------------------------------------------------------------------43

15 TCP状态转换-------------------------------------------------------------------------------------------46

16 TCP控制块----------------------------------------------------------------------------------------------49

17 TCP建立流程-------------------------------------------------------------------------------------------53

18 TCP状态机----------------------------------------------------------------------------------------------56

19 TCP输入输出函数1-----------------------------------------------------------------------------------60

20 TCP输入输出函数2-----------------------------------------------------------------------------------63

21 TCP滑动窗口-------------------------------------------------------------------------------------------66

22 TCP超时与重传----------------------------------------------------------------------------------------69

23 TCP慢启动与拥塞避免-------------------------------------------------------------------------------73

24 TCP快速恢复重传和Nagle算法-------------------------------------------------------------------76

25 TCP坚持与保活定时器-------------------------------------------------------------------------------80

26 TCP定时器----------------------------------------------------------------------------------------------84

27 TCP终结与小结----------------------------------------------------------------------------------------88

28 API实现及相关数据结构-----------------------------------------------------------------------------91

29 API消息机制--------------------------------------------------------------------------------------------94

30 API函数及编程实例-----------------------------------------------------------------------------------97

1 移植综述

如果你认为所谓的毅力是每分每秒的“艰苦忍耐”式的奋斗,那这是一种很不足的心理状态。毅力是一种习惯,毅力是一种状态,毅力是一种生活。看了这么久的代码觉得是不是该写点东西了,不然怎么对得起某人口中所说的科研人员这个光荣称号。初见这如山如海的代码,着实看出了一身冷汗。现在想想其实也不是那么难,那么多革命先辈经过N长时间才搞出来的东东怎么可能让你个毛小子几周之内搞懂。我见到的只是冰川的一小角,万里长征的一小步,九头牛身上的一小毛…再用某人的话说,写吧,昏写,瞎写,胡写,乱写,写写就懂了。

我想我很适合当一个歌颂者,青春在风中飘着。你知道,就算大雨让这座城市颠倒,我会给你怀抱;受不了,看见你背影来到,写下我度秒如年难捱的离骚;就算整个世界被寂寞绑票,我也不会奔跑;逃不了,最后谁也都苍老,写下我,时间和琴声交错的城堡。我正在听的歌。扯远了…

正题,嵌入式产品连入Internet网,这个MS是个愈演愈烈的趋势。想想,你可以足不出户对你的产品进行配置,并获取你关心的数据信息,多好。这也许也是物联网世界最基本的雏形。当然,你的产品要有如此功能,那可不容易,至少它得有个目前很Fashion的TCP/IP 协议栈。LWIP是一套用于嵌入式系统的开放源代码TCP/IP协议栈。在你的嵌入式处理器不是很NB,内部Flash和Ram不是很强大的情况下,用它还是很合适滴。

LWIP的设计者为像我这样的懒惰者提供了详细的移植说明文档,当然这还不够,他们还尽可能的包揽了大部分工作,懒人们只要做很少的工作就功德圆满了。纵观整个移植过程,使用者需要完成以下几个方面的东西:

首先是LWIP协议内部使用的数据类型的定义,如u8_t,s8_t,u16_t,u32_t等等等等。由于所移植平台处理器的不同和使用的编译器的不同,这些数据类型必须重新定义。想想,一个int型数据在64位处理器上其长度为8个字节,在32位处理器上为4个字节,而在16位处理器上就只有两个字节了。因此这部分需要使用者根据处理器位数和和使用的编译器的特点来编写。所以在ARM7处理器上使用的typedef unsigned int u32_t移植语句用在64位处理器的移植过程中那肯定是行不通的了。

其次是实现与信号量和邮箱操作相关的函数,比如建立、删除、等待、释放等。如果在裸机上直接跑LWIP,这点实现起来比较麻烦,使用者必须自己去建立一套信号量和邮箱相关的机制。一般情况下,在使用LWIP的嵌入式系统中都会有操作系统的支持,而在操作系统中信号量和邮箱往往是最基本的进程通信机制了。UC/OSII应该算是最简单的嵌入式操作系统了吧,它也无例外的能够提供信号量和邮箱机制,只要我们将UC/OSII中的相关函数做相应的封装,就可满足LWIP的需求。LWIP使用邮箱和信号量来实现上层应用与协议栈间、下层硬件驱动与协议栈间的信息交互。LWIP协议模拟了TCP/IP协议的分层思想,表面上看LWIP也是有分层思想的,但从实现上看,LWIP只在一个进程内实现了各个层次的所有工作。具体如下:LWIP完成相关初始化后,会阻塞在一个邮箱上,等待接收数据进行处理。这个邮箱内的数据可能来自底层硬件驱动接收到的数据包,也可能来自应用程序。当在该邮箱内取得数据后,LWIP会对数据进行解析,然后再依次调用协议栈内部上层相关处理函数处理数据。处理结束后,LWIP继续阻塞在邮箱上等待下一批数据。当然LWIP还有一大串的内存管理机制用以避免在各层间交互数据时大量的时间和内存开销,这将在后续讲解中慢慢道来。当然,但这样的设计使得代码理解难度加大,这一点让人头大。信号量也

可以用在应用程序与协议栈的互相通信中。比如,应用程序要发送数据了,它先把数据发到LWIP阻塞的邮箱上,然后它挂起在一个信号量上;LWIP从邮箱上取得数据处理后,释放一个信号量,告诉应用程序,你要发的数据我已经搞定了;此后,应用程序得到信号量继续运行,而LWIP继续阻塞在邮箱上等待下一批处理数据。

其其次,就是与等待超时相关的函数。上面说到LWIP协议栈会阻塞在邮箱上等待接收数据的到来。这种等待在外部看起来是一直进行的,但其实不然。一般在初始化LWIP进程的时候,都会同时的初始化一些超时事件,即当某些事件等待超时后,它们会自动调用一些超时处理函数做相关处理,以满足TCP/IP协议栈的需求。这样看来,当LWIP协议栈阻塞等待邮箱之前,它会精明的计算到底应该等待多久,如果LWIP进程中没有初始化任何超时事件,那好,这种情况最简单了,永远的挂起进程就可以了,这时的等待就可以看做是天长地久的….有点暧昧了。如果LWIP进程中有初始化的超时事件,这时就不能一直等了,因为这样超时事件没有任何被执行的机会。LWIP是这样做的,等待邮箱的时间设置为第一个超时事件的时间长度,如果时间到了,还没等到数据,那好,直接跳出邮箱等待转而执行超时事件,当执行完成超时事件后,再按照上述的方法继续阻塞邮箱。可以看出,对一个LWIP 进程,需要用一个链表来管理这些超时事件。这个链表的大部分工作已经被LWIP的设计者完成了,使用者只需要实现的仅有一个函数:该函数能够返回当前进程个超时事件链表的首地址。LWIP内部协议要利用该首地址来查找完成相关超时事件。

其其其次,如果LWIP是建立在多线程操作系统之上的话,则要实现创建一个新线程的函数。不支持多线程的操作系统,汗…表示还没听过。不过UC/OSII显然是支持多线程的,地球人都知道。这样一个典型的LWIP应用系统包括这样的三个进程:首先启动的是上层应用程序进程,然后是LWIP协议栈进程,最后是底层硬件数据包接收发送进程。通常LWIP 协议栈进程是在应用程序中调用LWIP协议栈初始化函数来创建的。注意LWIP协议栈进程一般具有最高的优先级,以便实时正确的对数据进行响应。

其其其其次,其他一些细节之处。比如临界区保护函数,用于LWIP协议栈处理某些临界区时使用,一般通过进临界区关中断、出临界区开中断的方式来实现;又如结构体定义时用到的结构体封装宏,LWIP的实现基于这样一种机制,即上层协议已经明确知道了下层所传上来的数据的结构特点,上层直接使用相关取地址计算得到想要的数据,而避免了数据递交时的复制与缓冲,所以定义结构体封装宏,禁止编译器的地址自动对齐是必须的;还有诸如调试输出、测量记录方面的宏不做讲解。

最后,也是比较重要的地方。底层网络驱动函数的实现。这取决于你嵌入式硬件系统所使用的网络接口芯片,也就是网卡芯片,常见的有RTL8201BL、ENC28J60等等。不同的接口芯片厂商都会提供丰富的驱动函数。我们只要将这些发送接收接口函数做相应的封装,将接收到得数据包封装为LWIP协议栈熟悉的数据结构、将发送的数据包分解为芯片熟悉的数据结构就基本搞定了。最起码的,发送一个数据包函数和接收一个数据包函数需要被实现。

那就这样了吧,虽然写得草草,但终于在撤退之前搞定。好的开始是成功的一半,那这暂且先算四分之一吧。不晓得一个月、两个月或者更多时间能写完否。预知后事如何,请见下回分解。

2 动态内存管理

最近电力局很不给力啊,隔三差五的停电,害得我们老是痛苦的双扣斗地主,不带这样的啊!今天还写吗?写,必须的。

昨天把LWIP的移植工作框架说了一下,网上也有一大筐的关于移植细节的文档。有兴趣的童鞋不妨去找找。这里,我很想探究LWIP内部协议实现的细节,以及所有盘根错节的问题的来龙去脉。以后的讨论研究将按照LWIP英文说明文档《Design and Implementation of the LWIP:TCP/IP Stack》的结构组织展开。

这里讨论LWIP的动态内存管理机制。

总的来说,LWIP的动态内存管理机制可以有三种:C运行时库自带的内存分配策略、动态内存堆(HEAP)分配策略和动态内存池(POOL)分配策略。

动态内存堆分配策略和C运行时库自带的内存分配策略具有很大的相似性,这是LWIP 模拟运行时库分配策略实现的。这两种策略使用者只能从中选择一种,这通过头文件lwippools.h中的宏定义MEM_LIBC_MALLOC来实现的,当它被定义为1时则使用标准C 运行时库自带的内存分配策略,而为0时则使用LWIP自身的动态内存堆分配策略。一般情况下,我们选择使用LWIP自身的动态内存堆分配策略,这里不对C运行时库自带的内存分配策略进行讨论。

同时,动态内存堆分配策略可以有两种实现方式,纠结….第一种就是如前所述的通过开辟一个内存堆,然后通过模拟C运行时库的内存分配策略来实现。第二种就是通过动态内存池的方式来实现,也即动态内存堆分配函数通过简单调用动态内存池(POOL)分配函数来完成其功能(太敷衍了事了),在这种情况下,用户需要在头文件lwippools.h中定义宏MEM_USE_POOLS和MEM_USE_CUSTOM_POOLS为1,同时还要开辟一些额外的缓冲池区,如下:

LWIP_MALLOC_MEMPOOL_START

LWIP_MALLOC_MEMPOOL(20, 256)

LWIP_MALLOC_MEMPOOL(10, 512)

LWIP_MALLOC_MEMPOOL(5, 1512)

LWIP_MALLOC_MEMPOOL_END

这几句摘自LWIP源码注释部分,表示为动态内存堆相关功能函数分配20个256字节长度的内存块,10个512字节的内存块,5个1512字节的内存块。内存池管理会根据以上的宏自动在内存中静态定义一个大片内存用于内存池。在内存分配申请的时候,自动根据所请求的大小,选择最适合他长度的池里面去申请,如果启用宏MEM_USE_POOLS_TRY_BIGGER_POOL,那么,如果上述的最适合长度的池中没有空间可以用了,分配器将从更大长度的池中去申请,不过这样会浪费更多的内存。晕乎乎…..就这样了,这种方式一般不会被用到。哎,就最后这句话给力。

下面讨论动态内存堆分配策略的第一种实现方式,这也是一般情况下被使用的方式。这部分讨论主要参照网上Oldtom’s Blog,TA写得很好(但是也有一点小小的错误),所以一不小心被我借用了。

动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。分配的内存块有个最小大小的限

制,要求请求的分配大小不能小于MIN_SIZE,否则请求会被分配到MIN_SIZE大小的内存空间。一般MIN_SIZE为12字节,在这12个字节中前几个字节会存放内存分配器管理用的私有数据,该数据区不能被用户程序修改,否则导致致命问题。内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。对于动态内存的使用,比较推荐的方法就是分配->释放->分配->释放,这种使用方法能够减少内存碎片。下面具体来看看LWIP是怎么来实现这些函数的。

mem_init( ) 内存堆的初始化函数,主要是告知内存堆的起止地址,以及初始化空闲列表,由lwip初始化时自己调用,该接口为内部私有接口,不对用户层开放。

mem_malloc( ) 申请分配内存。将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是NULL,分配的空间大小会收到内存对齐的影响,可能会比申请的略大。返回的内存是“没有“初始化的。这块内存可能包含任何随机的垃圾,你可以马上用有效数据或者至少是用零来初始化这块内存。内存的分配和释放,不能在中断函数里面进行。内存堆是全局变量,因此内存的申请、释放操作做了线程安全保护,如果有多个线程在同时进行内存申请和释放,那么可能会因为信号量的等待而导致申请耗时较长。

mem_calloc( ) 是对mem_malloc( )函数的简单包装,他有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小,与mem_malloc()不同的是它会把动态分配的内存清零。有经验的程序员更喜欢使用mem_ calloc (),因为这样的话新分配内存的内容就不会有什么问题,调用mem_ calloc ()肯定会清0,并且可以避免调用memset()。

休息…………

动态内存池(POOL)分配策略可以说是一个比较笨的分配策略了,但其分配策略实现简单,内存的分配、释放效率高,可以有效防止内存碎片的产生。不过,他的缺点是会浪费部分内存。

为什么叫POOL?这点很有趣,POOL有很多种,而这点依赖于用户配置LWIP的方式。例如用户在头文件opt.h文件中定义LWIP_UDP为1,则在编译的时候与UDP类型内存池就会被建立;定义LWIP_TCP为1,则在编译的时候与TCP类型内存池就会被建立。另外,还有很多其他类型的内存池,如专门存放网络包数据信息的PBUF_POOL、还有上面讲解动态内存堆分配策略时提到的CUSTOM_POOLS等等等等。某种类型的POOL其单个大小是固定的,而分配该类POOL的个数是可以用户配置的,用户应该根据协议栈实际使用状况进行配置。把协议栈中所有的POOL挨个放到一起,并把它们放在一片连续的内存区域,这呈现给用户的就是一个大的缓冲池。所以,所谓的缓冲池的内部组织应该是这样的:开始处放了A类型的POOL池a个,紧接着放上B类型的POOL池b个,再接着放上C类型的POOL池c个….直至最后N类型的POOL池n个。这一点很像UC/OSII中进程控制块和事件控制块,先开辟一堆各种类型的放那,你要用直接来取就是了。注意,这里的分配必须是以单个缓冲池为基本单位的,在这样的情况下,可能导致内存浪费的情况。这是很明显的啊,不解释。

下面我来看看在LWIP实现中是怎么开辟出上面所论述的大大的缓冲池的(‘的’这个字,今天让我们一群人笑了很久)。基本上绝大部分人看到这部分代码都会被打得晕头转向,完全不晓得作者是在干啥,但是仔细理解后,你不得不佩服作者超凡脱俗的代码写能力,差一点用了沉鱼落雁这个词,罪过。上代码:

static u8_t memp_memory static u8_t memp_memory [ MEM_ALIGNMENT MEM_ALIGNMENT -- 1 1

#define LWIP_MEMPOOL(name,num,size,desc) +#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) ) #include "lwip/memp_std.h"#include "lwip/memp_std.h"

];

上面的代码定义了缓冲池所使用的内存缓冲区,很多人肯定会怀疑这到底是不是一个数组的定义。定义一个数组,里面居然还有define 和include 关键字。解决问题的关键就在于头文件memp_std.h ,它里面的东西可以被简化为诸多条LWIP_MEMPOOL(name,num,size,desc)。又由于用了define 关键字将LWIP_MEMPOOL(name,num,size,desc)定义为

+((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))),所以,memp_std.h 被编译后就为一条一条的+(),+(),+(),+()….所以最终的数组memp_memory 等价定义为:

static u8_t memp_memory [ MEM_ALIGNMENT – 1

+()

+()….];

如果此刻你还没懂,只能说明我的表述能力有问题了。当然还有个小小的遗留问题,为什么数组要比实际需要的大MEM_ALIGNMENT – 1?作者考虑的是编译器的字对齐问题,到此打住,这个问题不能深究啊,以后慢慢讲。

复制上面的数组建立的方法,协议栈还建立了一些与缓冲池管理的全局变量:

memp_num :这个静态数组用于保存各种类型缓冲池的成员数目

memp_sizes :这个静态数组用于保存各种类型缓冲池的结构大小

memp_tab :这个指针数组用于指向各种类型缓冲池当前空闲节点

接下来就是理所当然的实现函数了:

memp_init ():内存池的初始化,主要是为每种内存池建立链表memp_tab ,其链表是逆序的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。

memp_malloc ():如果相应的memp_tab 链表还有空闲的节点,则从中切出一个节点返回,否则返回空。

memp_free ():把释放的节点添加到相应的链表memp_tab 头上。

从上面的三个函数可以看出,动态内存池分配过程时相当的简洁直观啊。

HC:百度说是胡扯的意思。哈哈……

3 数据包pbuf

高的地方,总是很冷。孤独,可以让人疯狂。没人能懂你!昨天讲过了LWIP的内存分配机制。再来总之一下,LWIP中常用到的内存分配策略有两种,一种是内存堆分配,一种是内存池分配。前者可以说能随心所欲的分配我们需要的合理大小的内存块(又是‘的’),缺点是当经过多次的分配释放后,内存堆中间会出现很多碎片,使得需要分配较大内存块时分配失败;后者分配速度快,就是简单的链表操作,因为各种类型的POOL是我们事先建立好的,但是采用POOL会有些情况下会浪费掉一定的内存空间。在LWIP中,将这两种分配策略混合使用,达到了很好的内存使用效率。

下面我们将来看看LWIP中是怎样合理利用这两种分配策略的。这就顺利的过渡到了这节要讨论的话题:LWIP的数据包缓冲的实现。

在协议栈中移动的数据包,最无疑的是整个内存管理中最重要的部分了。数据包的种类和大小也可以说是五花八门,数数,首先从网卡上来的原始数据包,它可以是长达上千个字节的TCP数据包,也可以是仅有几个字节的ICMP数据包;再从要发送的数据包看,上层应用可能将自己要发送的千奇百怪形态各异的数据包递交给LWIP协议栈发送,这些数据可能存在于应用程序管理的内存空间内,也可能存在于某个ROM上。注意,这里有个核心的东西是当数据在各层之间传递时,LWIP极力禁止数据的拷贝工作,因为这样会耗费大量的时间和内存。综上,LWIP必须有个高效的数据包管理核心,它即能海纳百川似的兼容各种类型的数据,又能避免在各层之间的复制数据的巨大开销。

数据包管理机构采用数据结构pbuf来描述数据包,其源码如下,

struct pbuf {

struct pbuf *next;

void *payload;

u16_t tot_len;

u16_t len;

u8_t type;

u8_t flags;

u16_t ref;

};

这个看似简单的数据结构,却够我讲一大歇的了!next字段指针指向下一个pbuf结构,因为实际发送或接收的数据包可能很大,而每个pbuf能够管理的数据可能很少,所以,往往需要多个pbuf结构才能完全描述一个数据包。所以,所有的描述同一个数据包的pbuf结构

需要链在一个链表上,这一点用next实现。payload是数据指针,指向该pbuf管理的数据的起始地址,这里,数据的起始地址可以是紧跟在pbuf结构之后的RAM,也可能是在ROM 上的某个地址,而决定这点的是当前pbuf是什么类型的,即type字段的值,这在下面将继续讨论。len字段表示当前pbuf中的有效数据长度,而tot_len表示当前pbuf和其后所有pbuf 的有效数据的长度。显然,tot_len字段是len字段与pbuf链中随后一个pbuf的tot_len字段的和;pbuf链中第一个pbuf的tot_len字段表示整个数据包的长度,而最后一个pbuf的tot_len 字段必和len字段相等。type字段表示pbuf的类型,主要有四种类型,这点基本上涉及到pbuf管理中最难的部分,将在下节仔细讨论。文档上说flags字段也表示pbuf的类型,不懂,type字段不是说明了pbuf的类型吗?不过在源代码里,初始化一个pbuf的时候,是将该字段的值设为0,而在其他地方也没有用到该字段,所以,这里直接忽略掉。最后ref字段表示该pbuf被引用的次数。这里又是一个纠结的地方啊。初始化一个pbuf的时候,ref字段值被设置为1,当有其他pbuf的next指针指向该pbuf时,该pbuf的ref字段值加一。所以,要删除一个pbuf时,ref的值必须为1才能删除成功,否则删除失败。

pbuf的类型,令人很晕的东西。pbuf有四类:PBUF_RAM、PBUF_ROM、PBUF_REF 和PBUF_POOL。下面,一个一个的来看看各种类型的特点。

PBUF_RAM类型的pbuf主要通过内存堆分配得到的。这种类型的pbuf在协议栈中是用得最多的。协议栈要发送的数据和应用程序要传递的数据一般都采用这个形式。申请PBUF_RAM类型时,协议栈会在内存堆中分配相应的大小,注意,这里的大小包括如前所述的pbuf结构头大小和相应数据缓冲区,他们是在一片连续的内存区的。下面来看看源代码是怎样申请PBUF_RAM型的。其中p是pbuf型指针。

p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));

可以看出,系统是调用内存堆分配函数mem_malloc进行内存分配的。分配空间的大小包括:pbuf结构头大小SIZEOF_STRUCT_PBUF,需要的数据存储空间大小length,还有一个offset。关于这个offset,也有一大堆可以讨论的东西,不过先到此打住。总之,分配成功的PBUF_RAM类型的pbuf如下图:

从图中可看出pbuf头和相应数据在同一片连续的内存区种,注意payload并没有指向pbuf 头结束即ref字段之后,而是隔了一定的区域。这段区域就是上面的offset的大小,这段区域用来存储数据的包头,如TCP包头,IP包头等。当然,offset也可以是0,具体值是多少,那就要看你是怎么个申请法了。如果还要深究,你肯定会更晕了。

PBUF_POOL类型和PBUF_RAM类型的pbuf有很大的相似之处,但它主要通过内存池分配得到的。这种类型的pbuf可以在极短的时间内得到分配。在接受数据包时,LWIP一般采用这种方式包装数据。申请PBUF_POOL类型时,协议栈会在内存池中分配适当的内存池个数以满足需要的申请大小。下面来看看源代码是怎样申请PBUF_POOL型的。其中p 是pbuf型指针。

p = memp_malloc(MEMP_PBUF_POOL);

可以看出,系统是调用内存池分配函数memp_malloc进行内存分配的。分配成功的PBUF_POOL类型的pbuf如下图:

图中是分配指定大小的数据缓冲的结果,系统调用会分配多个固定大小的PBUF_POOL类型pbuf,并把这些pbufs链成一个链表,以满足用户的分配空间请求。

PBUF_ROM和PBUF_REF类型的pbuf基本相同,它们的申请都是在内存堆中分配一个相应的pbuf结构头,而不申请数据区的空间。这就是它们与PBUF_RAM和PBUF_POOL的最大区别。PBUF_ROM和PBUF_REF类型的区别在于前者指向ROM空间内的某段数据,而后者指向RAM空间内的某段数据。下面来看看源代码是怎样申请PBUF_ROM和PBUF_REF类型的。其中p是pbuf型指针。

p = memp_malloc(MEMP_PBUF);

可以看出,系统是调用内存池分配函数memp_malloc进行内存分配的。而此刻请求的内存池类型为MEMP_PBUF,而不是MEMP_PBUF_POOL,晕啊…这个太让人郁闷了。MEMP_PBUF类型的内存池大小恰好为一个pbuf头的大小,因为这种池是LWIP专为PBUF_ROM和PBUF_REF类型的pbuf量身制作的。LWIP还是真的很周到啊,它会为不同的数据结构量身定做不同类型的池。正确分配的PBUF_ROM或PBUF_REF类型的pbuf,其结构如下图:

注:以上所有图片都来自文档《Design and Implementation of the LWIP:TCP/IP Stack》,这些图都有个共同的错误,即len和tot_len字段位置搞反了,窃喜。

最后说明,对于一个数据包,它可能使用上述的任意的pbuf类型,很可能的情况是,一大串不同类型的pbufs连在一起,用以保存一个数据包的数据。

广告……

下节看点,关于pbuf的内存释放问题。

4 pbuf释放

牢骚发完,Go On。昨天说到了数据缓冲pbuf的内存申请,今天继续来探究一下它的内存释放过程。由于pbuf的申请主要是通过内存堆分配和内存池分配来实现,所以,pbuf 的释放也必须按照这两种情况分别讨论。

别慌,在展开讨论之前,还得说说某个pbuf能被释放的前提。在LWIP中这点很容易判断,因为前节说到pbuf的ref字段表示该pbuf被引用的次数,当pbuf被创建时,该字段的初始值为1,由此可判断,当pbuf的ref字段为1时,该pbuf才可以被删除,所以位于pbufs链表中间的pbuf结构是不会被删除成功的,因为他们的ref值至少是2。由此总之一下,能被删除的pbuf必然是某个pbufs链的首节点。当然pbuf的删除工作远不如此的简单,其中另一个需要特别注意的地方是,想想,很可能的情况是某个pbufs链的首节点删除成功后,该pbufs链的第二个节点就自然的成为该pbufs链的首节点,此时,该节点的ref值可能变为1(该节点没有被引用了),这种情况下,该节点也会被删除,因为LWIP认为它和第一个节点一起存储同一数据包。当第二个节点也被删除后,LWIP又会去看看第三个节点是否满足删除条件…就这样一直删一下去。当然,如果首节点删除后,第二个节点的ref值大于1,表示该节点还在其他地方被引用,不能再被删除,删除工作至此结束。这段话写的很口水,不如我们举个例子来看看这个删除过程。假如现在我们的pbufs链表有A,B,C三个pbuf结构连接起来,结构为A--->B--->C,利用pbuf_free(A)函数来删除pbuf结构,下面用ABC的几组不同ref值来看看删除结果:

(1)1->2->3 函数执行后变为...1->3,节点BC仍在;

(2)3->3->3 函数执行后变为2->3->3,节点ABC仍在;

(3)1->1->2 函数执行后变为......1,节点C仍在;

(4)2->1->1函数执行后变为1->1->1,节点ABC仍在;

(5)1->1->1函数执行后变为.......,节点全部被删除。

如果您能说醍醐灌顶,那将是我最大的动力。

当可以删除某个pbuf结构时,LWIP首先检查这个pbuf是属于前节讲到的四个类型中的哪种,根据类型的不同,调用不同的内存释放函数进行删除。PBUF_POOL类型和PBUF_ROM类型、PBUF_REF类型需要通过memp_free()函数删除,PBUF_RAM类型需要通过mem_free()函数删除,原因不解释。

PBUF_RAM类型来自于内存堆,所以需通过mem_free()函数将pbuf释放回内存堆。这里,先得来看看内存堆的组织结构,见下图,在内存堆内部,内存堆管理模块通过在每一个内存分配块的顶部放置一个比较小的结构体来保存内存分配纪录(注意这个小小的结构体是内存管理模块自动附加上去的,独立于用户的申请大小)。这个结构体拥有三个成员变量,两个指针和一个标志,如图。next 与prev分别指向内存的下一个和上一个分配块,used标志表示该内存块是否已被分配。图中需要注意的两个地方,first,图中每个内存块的大小是不同且可能随时变化的。Second,当系统初始化的时候,整个内存堆就是一个内存块,下图中是经过多次分配释放后内存堆呈现出来的结果。

内存堆管理模块根据所申请分配的大小来搜索所有未被使用的内存分配块,检索到的最先满足条件的内存块将分配给申请者,注意这里并不包括前面说到的那个小小结构体,所以用户得到的是used后的那个地址。当分配成功后,内存管理模块会马上在已经分配走了的数据区后面再插一个小小的结构体,并用next和prev指针将这个结构体串起来,以便于

下次分配。经过几次的申请与释放,我们就看到了图中的内存堆组织模型。

当内存释放的时候,为了防止内存碎片的产生,上一个与下一个分配块的使用标志会被检查,如果他们中的任何一个还未被使用,这个内存块将被合并到一个更大的未使用内存块中。内存堆管理模块是这样做的,它根据用户提供的释放地址退后几个字节去寻找这个小小的结构体,利用这个结构体来实现内存堆得合并等操作。已经分配的内存块被回收后,使用标志used 清零。当然,如果上一个与下一个分配块都已被使用,这时的释放就是最简单的情况,但这也是产生内存堆碎片问题的根源。

哎,要了老命了。

接着来讲其他三种结构通过memp_free()函数将pbuf释放回内存池的情况。前面的内容已经讲过了内存池POOL的结构,PBUF_POOL型pbuf主要使用的是MEMP_PBUF_POOL 类型的POOL,PBUF_ROM和PBUF_REF型pbuf主要使用的是MEMP_PBUF型的POOL。这句话太绕了,你应该多读两遍。POOL结构的起始处有个next指针,用于指向同类型的下一个POOL,用于将同类型的POOL连接成一个单向链表,这里应该有必要仔细看看POOL 池是怎样初始化的,代码很简单:

memp = LWIP_MEM_ALIGN(memp_memory);

for (i = 0; i < MEMP_MAX; ++i) { //对各种类型的POOL依次操作

memp_tab[i] = NULL; //空闲链表头初始为空

for (j = 0; j < memp_num[i]; ++j) { //把同类POOL链成链表

memp->next = memp_tab[i];

memp_tab[i] = memp;

memp = (struct memp *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]);//取得下

} //一个POOL的地址

}

上面代码中有几个重要的全局变量,memp_memory是缓冲池的起始地址,前面已有所讨论;MEMP_MAX是POOL类型数; memp_tab用于指向某类POOL空闲链表的起始节点;memp_num表示各种类型POOL的个数;memp_sizes表示各种类型单个POOL的大小,对于MEMP_PBUF_POOL和MEMP_PBUF型的POOL,其大小是pbuf头和pbuf可装载数据大小的总和。

在这样的基础之上,POOL池的释放就简单了,首先根据POOL的类型找到相应空闲链表头memp_tab,将该POOL插在链表头上,并把memp_tab指向链表头,简单快捷。至于POOL池的的申请那自然而然的也就是对memp_tab头的操作了,这个相信你懂。

好了,到这里,LWIP的内存相关机制就基本介绍完毕。当然,它不可能这样简单,在以后使用到的地方,我会再加说明。

整理整理,收工。周末来啦….冬眠一天是必不可少的….哈哈

5 网络接口结构

我只是不想,将这份心动付诸言语。前面还有一句:信任他人,并不意味着软弱。我只是假装对万物一无所知,好借此获得你所有的温柔。谢谢你所做的一切,现在一切又将重新开始。我只有将这份无法忘怀的思念送给你。

人们总说”黑夜会过去”,但那只是善意的谎言。我想就算一个人,应该也能生存下去,因为你的笑容已经永远铭刻在我心中,还有那应该已经被我舍弃的信任别人的心。

以上内容系剽窃于某某美女的歌词。

今天我们来讨论LWIP是怎样来处理与底层硬件,即网卡芯片间的关系的。为什么要首先讨论这个问题呢?与许多其他的TCP/IP实现一样,LWIP也是以分层的协议为参照来设计实现TCP/IP的。LWIP从逻辑上看分为四层:链路层、网络层、传输层和应用层。注意,虽然LWIP也采用了分层机制,但它没有在各层之间进行严格的划分,各层协议之间可以进行或多或少的交叉存取,即上层可以意识到下层协议所使用的缓存处理机制。因此各层可以更有效地重用缓冲区。而且,应用进程和协议栈代码可以使用相同的内存,应用可以直接读写内部缓存,因此节省了执行拷贝的开销。我们将从LWIP的最底层链路层起步,开始整个LWIP内部协议之旅。

在LWIP中,是通过一个叫做netif的网络结构体来描述一个硬件网络接口的。这个接口结构比较简单,下面我们从源代码结构来分析分析这个结构:

struct netif {

struct netif *next; // 指向下一个netif结构的指针

struct ip_addr ip_addr; // IP地址相关配置

struct ip_addr netmask;

struct ip_addr gw;

err_t (* input)(struct pbuf *p, struct netif *inp); //调用这个函数可以从网卡上取得一个

// 数据包

err_t (* output)(struct netif *netif, struct pbuf *p, // IP层调用这个函数可以向网卡发送struct ip_addr *ipaddr); // 一个数据包

err_t (* linkoutput)(struct netif *netif, struct pbuf *p); // ARP模块调用这个函数向网

// 卡发送一个数据包void *state; // 用户可以独立发挥该指针,用于指向用户关心的网卡信息

u8_t hwaddr_len; // 硬件地址长度,对于以太网就是MAC地址长度,为6各字节

u8_t hwaddr[NETIF_MAX_HWADDR_LEN]; //MAC地址

u16_t mtu; // 一次可以传送的最大字节数,对于以太网一般设为1500

u8_t flags; // 网卡状态信息标志位

char name[2]; // 网络接口使用的设备驱动类型的种类

u8_t num; // 用来标示使用同种驱动类型的不同网络接口

};

next字段是指向下一个netif结构的指针。我们的一个产品可能会有多个网卡芯片,LWIP

会把所有网卡芯片的结构体链成一个链表进行管理,有一个netif_list的全局变量指向该链表的头部。next字段就是用于链表用。

ip_addr、netmask、gw三个字段用于发送和处理数据包用,分别表示IP地址、子网掩码和网关地址。前两个字段在数据包发送时有重要作用,第三个字段似乎没什么用。IP地址和网卡设备必须一一对应。如果你连什么叫IP地址、子网掩码和它们的作用都不晓得,那你有必要去看看TCP/IP协议详解卷1第三章。

input字段指向一个函数,这个函数将网卡设备接收到的数据包提交给IP层,使用时将input指针指向该函数即可,后面将详细讨论这个问题。该函数的两个参数是pbuf类型和netif 类型的,返回参数是err_t类型。其中pbuf代表接收到的数据包。

output字段向一个函数,这个函数和具体网络接口设备驱动密切相关,它用于IP层将一个数据包发送到网络接口上。用户需要根据实际网卡编写该函数,并将output字段指向该函数。该函数的三个参数是pbuf类型、netif类型和ip_addr类型,返回参数是err_t类型。其中pbuf代表要发送的数据包。ipaddr代表网卡需要将该数据包发送到的地址,该地址应该是接收实际的链路层帧的主机的IP 地址,而不一定为数据包最终需要到达的IP地址。例如,当要发送IP信息包到一个并不在本地网络里的主机上时,链路层帧会被发送到网络里的一个路由器上。在这种情况下,给output 函数的IP地址将是这个路由器的地址。

linkoutput字段和上面的output基本上是起相同的作用,但是这个函数是在ARP模块中被调用的,这里不赘述了。注意这个函数只有两个参数。实际上output字段函数的实现最终还是调用linkoutput字段函数将数据包发送出去的。

state字段可以指向用户关心的关于设备的一些信息,用户可以自由发挥,也可以不用。hwaddr_len和hwaddr[]表示MAC地址长度和MAC地址,一般MAC地址长度为6。

mtu字段表示该网络一次可以传送的最大字节数,对于以太网一般设为1500,不多说。

flags字段是网卡状态信息标志位,是很重要的控制字段,它包括网卡功能使能、广播使能、ARP使能等等重要控制位。

name[]字段用于保存每一个网络网络接口的名字。用两个字符的名字来标识网络接口使用的设备驱动的种类,名字由设备驱动来设置并且应该反映通过网络接口表示的硬件的种类。比如蓝牙设备(bluetooth)的网络接口名字可以是bt,而IEEE 802.11b WLAN设备的名字就可以是wl,当然设置什么名字用户是可以自由发挥的,这并不影响用户对网络接口的使用。当然,如果两个网络接口具有相同的网络名字,我们就用num字段来区分相同类别的不同网络接口。

到这里,你可能一头雾水,太抽象的东西太容易让人纠结。好吧,我们举个例子来看看一个以太网网卡接口结构是这样被初始化,还有数据包是如何接收和发送的。先来看初始化过程,源码:

static struct netif enc28j60; (1)

struct ip_addr ipaddr, netmask, gw; (2)

IP4_ADDR(&gw, 192,168,0,1); (3)

IP4_ADDR(&ipaddr, 192,168,0,60); (4)

IP4_ADDR(&netmask, 255,255,255,0); (5)

netif_init(); (6)

netif_add(&enc28j60, &ipaddr, &netmask, &gw, NULL, ethernetif_init, tcpip_input); (7) netif_set_default(&enc28j60); (8)

netif_set_up(&enc28j60); (9)

上面的(1)声明了一个netif结构的变量enc28j60,由于在我的板子上使用的是网卡芯片enc28j60,所以我选择使用了这个名字。(2)声明了三个分别用于暂存IP地址、子网掩码和网关地址的变量,它们是32位长度的。(3)~ (5)分别是对上述三个地址值的初始化,该过程简单。

(6)很简单,它只需初始化上面所述的全局变量netif_list即可:netif_list = NULL。

(7)调用netif_add函数初始化变量enc28j60,其中比较重要的两个参数是ethernetif_init 和tcpip_input,前者是用户自己定义的底层接口初始化函数,tcpip_input函数是向IP层递交数据包的函数,从前面的讲述中可以很明显的看出,该值会被传递给enc28j60的input字段。再来看看源码:

struct netif *

netif_add(struct netif *netif, struct ip_addr *ipaddr, struct ip_addr *netmask,

struct ip_addr *gw,

void *state,

err_t (* init)(struct netif *netif),

err_t (* input)(struct pbuf *p, struct netif *netif))

{

static u8_t netifnum = 0;

netif->ip_addr.addr = 0; //复位变量enc28j60中各字段的值

netif->netmask.addr = 0;

netif->gw.addr = 0;

netif->flags = 0; //该网卡不允许任何功能使能

netif->state = state; //指向用户关心的信息,这里为NULL

netif->num = netifnum++; //设置num字段,

netif->input = input; //如前所诉,input函数被赋值

netif_set_addr(netif, ipaddr, netmask, gw); //设置变量enc28j60的三个地址

if (init(netif) != ERR_OK) { //用户自己的底层接口初始化函数

return NULL;

}

netif->next = netif_list; //将初始化后的节点插入链表netif_list

netif_list = netif; // netif_list指向链表头

return netif;

}

上面的初始化函数调用了用户自己定义的底层接口初始化函数,这里为ethernetif_init,再来看看它的源代码:

err_t ethernetif_init(struct netif *netif)

{

netif->name[0] = IFNAME0; //初始化变量enc28j60的name字段

netif->name[1] = IFNAME1; // IFNAME在文件外定义的,这里不必关心它的具体值

netif->output = etharp_output; //IP层发送数据包函数

netif->linkoutput = low_level_output; // //ARP模块发送数据包函数

low_level_init(netif); //底层硬件初始化函数

return ERR_OK;

}

天,还有函数调用!low_level_init函数就是与我们使用的硬件密切相关的函数了。啊啊啊啊啊啊啊,没写完,明天再来吧!!

6 以太网数据接收

少壮不努力,长大写程序。悲剧!

昨天说到low_level_init函数是与我们使用的与硬件密切相关初始化函数,看看:

static void low_level_init(struct netif *netif)

{

netif->hwaddr_len = ETHARP_HWADDR_LEN; //设置变量enc28j60的hwaddr_len字段netif->hwaddr[0] = 'F'; //初始化变量enc28j60的MAC地址

netif->hwaddr[1] = 'O'; //设什么地址用户自由发挥吧,但是不要与其他

netif->hwaddr[2] = 'R'; //网络设备的MAC地址重复。

netif->hwaddr[3] = 'E';

netif->hwaddr[4] = 'S';

netif->hwaddr[5] = 'T';

netif->mtu = 1500; //最大允许传输单元

//允许该网卡广播和ARP功能,并且该网卡允许有硬件链路连接netif->flags = NETIF_FLAG_BROADCAST | \

NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;

enc28j60_init(netif->hwaddr); //与底层驱动硬件驱动程序密切相关的硬件初始化函数}

至此,终于变量enc28j60被初始化好了,而且它描述的网卡芯片enc28j60也被初始化好了,而且变量enc28j60也被链入链表netif_list。

接着上上上上面的语句(8)调用netif_set_default函数初始化缺省网络接口。协议栈除了有个netif_list全局变量指向netif网络接口结构的链表,还有个全局变量netif_default全局变量指向缺省的网络接口结构。当IP层有数据发送时,它首先会以netif_list为索引选择满足某个条件的网络接口发送数据包,但是,当找不到这样的接口时,协议栈就会调用缺省的网络接口直接发送数据包,所以(8)中的意思是把变量enc28j60描述的网络接口设置为缺省的网络接口。

(9) 调用函数netif_set_up使能网络接口,这通过一个简单语句来实现:

netif->flags |= NETIF_FLAG_UP;

至此,网卡初始化完成,能正常接收和发送数据包了。下面我们来讨论讨论关于网卡数据包的接收和发送。

LWIP中实现了接收一个数据包和发送一个数据包函数的框架,这两个函数分别是low_level_input和low_level_output,用户需要使用实际网卡驱动程序完成这两个函数。在第一篇中讲过,一个典型的LWIP应用系统包括这样的三个进程:首先是上层应用程序进程,然后是LWIP协议栈进程,最后是底层硬件数据包接收进程。这里我们就来讲讲第三个进程,看看数据包是怎样被接收并往上层传递的。但在这之前,有必要说说以太网网卡所收到的数据包的格式。如下图,

Zigbee协议栈系统事件

系统常用事件处理函数: -按键事件 -接收消息事件 -网络状态改变事件 -绑定确认事件 -匹配响应事件 1、按键事件 Case KEY_CHANGE: 当有按键事件发生的时,调用按键事件处理函数Sample_HandleKeys()来处理按键事件。 在SampleApp例程中按键处理函数处理了以下2件事情 -如果按键1按下,将向网络中的其他设备发送LED闪烁命令 -如果按键2按下,检测组ID号为SAMPLEAPP_FLASH_GROUP的组是否已经注册。如果已经注册,调用aps_RemoveGroup()将其删除;如果没注册就在APS层注册

2、接收消息事件 Case:AF_INCOMING_MSG_CMD: 如果有接收消息事件发生,则调用函数SampleApp_MessageMSGCB(MSG)对接收的消息进行处理。一般的接收消息事件是通过用户自定义的端点输入簇和输出簇来处理的。 在LED闪烁命令的发送函数中的输出簇为SAMPLEAPP_FLASH_CLUSTERID,所以在接收消息事件的输入簇中为SAMPLEAPP_FLASH_CLUSTERID即收到LED闪烁命令

3、网络状态改变事件 Case:ZDO_STATE_CHANGE 当有网络状态改变事件发生后,会调用函数SampleApp_NwkState()来处理网络状态改变事件。在SampleApp例程中,网络状态改变事件主要处理了以下事件: -判断设备类型(区分协调器、路由节点、终端节点) -当协调器网络建立成功后或其他类型节点加入网络后点亮led1 -通过调用osal_start_timerEx()设置一个定时事件,当时间到达后启用用户自定义事件SampleApp_Send_PERIODIC_MSG_EVT 备注:在使用过程中这里的3种设备类型不是全选,写一个就可以了,其他的删除

Xmodem协议详解以及源代码剖析

研究 Xmodem 协议必看的 11个问题 Xmodem 协议作为串口数据传输主要的方式之一,恐怕只有做过 bootloader 的才有机会接触一下, 网上有关该协议的内容要么是英语要么讲解不详细。笔者以前写 bootloader 时研究过 1k-Xmodem ,参考了不少相关资料。这里和大家交流一下我对 Xmodem 的理解,多多指教! 1. Xmodem 协议是什么? XMODEM协议是一种串口通信中广泛用到的异步文件传输协议。分为标准Xmodem 和 1k-Xmodem 两种,前者以 128字节块的形式传输数据,后者字节块为 1k 即 1024字节,并且每个块都使用一个校验和过程来进行错误检测。在校验过程中如果接收方关于一个块的校验和与它在发送方的校验和相同时,接收方就向发送方发送一个确认字节 (ACK。由于 Xmodem 需要对每个块都进行认可, 这将导致性能有所下降, 特别是延时比较长的场合, 这种协议显得效率更低。 除了 Xmodem ,还有 Ymodem , Zmodem 协议。他们的协议内容和 Xmodem 类似,不同的是 Ymodem 允许批处理文件传输,效率更高; Zmodem 则是改进的了Xmodem ,它只需要对损坏的块进行重发,其它正确的块不需要发送确认字节。减少了通信量。 2. Xmodem 协议相关控制字符 SOH 0x01 STX 0x02 EOT 0x04 ACK 0x06 NAK 0x15

CAN 0x18 CTRLZ 0x1A 3.标准 Xmodem 协议(每个数据包含有 128字节数据帧格式 _______________________________________________________________ | SOH | 信息包序号 | 信息包序号的补码 | 数据区段 | 校验和 | |_____|____________|___________________|__________|____________| 4. 1k-Xmodem (每个数据包含有 1024字节数据帧格式 _______________________________________________________________ | STX | 信息包序号 | 信息包序号的补码 | 数据区段 | 校验和 | |_____|____________|___________________|__________|____________| 5.数据包说明 对于标准 Xmodem 协议来说,如果传送的文件不是 128的整数倍,那么最后一个数据包的有效内容肯定小于帧长,不足的部分需要用 CTRL- Z(0x1A来填充。这里可能有人会问,如果我传送的是 bootloader 工程生成的 .bin 文件, mcu 收到后遇到0x1A 字符会怎么处理?其实如果传送的是文本文件,那么接收方对于接收的内容是很容易识别的,因为 CTRL-Z 不是前 128个 ascii 码, 不是通用可见字符, 如果是二进制文件, mcu 其实也不会把它当作代码来执行。哪怕是 excel 文件等,由于其内部会有些结构表示各个字段长度等,所以不会读取多余的填充字符。否则 Xmodem太弱了。对于 1k-Xmodem ,同上理。 6.如何启动传输?

Zigbee协议栈原理基础

1Zigbee协议栈相关概念 1.1近距离通信技术比较: 近距离无线通信技术有wifi、蓝牙、红外、zigbee,在无线传感网络中需求的网络通信恰是近距离需求的,故,四者均可用做无线传感网络的通信技术。而,其中(1)红外(infrared):能够包含的信息过少;频率低波衍射性不好只能视距通信;要求位置固定;点对点传输无法组网。(2)蓝牙(bluetooth):可移动,手机支持;通信距离10m;芯片价格贵;高功耗(3)wifi:高带宽;覆盖半径100m;高功耗;不能自组网;(4)zigbee:价格便宜;低功耗;自组网规模大。?????WSN中zigbee通信技术是最佳方案,但它连接公网需要有专门的网关转换→进一步学习stm32。 1.2协议栈 协议栈是网络中各层协议的总和,其形象的反映了一个网络中文件传输的过程:由上层协议到底层协议,再由底层协议到上层协议。 1.2.1Zigbee协议规范与zigbee协议栈 Zigbee各层协议中物理层(phy)、介质控制层(mac)规范由IEEE802.15.4规定,网络层(NWK)、应用层(apl)规范由zigbee联盟推出。Zigbee联盟推出的整套zigbee规范:2005年第一版ZigBeeSpecificationV1.0,zigbee2006,zigbee2007、zigbeepro zigbee协议栈:很多公司都有自主研发的协议栈,如TI公司的:RemoTI,Z-Stack,SimpliciTI、freakz、msstatePAN 等。 1.2.2z-stack协议栈与zigbee协议栈 z-stack协议栈与zigbee协议栈的关系:z-stack是zigbee协议栈的一种具体实现,或者说是TI公司读懂了zigbee 协议栈,自己用C语言编写了一个软件—---z-stack,是由全球几千名工程师共同开发的。ZStack-CC2530-2.3.1-1.4.0软件可与TI的SmartRF05平台协同工作,该平台包括MSP430超低功耗微控制器(MCU)、CC2520RF收发器以及CC2591距离扩展器,通信连接距离可达数公里。 Z-Stack中的很多关键的代码是以库文件的形式给出来,也就是我们只能用它们,而看不到它们的具体的实现。其中核心部分的代码都是编译好的,以库文件的形式给出的,比如安全模块,路由模块,和Mesh自组网模块。与z-stack 相比msstatePAN、freakz协议栈都是全部真正的开源的,它们的所有源代码我们都可以看到。但是由于它们没有大的商业公司的支持,开发升级方面,性能方面和z-stack相比差距很大,并没有实现商业应用,只是作为学术研究而已。 还可以配备TI的一个标准兼容或专有的网络协议栈(RemoTI,Z-Stack,或SimpliciTI)来简化开发,当网络节点要求不多在30个以内,通信距离500m-1000m时用simpliciti。 1.2.3IEEE802.15.4标准概述 IEEE802.15.4是一个低速率无线个人局域网(LowRateWirelessPersonalAreaNetworks,LR-WPAN)标准。定义了物理层(PHY)和介质访问控制层(MAC)。 LR-WPAN网络具有如下特点: ◆实现250kb/s,40kb/s,20kb/s三种传输速率。 ◆支持星型或者点对点两种网络拓扑结构。 ◆具有16位短地址或者64位扩展地址。 ◆支持冲突避免载波多路侦听技术(carriersensemultipleaccesswithcollisionavoidance,CSMA/CA)。(mac层) ◆用于可靠传输的全应答协议。(RTS-CTS) ◆低功耗。 ◆能量检测(EnergyDetection,ED)。 ◆链路质量指示(LinkQualityIndication,LQI)。 ◆在2.45GHz频带内定义了16个通道;在915MHz频带内定义了10个通道;在868MHz频带内定义了1个通道。 为了使供应商能够提供最低可能功耗的设备,IEEE(InstituteofElectricalandElectronicsEngineers,电气及电子工程师学会)定义了两种不同类型的设备:一种是完整功能设备(full.functionaldevice,FFD),另一种是简化功能设备

LWIP协议栈的分析和设计

---《计算机网络与控制》论文 LWIP协议栈的分析

摘要 近些年来,随着互联网和通讯技术的迅猛发展,除了计算机之外,大量的嵌入式设备也需求接入网络。目前,互联网中使用的通讯协议基本是TCP/IP协议族,可运行于不同的网络上,本文研究的就是嵌入式TCP/IP协议栈LWIP。文章首先分析了LWIP的整体结构和协议栈的实现,再介绍协议栈的内存管理,最后讲解协议栈应用程序接口。 关键词: 嵌入式系统;协议;LWIP;以太网 Abstract With the rapid development of internet and communication technology, Not only computers but also embeded equipments are need to connect networks. At present, the basic communication protocol using in internet is TCP/IP, it can run in different network. This paper analyses the Light-Weight TCP/IP. The process model of a protocol implementation and processing of every layer are described first, and then gives the detailed management of Buffer and memory. At last, a reference lwIP API is given. Key words: Embedded System, Protocol, Light weight TCP/IP,Ethernet 引言

ZigBee协议栈OSAL介绍

讨论ZigBee协议栈的构成以及内部OSAL的工作机理。 ZigBee协议栈OSAL介绍 操作系统抽象层 OSAL常用术语: 1.资源(Resource):任何任务所占用的实体都叫资源,如变量、数组、结构体 2.共享资源(Shared Resource):两个或两个以上任务使用的资源,为防止破坏资源,任务在操作共享资源时是独占状态。 3.任务(Task):即线程,简单的程序的执行过程。任务设计时将问题尽可能分成多个任务,每个任务独立完成某项功能,同时赋予优先级、CPU寄存器和堆栈空间。一般一个任务设计为一个无限循环。 4.多任务运行(Muti-task Running):其实同一时刻只有一个任务运行。 5.内核(Kernel):内核负责管理各个任务。包括:分配CPU时间;任务调度;任务间的通信。 6.互斥(Mutual Exclusion):多任务通信最常用方法是共享数据结构。 保护共享资源常用的方法: 关中断; 使用测试并置位指令(T&S指令); 禁止任务切换; 使用信号量; 7.消息队列(Message Queue):用于任务间传递消息。 OSAL提供如下功能: 任务注册、初始化和启动; 任务间的同步、互斥; 中断处理; 储存器分配和管理; OSAL运行机理: OSAL就是一种支持多任务运行的系统资源分配机制。 OSAL是一种基于事件驱动的轮询式操作系统。、 void osal_start_system(void)是ZigBee协议栈的灵魂,不断的查看事件列表,如果有事件发生就调用相应的事件处理函数。 SYS_EVENT_MSG是一个事件集合,是由协议栈定义的事件,即系统强制事件(Mandatory Events),它的定义为: #define SYS_EVENT_MSG 0x8000; 它包含如下事件: AF_INCOMING_MSG_CMD 收到一个新的无线数据

2020年Zigbee协议栈中文说明免费

1.概述 1.1解析ZigBee堆栈架构 ZigBee堆栈是在IEEE 802.15.4标准基础上建立的,定义了协议的MAC和PHY层。ZigBee设备应该包括IEEE802.15.4(该标准定义了RF射频以及与相邻设备之间的通信)的PHY和MAC层,以及ZigBee堆栈层:网络层(NWK)、应用层和安全服务提供层。图1-1给出了这些组件的概况。 1.1.1ZigBee堆栈层 每个ZigBee设备都与一个特定模板有关,可能是公共模板或私有模板。这些模板定义了设备的应用环境、设备类型以及用于设备间通信的簇。公共模板可以确保不同供应商的设备在相同应用领域中的互操作性。 设备是由模板定义的,并以应用对象(Application Objects)的形式实现(见图1-1)。每个应用对象通过一个端点连接到ZigBee堆栈的余下部分,它们都是器件中可寻址的组件。 图1-1 zigbe堆栈框架 从应用角度看,通信的本质就是端点到端点的连接(例如,一个带开关组件的设备与带一个或多个灯组件的远端设备进行通信,目的是将这些灯点亮)。 端点之间的通信是通过称之为簇的数据结构实现的。这些簇是应用对象之间共享信息所需的全部属性的容器,在特殊应用中使用的簇在模板中有定义。图1-1-2就是设备及其接口的一个例子:

图1-1-2 每个接口都能接收(用于输入)或发送(用于输出)簇格式的数据。一共有二个特殊的端点,即端点0和端点255。端点0用于整个ZigBee设备的配置和管理。应用程序可以通过端点0与ZigBee 堆栈的其它层通信,从而实现对这些层的初始化和配置。附属在端点0的对象被称为ZigBee设备对象 (ZD0)。端点255用于向所有端点的广播。端点241到254是保留端点。 所有端点都使用应用支持子层(APS)提供的服务。APS通过网络层和安全服务提供层与端点相接,并为数据传送、安全和绑定提供服务,因此能够适配不同但兼容的设备,比如带灯的开关。APS使用网络层(NWK)提供的服务。NWK负责设备到设备的通信,并负责网络中设备初始化所包含的活动、消息路由和网络发现。应用层可以通过ZigBee设备对象(ZD0)对网络层参数进行配置和访问。 1.1.2 80 2.15.4 MAC层 IEEE 802.15.4标准为低速率无线个人域网(LR-WPAN)定义了OSI模型开始的两层。PHY层定义了无线射频应该具备的特征,它支持二种不同的射频信号,分别位于2450MHz波段和868/915MHz 波段。2450MHz波段射频可以提供250kbps的数据速率和16个不同的信道。868 /915MHz波段中,868MHz支持1个数据速率为20kbps的信道,915MHz支持10个数据速率为40kbps的信道。MAC层负责相邻设备间的单跳数据通信。它负责建立与网络的同步,支持关联和去关联以及MAC 层安全:它能提供二个设备之间的可靠链接。 1.1.3 关于服务接入点 ZigBee堆栈的不同层与802.15.4 MAC通过服务接入点(SAP)进行通信。SAP是某一特定层提供的服务与上层之间的接口。 ZigBee堆栈的大多数层有两个接口:数据实体接口和管理实体接口。数据实体接口的目标是向上层提供所需的常规数据服务。管理实体接口的目标是向上层提供访问内部层参数、配置和管理数据的机制。 1.1.4 ZigBee的安全性 安全机制由安全服务提供层提供。然而值得注意的是,系统的整体安全性是在模板级定义的,这意味着模板应该定义某一特定网络中应该实现何种类型的安全。 每一层(MAC、网络或应用层)都能被保护,为了降低存储要求,它们可以分享安全钥匙。SSP是通过ZD0进行初始化和配置的,要求实现高级加密标准(AES)。ZigBee规范定义了信任中心的用

从Zigbee协议栈底层添加自己的按键配置

本实验是基于ZStack-CC2530-2.5.1a版本的协议栈来进行实验的,整个实验需要改动 hal_board_cfg.h、hal_board_cfg.h、hal_key.c、hal_key.h和自己定义的Coordinator.c这5个文件。 注意:添加自己的按键时尽量不要修改协议栈里面的按键程序,自己另行添加即可。 1、hal_key.h 在/* Switches (keys) */下面添加自己的按键定义 #define HAL_KEY_SW_8 0x80 图1: ---------------------------------------------------------------------------------------- 2、hal_board_cfg.h 在/* S6 */ #define PUSH1_BV BV(1) #define PUSH1_SBIT P0_1 #if defined (HAL_BOARD_CC2530EB_REV17) #define PUSH1_POLARITY ACTIVE_LOW #elif defined (HAL_BOARD_CC2530EB_REV13) #define PUSH1_POLARITY ACTIVE_LOW #else #error Unknown Board Indentifier #endif 下面模仿/* S6 */下的程序定义自己的按键值: /* S8 */ #define PUSH8_BV BV(4)//修改 #define PUSH8_SBIT P0_4//修改 #if defined (HAL_BOARD_CC2530EB_REV17)

lwip各层协议栈详解

竭诚为您提供优质文档/双击可除lwip各层协议栈详解 篇一:lwip协议栈源码分析 lwip源码分析 -----caoxw 1lwip的结构 lwip(lightweightinternetprotocol)的主要模块包括:配置模块、初始化模块、netif模块、mem(memp)模块、netarp模块、ip模块、udp模块、icmp模块、igmp模块、dhcp模块、tcp模块、snmp模块等。下面主要对我们需要关心的协议处理进行说明和梳理。配置模块: 配置模块通过各种宏定义的方式对系统、子模块进行了配置。比如,通过宏,配置了mem管理模块的参数。该配置模块还通过宏,配置了协议栈所支持的协议簇,通过宏定制的方式,决定了支持那些协议。主要的文件是opt.h。 初始化模块: 初始化模块入口的文件为tcpip.c,其初始化入口函数为: voidtcpip_init(void(*initfunc)(void*),void*arg)

该入口通过调用lwip_init()函数,初始化了所有的子模块,并启动了协议栈管理进程。同时,该函数还带有回调钩子及其参数。可以在需要的地方进行调用。 协议栈数据分发管理进程负责了输入报文的处理、超时处理、api函数以及回调的处理,原型如下: staticvoidtcpip_thread(void*arg) netif模块: netif模块为协议栈与底层驱动的接口模块,其将底层的一个网口设备描述成协议栈的一个接口设备(netinterface)。该模块的主要文件为netif.c。其通过链表的方式描述了系统中的所有网口设备。 netif的数据结构描述了网口的参数,包括ip地址、mac 地址、link状态、网口号、收发函数等等参数。一个网口设备的数据收发主要通过该结构进行。 mem(memp)模块: mem模块同一管理了协议栈使用的内容缓冲区,并管理pbuf结构以及报文的字段处理。主要的文件包括mem.c、memp.c、pbuf.c。 netarp模块: netarp模块是处理arp协议的模块,主要源文件为etharp.c。其主要入口函数为: err_tethernet_input(structpbuf*p,structnetif*netif)

ZigBee测试与协议分析

ZigBee测试与协议分析 1 前言 ZigBee协议栈包括物理层协议(IEEE802.15.4)和上层软件协议(ZigBee 2007以及其他的ZigBee网络协议)。本文将从这两方面来了解这些协议,通过介绍如何捕获及如何理解关键参数,深层次剖析ZigBee技术。有了这些本质性的认识,对于分析解决无线产品应用问题,会有很大的帮助。 2 物理层分析 ZigBee的物理层为IEEE802.15.4标准所规定,定义了ZigBee底层的调制编码方式。这些规约大多是芯片设计者需要关心的,对于应用开发来说,更关心的是衡量一个芯片、一个射频系统性能的参数。在过去的文章中,已介绍了输出功率、接收灵敏度和链路预算等参数,这一讲将更深入地介绍一个调制质量的参数:EVM。EVM指的是误差向量(包括幅度和相位的矢量),表征在一个给定时刻理想无误差基准信号与实际发射信号的向量差,。从EVM参数中,可以了解到一个输出信号的幅度误差及相位误差。 EVM是衡量一个RF系统总体调制质量的指标,定义为信号星座图上测量信号与理想信号之间的误差,它用来表示发射器的调制精度,调制解调器、PA、混频器、收发器等对它都会有影响。EVM数据和眼图。 了解完这个参数之后,再看看实际测试中是如何获取EVM参数的。 ZigBee物理层的测试,在产品研发、生产和维护阶段,可以分别采用不同的仪器。 (1)产品研发阶段要测量EVM参数,需要使用带协议解析的频谱仪,最好是自带相应协议插件的仪器,可以使用安捷伦PXA N9030A频谱分析仪+8960B插件(选配了ZigBee分析插件)。这些仪器可以测试出ZigBee调制信号的星座图、实时数据和眼图等信息,在芯片级开发过程中,需要考量高频电容电感以及滤波器等的单个及组合性能,特别需要注意的是ZigBee信号的临道抑制参数,利用PXA N9030A的高分辨率,可以查看点频的带外信号,这些细节在更换射频器件供应商时,需要仔细测量,一般数字电路抄板比较容易,因为器件性能的影响不是很大,只要值和封装对了就可以,但是射频前端的设计上,即使原样的封装、容值和感值,供应商不一样,射频参数也是不一样的,板材的选用也极大地影响着阻抗匹配,因此复制和再开发都有较大难度。合格的测试工具,加上有质量保证的射频器件供应商资源,方能真正具备RF设计能力。安捷伦PXA N9030A频谱分析仪。 (2)批量生产阶段在批量生产中,不可能将实验室的研发测试仪器搬到工厂,因此,需要便携小巧的测试设备,这时可用罗德与斯瓦茨公司的热功率探头,如NRP-Z22,做一个2.4 GHz的输出功率测试,保证能够输出公差允许的功率信号即可,因为在生产中,射频器件的焊接不良、馈线连接头的接触不良,都会造成输出功率的下降甚至消失。需要注意的是,探头非常容易被静电损坏,必须要带上防静电手套进行操作,返修过程如需要经过德国,则时间长,经费也不便宜,不是很严重的损坏倒是可以在深圳维修中心处理。NRP-Z22。 (3)应用阶段在现场出现问题时,ZigBee节点已经安装到现场,不能逐一拆下来测试,并且周围的电磁环境也是没办法在单个节点上检测到,这时就需要手持式的频谱仪进行现场勘查了,例如安捷伦公司的N9912A手持式频谱仪。使用该频谱仪,可以完成无线系统设计初期的现场勘查工作,检测现场各个地点是否有异常电磁干扰,对于ZigBee来说,当然是检测是否有持续的WIFI信号干扰了。同时,更为详细的现场勘查,还包括在定点进行数据发送,预期覆盖点进行信号强度分析,以实地评估墙体等障碍物的信号衰减,在已经架设好的ZigBee网络中,也可以检测信号覆盖,数据通信是否正常等。N9912A。

zigbee协议栈代码主要名词解释

zigbee协议重要名词解释及英文缩写(转载)网络层功能: 1. 加入和退出网络 2. 申请安全结构 3. 路由管理 4. 在设备之间发现和维护路由 5. 发现邻设备 6. 储存邻设备信息 当适当的重新分配地址联合其他设备,ZIGBEE2006可以依赖于网络协调者建立一个新网络. ZIGBEE应用层由APS(应用支持)、AF(应用结构)、ZDO(ZIGBEE设备对象)和厂商自定义应用对象组成。 APS功能 1. 绑定维持工作台,定义一个两个合拢的设备进行比较建立他们的需要和服务。 2. 促进信息在设备之间的限制 3. 组地址定义,移除和过滤组地址消息 4. 地址映射来自于64位IEEE地址和16位网络地址 5. 分裂、重新组装和可靠数据传输 ZDO功能 1. 定义设备内部网络(ZigBee协调者和终端接点) 2. 开始和/或回答绑定请求 3. 在网络设备中建立一个网络安全关系 4. 在网络中发现设备和决定供给哪个应用服务 ZDO同样有责任在网络中发现设备和为他们提供应用服务。 1.1.4 网络拓扑 ZIGBEE网络层支持星状、树状和网状拓扑。在星状拓扑中网络受约束与单个设备,呼叫COORD。COORD有责任建立和维持在网络中发现的设备和其他所有设备,都知道的终端接点直接和COORD 通信。在网状和树状拓扑中,COORD有责任建立一个网络和选择几个关键网络参数,但是网络有有可能直接应用于ZigBee路由器。在树状网络中,利用分等级路由策略完成路由传输数据和控制消息直通网络。树状网络在802.15.4-2003中可以采用信标引导通信。网状网络将允许所有对等网络通信。ZIGBEE 路又将不能在网状网络中发射规则的IEEE802.15.4-2003信标。

LwIP协议栈源码详解

LwIP协议栈源码详解 ——TCP/IP协议的实现 Created by.. 老衲五木 at.. UESTC Contact me.. for_rest@https://www.360docs.net/doc/ab14372109.html, 540535649@https://www.360docs.net/doc/ab14372109.html,

前言 最近一个项目用到LwIP,恰好看到网上讨论的人比较多,所以有了写这篇学习笔记的冲动,一是为了打发点发呆的时间,二是为了吹过的那些NB。往往决定做一件事是简单的,而坚持做完这件事却是漫长曲折的,但终究还是写完了,时间开销大概为四个月,内存开销无法估计。。 这篇文章覆盖了LwIP协议大部分的内容,但是并不全面。它主要讲解了LwIP协议最重要也是最常被用到的部分,包括内存管理,底层网络接口管理,ARP层,IP层,TCP层,API 层等,这些部分是LwIP的典型应用中经常涉及到的。而LwIP协议的其他部分,包括UDP,DHCP,DNS,IGMP,SNMP,PPP等不具有使用共性的部分,这篇文档暂时未涉及。 原来文章是发在空间中的,每节每节依次更新,后来又改发为博客,再后来就干脆懒得发了。现在终于搞定,于是将所有文章汇总。绞尽脑汁的想写一段空前绝后,人见人爱的序言,但越写越觉得像是猫儿抓的一样。就这样,PS:由于本人文笔有限,情商又低,下里巴人一枚,所以文中的很多语句可能让您很纠结,您可以通过邮箱与我联系。共同探讨才是进步的关键。 最后,欢迎读者以任何方式使用与转载,但请保留作者相关信息,酱紫!码字。。。世界上最痛苦的事情莫过于此。。。 ——老衲五木

目录 1 移植综述------------------------------------------------------------------------------------------------------4 2 动态内存管理------------------------------------------------------------------------------------------------6 3 数据包pbuf--------------------------------------------------------------------------------------------------9 4 pbuf释放---------------------------------------------------------------------------------------------------13 5 网络接口结构-----------------------------------------------------------------------------------------------16 6 以太网数据接收--------------------------------------------------------------------------------------------20 7 ARP表-----------------------------------------------------------------------------------------------------23 8 ARP表查询-----------------------------------------------------------------------------------------------26 9 ARP层流程-----------------------------------------------------------------------------------------------28 10 IP层输入-------------------------------------------------------------------------------------------------31 11 IP分片重装1--------------------------------------------------------------------------------------------34 12 IP分片重装2--------------------------------------------------------------------------------------------37 13 ICMP处理-----------------------------------------------------------------------------------------------40 14 TCP建立与断开----------------------------------------------------------------------------------------43 15 TCP状态转换-------------------------------------------------------------------------------------------46 16 TCP控制块----------------------------------------------------------------------------------------------49 17 TCP建立流程-------------------------------------------------------------------------------------------53 18 TCP状态机----------------------------------------------------------------------------------------------56 19 TCP输入输出函数1-----------------------------------------------------------------------------------60 20 TCP输入输出函数2-----------------------------------------------------------------------------------63 21 TCP滑动窗口-------------------------------------------------------------------------------------------66 22 TCP超时与重传----------------------------------------------------------------------------------------69 23 TCP慢启动与拥塞避免-------------------------------------------------------------------------------73 24 TCP快速恢复重传和Nagle算法-------------------------------------------------------------------76 25 TCP坚持与保活定时器-------------------------------------------------------------------------------80 26 TCP定时器----------------------------------------------------------------------------------------------84 27 TCP终结与小结----------------------------------------------------------------------------------------88 28 API实现及相关数据结构-----------------------------------------------------------------------------91 29 API消息机制--------------------------------------------------------------------------------------------94 30 API函数及编程实例-----------------------------------------------------------------------------------97

一文读懂zigbee技术的协议原理

一文读懂zigbee技术的协议原理 一.前言 从今天开始,我们要正式开始进行zigbee相关的通信实验了,我所使用的协议栈是ZStack 是TI ZStack-CC2530-2.3.0-1.4.0版本,大家也可以从TI的官网上直接下载TI公司为cc2530写的协议栈代码,毕竟,我们作为初学者,应该先不要去深究协议栈是怎么用代码编写的,毕竟zigbee已经相当成熟了,我们应该先学会使用zigbee协议栈进行通信,并能应用于实际项目中,比如说智能家具,不知道大家是不是有同感,所以下面我就先给大家介绍一下zigbee通信的原理以及体系架构。 二.ZStack 体系架构 ZStack 的体系结构由称为层的各模块组成。每一层为其上层提供特定的服务:即由数据服务实体提供数据传输服务;管理实体提供所有的其他管理服务。每个服务实体通过相应的服务接入点(SAP) 为其上层提供一个接口,每个服务接入点通过服务原语来完成所对应的功能。 ZStack 根据IEEE 802.15.4 和ZigBee 标准分为物理层,介质接入控制层,网络层,应用层。物理层提供了基础的服务,数据传输和接收,网络层提供了各个节点连入的服务,是zigbee网络通信的关键,应用层是我们关注的重点,提供了应用的框架和ZDO。大家如果想了解体系结构的具体内容,可以自己去看说明文档,下面我给大家介绍一下zigbee 工作原理。 ZStack 采用操作系统的思想来构建,采用事件轮循机制,而且有一个专门的Timer2 来负责定时。从CC2530 工作开始,Timer2 周而复始地计时,有采集、发送、接收、显示…等任务要执行时就执行。当各层初始化之后,系统进入低功耗模式,当事件发生时,唤醒系统,开始进入中断处理事件,结束后继续进入低功耗模式。如果同时有几个事件发生,判断优先级,逐次处理事件。这种软件构架可以极大地降级系统的功耗。 整个ZStack 的主要工作流程,如图所示,大致分为以下6 步:(1) 关闭所有中断;(2) 芯

lwip协议栈源码分析

LWIP源码分析 ----- caoxw 1 LWIP的结构 LWIP(Light weight internet protocol)的主要模块包括:配置模块、初始化模块、NetIf 模块、mem(memp)模块、netarp模块、ip模块、udp模块、icmp 模块、igmp模块、dhcp 模块、tcp模块、snmp模块等。下面主要对我们需要关心的协议处理进行说明和梳理。 配置模块: 配置模块通过各种宏定义的方式对系统、子模块进行了配置。比如,通过宏,配置了mem管理模块的参数。该配置模块还通过宏,配置了协议栈所支持的协议簇,通过宏定制的方式,决定了支持那些协议。主要的文件是opt.h。 初始化模块: 初始化模块入口的文件为tcpip.c,其初始化入口函数为: void tcpip_init(void (* initfunc)(void *), void *arg) 该入口通过调用lwip_init()函数,初始化了所有的子模块,并启动了协议栈管理进程。同时,该函数还带有回调钩子及其参数。可以在需要的地方进行调用。 协议栈数据分发管理进程负责了输入报文的处理、超时处理、API函数以及回调的处理,原型如下: static void tcpip_thread(void *arg) NetIf模块: Netif模块为协议栈与底层驱动的接口模块,其将底层的一个网口设备描述成协议栈的一个接口设备(net interface)。该模块的主要文件为netif.c。其通过链表的方式描述了系统中的所有网口设备。 Netif的数据结构描述了网口的参数,包括IP地址、MAC地址、link状态、网口号、收发函数等等参数。一个网口设备的数据收发主要通过该结构进行。 Mem(memp)模块: Mem模块同一管理了协议栈使用的内容缓冲区,并管理pbuf结构以及报文的字段处理。主要的文件包括mem.c、memp.c、pbuf.c。 netarp模块: netarp模块是处理arp协议的模块,主要源文件为etharp.c。其主要入口函数为: err_t ethernet_input(struct pbuf *p, struct netif *netif) 该入口函数通过判断输入报文p的协议类型来决定是按照arp协议进行处理还是将该报文提交到IP协议。如果报文是arp报文,该接口则调用etharp_arp_input,进行arp请求处理。 如果是ip报文,该接口就调用etharp_ip_input进行arp更新,并调用ip_input接口,将报文提交给ip层。 在该模块中,创建了设备的地址映射arp表,并提供地址映射关系查询接口。同时还提供了arp报文的发送接口。如下:

TI_zigbee协议栈结构分析应用

无线盛世《快速进入ZB世界》
Ver:1

进入Zigbee世界的准备工作
§ 首先,我们需具备一些硬件设备及平台。以下 我就罗列一下Zigbee开发基本工具: § 计算机:不管是设计电路还是编程开发都是离 不开它的。 § Zigbee开发板:对于初学者来说,Zigbee开发 板无疑是最佳选择。有了开发板,你可以在我 们成熟设计的基础上学习或者做自己的设计。 § Zigbee模块:集MCU,RF,天线设计于一体 的Zigbee模块。使用它,我们可省去设计天线 及IC周边电路设计的复杂工作。

进入Zigbee世界的准备工作
§ Zigbee仿真器:是集烧写程序、在线编程和在线仿真 功能于一身的开发过程工作中必不可少的开发工具。 编程器既能对CC243x芯片(其实包括TI产品中的CC 系列的大部分芯片)进行烧写程序(hex标准文件程序 ),也能对CC243x芯片进行在线编程和仿真,让我们 能方便地在线调试开发,从而大大地提高了开发效率 。 § Zigbee协议分析仪:ZigBee的设计开发者必不可少的 工具!ZigBee协议分析仪具有广泛的功能,包括:分 析以及解码在PHY、MAC、NETWORK/SECURITY、 APPLICATION FRAMEWORK、和APPLICATION PROFICES等各层协议上的信息包;显示出错的包以 及接入错误;指示触发包;在接收和登记过程中可连 续显示包。

进入Zigbee世界的准备工作
§ 再次,我们需要在将用于开发Zigbee的计 算机平台上安装这些软件: § Zigbee协议分析软件(sniffer) § 程序烧写软件(Flash Programmer) § IAR公司的EW8051 version 7.20I/W32 。

LwIP协议栈开发嵌入式网络的三种方法分析

LwIP协议栈开发嵌入式网络的三种方法分析 摘要轻量级的TCP/IP协议栈LwIP,提供了三种应用程序设计方法,且很容易被移植到多任务的操作系统中。本文结合μC/OS-II这一实时操作系统,以建立TCP服务器端通信为例,分析三种方法以及之间的关系,着重介绍基于raw API的应用程序设计。最后在ST公司STM32F107微处理器平台上验证,并给出了测试结果。 关键词LwIP协议栈;μC/OS-II;嵌入式网络;STM32F107; 随着嵌入式系统功能的多样化以及网络在各个领域的中的广泛应用,具备网络功能的嵌入式设备拥有更高的使用价值和更强的通用性。然而大部分嵌入式设备使用经济型处理器,受内存和速度限制,资源有限,不需要也不可能完整实现所有的TCP/IP协议,有时只需要满足实际需求就行。LwIP是由瑞典计算机科学研究院开发的轻量型TCP/IP协议栈,其特点是保持了以太网的基本功能,通过优化减少了对存储资源的占用。LwIP是免费、开源的,任何人可以使用,能够在裸机的环境下运行,当然设计的时候也考虑了将来的移植问题,可以很容易移植到多任务操作系统中。本文介绍了以ARM微处理器STM32F107和PHY接口DP83848为平台,构建的嵌入式系统中,采用LwIP和嵌入式操作系统μC/OS-II,使用协议栈提供的三种应用程序接口,实现嵌入式设备的网络通信功能。 1LwIP和μC/OS-II介绍 1.1 LwIP协议栈 LwIP协议是瑞士计算机科学院的Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP含义是light weight(轻型)IP协议,在实现时保持了TCP协议的主要功能基础上减少对RAM的占用,一般它只需要几十K的RAM和40K左右的ROM 就可以运行,这使LwIP协议栈很适合在低端嵌入式系统中使用。 LwIP协议栈的设计才用分层结构的思想,每一个协议都作为一个模块来实现,提供一些与其它协议的接口函数。所有的TCP/IP协议栈都在一个进程当中,这样TCP/IP协议栈就和操作系统内核分开了。而应用程序既可以是单独的进程也可以驻留在TCP/IP进程中,它们之间利用ICP机制进行通讯。如果应用程序是单独的线程可以通过操作系统的邮箱、消息队列等,与协议栈进程通讯。如果应用程序驻留在协议栈进程中,则应用程序可以通过内部回调函数和协议栈进程通讯。 1.2 μC/OS-II实时操作系统 μC/OS-II是一个源码公开、可移植、可固化、可裁剪及占先式的实时多任务操作系统,是专门为嵌入式应用设计的实时操作系统内核,已广泛的应用在各种嵌入式系统中。 μC/OS-II是多任务系统,内核负责管理各个任务,每个任务都有其优先级,μC/OS-II 最多可以管理64个任务,其每个任务都拥有自己独立的堆栈。μC/OS-II提供了非常丰富的系统服务功能,比如信号量、消息邮箱、消息队列、事件标志、内存管理和时间管理等,这些功能可以帮助用户实现非常复杂的应用。 1.3 LwIP协议栈移植到μC/OS-II LwIP协议栈在设计的时候就考虑到了将来的移植问题,因此把所有与硬件、操作系统、编译器有关的部分都全部独立起来,形成了一个操作系统模拟层。操作系统模拟层用进程间的信号量、邮箱机制处理通信问题,而μC/OS-II是一个基于任务调度的嵌入式实时操作系

相关文档
最新文档