第三章 TinyOS编程语言nesC

第三节 接口

    3.3.2  分阶段操作

    通常来说,硬件是分阶段(split-phase)操作的,即软件对硬件提出请求,硬件完成操作后发送信号(比如中断)给软件系统。在硬件中断时间较长的情况下,如果软件提出请求后一直处于阻塞状态,CPU和能量都会产生较大浪费。对于这个问题,传统的解决方法是使用多个线程(thread)等待硬件信号。某应用需要使用硬件时,操作系统建立请求,将线程放入等待队列,然后调度其他线程工作。当硬件中断到达,再恢复等待线程,并将其放在就绪队列中。由于每个线程都具有自己的堆栈,嵌入式系统中使用线程会带来较大的RAM开销。对于RAM资源非常有限的无线传感器网络节点等来说,等待状态的线程造成了堆栈空间的严重浪费。而且在系统中分配合适大小的堆栈也是非常棘手的问题。

    不同于传统的解决办法,TinyOS采用的方法是软件也使用分阶段操作,并使用分阶段接口来达到这个目的。分阶段接口是双向的,具有向下的函数调用(命令)来启动操作,具有向上的函数调用(事件)来通知操作完成。接口Read就是一个基本的分阶段接口,大部分传感器驱动程序都提供该接口。接口Read中包括一个命令read( )和一个事件readDone( )。当调用者调用命令Read.read( ),接口Read的提供者直接返回成功,并在将来读取到传感器数据后,再调用Read.readDone( )来通知Read接口的使用者。Read接口代码如下:

    interface Read<val_t>{

    command error_t read();

    event void readDone(error_t result, val_t val);

    }

    下面结合模块PeriodicReaderC来说明分阶段接口Read的应用。在模块PeriodicReaderC的实现代码中,接口函数StdControl.start( )启动了计时器。计时器时间到时,Timer.fired( )被通知,该事件的处理程序调用Read.read( )并返回(这是分阶段操作的第一阶段)。Read.read()由负责读取传感器数据的组件定义并提供。之后的某个时刻,读取传感器数据的组件通知Read.readDone( )事件,实参val为传感器数据。PeriodicReaderC对于该事件的处理操作是将采集的传感器数据存储在模块变量lastVal中(对事件进行处理,是分阶段操作的第二阶段)。

    这种同时包含命令和事件的接口也称为双向接口。双向接口使得调用者不需要使用函数指针,就能够注册回调函数。PeridoicReaderC模块代码如下:

    module PeridoicReaderC{

    provides interface StdControl;

    uses interface Timer<TMilli>;

    uses interface Read<uint16_t>;

    }

    implementation{      //模块代码实现开始

      uint16_t lastVal=0;  //定义局部变量

    command error_t StdControl.start(){  //定义StdControl接口中的start命令

    return call Timer.startPeriodic(1024);

      }

    command error_t StdControl.stop(){  //定义StdControl接口中的stop命令

    return call Timer.startPeriodic(1024);

      }

      event void Timer.fired(){  //定义Timer接口中的fired是将处理程序

    call Read.read();

      }

      event void Read.readDone(erroe_t err, uint16_t val){ //定义接口Read的事件readDone的处理程序

    if(err==SUCCESS)

    lastVal=val;

      }

    }//implementation代码结束

    3.3.3  通用接口

    前一小节中介绍的接口Boot、Init和Leds都是无类型的。本小节介绍一类新的将一个或多个类型作为参数的接口,如上面提到的接口Read,这类接口称为通用接口。接口的类型参数用尖括号括起来。接口定义中的事件和命令可以引用接口的参数,具体地说,可以在命令或事件的形参或者函数(指命令或事件)返回值类型等处使用。例如,在接口Read中,参数val_t定义了读取数据的类型,并将其作为事件readDone第二个形参的类型。在接口Get中,参数val_t定义获取数据的类型,将类型val_t作为命令get( )返回值的类型。Get接口代码如下:

    interface Get<val_t>{

    command val_t get();

    }

    对于通用接口,有以下三点说明。

    1)通用接口可以支持多个参数类型,在各参数类型之间使用逗号分隔。

    2)组件声明通用接口时必须指定参数。

    3)通用接口的类型也可能不被接口中的任意一个命令或事件使用。但是接口的提供者和使用者之间进行连接时,接口的类型参数一定要匹配。