nesC編程入門


1.接口

NesC程序主要由各式組件(component)構成,組件和組件之間通過特定的接口(interface)互相溝通。一個接口內聲明了提供相關服務的方法(C語言函數)。例如數據讀取接口(Read)內就包含了讀取(read)、讀取結束(readDone)函數。接口只是制定了組件之間交流的規范,也就是通過某一個接口,只能通過該接口提供的方法實現兩個組件之間的交流。但是接口終歸只是接口,只是一組函數的聲明,並為包含對接口的實現。

interface Read<val_t> {
  command error_t read();
  event void readDone( error_t result, val_t val );
}

2.組件

NesC程序由組件構成。組件內主要是包含了對各類接口的使用(uses)和提供(provides)。例如組件A提供了Read接口,那A就需要負責實現Read接口內的read命令,也就是read命令的函數體,即“具體這個值是如何讀取出來的”。因為命令(command)是由接口的提供者(provider)負責實現的。如果組件B使用了A提供的Read接口,那在讀取數據結束以后,系統會返回給B一個“讀取結束”的事件,而B則需要負責處理這個事件,即“數據讀取完畢以后,我用這個數據干什么”,將值返回給計算機,或者是通過無線發送給其他傳感器等等,所以事件(event)是由接口的使用者(user)來負責實現的。

組件分為兩類。分別是模塊(module)和配置(configuration)。模塊內包含了程序的主干,也就是對各類命令和事件的實現,是NesC程序的可執行代碼的主體。而配置則是負責將各個模塊,通過特定的接口連接起來,其本身並不負責實現任何特定的命令或者事件。

以TinyOS附帶的Blink(閃爍發光二極管)程序為例:

 1 // BlinkC.nc
 2 #include "Timer.h"
 3 
 4 module BlinkC @safe()
 5 {
 6   uses interface Timer<TMilli> as Timer0;
 7   uses interface Timer<TMilli> as Timer1;
 8   uses interface Timer<TMilli> as Timer2;
 9   uses interface Leds;
10   uses interface Boot;
11 }
12 implementation
13 {
14   event void Boot.booted()
15   {
16     call Timer0.startPeriodic( 250 );
17     call Timer1.startPeriodic( 500 );
18     call Timer2.startPeriodic( 1000 );
19   }
20 
21   event void Timer0.fired()
22   {
23     dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string());
24     call Leds.led0Toggle();
25   }
26  
27   event void Timer1.fired()
28   {
29     dbg("BlinkC", "Timer 1 fired @ %s \n", sim_time_string());
30     call Leds.led1Toggle();
31   }
32  
33   event void Timer2.fired()
34   {
35     dbg("BlinkC", "Timer 2 fired @ %s.\n", sim_time_string());
36     call Leds.led2Toggle();
37   }
38 }
View Code
 1 //BlinkAppC.nc
 2 configuration BlinkAppC
 3 {
 4 }
 5 implementation
 6 {
 7 components MainC, BlinkC, LedsC;
 8 components new TimerMilliC() as Timer0;
 9 components new TimerMilliC() as Timer1;
10 components new TimerMilliC() as Timer2;
11 
12 
13 BlinkC -> MainC.Boot;
14 
15 BlinkC.Timer0 -> Timer0;
16 BlinkC.Timer1 -> Timer1;
17 BlinkC.Timer2 -> Timer2;
18 BlinkC.Leds -> LedsC;
19 }
View Code

Blink程序由兩個組件構成。BlinkC.nc為模塊,BlinkAppC.nc為配置。

2.1 調用命令和事件信號

一個簡單commanda可以使用call a()來調用,一個簡單的event可以使用signal a()來觸發。

若帶參數的命令a有n個接口,類型為T1,...Tn由接口參數表達式e1...en調用如下:call a[e1,...en]();相應的可以用signal a[e1,...en](...)來觸發事件。

2.2 任務

任務是一個獨立的控制實體,由返回類型為void且無參數的函數定義。一個任務可以預先聲明。例如:task  void  myTask();  任務通過前綴post來提交,例如:post  myTask().

2.3 原子

原子通常是最小的運行單元,其主要目的是其運行時,沒有其他運算同時發生。一般用於更新並發性的互斥變量。例如:atomic{flag = 1;};

2.4 綁定

1)賦值綁定:endpoint1 = endpoint2;設S1是endpoint1的規范元素,S2是endpoint2的規范元素,則必須滿足一下條件之一:

S1是內部的,S2是外部的(反之亦然),而且S1和S2都是被提供或被使用。

S1和S2都是外部的,一個被提供,一個被使用。

2)聯編綁定:endpoint2->endpoint2,S1和S2都為內部的。

3.編程注意事項

3.1 所有的中斷處理程序都是異步的,因此它們不能調用同步的函數。在中斷處理程序中,執行同步函數唯一的方式是通過發布任務。任務的發布時一個異步過程,但任務本身的運行卻是同步的操作。

3.2 TinyOS應用程序編寫應當盡量采用同步代碼。

3.3 atomic語句塊能保證變量的讀取具有原子性。注意:這並不意味着atomic語句塊不會被搶占。即使是atomic語句塊,倘若兩個代碼塊使用不同得變量,也可以相互搶占。從理論上講,funC可以搶占funA不可冒犯的原子性,但funA不能搶占它自身,funC也一樣,即包含共同變量的atomic代碼塊不能相互搶占執行。

3.4 如果某個函數沒有包含在一個atomic代碼塊里,但它總是在atomic代碼塊里被調用,那么編譯器就不會發出警告。

3.5 atomic代碼塊會浪費cpu資源,應該盡量簡短,從而使中斷的延遲減少。

3.6 組件間的指針傳遞

組件調用send命令或者產生sendDone事件,就會放棄消息緩沖區的所有權,例如:組件A使用組件B提供的send接口,如果A調用了send傳遞參數message_t x,那么x的所有權就傳給了B。在B可能訪問x期間A不能再訪問x。當B產生以x作為參數的sengDone事件后,x的所有權又歸還給了A。

如果一個傳遞參數的接口有error_t類型的返回值,那么所有權只有在返回值為SUCCESS得時候才傳遞。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM