第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.

本例中: components Main, 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

運行命令:make pc

然後運行命令:export DBG=led

最後運行:build/pc/main.exe 3(這裏的3指設置了3個傳感器節點)

你就在console可以看到節點的輸出。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章