◇一个组件可以提供或使用接口
●组件中command接口由组件本身实现
●组件中event接口由调用者实现
●接口是双向的,调用command接口必须实现其event接口
◆实现
◇modules
●包含应用程序代码,实现接口
◇configurations
●装配模块,连接模块使用的接口到其提供者
●每个nesC应用程序都有一个顶级configuration连接内部模块
并发模型
◆TinyOS只能运行单个由所需的系统模块和自定义模块构成的应用程序
◆两个线程
◇任务
●一次运行完成,非抢占式
◇硬件事件处理
●处理硬件中断
●一次运行完成,抢占式
●用于硬件中断处理的command和event必须用async关键字声明
◆执行流程(race conditions)
◇nesC要避免任务排他性访问共享数据
◇nesC要避免所有共享数据访问都通过原子语句
◇nesC在编译过程中要检测数据流,但可能误报,可用norace关键字声明不检测,但对其使用 应格外小心
|module SenseM {
| }
| uses {
| interface Timer;
| interface ADC;
| interface StdControl as ADCControl;
| interface Leds;
| }
|}
------------------------------------------------
◆提供StdControl接口
◆使用Timer、ADC、StdControl、Leds接口
◇nesC程序中可以使用同一个接口的多个实例
●interface StdControl as ADCControl
●ADCControl是StdControl的实例
◇不提供实例名,则实例名与接口名相同
●interface ADC相当于interface ADC as ADC
Configuration of Sense Application
Sense.nc
--------------------------------------------------
|configuration Sense {
| // this module does not provide any interface
|}
|implementation
|{
| components Main, SenseM, LedsC, TimerC, Photo;
|
| Main.StdControl -> SenseM;
| Main.StdControl -> TimerC;
|
| SenseM.ADC -> Photo;
| SenseM.ADCControl -> Photo;
| SenseM.Leds -> LedsC;
| SenseM.Timer -> TimerC.Timer[unique("Timer")];
|}
--------------------------------------------------
◆不提供任何接口
◆使用Main、SenseM、LedsC、TimerC、Photo模块
◆连接Main.StdControl接口到SenseM.StdControl和TimerC.StdControl
◆连接SenseM.ADC接口到Photo.ADC
◆连接SenseM.ADCControl到Photo.StdControl
◆参数化接口
◇组件可以使用相同接口的不同实例,并分别为其命名
provides {
interface StdControl as fooControl;
interface StdControl as barControl;
}
◇参数化接口允许组件通过运行时或编译时参数值使用多个该接口的实例
provides interface Timer[uint8_t id];
◇每个Timer可以有256实例,每个实例对应一个8位数字
◇unique、uniqueCount函数
●产生一个唯一的8位数字与参数关联。
●unique("Timer")检查程序中共有多少个“unique("Timer")”,假如共有N个,然后每一个unique("Timer")的结果是产生一个在0~N范围内,而且不重复的数字;这个过程其实就像是在给“Timer”编号。
- 执行应用程序通用后台进程
- 任务的建立和调度
- TinyOS提供任务和硬件事件处理两级调度体系
- async关键字声明硬件事件处理的command和event
- 可以在任意时刻运行
- 做少量工作,要快速完成
- 任务用于处理复杂操作,比如后台数据处理,可以被硬件事件处理程序抢占
- 任务的定义语法
- taskname是给任务取的符号名字
- 任务不能有参数,必须返回void
- 分派任务语法
- 可以在command中提交任务
- 可以在event中提交任务
- 可以在Task中提交任务
- post后的任务被放到一个内部FIFO任务队列
----------------------------------------------------------
|configuration CntToLedsAndRfm {
|}
|implementation {
| components Main, Counter, IntToLeds, IntToRfm, TimerC;
|
| Main.StdControl -> Counter.StdControl;
| Main.StdControl -> IntToLeds.StdControl;
| Main.StdControl -> IntToRfm.StdControl;
| Main.StdControl -> TimerC.StdControl;
| Counter.Timer -> TimerC.Timer[unique("Timer")];
| IntToLeds <- Counter.IntOutput;
| Counter.IntOutput -> IntToRfm;
|}
----------------------------------------------------------
◆使用模块Main、Counter、IntToLeds、IntToRfm和TimerC
◆在Main中初始化Counter、IntToLeds、IntToRfm和TimerC
◆都是标准库
◆Counter处理Timer.fire()事件
◆IntOutput接口
◇output() Command:有一个16位的参数
◇outputComplete() Event:返回一个result_t
◆IntToLeds:在LED上显示值的低三位
◆IntToRrm:通过Radio广播
◆Counter使用IntToLeds和IntToRfm的IntOutput接口
◆箭头总是由使用者指向提供者
发送消息
◆TinyOS中的radio通信采用Active Message(AM)模型,网络中的每个包都有一个handler ID,接收结点 会触发这个ID对应的事件,可以认为这个ID是“端口号”,不同的结点可以把不同的事件关联到相同的handler ID。
◆在消息传递层,成功的通信涉及5个方面
◇标明发送数据
◇标明接收结点
◇回收与发送数据相关联的内存
◇缓存接收数据
◇处理消息
IntToRfm.nc
----------------------------------------------
|configuration IntToRfm
|{
| provides interface IntOutput;
| provides interface StdControl;
|}
|implementation
|{
| components IntToRfmM, GenericComm as Comm;
|
| IntOutput = IntToRfmM;
| StdControl = IntToRfmM;
|
| IntToRfmM.Send -> Comm.SendMsg[AM_INTMSG];
| IntToRfmM.StdControl -> Comm;
|}
----------------------------------------------
◆IntToRfm configuration提供了两个接口IntOputput和StdControl
◆提供了接口的configuration也成为了组件,可以被其它configuartion使用
◆组件别名
◇使用了GenericComm组件
◇取别名(local name)Comm
◇为了能方便地使用其它通信组件替换GenericComm而不用修改每一处作用该组件的代码
◆=(equal sign)
◇IntOutput = IntToRfmM
◇StdControl = IntToRfmM
◇模块中左面接口的实现等价于右边模块中接口的实现
◆AM_INTMSG是定义在tos/lib/Counters/IntMsg.h中的全局常量
IntToRfmM.nc
----------------------------------------------------------------
|bool pending;
|struct TOS_Msg data;
|
|
|
|command result_t IntOutput.output(uint16_t value) {
| IntMsg *message = (IntMsg *)data.data;
|
| if (!pending) {
| pending = TRUE;
|
| message->val = value;
| atomic {
| message->src = TOS_LOCAL_ADDRESS;
| }
|
| if (call Send.send(TOS_BCAST_ADDR, sizeof(IntMsg), &data))
| return SUCCESS;
|
| pending = FALSE;
| }
| return FAIL;
|}
----------------------------------------------------------------
◆IntMsg结构(tos/lib/Counters/IntMsg.h)
◇Field: val
◇Field: src
◆TOS_LOCAL_ADDRESS
◇全局常量
◇代表local source address
◆TOS_BCAST_ADDR
◇全局常量
◇代表radio广播地址
◆TOS_Msg(tos/system/AM.h)
◇send函数所使用的消息结构
◇IntMsg是对TOS_Msg的封装
◆调用Send.send()发送数据
◇数据分段
◇数据传完触发SendMsg.sendDone()事件
◇发送成功才接收下一个分段消息队列
◇发送不成功就不接受传输消息
◆TinyOS Active Message缓存管理
◇允许并发操作
◇遵守严格的可选所有者协议,避免太大内存管理开销
◇message layer接受send()后,管理缓存,传输完成前不允许请求者再修改缓存
◆pending flag
◇跟踪缓存状态
◇前面的消息没发完,则放弃output(),返回FAIL
◇缓存可用,则发送消息
◆GenericComm网络栈(tos/system/GenericComm.nc)
◇TinyOS generic网络栈的实现
◇使用低级接口实现通信
◇AMStandard实现Active Message的接收与发送
◇UARTNoCRCPacket实现mote的串口通信
◇RadioCRCPacket实现radio通信
◆RfmToLeds
◇ReceiveMsg接口(tos/interfaces/ReceiveMsg.nc)只定义了一个事件: receive()
◇接收消息的内存管理是动态继承的
◇Active Message层解码handler type并进行分派
◇缓存被传递给程序(通过ReceiveMsg.receive()事件),但关键的是,程序必须在处理完后返回指向缓存的指针
◇若是要保存消息内容以后处理,那么应复制消息内容到新的缓存,或向网络栈返回一个新缓存的指针
◆底层细节
◇消息头中包含group ID,使得多个mote组可以共享同一个radio channel
◇group ID是一个8位数
◇默认group ID是0x7D
◇使用DEFAULT_LOCAL_GROUP改变默认group ID
●DEFAULT_LOCAL_GROUP = 0x42 # for example...
◇使用MakeLocal文件改变所有程序胡group ID
◇消息头带有16位的目的结点地址
◇组中的每个通信结点在编译时都分配有惟一一个16位地址
◇TOS_BCAST_ADDR (0xfff)广播通用地址
◇TOS_UART_ADDR (0x007e)串口通用地址
◆TOSSIM是直接从TinyOS代码中编译而来的TinyOS模拟器
◆运行于桌面电脑和笔记本上
◆可同时模拟上千个运行相同程序的结点
◆可在运行时配置调试输出信息
Building and Running an Application
◆make pc编译得到TOSSIM
◆TOSSIM可执行文件是build/pc/main.exe
◆control-C停止模拟
◆默认输出所有调试信息
◆TOSSIM的最高频率为40KHZ
增加调试语句
◆Application components和调试的四种保留模式:usr1、usr2、 usr3、和temp
◆调试信息命令
◇语法
●dbg(<mode>, const char* format, ...);
◇<mode>指出以那种DBG模式输出调试信息
◇tos/types/dbg_modes.h包含全部可用模式
◇其它参数与printf()的参数同语义
/(.*/)是后面的/1所引用的实际的部分,必须要用括号括起来
$$指明以$结尾(前一个$表示普通的符号,后裔个$表示行的结尾)
所以,整个的功能应该是:获取以$结尾的完整路径的apps的上层路径。如果不以$结尾,则pattern space中不进行任何操作,而直接输出
1)在windowsXP下访问Xubuntos虚拟机里文件的方法:
(1)在Xubuntos中运行Applications->System->shared folders
(2)$sudo vim /etc/samba/smbuses (新创建smbuses文件),输入:
system_name = 用户名
(3)$sudo smbpasswd -a 用户名
New SMB password:(输入密码)
Retyp new SMB password:
2)$tos1 可设置为 Tinyos 1.X 的环境变量
$tos2 可设置为 Tinyos 2.X 的环境变量
其它有apps ,opt等。
3)$sudo -i 可以使用root 用户。
$sudo passwd root 可以修改root的密码。
1)编译程序,在程序目录下:
$make micaz install.38
micaz是平台,38是要设置的节点地址。
2)$make micaz docs 可以生成文档
存放于tinyos-2.x/doc/nesdoc目录下,index.html打开。
3)$make micaz sim
可以生成TOSSIM的仿真文件。
4)Tinyos默认使用的传感器VoltageC。在tinyos-2.x/tos/sensorboards/basicsb 中有光传感器Photot 和温度传感器Temp。
要更改默认的传感器可使用
$SENSORBOARD=basicsb make micaz install.38
注,Photo使用PortC1和ADC6.
1)在Vmware中如果要使用串口,首先要添加串口,并指定发实际电脑串口的对应关系,本例对应电脑的串口是com2,但是在VM中此串口名字应该是com1(如果这是你添加的第一个串口的话)。
在Xubuntos中为/dev/ttS0
2)$java net.tinyos.tools.listen -comm serial@/dev/ttyS0:micaz
打开对串口/dev/ttyS0的监听。波特率为micaz=57600。
3)$java net.tinyos.sf.Serialforwarder -comm serial@dev/ttyS0:micaz
可以打开一个图形化的窗口来对/dev/ttyS0进行监听。
4)Oscilloscope示波器的使用:
(1)在tinyos-2.x/apps/oscilloscope/java中打开一个命令终端,执行$make
(2)编译后再执行$./run
这样,就可以打开示波器了窗口了。注:打开Oscilloscope首先要对串口进行监听,运行(3)即可。