概述
◆TinyOS操作系统、库和程序服务程序是用nesC写的
◆nesC是一种开发组件式结构程序的语言
◆nesC是一种C语法风格的语言,但是支持TinyOS的并发模型,以及组织、命名和连接组件成为健壮的 嵌入式网络系统的机制
◇nesC应用程序是由有良好定义的双向接口的组件构建的
◇nesC定义了一个基于任务和硬件事件处理的并发模型,并能在编译时检测数据流组件
◆规范
◇nesC应用程序由一个或多个组件连接而成
◇一个组件可以提供或使用接口
●组件中command接口由组件本身实现
●组件中event接口由调用者实现
●接口是双向的,调用command接口必须实现其event接口
◆实现
◇modules
●包含应用程序代码,实现接口
◇configurations
●装配模块,连接模块使用的接口到其提供者
●每个nesC应用程序都有一个顶级configuration连接内部模块
并发模型
◆TinyOS只能运行单个由所需的系统模块和自定义模块构成的应用程序
◆两个线程
◇任务
●一次运行完成,非抢占式
◇硬件事件处理
●处理硬件中断
●一次运行完成,抢占式
●用于硬件中断处理的command和event必须用async关键字声明
◆执行流程(race conditions)
◇nesC要避免任务排他性访问共享数据
◇nesC要避免所有共享数据访问都通过原子语句
◇nesC在编译过程中要检测数据流,但可能误报,可用norace关键字声明不检测,但对其使用 应格外小心
Module of Sense Application
SenseM.nc
------------------------------------------------
|module SenseM {
| provides {
| interface StdControl;
| }
| 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")是产生一个唯一的数字与Timer串关联;unique("Timer")与
unique("MyTimer")可能产生相同的数;
●uniqueCount返回与参数关联的数的个数
任务
-
- async关键字声明硬件事件处理的command和event
-
- 任务用于处理复杂操作,比如后台数据处理,可以被硬件事件处理程序抢占
task void taskname() { ... }
post taskname();
CntToLedsAndRfm configuration
CntToLedsAndRfm.nc
----------------------------------------------------------
|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 configuration
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中的全局常量
IntToRfm module
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
◆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(, const char* format, ...);
◇指出以那种DBG模式输出调试信息
◇tos/types/dbg_modes.h包含全部可用模式
◇其它参数与printf()的参数同语义
在apps中修改或添加自己的文件,并编译通过。本来想会很烦,可能要涉及makefile。但是经过实践,原来只需小小的改动:只需在makefile中定义component(makefile所在的应用中主component,如Blink)。修改或添加文件时,只需注意两点:1.文件名与其内部所定义的component名相同;2.理顺各个components之间的调用和等同关系。原来只是如此简单,看来,不经实践,是不会知道深浅的。不过,也正因为此,仔细的学习了makefile,可谓一个意外收获吧。
sed 's@/(.*/)/apps.*$$@/1@'
解释: @是分隔符,同标准的/分隔符,由于不希望对很多的/进行转义,因此更换了分隔符为@
/(.*/)是后面的/1所引用的实际的部分,必须要用括号括起来
$$指明以$结尾(前一个$表示普通的符号,后裔个$表示行的结尾)
所以,整个的功能应该是:获取以$结尾的完整路径的apps的上层路径。如果不以$结尾,则pattern space中不进行任何操作,而直接输出