Flowers_World
分类: 嵌入式
2015-12-30 09:31:24
原文地址:Contiki基础 作者:fly123456789
简介
Contiki是一个开源多任务事件驱动操作系统,为网络嵌入式设备设计。它轻量级的足印(footprint)很适合内存有限的MCU。
Contiki集成了数个独立的模块,在一个事件驱动的类线程多任务的环境中,包含了protothread library、uIP TCP/IP(v4和v6)协议栈、无线传感器网络的协议套件—Rime协议栈。
Contik主要为网络应用程序而设计,但也可运行仅使用它的事件驱动内核的非网络程序。
事件
Contiki内核基于事件驱动。这类系统的核心思想是,程序的每次执行都是一个事件的响应。整个系统(内核+链接库+用户代码)可以多进程并行执行。
不同的进程一般执行一段时间,然后等待事件发生。在等待时,这个进程的状态称为阻塞。当一个事件发生时,内核执行由事件传递来的信息指向的进程。在所等待的事件发生时,内核负责调用相对应的进程。
事件被分为以下三种:
·定时器事件(timer events):进程可以设置一个定时器,在给定的时间之后生成一个事件,进程一直阻塞直到定时器终止,才继续执行。这对周期性操作很有用,或者用于网络协议,比如涉及同步。
·外部事件(external events):外围设备连接至具有中断功能的MCU的IO引脚,触发中断时可能生成事件。例如按键,射频芯片或脉冲探测加速器都是可以产生中断的装置,可以生成此类事件。进程可以等待到这类事件后相应地响应。
·内部事件(internal events):任何进程都有可以为自身或其他进程指定事件。这对进程间通讯很有用,例如通知某个进程,数据已经准备好可以进行计算。
对事件的操作被称为投递(posted),一个中断服务程序将投递一个事件至一个进程当它被执行时。事件具有以下信息:
·process:进程被事件寻址,它可以是特定的进程或所有注册进程。
·event type:事件类型。用户可以为进程定义一些事件类型用来区分它们,比如一个类型为收到数据包,另一个为发数据包。
·data:一些数据可以同事件一起提供给进程。
Contiki操作系统主要的理念是:事件被投递给进程,进程触发后开始执行直到阻塞,然后等待下一个事件。
进程
进程在Contiki中相当于任务(task)。进程的构造基于protothread library,使用local continuation library实现。更多信息参考下文提供的链接。
一个进程是一个C函数包括一个死循环和一些阻塞宏调用。由于Contiki事件驱动内核不是抢占式的,每个进程会在被事件阻塞时才停止执行。一些宏用来处理不同的阻塞情况。这允许使用编程状态机作为流控制。以下是contiki进程的架构,由contiki官方网站提供
1 | #include "contiki.h" |
2 | /* The PROCESS() statement defines the process' name. */ |
3 | PROCESS(my_example_process, "My example process"); |
4 | /* The AUTOSTART_PROCESS() statement selects what process(es) |
5 | to start when the module is loaded. */ |
6 | AUTOSTART_PROCESSES(&my_example_process); |
7 | /* The PROCESS_THREAD() contains the code of the process. */ |
8 | PROCESS_THREAD(my_example_process, ev, data) |
9 | { |
10 | /* Do not put code before the PROCESS_BEGIN() statement - |
11 | such code is executed every time the process is invoked. */ |
12 | PROCESS_BEGIN(); |
13 | /* Initialize stuff here. */ |
14 | while(1) { |
15 | PROCESS_WAIT_EVENT(); |
16 | /* Do the rest of the stuff here. */ |
17 | } |
18 | /* The PROCESS_END() statement must come at the end of the |
19 | PROCESS_THREAD(). */ |
20 | PROCESS_END(); |
21 | } |
由于是架构,很明显该代码没有完成任何事。它反复在等待一个事件发生,一遍又一遍……
以下有一些在使用protothreads(或Contiki processes)编程时需要特别注意的事项:
·局部变量未被保护:当进程调用一个阻塞宏,实际是当前进程函数返回了,让内核调用其他的进程。当某个事件被投递,内核将调用相同的进程函数,函数将跳转至它之前返回的位置。因此如果进程的局部变量(如果没有被声明为静态(static))在阻塞前被赋值,阻塞后继续执行,将不能保证仍为相同的值。一个好的方式是在进程函数中使用静态(static)变量。
·不要使用switches:protothreads使用local continuations在返回后找回函数状态时,使用了switch语句。只有case语句放在if或while的语句块中,才不会和另一个switch语句混淆。因此,最好就是不要在进程函数中使用switch函数。
uIP TCP/IP stack
Contiki包括了一个轻量级的TCP/IP协议栈叫做uIP。它实现了RFC-定义的IPv4,IPv6,TCP和UDP(后两个兼容IPv4和IPv6)。uIP很高效,只实现了协议要求的特性。例如整个协议栈只有一个buffer,用于接收和发送数据报。
Application API
这里有两种方法使用uIP协议栈写程序:
·raw API:uIP raw API很适合实现一个简单的应用,例如一个简单的’echo’ server可以监听一些TCP端口并且将它收到的每个数据发送回去。然而当实现一个全功能的程序时编码变得越来越复杂,或者当多个程序需要共同使用时。甚至TCP连接状态机都有些烦人。
·protosocket API:protosocket library利用 protothread library,提供一个更灵活的方式编写TCP/IP程序。这个库提供了一个类似于标准BSD sockets的接口,并允许在一个进程中编写程序。
Lower Layers
拥有一个有效的TCP/IP协议栈并且某些程序可以运行在其上当然很好,但是还不够。uIP协议栈要求一个更低的层(根据OSI模型)为了和peers通讯。我们将讨论两种类型的peers。
·节点(nodes):节点间通信是由无线连接实现的。uIP协议栈需要能够发送和接受数据包。取决于uIP版本,Contiki遵循不同的方法:
·当使用IPv6,Contiki将遵循route-over配置。因此,uIP6使用一个被称为sicslowmac的简单MAC层。除了头部压缩由6loWPAN模型提供,它仅通过射频转发数据包。
·然而,对于IPv4,Contiki选用mesh-under配置。这由Rime通讯协议栈完成。Rime提供mesh routing和路由发现,因此uIP使用它来转发网络上的数据包。从IP point的角度来看,所有的传感器网络节点组成了一个本地子网,虽然multiple radio hops可能被要求。
·网关(gateways):与WSN以外的网络通信,必须通过网关。网关连接WSN和另一个网络。一般接入的是PC,也可以是其他嵌入式系统。PC和mote是串口线连接的。IP报文通过SLIP协议在两者之间发送。在计算机端,必须运行一个程序作为串口线和网络接口的接口。根据uIP协议栈版本,节点的作用不同。
·使用uIPv6时,有一个节点将被装载一个非常简单的程序,转发从射频收到的所有数据包至串口线,或者反向发送,它不做任何的地址比对,没有IP协议栈运行在它上面,除了头部压缩/解压机制(6loWPAN)。这个节点被PC端仅视为一个以太网接口,由PC完成所有工作。
·使用uIPv4时则完全不同,节点作为网关连接至PC,有完整的IP协议栈在节点上运行。每当它收到数据包要发送,节点将检查报文的IP地址:如果它属于WSN子网范围,它将使用射频发送,否则它将使用串口线发送至PC。由PC运行一个程序来创建IP层网络接口。
Rime stack
Rime协议栈提供一种递阶型无线网络协议,从简单的匿名广播到mesh网络路由。一个复杂的协议(比如multihop mesh routing)实现会被分解成若干部分,复杂的模块利用相对简单的模块来组成。
以下是对Rime不同模块的简述:
·abc:匿名广播,它仅通过射频驱动发送数据包和接收所有的数据包,并将他们送至上层;
·broadcast:标识广播,它为发出的数据包添加了发送者地址,然后传递给abc模块;
·unicast:这个模块添加给数据包添加一个目标地址,再传递给broadcast模块。在接收端,如果数据包的目标地址和当前节点地址不符,该数据包将为丢弃。
·stunicast:当要求发送数据包至某个节点,它将在给定的时间周期内反复发送,直到要求停止。
·runicast:可信单播,它使用stunicast模块发送数据包,并等待确认报文,收到后停止持续重发数据包。为了防止无限次发送,必须指定一个最大重发次数。
·polite和ipolite:这两个模块几乎相同,当一个数据包必须在给定的时间帧内被发送,模块在到达时间的一半时,检查是否收到的数据包与它准备发送的相同。如果收到了,这个数据包将不被发送,否则发送。这是一个有效的泛洪技术,可以避免没必要的重发。
·multihop:这个模块要求一个路由表功能,当将要发送数据时它会请求路由表提供下一跳,并且使用unicast发送。当它收到一个数据包,如果本身即为目标节点就将数据包传至上层,否则再次请求路由表提供下一跳并转发。
MAC层
如我们所见,uIPv6协议栈使用了一个非常简单的MAC层协议称为sicsomac,没有其他选择。但是uIPv4协议栈使用Rime作为它的直接下层,因此可以选择MAC层协议。
以下是一些在Contiki上实现的MAC协议:
nullmac协议,正如其名,它什么都不做只是从更高层转发数据包至射频驱动,或者反向。
xmac协议,序文采样协议,节点周期性短时间监听射频频道。
lpp协议,探针协议,节点周期性发送一小段消息通知正在监听,之后监听一小段时间后休眠。
Eample
以下是本文提到的一些代码示例与解释:
本文翻译自: 如果转载,请注明来源!