和大家一样,我是按照 ../tinyos/cygwin/opt/tinyos-1.x/doc/tutorial中的8个lesson进行操作和学习的。虽然很痛苦,可是还真没有什么别的更好的方法来学习这门奇怪的嵌入式语言。相信绝大多数同学在面对NesC的时候,最大的问题就是不知道从哪里下手,和自己到底要写些什么。以下的步骤,至少可以让你知道,你要使用NesC去做什么。
第一步,我们要根据实际情况去选择使用什么组件。 以编写blink为例:
首先我们需要main, main是程序开始的组件,是每个的TinyOS 程序(application)都必须的组件。或者可以说是NesC程序的入口,类似于C语言的main(),“Main”调用其他的 component以实现程序的功能。
第二,需要一个来控制程序逻辑的组件,或者说具体实现程序逻辑功能的组件。一般表达程序的逻辑思路,用和配置文件一样的名字,但是多了一个M,表示是module文件,本例中就是BlinkM,也就是我们上一篇当中提到的module文件所对应的组件。
第三,因为程序中用到了LED,所以需要系统提供的ledc。 没办法,这个是只有多看系统lib才行。
第四,因为程序需要时间控制,所以用到系统提供的timer(或者是用户定义的singletimer,其实用户定义的singletimer依然是调用了系统的timer. 后面会附上修改好去掉simpletimer的blink代码,需要的同学自己看)
总结,没有任何好方法,只有对系统熟悉,才能完成对底层的控制,必须去了解和学习那些底层的interface,不然是没有办法学习nesC的。
第二步,选择合适的组件之后就需要编写顶层配置文件(configuration)
从逻辑上来说,当你选定了组件之后,就需要顶层配置文件来wiring组件们,让他们协同工作,以完成你需要的程序功能。
事实上,一个程序中可以有多个配置文件,但一定要有一个顶级配置文件,通常会以application的名字来命名。
配置文件configuration首先声明了其应用程序下的组件,关键字:components.
本例中: componentsMain, BlinkM, SingleTimer, LedsC;
声明了组件之后,通过->可以将两个组件的接口连接起来。
本例中:Main.StdControl ->BlinkM.StdControl;
Main.StdControl -> SingleTimer.StdControl;
BlinkM.Timer-> SingleTimer.Timer;
linkM.Leds-> LedsC
回忆上一篇,我们说到:
有两个关键字来实现wiring,我翻译成“连接”好了。关键字 “à”和“ß”永远是将一个使用(uses)的接口于一个提供(provides)的接口相连接。 也就是说只有使用者组件能够调用提供者组件的接口。反过来就不可以。
Tinyos中,组件和接口是一个多对多的关系,即一个组件可以连接到很多接口,反过来说,很多组件都可以提供一个相同的接口!(核心!最难理解的地方!)
前面说,不同的组件可以提供相同的接口,如果组件ComA,ComB都提供了某一个接口InterfaceC, 那么,当组件ComD需要去访问InterfaceC接口时,怎么办? 它访问的到底是ComA提供的InterfaceC还是ComB提供InterfaceC的呢? 要知道,虽然接口的名称是一样的,但是不同组件提供的相同接口却是实现不同的功能。
那么这里, Main.StdControl-> BlinkM.StdControl;这行代码就是把组件main和blinkm的stdcontrol连接起来,这样,就建立了两个组件之间的联系。当调用main.stdcontrol的时候就相当于调用了blinkm.stdcontrol。Main.StdControl -> SingleTimer.StdControl; 这行代码就是把main和singleTimer的stdcontrol连接起来了,也建立了main和singletimer的联系。可以看到main这个user同时和两个provider连接。Main的stdcontrol在被调用的时候,blinkm.stdcontrol和SingleTimer.StdControl都会被调用。
现在,我们已经知道某些组件提供和使用的某些接口,比如blinkM提供StdControl,因为他在箭头的后面(Main.StdControl ->BlinkM.StdControl),他是提供者;同时他还使用Timer和Leds,因为他在箭头的前面(BlinkM.Timer和linkM.Leds),他是使用者。而SingleTimer和LedsC都是提供者,因为他们都是系统提供的lib,让你去控制灯的闪烁和时间。
总结:在tinyos中组件也是分层次的。最底层的组件贴近硬件部分,是经过一层一层封装才有了上层的组件,封装的过程中就使用了配置文件。而一个应用程序需要一个顶级配置文件,在所有其他的配置文件的更高一层,编译时会首先参照该文件进行编译。
第三步,既然已经有了顶层配置文件,可以写module文件了。
有了顶层配置文件相当于我们的房子已经有图纸,那么你知道我们的房子要建多少层,每层有多少房间,卫生间和厨房在什么位置。那么module文件就是在给你的程序添砖加瓦。让它真的能住人。
前面刚刚提到,blinkM提供StdControl接口,使用singleTimer的Timer接口和LedsC的Leds接口。所以blinkM应该这样写:
Blinkm.nc
module BlinkM {
provides {
interface StdControl;
}
uses {
interface Timer;
interface Leds;
}
}
我们前面说过:
一个组件如果provide某个interface,就必须实现这个interface当中所有的command。
现在blinkM provide StdControl,所以他必须提供StdControl的所有command。分别是init(),start(), stop(). 那么blinkM就变成:
Blinkm.nc
module BlinkM {
provides {
interface StdControl;
}
uses {
interface Timer;
interface Leds;
}
implementation {
command result_t StdControl.init() {
return SUCCESS;
}
command result_t StdControl.start() {
}
command result_t StdControl.stop() {
}
}
原则:在tinyos中,要使用一个组件(模块)必须先要初始化(init)它。
main是整个application的启动的入口,那么当然main可以启动与之相连接的模块。Main已经和谁关联了? 对,main和BlinkM以及SingleTimer都关联了。而main与他们关联的接口是什么呢? 没错,是stdcontrol。前面说了,当调用main.stdcontrol的时候就相当于调用了blinkm.stdcontrol和singleTimer.stdcontrol.那么blinkM和singleTimer都被启动了。
那么可以看到,我们顶层配置文件当中的4个组件,main,BlinkM,SingleTimer都启动了,就剩ledC还没有初始化。 但是问题是ledC没有提供stdControl接口,所以不能用main与之关联的方式去启动它。观察LedsC提供的Leds接口, 发现leds接口中有init() command. 我们通过command result_t StdControl.init() 去call Leds.init();进行ledC的初始化。
command result_t StdControl.init() {
Leds.init();
return SUCCESS;
}
至此,所有的组件都已经初始化完毕。而且blinkM 提供 stdControl接口,也已经实现它。但是还有一个问题:
一个组件如果use某个interface,就必须实现这个interface当中的event。
blinkM 使用了leds接口和timer接口。
那么必须检查 leds 和timer接口,看是否有event,如果有event就必须实现。观察到leds是没有event,而timer接口是有event。
Timer.nc
interface Timer {
command result_t start(char type, uint32_t interval);
command result_t stop();
event result_t fired();
}
Timer接口有两个command和一个event。Start()命令被用于指定timer 的类型和那些即将过期的时间间隔。我们使用毫秒来计算时间间隔。有TIMER_REPEAT 和TIMER_ONE_SHOT 两种可用的类型。在使用TIMER_REPEAT模式的时候,我们可以用Start()命令形成一个循环,在指定的时间间隔过后,timer 将会结束,下一个重复的timer 将会继续执行,直到被stop()命令所终止。而当一个间隔到来时,事件 fired()就会被触发。
考虑程序的逻辑流程:
在我们需要的所有组件都启动后,Timer然后开始记录时间,当一个时间间隔过后,fired()事件被触发,并控制led,让灯闪烁。
所以把timer的start()放到blinkM的result_t StdControl.start()里,把timer的stop()放到blinkM的result_t StdControl.stop()里。所以最终的代码是:
Blinkm.nc
implementation {
command result_t StdControl.init() {
call Leds.init();
return SUCCESS;
}
command result_t StdControl.start() {
return call Timer.start(TIMER_REPEAT, 1000) ;
}
command result_t StdControl.stop() {
return call Timer.stop();
}
event result_t Timer.fired()
{
call Leds.redToggle();
return SUCCESS;
}
}
看到这里,其实一个标准的NesC程序就差不多明白了。
最后给出用tossim来模拟blink的方法,关键是给手头没有mote的同学看看tinyos程序的运行结果:
1、开始
在cygwin下,进入目录:c:/cygwin/opt/tinyos-1.x/apps/blink
运行命令:makepc
然后运行命令:exportDBG=led
最后运行:build/pc/main.exe3(这里的3指设置了3个传感器节点)
你就在console可以看到节点的输出。
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。