试验二:MicaZ-TinyOS2.x平台下LED试验:Blink
2012-07-13 21:15
127 查看
参考www.tinyos.net :
试验二:MicaZ-TinyOS2.x平台下LED试验:Blink
——TinyOS编程的“Hello
World”程序
--------------------------------------------------------------------------------------------
date: 2011-06-24
更新内容:添加MIB520的使用
更新原因:又拿到一块MIB520板子,学习一下如何使用MIB520给MicaZ节点烧写程序
更新文档:放在此实验的最后部分。
--------------------------------------------------------------------------------------------
试验目的:。通过试验一熟悉MicaZ硬件平台及MicaZ程序烧写,能够熟练MicaZ平台使用LED灯。其功能是实现MicaZ节点上三个LED灯闪烁,其中红,绿,黄灯闪烁频率分别为4Hz,2Hz,1Hz。并对Blink程序进行修改,熟悉nesC语言,及TinyOS2.x系统程序框架。
本实验所需硬件平台:1个micaz节点,1块mib510板,1根串口线。
一:熟悉硬件平台
图1所示的是试验用到的处理器&射频平台MicaZ节点(MPR2400CA)(注:MPR2400CA是MicaZ,MPR4x0是Mica2,MicaZ的射频频率为2.4G,Mica2的射频频率为433-900M),图2是基于串行端口的编程设备MIB510CA,图3是基于以太网的编程设备MIB600CA。
图1处理器&射频平台MicaZ(MPR2400CA) 图2基于串行端口的编程设备MIB510CA
图3
基于以太网的编程设备MIB600CA
MIB510CA允许使用一个串行界面和其他标准计算机平台一样在PC上传感器网络数据聚集。除数据传送外,MIB510CA也提供一个RS-232串行编程界面。
MIB600CA提供Ethernet(以太网)(10/100 Base-T)与“智能尘埃”MICA/IRIS家族的连接用于通讯和系统内编程。MIB600CA允许通过TCP/IP远程进入传感器网络数据。MIB600CA串行服务器与其他网络装置一样直接与一个10
Base-T LAN相连接。MIB600CA可连接一个网络中的“有线”和“无线”装置。MIB600CA也是传感器数据的一个有效管道。
接下来的试验我们均采用MIB510板+MicaZ节点。
使用MIB510首先要注意几点:
1).MIB要给节点烧程序的时候,要把switch开关拨到‘ON’的位置
2).MIB要做普通传感器基板时,要把switch开关拨到‘OFF’的位置,否则,节点无法正常通信
3).使用MIB510的命令行是:
make [re]install <addr> <platform>
这里要注意,如下两个地址是保留值,不可使用:
TOS_BCAST_ADDR(0xFFFF)和TOS_UART_ADDR(0x007E)。
例如,一个MicaZ平台,要设置节点ID为110,并使用串口1连接MIB510,那么应输入命令为:
make micaz install,110mib510,com1
二:连接MIB510和MicaZ节点
MIB510和MicaZ节点是由一个51针的连接槽连接起来的,其硬件图如图4所示
图4 MicaZ连接槽51针
MicaZ节点上的红、绿、黄三个LED通过连接槽连接到MIB510基板上相对应的红、绿、黄三个LED。这样,既方便了在调试程序的时候查看LED状态,同时也可以检测连接槽是否接触良好。
(本人在做试验的时候,经常没有把节点在槽中插牢固,起初总以为是接触不良)
三:应用程序源码分析
打开cygwin,进入到/opt/tinyos-2.x/apps/Blink目录下,如下图所示,可以看到tinyOS自带的用于LED闪烁的例子Blink,下面将通过分析该例子程序来认识和了解tinyOS以及nesC编程语言。
其中,目录下含有BlinkAppC.nc、BlinkC.nc文件以及Makefile文件,nc为后缀的文件是用nesC语言编辑的应用程序源文件,Makefile文件生成可执行文件时供make工具调用。
Blink应用程序由两个组件组成:一个名为“BlinkC”的模块,其所在文件为“BlinkC.nc”,和一个名为
“BlinkAppC”的配件,其所在文件为 “BlinkAppC.nc”。请记住,任何一个应用程序都有一个用应用程序名命名的顶层配件,在此处,配件BlinkAppC就是Blink应用程序的顶层配件,nesC编译器根据该文件的内容产生可执行文件。另一方面,模块BlinkC提供Blink应用程序的实现代码。正如所想的,Blink配件是用来连接组件:BlinkC模块和Blink应用程序用到的其它组件的。
让配件和模块之间有所区别的理由是:配件允许系统设计人员快速建立程序。例如:设计者设计一个应用程序可以只提供一个配件,这个配件只是简单地将一个或多个模块连接起来,而此时设计者实际上并没有实现任何东西。同样地,另一个开发人员会提供可以在广泛范围应用程序中使用的“库”模块。
3.1 Blink配件:BlinkAppC.nc
nesC 的编译器为ncc,它可以将包含顶层配件的文件编译成可执行的应用程序。一般而言,TinyOS应用程序还拥有一个标准的Makefile文件,允许进行平台选择以及在调用ncc时使用某些适当的选项。
让我们先看看Blink程序的配件:BlinkAppC。
nesC 的编译器为ncc,它可以将包含顶层配件的文件编译成可执行的应用程序。一般而言,TinyOS应用程序还拥有一个标准的Makefile文件,允许进行平台选择以及在调用ncc时使用某些适当的选项。
让我们先看看Blink程序的配件:BlinkAppC。
首先,需要注意的是关键字 configuration ,此词声明这是一个配件文件。头两行:
简单地说明这个配件名为 BlinkAppC。在声明后的这个花括号内可以指定 uses语句和 provides语句。有一点非常重要,必须记住:配件可以提供和使用接口。
配件实际的实现代码是在紧接着的implementation后面的一对大括号里面。components那行列出了此配件用到的一组组件,分别是MainC,BlinkC和LedsC。剩下的语句是连接使用到的接口到该接口的提供者。例如:BlinkC组件使用了接口Leds,BlinkC.Leds->
LedsC; 语句将其连接到提供者LedsC。
TinyOS应用程序中,首先执行的是Main组件。所以,一个TinyOS程序的顶层配件中必须有一个Main组件。
关于被使用的接口,很重要的一点是:其子组件必须明确地被使用者调用。
nesC使用箭头来指示接口之间的关系。您可以把向右箭头(à)当作“绑定到”(binds
to)。箭头左边的接口绑定一个接口到右边的(接口的)实现上。换句话说,组件使用的接口在左边,而右边组件提供的接口在右边。
这一语句将 BlinkC 组件使用到的Timer0接口连接到其提供者Timer0。箭头左边的BlinkC.Timer0引用名为Timer的接口(tos/lib/timer.nc),而箭头右边的Timer0.Timer0则指向引用名
Timer 接口的实现 (tos/system/TimerMilliP.nc)。箭头的作用就是将其左边的接口与其右边的实现绑定起来。
nesC 支持同一个接口的多个实现,Timer接口即是如此。SingleTimer组件实现了一个单一的 Timer接口;而另一个组件
TimerC (tos/lib/timer.nc)。
连接也可以是隐式写法。例如:
相当于
若箭头右边没有指定接口名,nesC 编译器缺省情况下尝试与箭头左边同名的接口进行绑定。
3.2 BlinkC模块:BlinkC.nc
现在该看一下BlinkC模块了:
第一部分代码声明这是一个名为 BlinkC 的模块,并且声明了所用到的接口和提供的接口。BlinkC模块使用了Boot接口,这意味着
BlinkC实现了该接口。如前所述,要使 BlinkC组件得以初始化和启动,必须要实现这个接口。BlinkC模块还使用了两个接口,分别是 Leds和
Timer。这意味着它可能调用这些接口中声明的任何命令以及必须实现这些接口中声明的任何事件。
Leds 接口 (tos/interfaces/Leds.nc)定义了多个命令,如:Led0On(),LedOff()等等,其作用是将节点上的LED(红、绿、黄)灯打开或关闭。由于BlinkC组件使用Leds接口,因此它可调用其中任一命令。但请注意,Leds仅仅只是一个接口,其实现在Blink配件指定。
Timer接口似乎更有趣些,其内容如下:
我们可以看到Timer接口定义了startPeriodic ()和stop()等多个命令,及一个事件:event()。
StartPeriodic ()命令用来指定定时器的类型及定时器事件触发间隔时间。间隔时间的单位是毫秒,重复执行,而startOneShot()是定时器在启动后,在指定的时间触发一次定时器事件就停止下来。而前者则会不停地在间隔时间后触发定时器事件,直到调用了stop()命令。
应用程序怎么知道定时器时间到了呢?答案是当应用程序收到一个事件。Timer接口提供了一个事件:
事件是这样的一种函数:当特定事件发生时,该接口的提供者会发出一个信号。在本例中,当指定的间隔时间到达时,fired()事件就会被触发。这是一个典型的双向接口的例子,一个接口不单单提供命令,还触发事件,该事件再调用该接口使用者的处理函数。可以认为事件是接口的实现者将会调用的一个回调函数。使用接口的模块必须实现接口使用的事件。
接着,让我们看看BlinkC剩下的部分:
这已经足够简单了。正如我们看到的,因为BlinkC模块提供Boot接口,故其实现Boot.Boot()事件。因为BlinkC组件使用了Timer接口,所以它还必须实现Tiemr接口中的fired()事件。
接口Boot中Boot()事件实现只是简单的调用了Timer0.startPeriodic(),Timer1.startPeriodic(),
Timer2.startPeriodic()命令以创建一个反复循环定时器,其周期分别为250ms,500 ms,1000 ms。stop()命令用以终止定时器。例,当每次Timer0.fired()事件被触发时,函数Leds.Led0Toggle()
将被调用,从而使红色的LED灯发亮或熄灭。
TinyOS 提供了一种在应用程序内将使用到的各组件之间的关系用图形化方法表示的工具。事实上,在TinyOS的源文件中包含了位于注释中的元数据,nesC的编译器ncc可用之来自动生成html格式的文档。使用方法是在应用程序(例如Blink应用程序)目录下,输入:
make<platform> docs
命令。输出的结果文档将位于 doc/nesdoc/<platform>
中,其中 <platform> 是正在使用的平台,如:mica或 mica2等。其中 doc/nesdoc/<platform>/index.html是所有文档化的应用程序的索引页面。例如:假如使用的是MicaZ节点,那么输入make
micazdocs命令,并按回车,将产生一些关于当前目录下应用程序(例如Blink)的文档,文档的位置则是:C:\cygwin\opt\tinyos-2.x\doc\nesdoc\micaz,如图6所示。
图6 生成图形化显示方法
打开index.html文件,在左边点击组件:BlinkAppC,可以看到Blink应用程序中用到组件的关系图,如图7。
图7 Blink应用程序中用到组件的关系图
四.编译Blink应用程序
TinyOS支持多种平台,每个平台在tos/platform目录下有自己的目录。在本教程中,我们使用MicaZ平台作为例子。要为MicaZ节点编译Blink应用程序,只需在cygwin
shell 中转到apps/Blink目录下,输入:
make micaz
即可。如果没有任何语法错误,将看到以下图8编译信息:
图8 成功为MicaZ节点编译程序
五.安装Blink程序到MicaZ节点并运行
要安装Blink程序到MicaZ节点,先将MicaZ节点插入MIB510基板,然后通过基板的串口连接到PC机的串口,在之前已编译的基础上,您只需要在apps/Blink目录下输入:
make micaz reinstall,110 mib510,com1
即可,如果成功的话可以看到以下图9安装信息:
图9 成功为MicaZ节点烧写程序
此时在MicaZ节点上应该可以看到三个LED灯在闪烁。
六.修改Blink程序,进一步熟悉nesC
Blink程序是通过三个没有关联的计时器来控制三个LED的闪烁,我们对它的逻辑进行一点改动:我们只采用1个计时器来控制LED闪烁,并保持一些LED状态。复制一份Blink应用程序,命名为BlinkSingle,然后进入它的目录中
用UltraEdit编辑器打开BlinkC.nc,我们把Timer1和Timer2注释掉:
然后,在BlinkC.nc中添加一个单字节状态量。和C语言是类似的,变量和函数必须在使用它之前进行声明。所以我们把添加的状态量放在实现模块的开头。
在标准C中,我们对类型命名为int,long,或者char,而nesC使用更明确的类型,直接来声明它所占空间大小。实际上,它们和标准C也是有映射关系的,但是对于不同的平台,它的映射关系可能会不一样。例如,在Mica和Telos节点中int是16位的,而在IntelMote2中int是32位的。而且在TinyOS中,使用无符号变量比较频繁。
继续看我们修改的BlinkC.nc程序,我们已经声明了一个无符号单字节变量,counter。当节点启动时,变量counter会初始化为0,接下来每当Timer0.fired()触发时,counter会加1,并通过LED显示结果:
直接执行
make micazinstall,110 mib510,com1
可以同时编译程序,并烧写到节点中。
其实,还有一个更简便的方法来实现上面的功能,即使用set指令:
编译并下载到节点中,我们可以看到执行效果和之前的是一样的。这时候,我们只用了一个timer来控制LEDS,而不是原始程序中的三个。
既然只使用了一个timer,而不需要timer1和timer2,它们浪费CPU资源和内存。重新打开BlinkC.nc,把Timer1和Timer2删除掉。程序如下:
进行编译,会出现错误
打开BlinkAppC.nc,然后删除掉Timer1和Timer2,以及它们的wiring,再重新编译:
编译成功,如果和之前未修改的Blink应用程序比较它们所占的ROM和RAM空间,会发现占的空间变小了一些。
七.关于接口(Interfaces),指令(Commands),和事件(Events)
返回到之前的应用程序tinyos-2.x/apps/Blink。如果一个组件使用一个接口的话,那么它可以调用这个接口的指令,并必须实现接口中所有事件。我们同样看到,BlinkC组件使用了Timer,Leds和Boot接口,我们来看一下这些接口:
浏览一下Boot,Leds和Timer接口,我们可以看到,既然BlinkC使用了这些接口,那么它必须实现Boot.booted()事件和Timer.fired()事件。Leds接口没有任何事件,所以BlinkC可以任意调用它的指令,而不用去实现任何事件。看一下BlinkC程序中对Boot.booted()事件的实现。
BlinkC使用了组件TimerMilliC的3个实例,并把Timer0,Timer1和Timer2接口贯通起来。Boot.booted()事件启动每一个实例。
每一个接口指令通过关键字call来调用。通过关键字signal调用每一个接口事件。BlinkC没有提供任何接口。在boot序列中,它通过信号唤醒Boot.booted()事件。
下面我们看一下Timer.fired()的实现:
因为BlinkC使用了Timer接口的3个实例,所以它必须要实现Timer.fired()事件的3个实例。当实现或调用一个接口函数时,函数的命名方式应该是接口.函数。例,3个Timer接口命名为Timer0,Timer1,Timer2。它实现3个函数Timer0.fired,Timer1.fired和Timer2.fired。
八:TinyOS执行模式:Tasks
之前我们看到的所有代码都是同步( Synchronous )的,代码在一个执行区中运行,不具有任何的优先权。即当同步代码运行的时候,它在运行结束之前不会让出CPU给其他的同步代码。这种简单的运行机制消耗RAM较小,维护比较简单。但是,这样意味着如果一段同步代码在运行的时候,会阻止其他代码运行。这样严重影响了系统的应答性。
至此,例子中还都是通过直接的函数调用,这样的代码不具有强占性。对于大型的计算工程来说,它的执行效果会很差。有必要把它们分成很多小份,在某一时间内执行其中一部分。
Task由组件来通知TinyOS何时来执行。
复制一份Blink应用程序,并命名为BlinkTask
打开当前目录下的BlinkC.nc,看一下Timer0.fired()
修改成如下:
即,每次Timer0.fired()事件触发时,led0就要反转状态400,001次,因为这是一个奇数,所以每次事件触发的结果都是一个之前的反转状态。把程序编译,烧到MicaZ节点中,可以发现,Led2和Led3几乎没有发生任何变化,因为这个计算过程中Timer0.fired()事件占用了太长时间,和其他timer的执行产生了冲突。我们需要告诉TinyOS等会再执行这段计算,我们可以通过Task来完成。
在实现模块中,通过如下代码来声明一个Task
Task必须返回一个void,要派遣一个Task去执行,需要使用如下语法:
一个组件可以在指令,事件或任务中去发送一个任务。一个任务也可以调用指令,并相应事件。一般的,命令不去相应事件,为了避免产生一个死循环,这样的死循环很难被发现,并且会产生一个很大的栈。
修改一下BlinkC.nc,使在任务中去执行一段循环。
这样的程序在MicaZ节点中能够正常运行,但是在Telos节点中就会产生冲突,因为task queue按照FIFO序列执行。任务间不具有强占性,但是一个任务可以被硬件中断(这里还没有举过硬件中断的例子)。
九:内部函数(Inernal Functions)
指令和事件是组件间可以调用的唯一途径,有时候,组件会有一些它内部自己使用的函数。尽管这些函数不可以对指令或事件进行修改,但是我们可以随意调用指令或相应事件。例如,我们可以使用内部函数给BlinkC写一段非常合理的nesC代码:
内部函数的使用和标准C函数使用是一样的,不需要call或signal这样的关键字。
十:分相操作(Split-PhaseOperations)
在TinyOS中由于Task之间不能相互占先执行,所以TinyOS没有提供任何阻塞操作,为了让一个耗时较长的操作尽快完成,一般来说都是将这个操作的需求和这个操作的完成分开来实现的,以便获得较高的执行效率。
与之相对的是Blocking操作。在Blocking操作中,如果一段程序执行了一段长事件运行的操作。那么调用在操作结束前不会返回。下面举例来说明两者间的不同。
Blocking Split-Phase
十一:要注意的问题
1).插在MIB510上的节点MicaZ,一定要插牢固。否则接触不良,导致烧写程序出错,或者程序运行结果有误。
2).MIB使用外部电源的时候,mica节点最好不要上电。
3).很多接口、指令及事件的大小写一定要注意,nesC对大小写要求很严格。
4).
要注意一点关于组件use和provide问题:
--------------------------------------------------------------------------------------------
date: 2011-06-24
更新内容:添加MIB520的使用
更新原因:又拿到一块MIB520板子,学习一下如何使用MIB520给MicaZ节点烧写程序
更新文档:放在此实验的最后部分。
--------------------------------------------------------------------------------------------
拿到一块基于USB的编程设备MIB520CA,下面我们来看一下如何使用这块板子:
图基于USB的编程设备MIB520CA
首先带USB接口的板子可以由USB接口直接提供5V直流电。因此不需要外接5V电源转换器。
通过USB线连接MIB520到PC上,提示安装USB驱动:
登录CrossBow的官网(www.xbow.com.cn 资源下载---产品手册---驱动和软件下载) 可以看到由官网提供的MIB520USB转串驱动,下载后解包,有两个子文件包。
分别对应自动驱动安装和手动驱动安装,先尝试了自动驱动安装,10min过去后,都没有安装完毕....这里推荐手动安装。即在新硬件向导中选择从指定位置安装,进行下一步操作,找到MIB520Drivers文件夹即可。这时在设备管理器中可以看到有两个虚拟的串口com5,com6,这时因为有一个专门虚拟为下程序端口,另一个虚拟为正常的232通信端口。一般小的为下载程序端口,这里com5,(若不是,可以尝试)
测试下载程序指令,与MIB510类似:
但这里报错,说我们使用的是MIB510,而我们明明使用的是MIB520。查看原因,可能是因为之前编译过的原因,这时我们执行makeclean,再重新编译下载:
仍然报错说我们在使用MIB510,继续追究....
重新测试MIB510板也出现同样问题,看来是重新安装了TinyOS2.1.1之后出现的问题。
现在首要解决MIB520的问题,所以重新解压TinyOS 2.1.0安装包和tools1.3,然后测试:
试验二:MicaZ-TinyOS2.x平台下LED试验:Blink
——TinyOS编程的“Hello
World”程序
--------------------------------------------------------------------------------------------
date: 2011-06-24
更新内容:添加MIB520的使用
更新原因:又拿到一块MIB520板子,学习一下如何使用MIB520给MicaZ节点烧写程序
更新文档:放在此实验的最后部分。
--------------------------------------------------------------------------------------------
试验目的:。通过试验一熟悉MicaZ硬件平台及MicaZ程序烧写,能够熟练MicaZ平台使用LED灯。其功能是实现MicaZ节点上三个LED灯闪烁,其中红,绿,黄灯闪烁频率分别为4Hz,2Hz,1Hz。并对Blink程序进行修改,熟悉nesC语言,及TinyOS2.x系统程序框架。
本实验所需硬件平台:1个micaz节点,1块mib510板,1根串口线。
一:熟悉硬件平台
图1所示的是试验用到的处理器&射频平台MicaZ节点(MPR2400CA)(注:MPR2400CA是MicaZ,MPR4x0是Mica2,MicaZ的射频频率为2.4G,Mica2的射频频率为433-900M),图2是基于串行端口的编程设备MIB510CA,图3是基于以太网的编程设备MIB600CA。
图1处理器&射频平台MicaZ(MPR2400CA) 图2基于串行端口的编程设备MIB510CA
图3
基于以太网的编程设备MIB600CA
MIB510CA允许使用一个串行界面和其他标准计算机平台一样在PC上传感器网络数据聚集。除数据传送外,MIB510CA也提供一个RS-232串行编程界面。
MIB600CA提供Ethernet(以太网)(10/100 Base-T)与“智能尘埃”MICA/IRIS家族的连接用于通讯和系统内编程。MIB600CA允许通过TCP/IP远程进入传感器网络数据。MIB600CA串行服务器与其他网络装置一样直接与一个10
Base-T LAN相连接。MIB600CA可连接一个网络中的“有线”和“无线”装置。MIB600CA也是传感器数据的一个有效管道。
接下来的试验我们均采用MIB510板+MicaZ节点。
使用MIB510首先要注意几点:
1).MIB要给节点烧程序的时候,要把switch开关拨到‘ON’的位置
2).MIB要做普通传感器基板时,要把switch开关拨到‘OFF’的位置,否则,节点无法正常通信
3).使用MIB510的命令行是:
make [re]install <addr> <platform>
这里要注意,如下两个地址是保留值,不可使用:
TOS_BCAST_ADDR(0xFFFF)和TOS_UART_ADDR(0x007E)。
例如,一个MicaZ平台,要设置节点ID为110,并使用串口1连接MIB510,那么应输入命令为:
make micaz install,110mib510,com1
二:连接MIB510和MicaZ节点
MIB510和MicaZ节点是由一个51针的连接槽连接起来的,其硬件图如图4所示
图4 MicaZ连接槽51针
MicaZ节点上的红、绿、黄三个LED通过连接槽连接到MIB510基板上相对应的红、绿、黄三个LED。这样,既方便了在调试程序的时候查看LED状态,同时也可以检测连接槽是否接触良好。
(本人在做试验的时候,经常没有把节点在槽中插牢固,起初总以为是接触不良)
三:应用程序源码分析
打开cygwin,进入到/opt/tinyos-2.x/apps/Blink目录下,如下图所示,可以看到tinyOS自带的用于LED闪烁的例子Blink,下面将通过分析该例子程序来认识和了解tinyOS以及nesC编程语言。
其中,目录下含有BlinkAppC.nc、BlinkC.nc文件以及Makefile文件,nc为后缀的文件是用nesC语言编辑的应用程序源文件,Makefile文件生成可执行文件时供make工具调用。
Blink应用程序由两个组件组成:一个名为“BlinkC”的模块,其所在文件为“BlinkC.nc”,和一个名为
“BlinkAppC”的配件,其所在文件为 “BlinkAppC.nc”。请记住,任何一个应用程序都有一个用应用程序名命名的顶层配件,在此处,配件BlinkAppC就是Blink应用程序的顶层配件,nesC编译器根据该文件的内容产生可执行文件。另一方面,模块BlinkC提供Blink应用程序的实现代码。正如所想的,Blink配件是用来连接组件:BlinkC模块和Blink应用程序用到的其它组件的。
让配件和模块之间有所区别的理由是:配件允许系统设计人员快速建立程序。例如:设计者设计一个应用程序可以只提供一个配件,这个配件只是简单地将一个或多个模块连接起来,而此时设计者实际上并没有实现任何东西。同样地,另一个开发人员会提供可以在广泛范围应用程序中使用的“库”模块。
3.1 Blink配件:BlinkAppC.nc
nesC 的编译器为ncc,它可以将包含顶层配件的文件编译成可执行的应用程序。一般而言,TinyOS应用程序还拥有一个标准的Makefile文件,允许进行平台选择以及在调用ncc时使用某些适当的选项。
让我们先看看Blink程序的配件:BlinkAppC。
nesC 的编译器为ncc,它可以将包含顶层配件的文件编译成可执行的应用程序。一般而言,TinyOS应用程序还拥有一个标准的Makefile文件,允许进行平台选择以及在调用ncc时使用某些适当的选项。
让我们先看看Blink程序的配件:BlinkAppC。
//BlinkAppC.nc configuration BlinkAppC { } implementation { components MainC, BlinkC, LedsC; components new TimerMilliC() as Timer0; components new TimerMilliC() as Timer1; components new TimerMilliC() as Timer2; BlinkC -> MainC.Boot; BlinkC.Timer0 -> Timer0; BlinkC.Timer1 -> Timer1; BlinkC.Timer2 -> Timer2; BlinkC.Leds -> LedsC; } |
configuration BlinkAppC{ } |
配件实际的实现代码是在紧接着的implementation后面的一对大括号里面。components那行列出了此配件用到的一组组件,分别是MainC,BlinkC和LedsC。剩下的语句是连接使用到的接口到该接口的提供者。例如:BlinkC组件使用了接口Leds,BlinkC.Leds->
LedsC; 语句将其连接到提供者LedsC。
TinyOS应用程序中,首先执行的是Main组件。所以,一个TinyOS程序的顶层配件中必须有一个Main组件。
关于被使用的接口,很重要的一点是:其子组件必须明确地被使用者调用。
nesC使用箭头来指示接口之间的关系。您可以把向右箭头(à)当作“绑定到”(binds
to)。箭头左边的接口绑定一个接口到右边的(接口的)实现上。换句话说,组件使用的接口在左边,而右边组件提供的接口在右边。
BlinkC.Timer0 -> Timer0; |
Timer 接口的实现 (tos/system/TimerMilliP.nc)。箭头的作用就是将其左边的接口与其右边的实现绑定起来。
nesC 支持同一个接口的多个实现,Timer接口即是如此。SingleTimer组件实现了一个单一的 Timer接口;而另一个组件
TimerC (tos/lib/timer.nc)。
连接也可以是隐式写法。例如:
BlinkC.Leds ->LedsC; |
BlinkC.Leds ->LedsC.Leds; |
3.2 BlinkC模块:BlinkC.nc
现在该看一下BlinkC模块了:
//BlinkC.nc #include "Timer.h" module BlinkC @safe() { uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; uses interface Timer<TMilli> as Timer2; uses interface Leds; uses interface Boot; } //Continued below… |
BlinkC实现了该接口。如前所述,要使 BlinkC组件得以初始化和启动,必须要实现这个接口。BlinkC模块还使用了两个接口,分别是 Leds和
Timer。这意味着它可能调用这些接口中声明的任何命令以及必须实现这些接口中声明的任何事件。
Leds 接口 (tos/interfaces/Leds.nc)定义了多个命令,如:Led0On(),LedOff()等等,其作用是将节点上的LED(红、绿、黄)灯打开或关闭。由于BlinkC组件使用Leds接口,因此它可调用其中任一命令。但请注意,Leds仅仅只是一个接口,其实现在Blink配件指定。
Timer接口似乎更有趣些,其内容如下:
//Timer.nc interface Timer<precision_tag> { command void startPeriodic(uint32_t dt); command void startOneShot(uint32_t dt); command void stop(); event void fired(); command bool isRunning(); command bool isOneShot(); command void startPeriodicAt(uint32_t t0, uint32_t dt); command void startOneShotAt(uint32_t t0, uint32_t dt); command uint32_t getNow(); command uint32_t gett0(); command uint32_t getdt(); } |
StartPeriodic ()命令用来指定定时器的类型及定时器事件触发间隔时间。间隔时间的单位是毫秒,重复执行,而startOneShot()是定时器在启动后,在指定的时间触发一次定时器事件就停止下来。而前者则会不停地在间隔时间后触发定时器事件,直到调用了stop()命令。
应用程序怎么知道定时器时间到了呢?答案是当应用程序收到一个事件。Timer接口提供了一个事件:
event void fired(); |
接着,让我们看看BlinkC剩下的部分:
//BlinkC.nc, continued implementation { event void Boot.booted() { call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } event void Timer0.fired() { dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string()); call Leds.led0Toggle(); } event void Timer1.fired() { dbg("BlinkC", "Timer 1 fired @ %s \n", sim_time_string()); call Leds.led1Toggle(); } event void Timer2.fired() { dbg("BlinkC", "Timer 2 fired @ %s.\n", sim_time_string()); call Leds.led2Toggle(); } } |
接口Boot中Boot()事件实现只是简单的调用了Timer0.startPeriodic(),Timer1.startPeriodic(),
Timer2.startPeriodic()命令以创建一个反复循环定时器,其周期分别为250ms,500 ms,1000 ms。stop()命令用以终止定时器。例,当每次Timer0.fired()事件被触发时,函数Leds.Led0Toggle()
将被调用,从而使红色的LED灯发亮或熄灭。
TinyOS 提供了一种在应用程序内将使用到的各组件之间的关系用图形化方法表示的工具。事实上,在TinyOS的源文件中包含了位于注释中的元数据,nesC的编译器ncc可用之来自动生成html格式的文档。使用方法是在应用程序(例如Blink应用程序)目录下,输入:
make<platform> docs
命令。输出的结果文档将位于 doc/nesdoc/<platform>
中,其中 <platform> 是正在使用的平台,如:mica或 mica2等。其中 doc/nesdoc/<platform>/index.html是所有文档化的应用程序的索引页面。例如:假如使用的是MicaZ节点,那么输入make
micazdocs命令,并按回车,将产生一些关于当前目录下应用程序(例如Blink)的文档,文档的位置则是:C:\cygwin\opt\tinyos-2.x\doc\nesdoc\micaz,如图6所示。
图6 生成图形化显示方法
打开index.html文件,在左边点击组件:BlinkAppC,可以看到Blink应用程序中用到组件的关系图,如图7。
图7 Blink应用程序中用到组件的关系图
四.编译Blink应用程序
TinyOS支持多种平台,每个平台在tos/platform目录下有自己的目录。在本教程中,我们使用MicaZ平台作为例子。要为MicaZ节点编译Blink应用程序,只需在cygwin
shell 中转到apps/Blink目录下,输入:
make micaz
即可。如果没有任何语法错误,将看到以下图8编译信息:
图8 成功为MicaZ节点编译程序
五.安装Blink程序到MicaZ节点并运行
要安装Blink程序到MicaZ节点,先将MicaZ节点插入MIB510基板,然后通过基板的串口连接到PC机的串口,在之前已编译的基础上,您只需要在apps/Blink目录下输入:
make micaz reinstall,110 mib510,com1
即可,如果成功的话可以看到以下图9安装信息:
图9 成功为MicaZ节点烧写程序
此时在MicaZ节点上应该可以看到三个LED灯在闪烁。
六.修改Blink程序,进一步熟悉nesC
Blink程序是通过三个没有关联的计时器来控制三个LED的闪烁,我们对它的逻辑进行一点改动:我们只采用1个计时器来控制LED闪烁,并保持一些LED状态。复制一份Blink应用程序,命名为BlinkSingle,然后进入它的目录中
用UltraEdit编辑器打开BlinkC.nc,我们把Timer1和Timer2注释掉:
event void Timer1.fired() { // call Leds.led1Toggle(); } event void Timer2.fired() { // call Leds.led2Toggle(); } |
implementation { uint8_t counter = 0; event void Boot.booted() { call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } |
继续看我们修改的BlinkC.nc程序,我们已经声明了一个无符号单字节变量,counter。当节点启动时,变量counter会初始化为0,接下来每当Timer0.fired()触发时,counter会加1,并通过LED显示结果:
event void Timer0.fired() { counter++; if(counter & 0x01){ call Leds.led0On(); } else{ call Leds.led0Off(); } if(counter & 0x02){ call Leds.led1On(); } else{ call Leds.led1Off(); } if(counter & 0x04){ call Leds.led2On(); } else{ call Leds.led2Off(); } } |
make micazinstall,110 mib510,com1
可以同时编译程序,并烧写到节点中。
其实,还有一个更简便的方法来实现上面的功能,即使用set指令:
event void Timer0.fired() { counter++; call Leds.set(counter); } |
既然只使用了一个timer,而不需要timer1和timer2,它们浪费CPU资源和内存。重新打开BlinkC.nc,把Timer1和Timer2删除掉。程序如下:
//BlinkC.nc module BlinkC @safe() { uses interface Timer<TMilli> as Timer0; uses interface Leds; uses interface Boot; } implementation { uint8_t counter = 0; event void Boot.booted() { call Timer0.startPeriodic( 250 ); } event void Timer0.fired() { counter++; call Leds.set(counter); } } |
打开BlinkAppC.nc,然后删除掉Timer1和Timer2,以及它们的wiring,再重新编译:
编译成功,如果和之前未修改的Blink应用程序比较它们所占的ROM和RAM空间,会发现占的空间变小了一些。
七.关于接口(Interfaces),指令(Commands),和事件(Events)
返回到之前的应用程序tinyos-2.x/apps/Blink。如果一个组件使用一个接口的话,那么它可以调用这个接口的指令,并必须实现接口中所有事件。我们同样看到,BlinkC组件使用了Timer,Leds和Boot接口,我们来看一下这些接口:
//tos/interfaces/Boot.nc: interface Boot { event void booted(); } |
//tos/interfaces/Leds.nc: interface Leds { async command void led0On(); async command void led0Off(); async command void led0Toggle(); async command void led1On(); async command void led1Off(); async command void led1Toggle(); async command void led2On(); async command void led2Off(); async command void led2Toggle(); async command uint8_t get(); async command void set(uint8_t val); } |
//tos/lib/timer/timer.nc: interface Timer<precision_tag> { command void startPeriodic(uint32_t dt); command void startOneShot(uint32_t dt); command void stop(); event void fired(); command bool isRunning(); command bool isOneShot(); command void startPeriodicAt(uint32_t t0, uint32_t dt); command void startOneShotAt(uint32_t t0, uint32_t dt); command uint32_t getNow(); command uint32_t gett0(); command uint32_t getdt(); } |
//apps/Blink/BlinkC.nc: event void Boot.booted() { call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } |
每一个接口指令通过关键字call来调用。通过关键字signal调用每一个接口事件。BlinkC没有提供任何接口。在boot序列中,它通过信号唤醒Boot.booted()事件。
下面我们看一下Timer.fired()的实现:
//apps/Blink/BlinkC.nc: event void Timer0.fired() { call Leds.led0Toggle(); } event void Timer1.fired() { call Leds.led1Toggle(); } event void Timer2.fired() { call Leds.led2Toggle(); } |
八:TinyOS执行模式:Tasks
之前我们看到的所有代码都是同步( Synchronous )的,代码在一个执行区中运行,不具有任何的优先权。即当同步代码运行的时候,它在运行结束之前不会让出CPU给其他的同步代码。这种简单的运行机制消耗RAM较小,维护比较简单。但是,这样意味着如果一段同步代码在运行的时候,会阻止其他代码运行。这样严重影响了系统的应答性。
至此,例子中还都是通过直接的函数调用,这样的代码不具有强占性。对于大型的计算工程来说,它的执行效果会很差。有必要把它们分成很多小份,在某一时间内执行其中一部分。
Task由组件来通知TinyOS何时来执行。
复制一份Blink应用程序,并命名为BlinkTask
打开当前目录下的BlinkC.nc,看一下Timer0.fired()
//apps/Blink/BlinkC.nc: event void Timer0.fired() { dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string()); call Leds.led0Toggle(); } |
//apps/Blink/BlinkC.nc: event void Timer0.fired(){ uint32_t i; dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string()); for(i=0;i<400001;i++){ call Leds.led0Toggle(); } } |
在实现模块中,通过如下代码来声明一个Task
Task void taskname(){ … } |
post taskname(); |
修改一下BlinkC.nc,使在任务中去执行一段循环。
//apps/Blink/BlinkC.nc: event void Timer0.fired(){ uint32_t i; dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string()); for(i=0;i<400001;i++){ call Leds.led0Toggle(); } } |
九:内部函数(Inernal Functions)
指令和事件是组件间可以调用的唯一途径,有时候,组件会有一些它内部自己使用的函数。尽管这些函数不可以对指令或事件进行修改,但是我们可以随意调用指令或相应事件。例如,我们可以使用内部函数给BlinkC写一段非常合理的nesC代码:
//apps/Blink/BlinkC.nc: module BlinkC @safe() { uses interface Timer<TMilli> as Timer0; uses interface Timer<TMilli> as Timer1; uses interface Timer<TMilli> as Timer2; uses interface Leds; uses interface Boot; } implementation { void startTimers(){ call Timer0.startPeriodic( 250 ); call Timer1.startPeriodic( 500 ); call Timer2.startPeriodic( 1000 ); } event void Boot.booted() { startTimers(); } event void Timer0.fired() { call Leds.led0Toggle(); } event void Timer1.fired() { call Leds.led1Toggle(); } event void Timer2.fired() { call Leds.led2Toggle(); } } |
十:分相操作(Split-PhaseOperations)
在TinyOS中由于Task之间不能相互占先执行,所以TinyOS没有提供任何阻塞操作,为了让一个耗时较长的操作尽快完成,一般来说都是将这个操作的需求和这个操作的完成分开来实现的,以便获得较高的执行效率。
与之相对的是Blocking操作。在Blocking操作中,如果一段程序执行了一段长事件运行的操作。那么调用在操作结束前不会返回。下面举例来说明两者间的不同。
Blocking Split-Phase
if (send()==SUCCESS){ sendCount++; } | //start phase send(); //completion phase void sendDone(){ if(err==SUCCESS){ sendCount++; } } |
1).插在MIB510上的节点MicaZ,一定要插牢固。否则接触不良,导致烧写程序出错,或者程序运行结果有误。
2).MIB使用外部电源的时候,mica节点最好不要上电。
3).很多接口、指令及事件的大小写一定要注意,nesC对大小写要求很严格。
4).
要注意一点关于组件use和provide问题:
Component | Commands | Events |
Use | Can call | Must Implement |
Provide | Must Implement | Can signal |
date: 2011-06-24
更新内容:添加MIB520的使用
更新原因:又拿到一块MIB520板子,学习一下如何使用MIB520给MicaZ节点烧写程序
更新文档:放在此实验的最后部分。
--------------------------------------------------------------------------------------------
拿到一块基于USB的编程设备MIB520CA,下面我们来看一下如何使用这块板子:
图基于USB的编程设备MIB520CA
首先带USB接口的板子可以由USB接口直接提供5V直流电。因此不需要外接5V电源转换器。
通过USB线连接MIB520到PC上,提示安装USB驱动:
登录CrossBow的官网(www.xbow.com.cn 资源下载---产品手册---驱动和软件下载) 可以看到由官网提供的MIB520USB转串驱动,下载后解包,有两个子文件包。
分别对应自动驱动安装和手动驱动安装,先尝试了自动驱动安装,10min过去后,都没有安装完毕....这里推荐手动安装。即在新硬件向导中选择从指定位置安装,进行下一步操作,找到MIB520Drivers文件夹即可。这时在设备管理器中可以看到有两个虚拟的串口com5,com6,这时因为有一个专门虚拟为下程序端口,另一个虚拟为正常的232通信端口。一般小的为下载程序端口,这里com5,(若不是,可以尝试)
测试下载程序指令,与MIB510类似:
但这里报错,说我们使用的是MIB510,而我们明明使用的是MIB520。查看原因,可能是因为之前编译过的原因,这时我们执行makeclean,再重新编译下载:
仍然报错说我们在使用MIB510,继续追究....
重新测试MIB510板也出现同样问题,看来是重新安装了TinyOS2.1.1之后出现的问题。
现在首要解决MIB520的问题,所以重新解压TinyOS 2.1.0安装包和tools1.3,然后测试:
相关文章推荐
- 试验三:MicaZ-TinyOS2.x平台下点对点通讯试验-----BlinktoRadio实验
- tinyos学习笔记3--基于cc2538平台的Blink、串口、timerTest例程测试
- tinyos imote2 平台安装及测试
- Oracle goldengate Windows平台oracle-oracle单向复制试验
- 三星6410 led平台驱动
- tinyos学习笔记1-Blink
- 基于Wiki的网络协作/知识积累试验平台概要设计(未完成v0.2.0204)
- 木其工作室(专业程序代写服务)[原]ok6410学习笔记(15.platform平台总线驱动模型之混杂设备驱动led)
- 无线传感器网络研究平台TinyOS下载、配置与开发步骤
- MT7621方案 LED 灯控制 (基于OpenWrt平台)
- 2013-10-1目前版本下TinyOS下载遇到问题:Uploading to MICAZ: Direct Parallel Access not defined
- linux平台总线驱动设备模型之点亮LED
- OneNET平台控制W5500开发板LED灯
- mtk 6572平台led指示灯 调试
- ble学习笔记十一-----------ble协议栈之led试验
- 2013-09-30TinyOS下载MicaZ问题:Programmer is not responding
- 澳洲证交所:现有的交易后服务-区块链试验平台
- windows 平台以太坊geth 私链搭建试验步骤(无套路,无坑)
- 基于FL2440平台在linux内核上led驱动开发
- z-stack/cc2530协议栈如何修改LED的设置及HalLedBlink的使用