第三章 TinyOS编程语言nesC
第四节 模块
3.4 模块
在介绍了组件和接口等概念的基础之上,本节着重介绍模块的实现。
模块实现包括分配状态和实现执行逻辑,主要包括两类代码:数据定义或声明和函数定义。
在模块实现代码部分的数据可以分为变量和常量两种类型。模块变量是静态存储变量。除特别声明外,函数内部的变量都是动态存储变量。为了屏蔽多平台的边界对齐差异以及同一平台上MCU与无线通信芯片字节顺序差异,nesC支持一种新的平台独立类型的数据。模块实现中使用的常量可以通过常值变量、宏定义和枚举常量等多种方式来定义。
函数可以细分为接口函数、任务和一般的内部函数。接口函数主要对接口中的命令或事件进行定义。任务是TinyOS并发执行模型的主要组成部分,是轻量级的可以延后执行的过程调用,可以在某时刻提交并被调度器按照一定的调度策略调度执行。
总地来说,模块实现部分的大部分nesC代码与C非常相似,主要的区别在于:接口函数的定义与调用、任务定义与提交以及模块变量的命名范围等。
3.4.1 接口函数
1.接口函数的定义与调用
我们知道,接口由一组被称为命令或事件的函数组成,组件必须实现它提供接口中的命令以及使用接口中的事件。因此,在模块实现部分就要定义接口函数。不同的接口中可能存在相同的命令名字或事件名字,为了保证函数定义的唯一性,在模块实现中定义接口函数时,函数名字由接口名字与接口中的命令或事件名字组合而成,中间使用“.”连接到一起,避免了不同接口中可能存在的同名命令或事件带来的混乱。下面的程序是模块PowerupC和RealmainP的定义,关键字implementation后面大括号中的代码是模块实现。由于PowerupC使用Boot接口,Boot接口中有一个事件booted,因此,模块PowerupC中必须定义接口函数event void Boot.booted( ){…}。接口函数名字由接口名字Boot与事件名字booted联合组成。当接口Boot的提供者RealmainP向模块PowerupC通知系统已经启动的事件(signal Boot.booted())时,就调用了模块PowerupC中的Boot.booted( )函数,运行模块PowerupC中Boot.booted( )函数,会进一步调用接口命令Leds.Leds0on( ),打开相应的二极管。从模块PowerupC以及RealmainP中的例子可以看到,模块实现部分除了接口函数的定义外,接口函数调用也与C中函数调用的用法不同,需要使用新的关键字。
·调用接口命令函数时使用关键字call,如模块PowerupC中的调用接口命令语句:
call Leds.led0on();
·调用接口事件函数使用关键字signal,如模块RealmainP中的调用接口事件语句:
signal Boot.booted();
在上述的描述中,读者自然会产生这样一个疑问:为什么在模块RealMainP中运行signal Boot.booted( )语句,会去调用模块PoweupC中定义的接口函数Boot.booted( )呢?这些函数名字的调用关系是如何绑定的呢?答案是:只需要将接口(比如这里的Boot)的提供者和使用者连接上就可以了。连接接口的使用者和提供者是配件的工作,我们将在下一节讨论。模块PowerupC和RealmainP的定义代码如下:
module PowerupC{ module RealMainP@safe(){
uses interface Boot; provides interface Boot;
uses interface Leds; …
} }
implementation{ //模块实现部分开始implementation{
event void Boot.booted() int main()@spontaneous()//组件的内部函数
{ {
call Leds.led0On(); …
} signal Boot.booted();//通知系统已经启动的事件
} }
}