




版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡(jiǎn)介
第2章nesC語(yǔ)言基礎(chǔ)
2.1nesC概述2.2nesC和C的比較2.3nesC程序結(jié)構(gòu)2.4接口2.5組件2.6nesC高級(jí)編程2.7并發(fā)模型2.8常用接口和組件2.9可視化組件關(guān)系圖
2.1nesC概述
nesC(NetworkEmbeddedSystemC)是專門為編寫TinyOS以及進(jìn)行TinyOS編寫應(yīng)用程序而發(fā)明的一種語(yǔ)言,用nesC而不是C語(yǔ)言編寫TinyOS(TinyOS最初是由C和匯編編寫的)主要基于以下兩點(diǎn):
nesC在語(yǔ)法上支持TinyOS需要的并發(fā)執(zhí)行模型。
nesC編寫的源碼為編譯器的優(yōu)化提供了可能,最終可以縮小可執(zhí)行代碼尺寸。
因此,使用nesC生成的程序,更加適合無(wú)線傳感網(wǎng)絡(luò)節(jié)點(diǎn)。由于nesC的學(xué)習(xí)有點(diǎn)難度,建議從C和nesC的區(qū)別入手,輔之以示例,逐漸掌握其編程規(guī)則。
nesC的版本并沒(méi)有遵循與TinyOS的版本標(biāo)識(shí)一同發(fā)布,TinyOS目前的最新版本是2.1.2,而nesC語(yǔ)言的最新版是1.3。nesC的主要版本變化如表2-1所示。
2.2nesC和C的比較
按照TinyOS官方說(shuō)法,nesC來(lái)源于C,是C語(yǔ)言的一種變種,所有的C語(yǔ)言中的結(jié)構(gòu)體、函數(shù)、甚至指針(建議少使用指針)、數(shù)據(jù)類型以及注釋格式在nesC中依然是合法的,對(duì)于C程序員來(lái)說(shuō),nesC提出了三個(gè)“新概念”:
組件:一組可重用的代碼和數(shù)據(jù)組合,類似于C++中的類。
接口:一組可為其他組件服務(wù)的函數(shù)和事件集合,是組件之間交互的界面。
并發(fā)執(zhí)行模型:基于任務(wù)和中斷的處理機(jī)制,定義了組件之間如何調(diào)用,以及中斷代碼和非中斷代碼如何交互執(zhí)行規(guī)則。
nesC與C語(yǔ)言的區(qū)別主要有程序組成主體、模塊之間的調(diào)用、命名空間、編程思想等幾方面,下面將對(duì)這幾個(gè)方面進(jìn)行分析。2.2.1程序組成主體
C程序是由一系列的函數(shù)組成的,而nesC程序是由“組件”組成的。用C語(yǔ)言實(shí)現(xiàn)第1章中的LedOn示例如示例2-1所示。
【示例2-1】main.c、LedOn.h、LedOn.c
/*C語(yǔ)言實(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個(gè)文件:
main.c:main()函數(shù)所在文件,包含程序的主邏輯代碼。
LedOn.h:頭文件,配合LedOn.c文件實(shí)現(xiàn)函數(shù)聲明,即LedOn模塊的接口聲明文件。
LedOn.c:LedOn模塊的實(shí)現(xiàn)文件,通過(guò)LedOn()函數(shù)實(shí)現(xiàn)LED的點(diǎn)亮。
通過(guò)上述代碼與第1章中的nesC程序示例相比較可以看出:
nesC程序組成:以文件為單位來(lái)組織程序,每個(gè)文件由一個(gè)“組件”組成,“組件”是nesC程序的基本“模塊”,在組件中可以聲明“使用(uses)”接口或“提供(provides)”接口,可以定義變量和函數(shù)。
C程序組成:以文件為單位來(lái)組織程序,文件由一系列函數(shù)組成,函數(shù)是C程序的基本“模塊”。
nesC“接口”類似于C語(yǔ)言中的頭文件,它聲明了一系列的函數(shù)集合,為組件提供服務(wù),即被組件使用或由某個(gè)組件向外提供其實(shí)現(xiàn)。2.2.2模塊之間的調(diào)用
C程序與nesC程序的模塊之間調(diào)用規(guī)則有以下不同:
nesC程序模塊(即組件)之間的調(diào)用,需要顯示指定(通過(guò)“配件”指定,如第1章示例中的“LedOnAppC”組件)其連接關(guān)系,其在編譯之前就已經(jīng)明確了,因此是“靜態(tài)”調(diào)用關(guān)系。
C程序模塊(即函數(shù))之間的調(diào)用,根據(jù)代碼邏輯可以“隨意”調(diào)用,甚至可以通過(guò)“函數(shù)指針”動(dòng)態(tài)改變調(diào)用關(guān)系。2.2.3命名空間
從語(yǔ)法上看,nesC“組件”本質(zhì)上是由C語(yǔ)言的變量和函數(shù)組成的,nesC組件、函數(shù)和變量的作用域(命名空間)有以下特點(diǎn):
在nesC中也可以定義全局變量,但建議盡量少使用全局變量。
nesC組件作用域是全局的。
nesC組件中的變量和函數(shù)對(duì)整個(gè)組件可見(jiàn),組件外部不可以訪問(wèn)。2.2.4編程思想
相對(duì)于C語(yǔ)言來(lái)說(shuō),nesC的程序組成主體、模塊調(diào)用等不同所帶來(lái)的直接結(jié)果就是編程思想的不同,這也是nesC與C語(yǔ)言最本質(zhì)的區(qū)別。
nesC編程思想就是事件驅(qū)動(dòng)、組件式編程?;诖?,程序設(shè)計(jì)時(shí)要注意以下兩點(diǎn):
通過(guò)使用“組件”來(lái)劃分程序的業(yè)務(wù)功能。
要注意捕捉系統(tǒng)事件(如硬件中斷),并且通過(guò)處理事件來(lái)完成整個(gè)程序的運(yùn)行。
另外,在進(jìn)行nesC編程時(shí),應(yīng)使用公認(rèn)的編程約定(詳細(xì)內(nèi)容參見(jiàn)本書實(shí)踐篇2的知識(shí)拓展)。
總結(jié)起來(lái),nesC與C語(yǔ)言的區(qū)別如表2-2所示。
2.3nesC程序結(jié)構(gòu)
本節(jié)從宏觀角度介紹nesC程序的組成。
2.3.1程序文件
一般情況下,nesC程序文件組成有以下幾部分:
C語(yǔ)言頭文件:TinyOS程序的運(yùn)行需要少量的C語(yǔ)言頭文件,它們被組件文件包含從而參與程序的編譯。這些頭文件主要包括結(jié)構(gòu)體、數(shù)據(jù)類型以及宏定義等。
接口文件:當(dāng)系統(tǒng)提供的接口不能滿足要求時(shí),用戶可自定義接口類型。
組件文件:包括程序中的邏輯算法代碼和組件配置關(guān)系文件。
Makefile文件:被make工具調(diào)用的編譯管理文件。
接口文件和組件文件是nesC程序的重要語(yǔ)法文件,也是本章介紹的重點(diǎn)。2.3.2組件
一個(gè)完整的nesC程序是由多個(gè)組件組成的,組件是nesC程序的可運(yùn)行模塊,它們交互使用(通過(guò)接口相互調(diào)用)形成有意義的nesC程序。組件分為兩類:模塊和配置組件(簡(jiǎn)稱配件)。
模塊(Module)是nesC程序的邏輯功能實(shí)體,通過(guò)提供接口或使用接口以實(shí)現(xiàn)某個(gè)確切的業(yè)務(wù)算法。
配件(Configuration)負(fù)責(zé)把其他組件裝配起來(lái),把組件“使用的接口”綁定到“提供該接口”的組件上。
通俗地說(shuō),模塊是包含“可執(zhí)行代碼”的組件,配件是包含“組件關(guān)系”的組件。2.3.3程序結(jié)構(gòu)
一般情況下,一個(gè)可運(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語(yǔ)言不同,nesC程序的入口需要在“核心應(yīng)用模塊中”使用(uses)系統(tǒng)提供的“Boot”接口,然后在程序中實(shí)現(xiàn)該接口的“booted”事件函數(shù),此函數(shù)就是nesC應(yīng)用程序的入口,關(guān)于接口的詳細(xì)信息見(jiàn)下節(jié)。當(dāng)程序運(yùn)行時(shí),由TinyOS系統(tǒng)自動(dòng)調(diào)用該函數(shù)。對(duì)于入口函數(shù)要注意以下幾點(diǎn):
入口函數(shù)必須存在于某個(gè)模塊中(即核心應(yīng)用模塊中),本質(zhì)上是系統(tǒng)接口Boot的“事件函數(shù)”。入口函數(shù)有且只能有一個(gè)。
入口函數(shù)的主要功能如下:
應(yīng)用程序初始化,如啟動(dòng)定時(shí)器、點(diǎn)亮LED等。
啟動(dòng)程序的其他函數(shù)。
入口函數(shù)的語(yǔ)法如語(yǔ)法2-1所示。
【語(yǔ)法2-1】入口函數(shù)
//入口函數(shù)—其實(shí)是Boot接口的事件函數(shù)
eventvoidBoot.booted()
{
//程序啟動(dòng)代碼
//一般是啟動(dòng)一個(gè)任務(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ù)直接或間接啟動(dòng)(或調(diào)用)的。
2.4接口
2.4.1接口規(guī)則
接口提供給組件來(lái)使用,并且必須由某個(gè)組件來(lái)實(shí)現(xiàn)才有意義。nesC關(guān)于接口的詳細(xì)規(guī)定如下:
接口由一個(gè)或多個(gè)命令(command)函數(shù)和事件(event)函數(shù)組成,可以只有命令函數(shù)或只有事件函數(shù)。
接口可以被多個(gè)組件來(lái)實(shí)現(xiàn)(由配件來(lái)指定具體使用哪個(gè)實(shí)現(xiàn))。
實(shí)現(xiàn)接口的組件,必須實(shí)現(xiàn)接口中的所有命令函數(shù)。
使用接口的組件,必須實(shí)現(xiàn)接口中的所有事件函數(shù)。
一個(gè)接口對(duì)應(yīng)一個(gè)nesC源文件(即一個(gè)nc文件中只能有一個(gè)接口定義)。
關(guān)于組件和配件的詳細(xì)內(nèi)容見(jiàn)1.4節(jié)。2.4.2接口的定義
接口在nc文件中通過(guò)關(guān)鍵字“interface”和一對(duì)大括號(hào)來(lái)定義,如語(yǔ)法2-2所示。
【語(yǔ)法2-2】接口定義
interface
接口名
{
command
類型函數(shù)名(形參列表);/*接口命令聲明*/
…
event
類型函數(shù)名(形參列表);/*接口事件聲明*/
…
}接口中的函數(shù)有兩類:
命令函數(shù),使用“command”關(guān)鍵字修飾,由實(shí)現(xiàn)該接口的組件(稱為提供者)提供具體的函數(shù)實(shí)現(xiàn),并且由使用該接口的組件(稱為使用者)來(lái)調(diào)用。
事件函數(shù),使用“event”關(guān)鍵字修飾,由使用該接口的組件提供具體的函數(shù)實(shí)現(xiàn),并且由提供該接口的組件來(lái)調(diào)用,以用于實(shí)現(xiàn)“事件通知”效果。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.1,編寫一個(gè)溫度傳感器接口文件,聲明“采集數(shù)據(jù)”的命令函數(shù)和“采集完成”的事件函數(shù)。【描述2.D.1】TempSensor.nc
interfaceTempSensor
{
/**
*讀傳感器
*無(wú)參數(shù)
*/
commandvoidReadData();
/**
*通知傳感器數(shù)據(jù)已經(jīng)讀完
*參數(shù):pData,返回的數(shù)據(jù)指針
*參數(shù):nByteSize,返回?cái)?shù)據(jù)的字節(jié)數(shù)
*/
eventvoidReadDone(char*pData,intnByteSize);
}2.4.3分階段操作
nesC提供了分階段(Split-phase)操作機(jī)制,具體實(shí)現(xiàn)是通過(guò)“分階段操作接口”解決某些耗時(shí)操作的等待問(wèn)題,例如讀取傳感器時(shí)需要等待模數(shù)轉(zhuǎn)換完成。前面例子中的TempSensor接口即是分階段接口。
分階段接口是雙向的,至少包含以下兩個(gè)函數(shù):
向下的命令函數(shù):用于啟動(dòng)耗時(shí)操作(如ReadData命令函數(shù))。命令函數(shù)被看做為分階段作業(yè)的起始部分。
向上的事件函數(shù):用于通知調(diào)用操作完成(如ReadDone事件函數(shù))。事件函數(shù)被看做為分階段作業(yè)的后續(xù)部分。
編寫nesC程序時(shí),要善于使用系統(tǒng)提供的分階段操作接口,必要時(shí)用戶自己也可以編寫分階段操作接口。
2.5組件
2.5.1組件定義
每個(gè)組件(模塊或配件)對(duì)應(yīng)一個(gè)nc源文件,組件定義語(yǔ)法如語(yǔ)法2-3所示。【語(yǔ)法2-3】組件定義
module
模塊名configuration
配件名
{{
//接口聲明//接口聲明
}}
implemention
implemention
{{
//模塊實(shí)現(xiàn)//模塊實(shí)現(xiàn)
}}2.5.2接口聲明
模塊或配件的聲明區(qū)是一樣的,都是用于聲明組件“要使用”或?qū)ν狻疤峁钡慕涌凇B暶鹘涌诘耐暾Z(yǔ)法如語(yǔ)法2-4所示。
【語(yǔ)法2-4】接口聲明
//聲明要使用的接口
uses
{
interfaceXasY
...
}
//聲明要提供的接口
provides
{
interfaceAasB
...
}
或者:
【語(yǔ)法2-5】接口聲明
//聲明要使用的接口
usesinterfaceXasY
...
//聲明要提供的接口
providesinterfaceAasB
...
其中關(guān)鍵字“as”用于為接口“X”或“A”命名接口別名,它們可以省略,如語(yǔ)法2-6所示。
【語(yǔ)法2-6】接口聲明
usesinterfaceX
其實(shí),上述代碼是“usesinterfaceXasX”的縮寫。
當(dāng)需要多次提供或使用同一接口時(shí),必須使用關(guān)鍵字“as”來(lái)區(qū)分同一類型接口的多個(gè)實(shí)例。例如模塊LedsP通過(guò)使用關(guān)鍵字“as”,為接口GenerolIO的三個(gè)實(shí)例定義了不同的別名,如示例2-3所示?!臼纠?-3】LedsP.nc
//模塊LedsP,“@”代表屬性
moduleLedsP@safe()
{
provides
{
interfaceInit;
interfaceLeds;
}
uses
{
interfaceGeneralIOasLed0;
interfaceGeneralIOasLed1;
interfaceGeneralIOasLed2;
interfaceGeneralIOasLed3;
}}
implementation
{
…
}
2.5.3模塊
模塊的通用定義語(yǔ)法如語(yǔ)法2-7所示。
【語(yǔ)法2-7】模塊定義
module模塊名
{
//接口聲明
}
//模塊實(shí)現(xiàn)區(qū)
implementation
{
變量定義
普通函數(shù)
{
…
}
task任務(wù)函數(shù)
{
…
}
command命令函數(shù)
{
…
}
event事件函數(shù)
{
…
}
}
1.模塊變量
模塊中變量的定義與C語(yǔ)言中的變量是一致的,可以在定義時(shí)進(jìn)行初始化,也可先定義后初始化。關(guān)于模塊變量有以下幾點(diǎn)要注意:
模塊變量的作用域在模塊內(nèi),其他組件不可訪問(wèn)。
模塊變量是“靜態(tài)變量”,在程序生存期內(nèi)一直存在,類似于C語(yǔ)言中用static聲明的變量。
不提倡使用malloc或其他庫(kù)函數(shù)來(lái)動(dòng)態(tài)分配內(nèi)存。推薦使用靜態(tài)內(nèi)存,例如數(shù)組。
2.模塊普通函數(shù)
模塊中的普通函數(shù)的定義與C語(yǔ)言中的變量是一致的,在使用前先聲明。與模塊變量作用域一樣,普通函數(shù)的作用域在模塊內(nèi),其他組件不能訪問(wèn)。
3.命令函數(shù)實(shí)現(xiàn)
命令函數(shù)由提供接口的代碼來(lái)實(shí)現(xiàn),需要用到關(guān)鍵字“command”,語(yǔ)法格式如語(yǔ)法2-8所示。
【語(yǔ)法2-8】命令函數(shù)
command
類型接口名.命令函數(shù)(形參表)
{
//命令函數(shù)的實(shí)現(xiàn)
}
4.調(diào)用命令函數(shù)
接口的使用者,要調(diào)用接口的命令函數(shù)來(lái)執(zhí)行接口的某項(xiàng)功能,使用call關(guān)鍵字,其語(yǔ)法格式如語(yǔ)法2-9所示。
【語(yǔ)法2-9】調(diào)用命令函數(shù)
//不需要獲得返回值
call
接口名.命令函數(shù)(函數(shù)實(shí)參);
//需要獲得返回值
變量=call接口名.命令函數(shù)(函數(shù)實(shí)參);
上述語(yǔ)句,可以認(rèn)為是“call+函數(shù)調(diào)用”,請(qǐng)注意,語(yǔ)句后面的分號(hào)不能省略。
5.事件觸發(fā)
為接口提供實(shí)現(xiàn)的模塊,一般要提供調(diào)用事件函數(shù)的代碼,即觸發(fā)事件,以通知調(diào)用該接口的組件,使用signal關(guān)鍵字,其語(yǔ)法格式如語(yǔ)法2-10所示。
【語(yǔ)法2-10】事件觸發(fā)
signal接口名.事件函數(shù)(函數(shù)參數(shù));
上述語(yǔ)句,可以認(rèn)為是“signal+函數(shù)調(diào)用”語(yǔ)句,請(qǐng)注意,語(yǔ)句后面的分號(hào)不能省略。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.2,在2.D.1的基礎(chǔ)上,編寫一個(gè)模塊實(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)是由使用接口的組件來(lái)提供的,需要用到關(guān)鍵字“event”,其語(yǔ)法格式如語(yǔ)法2-11所示。
【語(yǔ)法2-11】事件實(shí)現(xiàn)
event
類型接口名.事件函數(shù)(形參表)
{
//事件函數(shù)的實(shí)現(xiàn)
}
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.3,在2.D.2的基礎(chǔ)上,編寫一個(gè)模塊使用溫度傳感器接口?!久枋?.D.3】ReadTempSensorC.nc
moduleReadTempSensorC
{
usesinterfaceBoot;
usesinterfaceTempSensor;
usesinterfaceLeds;
}
implementation
{
taskvoidReadTempSensor()
{
//點(diǎn)亮LED0表示啟動(dòng)了數(shù)據(jù)采集
callLeds.led0On();
//調(diào)用TempSensor接口的ReadData函數(shù)開(kāi)始數(shù)據(jù)采集
callTempSensor.ReadData();
}
eventvoidTempSensor.ReadDone(char*pData,intnByteSize)
{
//該函數(shù)被調(diào)用時(shí),表示數(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編譯器才能將程序編譯成一個(gè)合法的可運(yùn)行的程序。配件的通用定義語(yǔ)法如語(yǔ)法2-12所示?!菊Z(yǔ)法2-12】配件定義
configuration配件名
{
//接口聲明
}
implementation
{
//組件聲明語(yǔ)句
//組件連接語(yǔ)句
}
1.組件聲明
聲明組件的作用是聲明配件要管理的所有組件(包括模塊和組件),為后面的組件鏈接語(yǔ)句提供合法標(biāo)示。組件聲明使用關(guān)鍵字“components”,并且可以使用關(guān)鍵字“as”指定別名,語(yǔ)法格式如語(yǔ)法2-13所示。
【語(yǔ)法2-13】組件聲明
components
組件AasAA,組件BasBB,組件CasCC,…;
或者:
【語(yǔ)法2-14】組件聲明
components組件AasAA;
components組件BasBB;
components組件CasCC;
…與接口聲明類似,“as+別名”可以省略。使用關(guān)鍵字“as”設(shè)定別名的意義有以下幾個(gè):
簡(jiǎn)化復(fù)雜長(zhǎng)名字的組件。
多次實(shí)例化同一通用組件(關(guān)于通用組件,請(qǐng)參見(jiàn)2.6.3節(jié))。
代碼易于移植,例如當(dāng)需要在配件中更換組件時(shí),只需替換組件聲明這一行,不需要修改程序中使用該組件的代碼行。
2.連接
配件中的連接(或綁定,英語(yǔ)名稱是Wiring)代碼用于把配件聲明的接口或組件聯(lián)系在一起,其作用有以下幾點(diǎn):
明確某個(gè)組件使用的接口是由哪個(gè)組件提供的實(shí)現(xiàn)。
明確當(dāng)前配件對(duì)外提供的接口是由哪個(gè)組件提供的實(shí)現(xiàn)。
連接操作的語(yǔ)法如語(yǔ)法2-15所示。
【語(yǔ)法2-15】連接操作
組件.接口連接符組件.接口
其中連接符有兩類:
“->”或“<-”:這兩個(gè)操作符作用是一樣的,箭頭從使用接口的組件指向提供接口的組件。使用時(shí)要求符號(hào)兩邊的組件必須是配件“實(shí)現(xiàn)區(qū)”聲明的組件。
“=”:主要用于輸出配件中對(duì)外提供的接口。使用時(shí)要求符號(hào)兩邊至少有一個(gè)是配件“聲明區(qū)”聲明的接口。
在實(shí)際使用上述語(yǔ)法時(shí),其中一端的“.接口”可以省略,例如:
LedsP<-PlatformLedsC.Init;
等價(jià)于
LedsP.Init<-PlatformLedsC.Init;
系統(tǒng)提供的Leds配件連接操作示例如示例2-4所示?!臼纠?-4】LedsC.nc
configurationLedsC
{
//聲明對(duì)外提供的接口
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高級(jí)編程
2.6.1參數(shù)化接口
參數(shù)化接口(ParameterizedInterfaces)的作用就是允許為組件提供同類型接口的多個(gè)實(shí)例。參數(shù)化接口實(shí)質(zhì)上是接口數(shù)組,數(shù)組的索引是接口參數(shù),其標(biāo)示了調(diào)用組件,并且在編譯時(shí)確定。參數(shù)化接口的定義與普通接口的定義沒(méi)有區(qū)別,下面介紹其聲明、實(shí)現(xiàn)和連接的語(yǔ)法。
1.參數(shù)化接口聲明
只有當(dāng)組件在對(duì)外提供接口時(shí)(使用時(shí)的聲明與普通接口一樣)才可能出現(xiàn)參數(shù)化接口(接口數(shù)組)的聲明,接口聲明時(shí)要有數(shù)組下標(biāo),語(yǔ)法格式如語(yǔ)法2-16所示。
【語(yǔ)法2-16】參數(shù)接口的聲明
providesinterface接口名[數(shù)據(jù)類型標(biāo)示符];
例如系統(tǒng)組件ActiveMessageC對(duì)外提供了四個(gè)參數(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ù)名后要有數(shù)組下標(biāo),語(yǔ)法格式如語(yǔ)法2-17所示。
【語(yǔ)法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ù)化接口在連接時(shí),根據(jù)連接操作符兩端的接口是否是參數(shù)化接口,其語(yǔ)法格式分為兩種情況。
(1)操作符兩端都是參數(shù)化接口。
這種情況下,連接語(yǔ)句的寫法與兩端都是普通接口的寫法一致。例如,系統(tǒng)配件TinySchedulerC對(duì)外提供參上述數(shù)化接口TaskBasic,其連接代碼如示例2-7所示?!臼纠?-7】TinySchedulerC.nc
configurationTinySchedulerC
{
providesinterfaceScheduler;
providesinterfaceTaskBasic[uint8_tid];
}
implementation
{
componentsSchedulerBasicPasSched;
componentsMcuSleepCasSleep;
Scheduler=Sched;
//等價(jià)于TaskBasic=SchedulerBasicP.TaskBasic,兩端都是參數(shù)化接口
TaskBasic=Sched;
Sched.McuSleep->Sleep;
}
(2)其中一端是參數(shù)化接口。
這種情況下,參數(shù)化接口要填寫確定的參數(shù)(即數(shù)組索引)。語(yǔ)法格式如語(yǔ)法2-18所示。
【語(yǔ)法2-18】參數(shù)化接口連接
組件1.接口名1連接符組件2.接口名2[索引值]
其中,索引值是一個(gè)整數(shù),由用戶提供,告訴編譯器以標(biāo)示調(diào)用組件(即接口的使用者是誰(shuí))。為了避免程序出錯(cuò),索引值可以使用以下兩個(gè)函數(shù)獲得:
unique(char*identifer)函數(shù):參數(shù)為字符串,如果程序包含n個(gè)相同字符串作為參數(shù)的unique()調(diào)用,每個(gè)調(diào)用返回一個(gè)0~n-1之間的無(wú)符號(hào)整數(shù),且互不相同(注意:不同字符串參數(shù)的調(diào)用返回值有可能相同)。
uniqueCount(char*identifer)函數(shù):參數(shù)為字符串,如果程序包含n個(gè)相同字符串為參數(shù)的uniqueCount()調(diào)用,每個(gè)調(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í)要使用尖括號(hào)將數(shù)據(jù)類型擴(kuò)起來(lái),語(yǔ)法如語(yǔ)法2-19所示。
【語(yǔ)法2-19】通用接口定義
interface接口名<類型1,類型2,類型n>
{
//事件函數(shù)或命令函數(shù)聲明
}
對(duì)于上述定義有以下規(guī)則:
大括號(hào)內(nèi)的類型,可以有一個(gè)或多個(gè),之間使用逗號(hào)分隔。
接口內(nèi)聲明的事件函數(shù)或命令函數(shù)可以使用或不適用大括號(hào)內(nèi)的類型。
示例2-9是TinyOS中的通用接口Read的定義。
【示例2-9】Read.nc
interfaceRead<val_t>
{
commanderror_tread();
eventvoidreadDone(error_tresult,val_tval);
}
上述代碼中,通用接口Read具有一個(gè)參數(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)
提供或使用通用接口的模塊在聲明通用接口時(shí)要在大括號(hào)內(nèi)提供確切的數(shù)據(jù)類型,而實(shí)現(xiàn)通用接口的命令和事件函數(shù)時(shí),要相應(yīng)使用聲明時(shí)提供的類型。其語(yǔ)法格式如語(yǔ)法2-20所示?!菊Z(yǔ)法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代碼。【示例2-11】TempSensorC.nc
moduleTempSensorC
{
//聲明要提供的通用接口
providesinterfaceTempSensor<char*>;
}
implementation
{
charsBuf[255];commandvoidTempSensor.ReadData()
{
…
signalTempSensor.ReadDone(sBuf,255);
}
}
示例2-12是任務(wù)描述2.D.3修改之后的ReadTempSensorC模塊代碼。【示例2-12】ReadTempSensorC.nc
moduleReadTempSensorC
{
usesinterfaceBoot;
usesinterfaceTempSensor<char*>;
usesinterfaceLeds;
}
implementation
{
…
eventvoidTempSensor.ReadDone(char*pData,intnByteSize)
{
…
}
…
}
3.通用接口連接
通用接口連接時(shí)必須保證提供者與使用者組件所聲明的接口類型參數(shù)必須匹配,否則將出現(xiàn)編譯錯(cuò)誤。上例中的通用接口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.通用組件定義
通用組件定義時(shí)使用關(guān)鍵字generic,并且通用組件有一個(gè)參數(shù)列表(可以為空)。通用組件分為通用模塊和通用配件,其定義語(yǔ)法如語(yǔ)法2-21和2-22所示?!菊Z(yǔ)法2-21】通用模塊定義
genericmodule模塊名(參數(shù)列表)
{
//聲明接口
}
implemention
{
//模塊實(shí)現(xiàn)
}【語(yǔ)法2-22】通用組件定義
genericconfiguration配件名(參數(shù)列表)
{
//聲明接口
}
implemention
{
//配件實(shí)現(xiàn)
}對(duì)于上述定義,參數(shù)列表有以下規(guī)則:
參數(shù)列表可以為空,但是組件名后面的括號(hào)不能省略。
組件實(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)先出計(jì)算的隊(duì)列組件)的代碼。【示例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ù)列表中有兩個(gè)參數(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í)器配件,可以被多次實(shí)例化,在程序中表示多個(gè)定時(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í)例化,實(shí)例化需使用關(guān)鍵字new,并傳遞實(shí)例參數(shù)(如果通用組件參數(shù)列表有參數(shù))。語(yǔ)法如語(yǔ)法2-23所示。
【語(yǔ)法2-23】通用組件實(shí)例化
componentsnew組件名(實(shí)參)as別名
上述語(yǔ)法中,如果不是多次實(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,編寫一個(gè)nesC程序使用定時(shí)器接口實(shí)現(xiàn)LED閃爍。本程序設(shè)計(jì)為三個(gè)文件:
核心應(yīng)用模塊文件,命名為“TimerLedC.nc”。
頂層配置組件文件,命名為“TimerLedAppC.nc”。
Makefile文件:編譯管理文件。
1.建立應(yīng)用程序目錄
在Cygwin的“opt/mytinyos/apps/”目錄下建立“TimerLed”目錄。如圖2-2所示。
以下三個(gè)文件使用EditPlus程序建立并保存在“TimerLed”目錄內(nèi)。圖2-2建立應(yīng)用程序目錄
2.編寫核心應(yīng)用模塊文件
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.4,使用定時(shí)器通用接口Timer聲明兩個(gè)定時(shí)器,用它們分別控制LED0和LED1,具體代碼如描述2.D.4TimerLedC.nc?!久枋?.D.4】TimerLedC.nc
moduleTimerLedC
{
uses
{
interfaceBoot;
interfaceLeds;
//使用定時(shí)器通用接口聲明兩個(gè)定時(shí)器
interfaceTimer<TMilli>asTimer0;
interfaceTimer<TMilli>asTimer1;
}
}implementation
{
//任務(wù)函數(shù)內(nèi),內(nèi)容是空
taskvoidDoStartTimer()
{
}
//程序入口事件函數(shù)
eventvoidBoot.booted()
{
//啟動(dòng)兩個(gè)定時(shí)器周期分別是250和500毫秒
callTimer0.startPeriodic(250);
callTimer1.startPeriodic(500);
postDoStartTimer();
}//定時(shí)器0事件,閃爍LED0
eventvoidTimer0.fired()
{
callLeds.led0Toggle();
}
//定時(shí)器1事件,閃爍LED1
eventvoidTimer1.fired()
{
callLeds.led1Toggle();
}
}
3.編寫頂層配置組件
通用組件TimerMilliC提供Timer接口,因此在頂層配置組件中將TimerLedC與TimerMilliC組件連接起來(lái),代碼如下。
【描述2.D.4】TimerLedAppC.nc
configurationTimerLedAppC
{
}
implementation
{
componentsMainC,TimerLedC,LedsC;
//實(shí)例化兩個(gè)通用組件
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ì)說(shuō)明參見(jiàn)第4章)。
5.編譯并下載程序
用仿真器連接好設(shè)備后,打開(kāi)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修飾的無(wú)返回值的模塊函數(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)會(huì)在合適的時(shí)間運(yùn)行它(這個(gè)時(shí)間很短),而不是立即執(zhí)行,表現(xiàn)出一種“后臺(tái)處理行為”。
3.任務(wù)函數(shù)
任務(wù)函數(shù)的語(yǔ)法如語(yǔ)法2-24所示。
【語(yǔ)法2-24】任務(wù)函數(shù)
taskvoid函數(shù)名()
{
//任務(wù)代碼
}
對(duì)于任務(wù)函數(shù)的編寫要注意以下幾點(diǎn):
關(guān)鍵字task不可省略。
函數(shù)不能有返回值。
為了降低能耗等因素,有必要盡量減少任務(wù)代碼的執(zhí)行時(shí)間,必要的情況下可將大任務(wù)分割為小任務(wù)。任務(wù)函數(shù)中的“任務(wù)代碼”是由和任務(wù)相關(guān)的算法組成的,例如讀傳感器、采樣處理等。任務(wù)被認(rèn)為是所在組件內(nèi)的一個(gè)較為耗時(shí)的操作,任務(wù)執(zhí)行完畢最終要進(jìn)行任務(wù)結(jié)果的輸出,有兩種途徑:
調(diào)用某接口的命令函數(shù)。
調(diào)用接口的事件函數(shù)來(lái)實(shí)現(xiàn)事件通知,即觸發(fā)事件。
命令函數(shù)調(diào)用和觸發(fā)事件前面已經(jīng)講過(guò),分別使用call語(yǔ)句和signal語(yǔ)句。
4.任務(wù)提交
任務(wù)函數(shù)通過(guò)post(關(guān)鍵字)語(yǔ)句提交給系統(tǒng)去調(diào)度執(zhí)行,這個(gè)過(guò)程也叫任務(wù)發(fā)布,語(yǔ)法如語(yǔ)法2-25所示。
【語(yǔ)法2-25】任務(wù)提交
post
任務(wù)函數(shù)名();
注意上述語(yǔ)句的分號(hào)不能省略。post語(yǔ)句被執(zhí)行后,立即返回,系統(tǒng)將在合適的時(shí)間稍后執(zhí)行被提交的任務(wù)。
以下代碼用于實(shí)現(xiàn)任務(wù)描述2.D.5,編寫一個(gè)nesC程序,執(zhí)行一個(gè)耗時(shí)任務(wù),當(dāng)任務(wù)執(zhí)行時(shí)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;
//開(kāi)始執(zhí)行任務(wù),打開(kāi)LED0
callLeds.led0On();
//模擬耗時(shí)任務(wù)-數(shù)據(jù)累加
for(i=0;i<300000;i++)
{
//執(zhí)行空指令
asm("NOP");
}
//任務(wù)執(zhí)行完畢-關(guān)閉LED1
callLeds.led0Off();
}
eventvoidBoot.booted()
{
//提交耗時(shí)任務(wù)
postCalcTask();
}
}
#Makefile
COMPONENT=TaskLedAppC
include$(MAKERULES)
編譯下載程序到開(kāi)發(fā)板中,觀察結(jié)果,將看到LED0亮大約0.2秒鐘然后滅掉。2.7.2同步與異步
nesC語(yǔ)言區(qū)分異步(async)和同步(sync)代碼,其中:
異步代碼:指中斷處理程序,以及它調(diào)用的命令函數(shù)和事件函數(shù)。異步函數(shù)用關(guān)鍵字async修飾。
同步代碼:異步代碼以外的代碼都是同步代碼。同步函數(shù)用關(guān)鍵字sync修飾或默認(rèn)不使用。
nesC對(duì)異步和同步代碼有如下規(guī)定:
在異步代碼中不能包含任何同步函數(shù)。在異步代碼中執(zhí)行同步函數(shù)的唯一方法是提交(post)一個(gè)任務(wù)。
任務(wù)的運(yùn)行的是同步操作。
同步函數(shù)可以調(diào)用異步函數(shù)。
應(yīng)用程序應(yīng)盡量使用同步代碼,因?yàn)楫惒酱a會(huì)出現(xiàn)數(shù)據(jù)競(jìng)爭(zhēng)的沖突。
本章前面例子操作代碼都是同步的,不再舉例。這里舉例說(shuō)明一下異步命令函數(shù)的寫法。接口Random是系統(tǒng)提供的用于操作隨機(jī)數(shù)的,其接口定義代碼如代碼2-2所示。【代碼2-2】Random.nc
/**
*Random.nc
*/
interfaceRandom
{
//異步命令函數(shù):產(chǎn)生一個(gè)32位的隨機(jī)數(shù)
asynccommanduint32_trand32();
//異步命令函數(shù):產(chǎn)生一個(gè)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原子性代碼
異步代碼將帶來(lái)一個(gè)問(wèn)題:某段代碼在執(zhí)行過(guò)程可能被搶占,即被暫停運(yùn)行,在系統(tǒng)執(zhí)行完其他代碼后,再回到被中斷的位置繼續(xù)往下運(yùn)行。在實(shí)際應(yīng)用中若需要保護(hù)某段代碼不被搶占(如中斷處理函數(shù)中的數(shù)據(jù)處理),可以使用atomic(關(guān)鍵字)代碼塊,即原子性代碼,其語(yǔ)法如語(yǔ)法2-26所示。
【語(yǔ)法2-26】atomic代碼塊
atomic
{
//代碼
}例如,接口Leds(該接口的詳細(xì)說(shuō)明見(jiàn)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語(yǔ)言可以使用中斷函數(shù)實(shí)現(xiàn)中斷,中斷函數(shù)代碼要在模塊內(nèi)實(shí)現(xiàn),其常用語(yǔ)法格式如語(yǔ)法2-27所示。
【語(yǔ)法2-27】中斷函數(shù)
CC2530_INTERRUPT(中斷向量)
{
atomic
{
//中斷函數(shù)代碼
}
}
2.8常用接口和組件
2.8.1系統(tǒng)啟動(dòng)接口Boot
1.接口簡(jiǎn)介
Boot接口用于通知應(yīng)用程序TinyOS已經(jīng)啟動(dòng)完畢(所有的必要組件已經(jīng)初始化完成)。Boot接口定義在“tos/interfaces/Boot.nc”文件中,該接口只有一個(gè)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.用法簡(jiǎn)介
每個(gè)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.接口簡(jiǎn)介
接口Leds用于控制設(shè)備的LED,該接口使用比較頻繁,既可以用于設(shè)備的輸出提示,又可以在程序開(kāi)發(fā)過(guò)程中用于代碼調(diào)試。該接口定義在“tos/interfaces/Leds.nc”文件中,本書配套的平臺(tái)mytinyos新增了LED3,移植后的接口在“tos/platforms/cc2530/Leds.nc”文件中,代碼如代碼2-8所示(相關(guān)功能請(qǐng)參考代碼中的注釋)?!敬a2-8】LedsC.nc
#include"Leds.h"
interfaceLeds
{
//打開(kāi)LED0
asynccommandvoidled0On();
//關(guān)閉LED0
asynccommandvoidled0Off();
//切換LED0狀態(tài)(原來(lái)是關(guān)則打開(kāi),原來(lái)是開(kāi)則關(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.用法簡(jiǎn)介
當(dāng)組件要使用Leds接口時(shí),需要做以下工作:
在組件內(nèi)聲明Leds接口。
在組件中調(diào)用Leds接口的相關(guān)命令函數(shù)控制LED。
在程序的配置組件內(nèi)將使用Leds接口的組件連接至接口的提供者LedsC。
示例代碼請(qǐng)參考任務(wù)描述2.D.4。2.8.3定時(shí)器接口Timer
1.接口簡(jiǎn)介
定時(shí)器接口是一個(gè)通用接口,可以多次實(shí)例化,為程序提供定時(shí)作用。該接口定義在“tos/lib/timer/Timer.nc”文件中,代碼如代碼2-10所示(命令和事件函數(shù)的作用參見(jiàn)注釋)?!敬a2-10】Timer.nc
#include"Timer.h"
interfaceTimer<precision_tag>
{
//設(shè)置定時(shí)器的時(shí)間周期為dt,并啟動(dòng)定時(shí)器
commandvoidstartPeriodic(uint32_tdt);
//啟動(dòng)一次定時(shí)器,dt是超時(shí)時(shí)間
commandvoidstar
溫馨提示
- 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。
最新文檔
- GB/T 25417-2025馬鈴薯種植機(jī)技術(shù)規(guī)范
- 2025年中國(guó)潤(rùn)滑油行業(yè)市場(chǎng)供需態(tài)勢(shì)及未來(lái)前景研判報(bào)告
- 《跨境電商實(shí)務(wù)》課件全套 李穎芬 項(xiàng)目1-8 認(rèn)識(shí)跨境電商 - 掌握跨境客戶服務(wù)技巧
- 安全知識(shí)競(jìng)賽試題庫(kù)單選七
- 2025屆北京昌平臨川育人學(xué)校高一下化學(xué)期末質(zhì)量跟蹤監(jiān)視模擬試題含解析
- 江蘇省無(wú)錫市宜興中學(xué)2024-2025學(xué)年高二下學(xué)期期中考前地理模擬卷三(含答案)
- 2025年黑龍江省普通高等學(xué)校招生選擇性考試 思想政治(無(wú)答案)
- 市場(chǎng)整頓活動(dòng)方案
- 少先隊(duì)隊(duì)歌活動(dòng)方案
- 展廳布置活動(dòng)方案
- 2025年高考軍隊(duì)院校征集和招錄人員政治考核表(原表)
- TCCEAS001-2022建設(shè)項(xiàng)目工程總承包計(jì)價(jià)規(guī)范
- 2024年河北省物理組招生計(jì)劃
- 中華民族共同體概論課件專家版3第三講 文明初現(xiàn)與中華民族起源(史前時(shí)期)
- 基于MATLAB牛頭刨床仿真分析畢業(yè)設(shè)計(jì)
- 新世紀(jì)大學(xué)英語(yǔ)綜合教程4 Unit1
- 振型中的節(jié)點(diǎn),節(jié)線,節(jié)徑和節(jié)圓
- 虹鱒魚(yú)養(yǎng)殖項(xiàng)目可行性研究報(bào)告寫作范文
- 工業(yè)清洗劑PPT課件
- 質(zhì)量管理七大手法(英文版)
- 吊籃維保記錄
評(píng)論
0/150
提交評(píng)論