版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
第2章nesC語言基礎(chǔ)
2.1nesC概述2.2nesC和C的比較2.3nesC程序結(jié)構(gòu)2.4接口2.5組件2.6nesC高級編程2.7并發(fā)模型2.8常用接口和組件2.9可視化組件關(guān)系圖
2.1nesC概述
nesC(NetworkEmbeddedSystemC)是專門為編寫TinyOS以及進(jìn)行TinyOS編寫應(yīng)用程序而發(fā)明的一種語言,用nesC而不是C語言編寫TinyOS(TinyOS最初是由C和匯編編寫的)主要基于以下兩點(diǎn):
nesC在語法上支持TinyOS需要的并發(fā)執(zhí)行模型。
nesC編寫的源碼為編譯器的優(yōu)化提供了可能,最終可以縮小可執(zhí)行代碼尺寸。
因此,使用nesC生成的程序,更加適合無線傳感網(wǎng)絡(luò)節(jié)點(diǎn)。由于nesC的學(xué)習(xí)有點(diǎn)難度,建議從C和nesC的區(qū)別入手,輔之以示例,逐漸掌握其編程規(guī)則。
nesC的版本并沒有遵循與TinyOS的版本標(biāo)識一同發(fā)布,TinyOS目前的最新版本是2.1.2,而nesC語言的最新版是1.3。nesC的主要版本變化如表2-1所示。
2.2nesC和C的比較
按照TinyOS官方說法,nesC來源于C,是C語言的一種變種,所有的C語言中的結(jié)構(gòu)體、函數(shù)、甚至指針(建議少使用指針)、數(shù)據(jù)類型以及注釋格式在nesC中依然是合法的,對于C程序員來說,nesC提出了三個“新概念”:
組件:一組可重用的代碼和數(shù)據(jù)組合,類似于C++中的類。
接口:一組可為其他組件服務(wù)的函數(shù)和事件集合,是組件之間交互的界面。
并發(fā)執(zhí)行模型:基于任務(wù)和中斷的處理機(jī)制,定義了組件之間如何調(diào)用,以及中斷代碼和非中斷代碼如何交互執(zhí)行規(guī)則。
nesC與C語言的區(qū)別主要有程序組成主體、模塊之間的調(diào)用、命名空間、編程思想等幾方面,下面將對這幾個方面進(jìn)行分析。2.2.1程序組成主體
C程序是由一系列的函數(shù)組成的,而nesC程序是由“組件”組成的。用C語言實(shí)現(xiàn)第1章中的LedOn示例如示例2-1所示。
【示例2-1】main.c、LedOn.h、LedOn.c
/*C語言實(shí)現(xiàn)LedOn示例*/
//main.c文件
#include“LedOn.h”
intmain()
{
LedOn();
while(1)
{
;
}
}//LedOn.h文件
#include<iocc2530.h>
voidLedOn();
//LedOn.c文件
#include"LedOn.h"
voidLedOn()
{
//將P1_0設(shè)置為輸出
P1DIR|=0x01;
//點(diǎn)亮LED1
P1_0=0;
}上述C程序符合一般項(xiàng)目的編寫規(guī)則,共分為3個文件:
main.c:main()函數(shù)所在文件,包含程序的主邏輯代碼。
LedOn.h:頭文件,配合LedOn.c文件實(shí)現(xiàn)函數(shù)聲明,即LedOn模塊的接口聲明文件。
LedOn.c:LedOn模塊的實(shí)現(xiàn)文件,通過LedOn()函數(shù)實(shí)現(xiàn)LED的點(diǎn)亮。
通過上述代碼與第1章中的nesC程序示例相比較可以看出:
nesC程序組成:以文件為單位來組織程序,每個文件由一個“組件”組成,“組件”是nesC程序的基本“模塊”,在組件中可以聲明“使用(uses)”接口或“提供(provides)”接口,可以定義變量和函數(shù)。
C程序組成:以文件為單位來組織程序,文件由一系列函數(shù)組成,函數(shù)是C程序的基本“模塊”。
nesC“接口”類似于C語言中的頭文件,它聲明了一系列的函數(shù)集合,為組件提供服務(wù),即被組件使用或由某個組件向外提供其實(shí)現(xiàn)。2.2.2模塊之間的調(diào)用
C程序與nesC程序的模塊之間調(diào)用規(guī)則有以下不同:
nesC程序模塊(即組件)之間的調(diào)用,需要顯示指定(通過“配件”指定,如第1章示例中的“LedOnAppC”組件)其連接關(guān)系,其在編譯之前就已經(jīng)明確了,因此是“靜態(tài)”調(diào)用關(guān)系。
C程序模塊(即函數(shù))之間的調(diào)用,根據(jù)代碼邏輯可以“隨意”調(diào)用,甚至可以通過“函數(shù)指針”動態(tài)改變調(diào)用關(guān)系。2.2.3命名空間
從語法上看,nesC“組件”本質(zhì)上是由C語言的變量和函數(shù)組成的,nesC組件、函數(shù)和變量的作用域(命名空間)有以下特點(diǎn):
在nesC中也可以定義全局變量,但建議盡量少使用全局變量。
nesC組件作用域是全局的。
nesC組件中的變量和函數(shù)對整個組件可見,組件外部不可以訪問。2.2.4編程思想
相對于C語言來說,nesC的程序組成主體、模塊調(diào)用等不同所帶來的直接結(jié)果就是編程思想的不同,這也是nesC與C語言最本質(zhì)的區(qū)別。
nesC編程思想就是事件驅(qū)動、組件式編程?;诖?,程序設(shè)計時要注意以下兩點(diǎn):
通過使用“組件”來劃分程序的業(yè)務(wù)功能。
要注意捕捉系統(tǒng)事件(如硬件中斷),并且通過處理事件來完成整個程序的運(yùn)行。
另外,在進(jìn)行nesC編程時,應(yīng)使用公認(rèn)的編程約定(詳細(xì)內(nèi)容參見本書實(shí)踐篇2的知識拓展)。
總結(jié)起來,nesC與C語言的區(qū)別如表2-2所示。
2.3nesC程序結(jié)構(gòu)
本節(jié)從宏觀角度介紹nesC程序的組成。
2.3.1程序文件
一般情況下,nesC程序文件組成有以下幾部分:
C語言頭文件:TinyOS程序的運(yùn)行需要少量的C語言頭文件,它們被組件文件包含從而參與程序的編譯。這些頭文件主要包括結(jié)構(gòu)體、數(shù)據(jù)類型以及宏定義等。
接口文件:當(dāng)系統(tǒng)提供的接口不能滿足要求時,用戶可自定義接口類型。
組件文件:包括程序中的邏輯算法代碼和組件配置關(guān)系文件。
Makefile文件:被make工具調(diào)用的編譯管理文件。
接口文件和組件文件是nesC程序的重要語法文件,也是本章介紹的重點(diǎn)。2.3.2組件
一個完整的nesC程序是由多個組件組成的,組件是nesC程序的可運(yùn)行模塊,它們交互使用(通過接口相互調(diào)用)形成有意義的nesC程序。組件分為兩類:模塊和配置組件(簡稱配件)。
模塊(Module)是nesC程序的邏輯功能實(shí)體,通過提供接口或使用接口以實(shí)現(xiàn)某個確切的業(yè)務(wù)算法。
配件(Configuration)負(fù)責(zé)把其他組件裝配起來,把組件“使用的接口”綁定到“提供該接口”的組件上。
通俗地說,模塊是包含“可執(zhí)行代碼”的組件,配件是包含“組件關(guān)系”的組件。2.3.3程序結(jié)構(gòu)
一般情況下,一個可運(yùn)行的nesC應(yīng)用程序結(jié)構(gòu)應(yīng)有如圖2-1所示的結(jié)構(gòu)。圖2-1nesC程序結(jié)構(gòu)2.3.4核心應(yīng)用模塊
1.入口函數(shù)
與C語言不同,nesC程序的入口需要在“核心應(yīng)用模塊中”使用(uses)系統(tǒng)提供的“Boot”接口,然后在程序中實(shí)現(xiàn)該接口的“booted”事件函數(shù),此函數(shù)就是nesC應(yīng)用程序的入口,關(guān)于接口的詳細(xì)信息見下節(jié)。當(dāng)程序運(yùn)行時,由TinyOS系統(tǒng)自動調(diào)用該函數(shù)。對于入口函數(shù)要注意以下幾點(diǎn):
入口函數(shù)必須存在于某個模塊中(即核心應(yīng)用模塊中),本質(zhì)上是系統(tǒng)接口Boot的“事件函數(shù)”。入口函數(shù)有且只能有一個。
入口函數(shù)的主要功能如下:
應(yīng)用程序初始化,如啟動定時器、點(diǎn)亮LED等。
啟動程序的其他函數(shù)。
入口函數(shù)的語法如語法2-1所示。
【語法2-1】入口函數(shù)
//入口函數(shù)—其實(shí)是Boot接口的事件函數(shù)
eventvoidBoot.booted()
{
//程序啟動代碼
//一般是啟動一個任務(wù)函數(shù)
}
示例2-2演示了入口函數(shù)的寫法。【示例2-2】SampleLed.nc
moduleSampleLed
{
usesinterfaceBoot;
usesinterfaceLeds;
}
implementation
{
//任務(wù)函數(shù)
taskvoidDoLedOn()
{
callLeds.led0On();
}
//入口事件函數(shù)
eventvoidBoot.booted()
{
postDoLedOn();
}
}
2.其他函數(shù)
其他函數(shù)包括模塊使用的除Boot接口以外的“其他接口事件函數(shù)”和“程序功能算法函數(shù)”,如上述代碼中的任何函數(shù)DoLedOn()。其他函數(shù)都是被入口事件函數(shù)直接或間接啟動(或調(diào)用)的。
2.4接口
2.4.1接口規(guī)則
接口提供給組件來使用,并且必須由某個組件來實(shí)現(xiàn)才有意義。nesC關(guān)于接口的詳細(xì)規(guī)定如下:
接口由一個或多個命令(command)函數(shù)和事件(event)函數(shù)組成,可以只有命令函數(shù)或只有事件函數(shù)。
接口可以被多個組件來實(shí)現(xiàn)(由配件來指定具體使用哪個實(shí)現(xiàn))。
實(shí)現(xiàn)接口的組件,必須實(shí)現(xiàn)接口中的所有命令函數(shù)。
使用接口的組件,必須實(shí)現(xiàn)接口中的所有事件函數(shù)。
一個接口對應(yīng)一個nesC源文件(即一個nc文件中只能有一個接口定義)。
關(guān)于組件和配件的詳細(xì)內(nèi)容見1.4節(jié)。2.4.2接口的定義
接口在nc文件中通過關(guān)鍵字“interface”和一對大括號來定義,如語法2-2所示。
【語法2-2】接口定義
interface
接口名
{
command
類型函數(shù)名(形參列表);/*接口命令聲明*/
…
event
類型函數(shù)名(形參列表);/*接口事件聲明*/
…
}接口中的函數(shù)有兩類:
命令函數(shù),使用“command”關(guān)鍵字修飾,由實(shí)現(xiàn)該接口的組件(稱為提供者)提供具體的函數(shù)實(shí)現(xiàn),并且由使用該接口的組件(稱為使用者)來調(diào)用。
事件函數(shù),使用“event”關(guān)鍵字修飾,由使用該接口的組件提供具體的函數(shù)實(shí)現(xiàn),并且由提供該接口的組件來調(diào)用,以用于實(shí)現(xiàn)“事件通知”效果。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.1,編寫一個溫度傳感器接口文件,聲明“采集數(shù)據(jù)”的命令函數(shù)和“采集完成”的事件函數(shù)。【描述2.D.1】TempSensor.nc
interfaceTempSensor
{
/**
*讀傳感器
*無參數(shù)
*/
commandvoidReadData();
/**
*通知傳感器數(shù)據(jù)已經(jīng)讀完
*參數(shù):pData,返回的數(shù)據(jù)指針
*參數(shù):nByteSize,返回數(shù)據(jù)的字節(jié)數(shù)
*/
eventvoidReadDone(char*pData,intnByteSize);
}2.4.3分階段操作
nesC提供了分階段(Split-phase)操作機(jī)制,具體實(shí)現(xiàn)是通過“分階段操作接口”解決某些耗時操作的等待問題,例如讀取傳感器時需要等待模數(shù)轉(zhuǎn)換完成。前面例子中的TempSensor接口即是分階段接口。
分階段接口是雙向的,至少包含以下兩個函數(shù):
向下的命令函數(shù):用于啟動耗時操作(如ReadData命令函數(shù))。命令函數(shù)被看做為分階段作業(yè)的起始部分。
向上的事件函數(shù):用于通知調(diào)用操作完成(如ReadDone事件函數(shù))。事件函數(shù)被看做為分階段作業(yè)的后續(xù)部分。
編寫nesC程序時,要善于使用系統(tǒng)提供的分階段操作接口,必要時用戶自己也可以編寫分階段操作接口。
2.5組件
2.5.1組件定義
每個組件(模塊或配件)對應(yīng)一個nc源文件,組件定義語法如語法2-3所示?!菊Z法2-3】組件定義
module
模塊名configuration
配件名
{{
//接口聲明//接口聲明
}}
implemention
implemention
{{
//模塊實(shí)現(xiàn)//模塊實(shí)現(xiàn)
}}2.5.2接口聲明
模塊或配件的聲明區(qū)是一樣的,都是用于聲明組件“要使用”或?qū)ν狻疤峁钡慕涌凇B暶鹘涌诘耐暾Z法如語法2-4所示。
【語法2-4】接口聲明
//聲明要使用的接口
uses
{
interfaceXasY
...
}
//聲明要提供的接口
provides
{
interfaceAasB
...
}
或者:
【語法2-5】接口聲明
//聲明要使用的接口
usesinterfaceXasY
...
//聲明要提供的接口
providesinterfaceAasB
...
其中關(guān)鍵字“as”用于為接口“X”或“A”命名接口別名,它們可以省略,如語法2-6所示。
【語法2-6】接口聲明
usesinterfaceX
其實(shí),上述代碼是“usesinterfaceXasX”的縮寫。
當(dāng)需要多次提供或使用同一接口時,必須使用關(guān)鍵字“as”來區(qū)分同一類型接口的多個實(shí)例。例如模塊LedsP通過使用關(guān)鍵字“as”,為接口GenerolIO的三個實(shí)例定義了不同的別名,如示例2-3所示?!臼纠?-3】LedsP.nc
//模塊LedsP,“@”代表屬性
moduleLedsP@safe()
{
provides
{
interfaceInit;
interfaceLeds;
}
uses
{
interfaceGeneralIOasLed0;
interfaceGeneralIOasLed1;
interfaceGeneralIOasLed2;
interfaceGeneralIOasLed3;
}}
implementation
{
…
}
2.5.3模塊
模塊的通用定義語法如語法2-7所示。
【語法2-7】模塊定義
module模塊名
{
//接口聲明
}
//模塊實(shí)現(xiàn)區(qū)
implementation
{
變量定義
普通函數(shù)
{
…
}
task任務(wù)函數(shù)
{
…
}
command命令函數(shù)
{
…
}
event事件函數(shù)
{
…
}
}
1.模塊變量
模塊中變量的定義與C語言中的變量是一致的,可以在定義時進(jìn)行初始化,也可先定義后初始化。關(guān)于模塊變量有以下幾點(diǎn)要注意:
模塊變量的作用域在模塊內(nèi),其他組件不可訪問。
模塊變量是“靜態(tài)變量”,在程序生存期內(nèi)一直存在,類似于C語言中用static聲明的變量。
不提倡使用malloc或其他庫函數(shù)來動態(tài)分配內(nèi)存。推薦使用靜態(tài)內(nèi)存,例如數(shù)組。
2.模塊普通函數(shù)
模塊中的普通函數(shù)的定義與C語言中的變量是一致的,在使用前先聲明。與模塊變量作用域一樣,普通函數(shù)的作用域在模塊內(nèi),其他組件不能訪問。
3.命令函數(shù)實(shí)現(xiàn)
命令函數(shù)由提供接口的代碼來實(shí)現(xiàn),需要用到關(guān)鍵字“command”,語法格式如語法2-8所示。
【語法2-8】命令函數(shù)
command
類型接口名.命令函數(shù)(形參表)
{
//命令函數(shù)的實(shí)現(xiàn)
}
4.調(diào)用命令函數(shù)
接口的使用者,要調(diào)用接口的命令函數(shù)來執(zhí)行接口的某項(xiàng)功能,使用call關(guān)鍵字,其語法格式如語法2-9所示。
【語法2-9】調(diào)用命令函數(shù)
//不需要獲得返回值
call
接口名.命令函數(shù)(函數(shù)實(shí)參);
//需要獲得返回值
變量=call接口名.命令函數(shù)(函數(shù)實(shí)參);
上述語句,可以認(rèn)為是“call+函數(shù)調(diào)用”,請注意,語句后面的分號不能省略。
5.事件觸發(fā)
為接口提供實(shí)現(xiàn)的模塊,一般要提供調(diào)用事件函數(shù)的代碼,即觸發(fā)事件,以通知調(diào)用該接口的組件,使用signal關(guān)鍵字,其語法格式如語法2-10所示。
【語法2-10】事件觸發(fā)
signal接口名.事件函數(shù)(函數(shù)參數(shù));
上述語句,可以認(rèn)為是“signal+函數(shù)調(diào)用”語句,請注意,語句后面的分號不能省略。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.2,在2.D.1的基礎(chǔ)上,編寫一個模塊實(shí)現(xiàn)溫度傳感器接口?!久枋?.D.2】TempSensorC.nc
moduleTempSensorC
{
//提供上例定義的TempSensor接口
providesinterfaceTempSensor;
}
implementation
{
charsBuf[255];
//實(shí)現(xiàn)TempSensor接口的ReadData命令函數(shù)
commandvoidTempSensor.ReadData()
{
//數(shù)據(jù)采集代碼
…
//數(shù)據(jù)采集完畢后,用事件通知調(diào)用該接口的組件
signalTempSensor.ReadDone(sBuf,255);
}
}
6.事件函數(shù)的實(shí)現(xiàn)
事件函數(shù)的實(shí)現(xiàn)是由使用接口的組件來提供的,需要用到關(guān)鍵字“event”,其語法格式如語法2-11所示。
【語法2-11】事件實(shí)現(xiàn)
event
類型接口名.事件函數(shù)(形參表)
{
//事件函數(shù)的實(shí)現(xiàn)
}
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.3,在2.D.2的基礎(chǔ)上,編寫一個模塊使用溫度傳感器接口?!久枋?.D.3】ReadTempSensorC.nc
moduleReadTempSensorC
{
usesinterfaceBoot;
usesinterfaceTempSensor;
usesinterfaceLeds;
}
implementation
{
taskvoidReadTempSensor()
{
//點(diǎn)亮LED0表示啟動了數(shù)據(jù)采集
callLeds.led0On();
//調(diào)用TempSensor接口的ReadData函數(shù)開始數(shù)據(jù)采集
callTempSensor.ReadData();
}
eventvoidTempSensor.ReadDone(char*pData,intnByteSize)
{
//該函數(shù)被調(diào)用時,表示數(shù)據(jù)采集已經(jīng)結(jié)束
//點(diǎn)亮LED1表示數(shù)據(jù)采集結(jié)束
callLeds.led1On();
//其他數(shù)據(jù)處理代碼
}
//入口函數(shù)
eventvoidBoot.booted()
{
postReadTempSensor();
}
}2.5.4配件
配件用于確定組件之間的接口連接(Wiring)關(guān)系。只有確定了組件之間的連接(Wiring),nesC編譯器才能將程序編譯成一個合法的可運(yùn)行的程序。配件的通用定義語法如語法2-12所示。【語法2-12】配件定義
configuration配件名
{
//接口聲明
}
implementation
{
//組件聲明語句
//組件連接語句
}
1.組件聲明
聲明組件的作用是聲明配件要管理的所有組件(包括模塊和組件),為后面的組件鏈接語句提供合法標(biāo)示。組件聲明使用關(guān)鍵字“components”,并且可以使用關(guān)鍵字“as”指定別名,語法格式如語法2-13所示。
【語法2-13】組件聲明
components
組件AasAA,組件BasBB,組件CasCC,…;
或者:
【語法2-14】組件聲明
components組件AasAA;
components組件BasBB;
components組件CasCC;
…與接口聲明類似,“as+別名”可以省略。使用關(guān)鍵字“as”設(shè)定別名的意義有以下幾個:
簡化復(fù)雜長名字的組件。
多次實(shí)例化同一通用組件(關(guān)于通用組件,請參見2.6.3節(jié))。
代碼易于移植,例如當(dāng)需要在配件中更換組件時,只需替換組件聲明這一行,不需要修改程序中使用該組件的代碼行。
2.連接
配件中的連接(或綁定,英語名稱是Wiring)代碼用于把配件聲明的接口或組件聯(lián)系在一起,其作用有以下幾點(diǎn):
明確某個組件使用的接口是由哪個組件提供的實(shí)現(xiàn)。
明確當(dāng)前配件對外提供的接口是由哪個組件提供的實(shí)現(xiàn)。
連接操作的語法如語法2-15所示。
【語法2-15】連接操作
組件.接口連接符組件.接口
其中連接符有兩類:
“->”或“<-”:這兩個操作符作用是一樣的,箭頭從使用接口的組件指向提供接口的組件。使用時要求符號兩邊的組件必須是配件“實(shí)現(xiàn)區(qū)”聲明的組件。
“=”:主要用于輸出配件中對外提供的接口。使用時要求符號兩邊至少有一個是配件“聲明區(qū)”聲明的接口。
在實(shí)際使用上述語法時,其中一端的“.接口”可以省略,例如:
LedsP<-PlatformLedsC.Init;
等價于
LedsP.Init<-PlatformLedsC.Init;
系統(tǒng)提供的Leds配件連接操作示例如示例2-4所示。【示例2-4】LedsC.nc
configurationLedsC
{
//聲明對外提供的接口
providesinterfaceLeds;
}
implementation
{
//聲明配件所管理的組件
componentsLedsP,PlatformLedsC;
//輸出Leds接口,由LedsP實(shí)現(xiàn)
Leds=LedsP;
//PlatformLedsC.Init由LedsP.Init提供
LedsP.Init<-PlatformLedsC.Init;
//PlatformLedsC.Led0由LedsP.Led0提供
LedsP.Led0->PlatformLedsC.Led0;
LedsP.Led1->PlatformLedsC.Led1;
LedsP.Led2->PlatformLedsC.Led2;
LedsP.Led3->PlatformLedsC.Led3;
}
2.6nesC高級編程
2.6.1參數(shù)化接口
參數(shù)化接口(ParameterizedInterfaces)的作用就是允許為組件提供同類型接口的多個實(shí)例。參數(shù)化接口實(shí)質(zhì)上是接口數(shù)組,數(shù)組的索引是接口參數(shù),其標(biāo)示了調(diào)用組件,并且在編譯時確定。參數(shù)化接口的定義與普通接口的定義沒有區(qū)別,下面介紹其聲明、實(shí)現(xiàn)和連接的語法。
1.參數(shù)化接口聲明
只有當(dāng)組件在對外提供接口時(使用時的聲明與普通接口一樣)才可能出現(xiàn)參數(shù)化接口(接口數(shù)組)的聲明,接口聲明時要有數(shù)組下標(biāo),語法格式如語法2-16所示。
【語法2-16】參數(shù)接口的聲明
providesinterface接口名[數(shù)據(jù)類型標(biāo)示符];
例如系統(tǒng)組件ActiveMessageC對外提供了四個參數(shù)接口,代碼如示例2-5所示?!臼纠?-5】ActiveMessageC.n
configurationActiveMessageC
{
provides
{
interfaceSplitControl;
interfaceAMSend[uint8_tid];
interfaceReceive[uint8_tid];
interfaceReceiveasSnoop[uint8_tid];
interfaceSendNotifier[am_id_tid];
……
}
}
implementation
{
…
}
2.參數(shù)化接口實(shí)現(xiàn)
在模塊內(nèi)實(shí)現(xiàn)參數(shù)化接口的命令或事件函數(shù)時,函數(shù)名后要有數(shù)組下標(biāo),語法格式如語法2-17所示。
【語法2-17】參數(shù)化接口實(shí)現(xiàn)
command類型接口名.函數(shù)名[數(shù)據(jù)類型標(biāo)示符](形參)
{
…
}
event類型接口名.函數(shù)名[數(shù)據(jù)類型標(biāo)示符](形參)
{
…
}
例如系統(tǒng)組件SchedulerBasicP(任務(wù)調(diào)度組件)中用到了參數(shù)化接口,代碼如示例2-6所示。
【示例2-6】SchedulerBasicP.nc
moduleSchedulerBasicP@safe()
{
providesinterfaceScheduler;
//參數(shù)化接口聲明
providesinterfaceTaskBasic[uint8_tid];
usesinterfaceMcuSleep;
}
implementation
{
…
//參數(shù)化接口的命令函數(shù)實(shí)現(xiàn)
asynccommanderror_tTaskBasic.postTask[uint8_tid]()
{
atomic{returnpushTask(id)?SUCCESS:EBUSY;}
}
…
}
3.參數(shù)化接口連接
參數(shù)化接口在連接時,根據(jù)連接操作符兩端的接口是否是參數(shù)化接口,其語法格式分為兩種情況。
(1)操作符兩端都是參數(shù)化接口。
這種情況下,連接語句的寫法與兩端都是普通接口的寫法一致。例如,系統(tǒng)配件TinySchedulerC對外提供參上述數(shù)化接口TaskBasic,其連接代碼如示例2-7所示?!臼纠?-7】TinySchedulerC.nc
configurationTinySchedulerC
{
providesinterfaceScheduler;
providesinterfaceTaskBasic[uint8_tid];
}
implementation
{
componentsSchedulerBasicPasSched;
componentsMcuSleepCasSleep;
Scheduler=Sched;
//等價于TaskBasic=SchedulerBasicP.TaskBasic,兩端都是參數(shù)化接口
TaskBasic=Sched;
Sched.McuSleep->Sleep;
}
(2)其中一端是參數(shù)化接口。
這種情況下,參數(shù)化接口要填寫確定的參數(shù)(即數(shù)組索引)。語法格式如語法2-18所示。
【語法2-18】參數(shù)化接口連接
組件1.接口名1連接符組件2.接口名2[索引值]
其中,索引值是一個整數(shù),由用戶提供,告訴編譯器以標(biāo)示調(diào)用組件(即接口的使用者是誰)。為了避免程序出錯,索引值可以使用以下兩個函數(shù)獲得:
unique(char*identifer)函數(shù):參數(shù)為字符串,如果程序包含n個相同字符串作為參數(shù)的unique()調(diào)用,每個調(diào)用返回一個0~n-1之間的無符號整數(shù),且互不相同(注意:不同字符串參數(shù)的調(diào)用返回值有可能相同)。
uniqueCount(char*identifer)函數(shù):參數(shù)為字符串,如果程序包含n個相同字符串為參數(shù)的uniqueCount()調(diào)用,每個調(diào)用都返回n。
例如,可以編寫示例2-8代碼使用上述系統(tǒng)組件ActiveMessageC提供的參數(shù)化接口AMSend和Receive。
【示例2-8】AMTestC.nc、AMTestAppC.nc
//AMTestC.nc
moduleAMTestC
{
uses
{
interfaceRecieve;
interfaceAMSend;
}
}
implementation
{
…
}
//AMTestAppC.nc
configurationAMTestAppC{
…
}
implementation
{
…
componentsActiveMessageC;
componentsAMTestCasApp;
App.Receive->ActiveMessageC.Receive[uniqueCount("MyRadio")];
App.AMSend->ActiveMessageC.AMSend[uniqueCount("MyRadio")];
}2.6.2通用接口
1.通用接口定義
通用接口在定義時要使用尖括號將數(shù)據(jù)類型擴(kuò)起來,語法如語法2-19所示。
【語法2-19】通用接口定義
interface接口名<類型1,類型2,類型n>
{
//事件函數(shù)或命令函數(shù)聲明
}
對于上述定義有以下規(guī)則:
大括號內(nèi)的類型,可以有一個或多個,之間使用逗號分隔。
接口內(nèi)聲明的事件函數(shù)或命令函數(shù)可以使用或不適用大括號內(nèi)的類型。
示例2-9是TinyOS中的通用接口Read的定義。
【示例2-9】Read.nc
interfaceRead<val_t>
{
commanderror_tread();
eventvoidreadDone(error_tresult,val_tval);
}
上述代碼中,通用接口Read具有一個參數(shù)類型val_t,用于定義讀取的數(shù)據(jù)類型,其中事件函數(shù)readDone中使用該類型返回讀取到的數(shù)據(jù)。
為了使任務(wù)描述2.D.1中的TempSensor接口具有通用性,可以修改為通用接口,示例代碼如示例2-10所示。
【示例2-10】TempSensor.nc
interfaceTempSensor<dataType>
{
commandvoidReadData();
eventvoidReadDone(dataTypepData,intnByteSize);
}
2.通用接口聲明和實(shí)現(xiàn)
提供或使用通用接口的模塊在聲明通用接口時要在大括號內(nèi)提供確切的數(shù)據(jù)類型,而實(shí)現(xiàn)通用接口的命令和事件函數(shù)時,要相應(yīng)使用聲明時提供的類型。其語法格式如語法2-20所示?!菊Z法2-20】通用接口聲明和實(shí)現(xiàn)
module模塊名
{
//聲明提供的通用接口
providesinterface通用接口名A<類型1,類型2,類型n>;
//聲明使用的通用接口
usesinterface通用接口名B<類型1,類型2,類型n>;
}
implementation
{
//實(shí)現(xiàn)通用接口A的命令函數(shù)command函數(shù)返回類型通用接口名A.命令函數(shù)(參數(shù));
…
//實(shí)現(xiàn)通用接口B的事件函數(shù)
event函數(shù)返回類型通用接口名B.事件函數(shù)(參數(shù));
…
}
示例2-11是任務(wù)描述2.D.2修改之后的模塊TempSensorC代碼?!臼纠?-11】TempSensorC.nc
moduleTempSensorC
{
//聲明要提供的通用接口
providesinterfaceTempSensor<char*>;
}
implementation
{
charsBuf[255];commandvoidTempSensor.ReadData()
{
…
signalTempSensor.ReadDone(sBuf,255);
}
}
示例2-12是任務(wù)描述2.D.3修改之后的ReadTempSensorC模塊代碼?!臼纠?-12】ReadTempSensorC.nc
moduleReadTempSensorC
{
usesinterfaceBoot;
usesinterfaceTempSensor<char*>;
usesinterfaceLeds;
}
implementation
{
…
eventvoidTempSensor.ReadDone(char*pData,intnByteSize)
{
…
}
…
}
3.通用接口連接
通用接口連接時必須保證提供者與使用者組件所聲明的接口類型參數(shù)必須匹配,否則將出現(xiàn)編譯錯誤。上例中的通用接口TempSensor的提供者TempSensorC與使用者ReadTempSensorC所聲明的接口類型參數(shù)都是“<char*>”,因此直接使用連接符進(jìn)行連接即可,示例代碼如示例2-13所示。
【示例2-13】ReadTempSensorAppC.nc
configurationReadTempSensorAppC
{
}
implementation
{
componentsReadTempSensorC,TempSensorC;
…
ReadTempSensorC.TempSensor->TempSensorC.TempSensor;
…
}2.6.3通用組件
1.通用組件定義
通用組件定義時使用關(guān)鍵字generic,并且通用組件有一個參數(shù)列表(可以為空)。通用組件分為通用模塊和通用配件,其定義語法如語法2-21和2-22所示。【語法2-21】通用模塊定義
genericmodule模塊名(參數(shù)列表)
{
//聲明接口
}
implemention
{
//模塊實(shí)現(xiàn)
}【語法2-22】通用組件定義
genericconfiguration配件名(參數(shù)列表)
{
//聲明接口
}
implemention
{
//配件實(shí)現(xiàn)
}對于上述定義,參數(shù)列表有以下規(guī)則:
參數(shù)列表可以為空,但是組件名后面的括號不能省略。
組件實(shí)現(xiàn)中可以使用參數(shù)列表中的參數(shù),也可以不使用。
參數(shù)列表的內(nèi)容可以是:
使用關(guān)鍵字typedef聲明的數(shù)據(jù)類型,例如“typedefqueue_t”。
帶有類型的數(shù)字常量,例如“uint16_tmax_size”。
帶有類型的字符串常量,例如“char*resourceName”或“charresourceName[]”。
示例2-14代碼是TinyOS系統(tǒng)提供的通用模塊QueueC(用作先進(jìn)先出計算的隊列組件)的代碼。【示例2-14】QueueC.nc
genericmoduleQueueC(typedefqueue_t,uint8_tQUEUE_SIZE)
{
providesinterfaceQueue<queue_t>;
}
implementation
{
queue_tONE_NOKqueue[QUEUE_SIZE];
…
commandqueue_tQueue.head()
{
returnqueue[head];
}
…
}上述代碼中,參數(shù)列表中有兩個參數(shù),queue_t是用typedef聲明的類型,并且在模塊的實(shí)現(xiàn)代碼中用作修飾Queue.head()函數(shù)的返回值;QUEUE_SIZE是uint8_t型的常量,在模塊的實(shí)現(xiàn)代碼中用作數(shù)組的下標(biāo)值。
TinyOS系統(tǒng)中的通用配件TimerMilliC是程序經(jīng)常要用到的定時器配件,可以被多次實(shí)例化,在程序中表示多個定時器。其代碼如示例2-15所示。
【示例2-15】TimerMilliC.nc
genericconfigurationTimerMilliC()
{
providesinterfaceTimer<TMilli>;
}
implementation
{
componentsTimerMilliP;
Timer=TimerMilliP.TimerMilli[unique(UQ_TIMER_MILLI)];
}
從上述代碼可以看出該配件的參數(shù)列表是空,且TimerMilliC配件提供了通用接口Timer。
2.通用組件實(shí)例化
通用組件在聲明時被實(shí)例化,實(shí)例化需使用關(guān)鍵字new,并傳遞實(shí)例參數(shù)(如果通用組件參數(shù)列表有參數(shù))。語法如語法2-23所示。
【語法2-23】通用組件實(shí)例化
componentsnew組件名(實(shí)參)as別名
上述語法中,如果不是多次實(shí)例化,“as別名”可以省略。例如上述示例中的QueueC模塊和TimerMilliC配件可以使用代碼2-1實(shí)例化。
【代碼2-1】通用組件實(shí)例化componentsnewQueueC(uint8_t,64)
componentsnewTimerMilliC()asTimer0;
componentsnewTimerMilliC()asTimer1;2.6.4編程實(shí)例
以下內(nèi)容用以實(shí)現(xiàn)任務(wù)描述2.D.4,編寫一個nesC程序使用定時器接口實(shí)現(xiàn)LED閃爍。本程序設(shè)計為三個文件:
核心應(yīng)用模塊文件,命名為“TimerLedC.nc”。
頂層配置組件文件,命名為“TimerLedAppC.nc”。
Makefile文件:編譯管理文件。
1.建立應(yīng)用程序目錄
在Cygwin的“opt/mytinyos/apps/”目錄下建立“TimerLed”目錄。如圖2-2所示。
以下三個文件使用EditPlus程序建立并保存在“TimerLed”目錄內(nèi)。圖2-2建立應(yīng)用程序目錄
2.編寫核心應(yīng)用模塊文件
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.4,使用定時器通用接口Timer聲明兩個定時器,用它們分別控制LED0和LED1,具體代碼如描述2.D.4TimerLedC.nc?!久枋?.D.4】TimerLedC.nc
moduleTimerLedC
{
uses
{
interfaceBoot;
interfaceLeds;
//使用定時器通用接口聲明兩個定時器
interfaceTimer<TMilli>asTimer0;
interfaceTimer<TMilli>asTimer1;
}
}implementation
{
//任務(wù)函數(shù)內(nèi),內(nèi)容是空
taskvoidDoStartTimer()
{
}
//程序入口事件函數(shù)
eventvoidBoot.booted()
{
//啟動兩個定時器周期分別是250和500毫秒
callTimer0.startPeriodic(250);
callTimer1.startPeriodic(500);
postDoStartTimer();
}//定時器0事件,閃爍LED0
eventvoidTimer0.fired()
{
callLeds.led0Toggle();
}
//定時器1事件,閃爍LED1
eventvoidTimer1.fired()
{
callLeds.led1Toggle();
}
}
3.編寫頂層配置組件
通用組件TimerMilliC提供Timer接口,因此在頂層配置組件中將TimerLedC與TimerMilliC組件連接起來,代碼如下。
【描述2.D.4】TimerLedAppC.nc
configurationTimerLedAppC
{
}
implementation
{
componentsMainC,TimerLedC,LedsC;
//實(shí)例化兩個通用組件
componentsnewTimerMilliC()asTimer0;
componentsnewTimerMilliC()asTimer1;
TimerLedC.Boot->MainC.Boot;
TimerLedC.Timer0->Timer0;
TimerLedC.Timer1->Timer1;
TimerLedC.Leds->LedsC;
}
4.編寫Makefile文件
文件內(nèi)容如描述2.D.4Makefile所示:
【描述2.D.4】Makefile
COMPONENT=TimerLedAppC
include$(MAKERULES)
上述代碼中,第一行將頂層配置組件的名字賦值給“COMPONENT”變量,第二行是固定寫法(詳細(xì)說明參見第4章)。
5.編譯并下載程序
用仿真器連接好設(shè)備后,打開Cygwin,在命令行上,用“cdTimerLed”命令進(jìn)入程序目錄,而后運(yùn)行“makecc2530install”命令,執(zhí)行結(jié)果如圖2-3所示。圖2-3程序編譯下載
2.7并發(fā)模型
2.7.1任務(wù)
1.任務(wù)概述
任務(wù)也稱“任務(wù)函數(shù)”,是由關(guān)鍵字task修飾的無返回值的模塊函數(shù),該函數(shù)被TinyOS系統(tǒng)進(jìn)行調(diào)度執(zhí)行,類似于Windows程序的線程。
2.任務(wù)機(jī)制
任務(wù)機(jī)制關(guān)鍵點(diǎn)如下:
任務(wù)之間不能被搶占,任務(wù)一旦運(yùn)行就必須完成(但可以被硬件中斷打斷)。
任務(wù)被提交給系統(tǒng)后,系統(tǒng)會在合適的時間運(yùn)行它(這個時間很短),而不是立即執(zhí)行,表現(xiàn)出一種“后臺處理行為”。
3.任務(wù)函數(shù)
任務(wù)函數(shù)的語法如語法2-24所示。
【語法2-24】任務(wù)函數(shù)
taskvoid函數(shù)名()
{
//任務(wù)代碼
}
對于任務(wù)函數(shù)的編寫要注意以下幾點(diǎn):
關(guān)鍵字task不可省略。
函數(shù)不能有返回值。
為了降低能耗等因素,有必要盡量減少任務(wù)代碼的執(zhí)行時間,必要的情況下可將大任務(wù)分割為小任務(wù)。任務(wù)函數(shù)中的“任務(wù)代碼”是由和任務(wù)相關(guān)的算法組成的,例如讀傳感器、采樣處理等。任務(wù)被認(rèn)為是所在組件內(nèi)的一個較為耗時的操作,任務(wù)執(zhí)行完畢最終要進(jìn)行任務(wù)結(jié)果的輸出,有兩種途徑:
調(diào)用某接口的命令函數(shù)。
調(diào)用接口的事件函數(shù)來實(shí)現(xiàn)事件通知,即觸發(fā)事件。
命令函數(shù)調(diào)用和觸發(fā)事件前面已經(jīng)講過,分別使用call語句和signal語句。
4.任務(wù)提交
任務(wù)函數(shù)通過post(關(guān)鍵字)語句提交給系統(tǒng)去調(diào)度執(zhí)行,這個過程也叫任務(wù)發(fā)布,語法如語法2-25所示。
【語法2-25】任務(wù)提交
post
任務(wù)函數(shù)名();
注意上述語句的分號不能省略。post語句被執(zhí)行后,立即返回,系統(tǒng)將在合適的時間稍后執(zhí)行被提交的任務(wù)。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.5,編寫一個nesC程序,執(zhí)行一個耗時任務(wù),當(dāng)任務(wù)執(zhí)行時LED亮,任務(wù)執(zhí)行完畢LED滅?!久枋?.D.5】TaskLedAppC.nc、TaskLedC.nc、Makefile
/**
*TaskLedAppC.nc
*/
configurationTaskLedAppC
{
}
implementation
{
componentsMainC,TaskLedC,LedsC;
TaskLedC.Boot->MainC;
TaskLedC.Leds->LedsC;
}/**
*TaskLedC.nc
*/
moduleTaskLedC
{
uses
{
interfaceBoot;
interfaceLeds;
}
}
implementation
{
taskvoidCalcTask()
{
uint32_ti;
//開始執(zhí)行任務(wù),打開LED0
callLeds.led0On();
//模擬耗時任務(wù)-數(shù)據(jù)累加
for(i=0;i<300000;i++)
{
//執(zhí)行空指令
asm("NOP");
}
//任務(wù)執(zhí)行完畢-關(guān)閉LED1
callLeds.led0Off();
}
eventvoidBoot.booted()
{
//提交耗時任務(wù)
postCalcTask();
}
}
#Makefile
COMPONENT=TaskLedAppC
include$(MAKERULES)
編譯下載程序到開發(fā)板中,觀察結(jié)果,將看到LED0亮大約0.2秒鐘然后滅掉。2.7.2同步與異步
nesC語言區(qū)分異步(async)和同步(sync)代碼,其中:
異步代碼:指中斷處理程序,以及它調(diào)用的命令函數(shù)和事件函數(shù)。異步函數(shù)用關(guān)鍵字async修飾。
同步代碼:異步代碼以外的代碼都是同步代碼。同步函數(shù)用關(guān)鍵字sync修飾或默認(rèn)不使用。
nesC對異步和同步代碼有如下規(guī)定:
在異步代碼中不能包含任何同步函數(shù)。在異步代碼中執(zhí)行同步函數(shù)的唯一方法是提交(post)一個任務(wù)。
任務(wù)的運(yùn)行的是同步操作。
同步函數(shù)可以調(diào)用異步函數(shù)。
應(yīng)用程序應(yīng)盡量使用同步代碼,因?yàn)楫惒酱a會出現(xiàn)數(shù)據(jù)競爭的沖突。
本章前面例子操作代碼都是同步的,不再舉例。這里舉例說明一下異步命令函數(shù)的寫法。接口Random是系統(tǒng)提供的用于操作隨機(jī)數(shù)的,其接口定義代碼如代碼2-2所示?!敬a2-2】Random.nc
/**
*Random.nc
*/
interfaceRandom
{
//異步命令函數(shù):產(chǎn)生一個32位的隨機(jī)數(shù)
asynccommanduint32_trand32();
//異步命令函數(shù):產(chǎn)生一個16位的隨機(jī)數(shù)
asynccommanduint16_trand16();
}
實(shí)現(xiàn)Random接口的組件是RandomMlcgC組件,它的部分代碼如代碼2-3所示?!敬a2-3】RandomMlcgC.nc
/**
*RandomMlcgC.nc
*/
moduleRandomMlcgC@safe(){
providesinterfaceInit;
providesinterfaceParameterInit<uint16_t>asSeedInit;
providesinterfaceRandom;
}
implementation
{
…
asynccommanduint32_tRandom.rand32()
{
uint32_tmlcg,p,q;
uint64_ttmpseed;
atomic
{
tmpseed=(uint64_t)33614U*(uint64_t)seed;
q=tmpseed; /*low*/
q=q>>1;
p=tmpseed>>32; /*hi*/
mlcg=p+q;
if(mlcg&0x80000000)
{ mlcg=mlcg&0x7FFFFFFF;
mlcg++;
}
seed=mlcg;
}
returnmlcg;
}
asynccommanduint16_tRandom.rand16()
{
return(uint16_t)callRandom.rand32();
}2.7.3原子性代碼
異步代碼將帶來一個問題:某段代碼在執(zhí)行過程可能被搶占,即被暫停運(yùn)行,在系統(tǒng)執(zhí)行完其他代碼后,再回到被中斷的位置繼續(xù)往下運(yùn)行。在實(shí)際應(yīng)用中若需要保護(hù)某段代碼不被搶占(如中斷處理函數(shù)中的數(shù)據(jù)處理),可以使用atomic(關(guān)鍵字)代碼塊,即原子性代碼,其語法如語法2-26所示。
【語法2-26】atomic代碼塊
atomic
{
//代碼
}例如,接口Leds(該接口的詳細(xì)說明見2.8節(jié))的具體實(shí)現(xiàn)組件是LedsP,在該組件中實(shí)現(xiàn)的Leds.get()命令函數(shù)可以返回各LED設(shè)置的位掩碼,為了防止被搶占,其中用到了原子性代碼,如代碼2-4?!敬a2-4】LedsP.nc
moduleLedsP@safe()
{
provides
{
interfaceInit;
interfaceLeds;
}
uses
{
…
}
}
implementation
{…
asynccommanduint8_tLeds.get()
{
uint8_trval;
atomic
{
rval=0;
if(!callLed0.get())
{
rval|=LEDS_LED0;
}
if(!callLed1.get())
{
rval|=LEDS_LED1;
}
if(!callLed2.get()) {
rval|=LEDS_LED2;
}
if(!callLed3.get())
{
rval|=LEDS_LED3;
}
}
returnrval;
}
}2.7.4中斷
中斷是實(shí)現(xiàn)異步操作的重要手段,nesC語言可以使用中斷函數(shù)實(shí)現(xiàn)中斷,中斷函數(shù)代碼要在模塊內(nèi)實(shí)現(xiàn),其常用語法格式如語法2-27所示。
【語法2-27】中斷函數(shù)
CC2530_INTERRUPT(中斷向量)
{
atomic
{
//中斷函數(shù)代碼
}
}
2.8常用接口和組件
2.8.1系統(tǒng)啟動接口Boot
1.接口簡介
Boot接口用于通知應(yīng)用程序TinyOS已經(jīng)啟動完畢(所有的必要組件已經(jīng)初始化完成)。Boot接口定義在“tos/interfaces/Boot.nc”文件中,該接口只有一個booted事件,代碼如代碼2-6所示。
【代碼2-6】Boot.nc
interfaceBoot
{
eventvoidbooted();
}
2.接口提供者
Boot接口由MainC組件提供,組件定義在“tos/system/MainC.nc”中,代碼如代碼2-7所示。
【代碼2-7】MainC.nc
configurationMainC
{
providesinterfaceBoot;
usesinterfaceInitasSoftwareInit;
}
implementation
{
componentsPlatformC,RealMainP,TinySchedulerC;
…
Boot=RealMainP;
}
3.用法簡介
每個TinyOS應(yīng)用程序都需要在核心應(yīng)用模塊中使用Boot接口(即前述的入口事件函數(shù)),且要完成以下工作:
實(shí)現(xiàn)“eventvoidBoot.booted()”事件函數(shù)作為程序入口。
在頂層配置組件中實(shí)例化MainC組件,并將核心應(yīng)用模塊連接至MainC組件。
示例代碼如示例2-16所示?!臼纠?-16】Boot接口的使用
//核心應(yīng)用模塊文件:TestC.nc
moduleTestC
{
usesinterfaceBoot;
…
}
implementation
{
…
eventvoidBoot.booted()
{
…
}
}//頂層配置組件文件:TestAppC.nc
configurationTestAppC
{
}
implementation
{
componentsMainC,TestC;
…
MainC.Boot<-TestC.Boot;
…
}2.8.2LED接口Leds
1.接口簡介
接口Leds用于控制設(shè)備的LED,該接口使用比較頻繁,既可以用于設(shè)備的輸出提示,又可以在程序開發(fā)過程中用于代碼調(diào)試。該接口定義在“tos/interfaces/Leds.nc”文件中,本書配套的平臺mytinyos新增了LED3,移植后的接口在“tos/platforms/cc2530/Leds.nc”文件中,代碼如代碼2-8所示(相關(guān)功能請參考代碼中的注釋)。【代碼2-8】LedsC.nc
#include"Leds.h"
interfaceLeds
{
//打開LED0
asynccommandvoidled0On();
//關(guān)閉LED0
asynccommandvoidled0Off();
//切換LED0狀態(tài)(原來是關(guān)則打開,原來是開則關(guān))
asynccommandvoidled0Toggle();
//LED1控制
asynccommandvoidled1On();
asynccommandvoidled1Off();
asynccommandvoidled1Toggle();//LED2控制
asynccommandvoidled2On();
asynccommandvoidled2Off();
asynccommandvoidled2Toggle();
//LED3控制
asynccommandvoidled3On();
asynccommandvoidled3Off();
asynccommandvoidled3Toggle();
//獲得LED設(shè)置的掩碼
asynccommanduint8_tget();
//用掩碼設(shè)置LED
asynccommandvoidset(uint8_tval);
}
2.接口提供者
Leds接口由LedsC組件提供,組件定義在“tos/platforms/cc2530/LedsC.nc”文件中,代碼如代碼2-9所示。
【代碼2-9】LedsC.nc
configurationLedsC
{
providesinterfaceLeds;
}implementation
{
componentsLedsP,PlatformLedsC;
Leds=LedsP;
LedsP.Init<-PlatformLedsC.Init;
LedsP.Led0->PlatformLedsC.Led0;
LedsP.Led1->PlatformLedsC.Led1;
LedsP.Led2->PlatformLedsC.Led2;
LedsP.Led3->PlatformLedsC.Led3;
}
3.用法簡介
當(dāng)組件要使用Leds接口時,需要做以下工作:
在組件內(nèi)聲明Leds接口。
在組件中調(diào)用Leds接口的相關(guān)命令函數(shù)控制LED。
在程序的配置組件內(nèi)將使用Leds接口的組件連接至接口的提供者LedsC。
示例代碼請參考任務(wù)描述2.D.4。2.8.3定時器接口Timer
1.接口簡介
定時器接口是一個通用接口,可以多次實(shí)例化,為程序提供定時作用。該接口定義在“tos/lib/timer/Timer.nc”文件中,代碼如代碼2-10所示(命令和事件函數(shù)的作用參見注釋)?!敬a2-10】Timer.nc
#include"Timer.h"
interfaceTimer<precision_tag>
{
//設(shè)置定時器的時間周期為dt,并啟動定時器
commandvoidstartPeriodic(uint32_tdt);
//啟動一次定時器,dt是超時時間
commandvoidstar
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 優(yōu)化教育資源分配提升學(xué)校教學(xué)水平
- 2025年北海貨物運(yùn)輸駕駛員從業(yè)資格考試系統(tǒng)
- 2025年黔南客貨運(yùn)從業(yè)資格證考試教材
- 2025年馬鞍山貨運(yùn)資格證模擬考試題
- 2025年浙江從業(yè)資格證模擬考試題貨運(yùn)考題
- 以創(chuàng)意引領(lǐng)的現(xiàn)代科技產(chǎn)品包裝設(shè)計
- 2025年河源貨運(yùn)上崗證模擬考試試題
- 從職場小白到創(chuàng)業(yè)領(lǐng)袖的成長歷程
- 企業(yè)培訓(xùn)中提升員工情商的方法和技巧
- 不同行業(yè)的客戶關(guān)系管理策略對比
- 鄉(xiāng)村振興背景下農(nóng)村電商發(fā)展策略研究
- 瓦斯隧道瓦斯監(jiān)測及檢測專業(yè)方案
- 最優(yōu)化計算智慧樹知到答案2024年華南理工大學(xué)
- 22G101三維彩色立體圖集
- 從創(chuàng)意到創(chuàng)業(yè)智慧樹知到期末考試答案章節(jié)答案2024年湖南師范大學(xué)
- 力的合成與分解 說課課件-2024-2025學(xué)年高一上學(xué)期物理人教版(2019)必修第一冊
- 建筑施工安全生產(chǎn)治本攻堅三年行動方案(2024-2026年)
- 瀝青路面養(yǎng)護(hù)銑刨施工技術(shù)規(guī)范.文檔
- 油浸式電力變壓器(電抗器)現(xiàn)場低頻加熱試驗(yàn)導(dǎo)則
- 橋式、門式起重機(jī)安裝竣工試驗(yàn)報告書
- 中央空調(diào)采購安裝投標(biāo)方案(技術(shù)方案)
評論
0/150
提交評論