[置顶] 第3篇TinyOS/NesC程序的基本结构和入手写法(教程lesson 1 blink)


和大家一样,我是按照 ../tinyos/cygwin/opt/tinyos-1.x/doc/tutorial中的8lesson进行操作和学习的。虽然很痛苦,可是还真没有什么别的更好的方法来学习这门奇怪的嵌入式语言。相信绝大多数同学在面对NesC的时候,最大的问题就是不知道从哪里下手,和自己到底要写些什么。以下的步骤,至少可以让你知道,你要使用NesC去做什么。

 

第一步,我们要根据实际情况去选择使用什么组件。 以编写blink为例:

 

首先我们需要main main是程序开始的组件,是每个的TinyOS 程序(application)都必须的组件。或者可以说是NesC程序的入口,类似于C语言的main()“Main”调用其他的 component以实现程序的功能。

 

第二,需要一个来控制程序逻辑的组件,或者说具体实现程序逻辑功能的组件。一般表达程序的逻辑思路,用和配置文件一样的名字,但是多了一个M,表示是module文件,本例中就是BlinkM,也就是我们上一篇当中提到的module文件所对应的组件。

 

第三,因为程序中用到了LED,所以需要系统提供的ledc 没办法,这个是只有多看系统lib才行。

 

第四,因为程序需要时间控制,所以用到系统提供的timer(或者是用户定义的singletimer,其实用户定义的singletimer依然是调用了系统的timer. 后面会附上修改好去掉simpletimerblink代码,需要的同学自己看)

 

总结,没有任何好方法,只有对系统熟悉,才能完成对底层的控制,必须去了解和学习那些底层的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;这行代码就是把组件mainblinkmstdcontrol连接起来,这样,就建立了两个组件之间的联系。当调用main.stdcontrol的时候就相当于调用了blinkm.stdcontrolMain.StdControl -> SingleTimer.StdControl; 这行代码就是把mainsingleTimerstdcontrol连接起来了,也建立了mainsingletimer的联系。可以看到main这个user同时和两个provider连接。Mainstdcontrol在被调用的时候,blinkm.stdcontrolSingleTimer.StdControl都会被调用。

 

现在,我们已经知道某些组件提供和使用的某些接口,比如blinkM提供StdControl,因为他在箭头的后面(Main.StdControl ->BlinkM.StdControl),他是提供者;同时他还使用TimerLeds,因为他在箭头的前面(BlinkM.TimerlinkM.Leds),他是使用者。而SingleTimerLedsC都是提供者,因为他们都是系统提供的lib,让你去控制灯的闪烁和时间。

 

总结:在tinyos中组件也是分层次的。最底层的组件贴近硬件部分,是经过一层一层封装才有了上层的组件,封装的过程中就使用了配置文件。而一个应用程序需要一个顶级配置文件,在所有其他的配置文件的更高一层,编译时会首先参照该文件进行编译。

 

第三步,既然已经有了顶层配置文件,可以写module文件了。

有了顶层配置文件相当于我们的房子已经有图纸,那么你知道我们的房子要建多少层,每层有多少房间,卫生间和厨房在什么位置。那么module文件就是在给你的程序添砖加瓦。让它真的能住人。

前面刚刚提到,blinkM提供StdControl接口,使用singleTimerTimer接口和LedsCLeds接口。所以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已经和谁关联了? 对,mainBlinkM以及SingleTimer都关联了。而main与他们关联的接口是什么呢? 没错,是stdcontrol。前面说了,当调用main.stdcontrol的时候就相当于调用了blinkm.stdcontrolsingleTimer.stdcontrol.那么blinkMsingleTimer都被启动了。

那么可以看到,我们顶层配置文件当中的4个组件,mainBlinkMSingleTimer都启动了,就剩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和一个eventStart()命令被用于指定timer 的类型和那些即将过期的时间间隔。我们使用毫秒来计算时间间隔。有TIMER_REPEAT TIMER_ONE_SHOT 两种可用的类型。在使用TIMER_REPEAT模式的时候,我们可以用Start()命令形成一个循环,在指定的时间间隔过后,timer 将会结束,下一个重复的timer 将会继续执行,直到被stop()命令所终止。而当一个间隔到来时,事件 fired()就会被触发。

 

考虑程序的逻辑流程:

在我们需要的所有组件都启动后,Timer然后开始记录时间,当一个时间间隔过后,fired()事件被触发,并控制led,让灯闪烁。

 

所以把timerstart()放到blinkMresult_t StdControl.start()里,把timerstop()放到blinkMresult_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可以看到节点的输出。

智能推荐

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
© 2014-2019 ITdaan.com 粤ICP备14056181号  

赞助商广告