第三章 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)通用接口的类型也可能不被接口中的任意一个命令或事件使用。但是接口的提供者和使用者之间进行连接时,接口的类型参数一定要匹配。