当前位置:首页 >> 信息与通信 >>

Tinyos移植 0414


一、 NECS 语言学习
一种支持组件化的编程语言,把组件化/模块化和基于事件驱动的执行模型 结合起来,这提高了应用开发的方便性和应用执行的可靠性,有利于代码重用。

(一)necs 语言的基本设计思想:
(1)结构和内容的分离:应用程序由由许多功能独立但又相互联系的软件组件组 成,各个组件通过连接(wire)而构成一个应用程序. (2)组

件的特性由它提供或使用的接口(interfaces)来访问. (3) 接口是双向的:接口事实上就是组件实现的函数的声明,这些函数可能是被提 供接口的租件实现(commands)也有可能是被使用接口的组件实现(events) (4)各个组件通过接口相互调用 (5) nesc 代码生成是由整个程序编译时一起产生的.数据冲突检测在编译时就完 成了. (6)中断程序和 Rtc 任务,中断程序之间的可能存在的数据访问冲突是在编译时 加以说明的.

(二) Nesc 要点
(1) An extension of c c 高效性 结构欠佳 代码安全性不好 nesc:高效 结构好(基于组件) 代码安全—剔掉了指针,函数指针 (2)Whole-program analysis “Whole-program inling” (3)Static language 无动态内存分配 编译时即可看到 call-graph

(三)Nesc 的语法学习
NesC 是标准 C 的扩展,应用背景是传感器网络这样的嵌入式系统,这类 系统的特点是内存有限,存在任务和中断两类操作。NesC 的语法和标准 C 基本 没有区别(NesC 应该不能动态分配内存) 。NesC 程序的基本组成是 Component, 一个 Component 是一个*.nc 文件。每个 Component 可以完成一定的工作,一个 app 一般有一个称为“Main”的 Component 作为程序的执行体(类似于 C 的 main 函数) “Main”调用其他的 component 以实现程序的功能。 , “Main”调用其他 Component,以及一个 Component 调用其他的 Component 的方式是“interface” 的连接,Component “uses”的 interface 连接到其他 component “provides” 的 interface。Interface 可以看作函数声明的一种封装,一个 interface 的内

容是几个函数的声明(没有函数的定义) ,TinyOS 系统提供了一系列 interface (interface 应该是由系统提供,不用自己写的) 。可以理解为 Interface 是 Component 的 属 性 , 函 数 是 interface 的 属 性 。 Component 分 为 两 类 , “configuration”用来完成 component 之间的连接, “module”用来完成该 Component 的功能(内容是“provides”的 interface 中函数的定义) 。NesC 定 义了两类特殊的函数, “command”和“event” 。函数调用时,Command 用“call” , event 用 “signal” 在一个 component 中, , provides 的 interface 中的 command 函数必须被实现(在 implementation 中定义) ,uses 的 interface 中的 event 函数必须被实现。 “async”指出这个 command 或者 event 可以在有中断时使用。 为了协调任务和中断的执行,nesC 使用“atomic”指出该段代码“不可被打断” 。 另外定义了“task”封装一些代码来完成一个任务,系统有 FIFO 的 task 队列。 不同的 Task 之间没有优先级,但 task 可以被 interrupt handler 打断。为防止 全局变量等公用数据被非正常修改, nesC 规定只在 task 中进入公共的数据部分。

(四)Nesc 中的并发性和原子操作----竞争处理机制
(1) tinyos 采用静态分配方式---编译时就确定全部应用程序所需存储器空 间。 (2) tinyos 不提供动态存储保护,共享变量的保护也是在编译时就完成的。 (3) nesc 假定程序的执行模型是由 Rtc(运行至完成)任务+由硬件信号异步通知 的中断处理程序共同组成的。 (4) 基于 nesc 的调度器必须按照 Rtc 方式来执行任务(标准的 tinyos 调度器遵 循 fifo 策略).由于任务是不被抢占并且 rtc 的,因此它们之间相对来说是原子的, 但相对中断处理来路说就不是的了。 (5) 形式上,我们把 nesc 程序代码分成两部分: Synchronous Code (SC):这种代码(函数 命令 事件 任务)是仅仅由任务来访问 的。 Asynchronous Code (AC):这种代码至少可以被一个中断处理程序访问。 (6)nesc 是一种编译时就进行数据冲突检测语言 关 键 词 :Invariant--- 无 变 化 的 ----nesc 编 译 时 将 检 测 应 用 程 序 是 否 具 备 Invariant 性 可能因此数据竞争的情况---即程序的 no Invariant 性 claim1:AC 改变共享状态(即全局变量) claim2:SC 改变能被 AC 访问的共享状态 竞争避免 claim1:所有共享状态仅被 AC 访问 claim1:采用原子操作

(五)编译器检测应用程序 Invariant 的原则
(1)Race-Free Invariant(无竞争不变性): 共享状态的任意 改变只能在 sc 或者是原子语句中进行。 (2)编译器认定:

? 只要对函数 f 的调用都是在原子语句中进行的,那么我们就认为这 个函数 f 主体出于原子语句当中. ? 对任意出于 ac 代码内的但又未做 async 声明的命令或者事件 nesc 将会报编译时错误 (3)可能的情况: ? nesc 可能报告出实际中并不发生的数据冲突,例如:假如所有的访 问都被其它的一些变量保护时. ? Norace 关键词:用特定的 norace 存储类型来注释变量 v 可以消除 对 v 的数据竞争警告 ? norace 关键词使用时应小心谨慎.

二、Nesc 到 c 的映射
我们知道 nesc 是 c 的扩展,事实上 ncc 编译器也是首先将 nesc 文件预编译 成 c 文件再交叉编译成可执行文件的.那么 nesc 如何整合成 c 呢?其中涉及一下 三个问题:

(1)作用域问题
一个 nesc 应用程序应由三部分组成: 一套 C 声明和定义列表;一套接口集合; 一套组件集合;nesc 的作用域按照下面方式来组织:

a. 最外层的是包含三个名字空间的全局作用域:C 变量名字空间,关于 c 声明和 定义的标签名字空间,c 组件和接口名字空间. b. 在全局空间中 C 声明和定义名字空间可能引入它们自己的嵌套空间,这是比 较常见的(对函数的说明和定义,它内部的码块就在这个函数空间内) c.每个接口类型引入一个包含接口命令或者事件的空间.这个空间也嵌套在全局 空间中,因此命令和事件定义能访问定义在全局空间中的 C 类型和标签. d.每个组件引入两个新空间.规范空间--嵌套在全局空间--包含一个含有组件规 范元素的变量名字空间;实现空间--嵌套在 规范空间内--包含一个变量名字空 间和一个标签名字空间.

(2)c 文件、接口和组件的整合
a.装入 c 文件:首先定位 c 文件,接着预处理.

b.装入接口:定位接口,接着装入该接口包含的头文件 c.装入组件:定位组件,再递归装入该组件使用的其它组件和相关文件.

(3)Nesc 语言命名到 c 语言命名的映射
规则如下: a.模块 m 中的变量 x 映射成 m$x; b.模块 m 中的函数 f 映射成 m$c; c.模块 m 中的命令或者事件 c 映射成 m$c d.模块 m 中的接口 i 中的命令或者事件 c 被映射成 m$i$c.

三、TinyOs 特点
(1)Componented-Based Architecture
T inyOS 提供一系列可重用的组件,一个应用程序可以通过连接配置文 件(A Wiring Specification)将各种组件连接起来,以完成它所需要的功能。

(2)Event-Driven Architecture
T inyOS 的应用程序都是基于事件驱动模式的,采用事件触发去唤醒传 感器工作。

(3)Tasks And Events Concurrency Model
tasks 一般用在对于时间要求不是很高的应用中,且 tasks 之间是平等 的,即在执行时是按顺序先后来得,而不能互相占先执行,一般为了减少 tasks 的运行时间,要求每一个 task 都很短小,能够使系统的负担较轻; events 一般用在对于时间的要求很严格的应用中, 而且它可以占先优于 tasks 和其他 events 执行,它可以被一个操作的完成或是来自外部环境的事 件触发,在 T inyOS 中一般由硬件中断处理来驱动事件。

(4)Split-Phase Operations
在 TinyOS 中由于 tasks 之间不能互相占先执行,所以 TinyOS 没有提供 任何阻塞操作,为了让一个耗时较长的操作尽快完成,一般来说都是将对 这个操作的需求和这个操作的完成分开来实现,以便获得较高的执行效率。

四、TinyOs 内核调度
(一)TinyOs 内核

调度机制:任务+事件两层调度机制 Task: RTc 任务 事件:主要来自硬件中断(时钟和 radio 模块) 优先级:事件>任务 调度策略:fifo,其中调度队列默认长度为 7 特点:单一任务栈,任务间互不抢占,而事件可抢占--不可剥夺型内核。由于资源 极端受限,tinyos 不可那像通常的 os 那样采用基于堆栈的线程调度机制,而基于 事件的调度机制则允许只有栈和一个简单的进程执行上下文。 中断服务程序设计策略:为了减少中断服务程序的运行时间, 降低中断响应延迟, 中断服务程序的设计应尽可能精简,以此来缩短中断响应时间。TinyOS 把一些 不需要在中断服务程序中立即执行的代码以函数的形式封装成任务, 在中断服务 程序中将任务函数地址放入任务队列,退出中断服务程序后由内核调度执行。内 核使用一个循环队列来维护任务列表

任务队列为空

调度情况

任务队列中任务数为三

(二)Tinyos 调度特点
(1) 任务单线程运行到结束, 只分配单个任务栈, 这对内存受限的系统很有利 。 (2) 没有进程管理的概念,对任务按简单的 FIFO 队列进行调度。对资源采取 预先分配,且目前这个队列里最多只能有 7 个待运行的任务。 (3) FIFO 的任务调度策略是电源敏感的。当任务队列为空时,处理器休眠, 随后由外部事件唤醒 CPU 进行任务调度 。 (4) 两级的调度结构可以实现优先执行少量同事件相关的处理,同时打断长时 间运行的任务 。 (5) 基于事件的调度策略,只需少量空间就可获得并发性,并允许独立的组件 共享单个执行上下文。同事件相关的任务集合可以很快被处理,不允许阻塞,具 有高度并发性 。 (6) 任务之间互相平等,没有优先级的概念 。

(三)Tinyos 调度中存在的问题:
(1) TinyOS 中的过载现象 节点发送数据频率过高,或者节点密度大,节点上中断发生频率很高,导致 CPU 除了进行中断处理外不执行行其任何任务。 (2)实时性问题 由于任务间是互不抢占的若某些实时任务处在一些需长时间运行的任务后。 (3)任务丢失 由于任务队列的长度为 8,当本地任务发生频率高时将会发生任务丢失。 (4)调度的不健壮 若某一正在运行的任务意外发生阻塞,将影响后续任务的执行。

五、硬件抽象化结构(TEP2)
(一)介绍
采用硬件抽象操作系统已经有价值的增加便携性和简化应用发展隐藏的硬 件复杂性的其余系统。 然而, 硬件抽象到相冲突性能和能源利用效率要求的传感 器网络应用。 这种驱动器需要一个明确的架构的硬件抽象, 可两者之间取得平衡这些相互 矛盾的目标。主要的挑战是选择适当的水平抽象和组织形式,它们 TinyOS 组件 支持重用性, 同时保持能源利用效率, 通过访问充分的硬件功能时, 这是必需的。 这个 TEP 为 TinyOS 2.0 的优势相结合的组成部分提出了一个三层的硬件抽 象架构(HAA)。高层抽象促进便携性的提供一个平台独立的硬件接口,中间层 有助于提高效率, 通过丰富的硬件接口和具体的最低层结构访问硬件寄存器和中 断。

(二)架构
在 Fig.1 的图示中, 硬件抽象化的功能包括 3 个不同的组件层。每一层已经 清楚的定义了自己的职责以及依赖于他的底层提供的界面。 基本的硬件能力是在 操作系统和应用程序之间慢慢适应到所建立的平台独立的界面。 正如我们从硬件 转移到最高层的界面, 组件就会慢慢脱离对硬件的依赖,给予开发者更多的自由 来设计和开发可重复使用的程序。
.. _Fig.1: :: +-----------------------------+ | | | Cross-platform applications | | | +--------------+--------------+ +-----------------+ | +-----------------+ |Platform-specific| | |Platform-specific| | applications | | | applications | +--------+--------+ | +--------+--------+ | Platform-independent | hardware interface | | +-------------+--------+----+-------------+ | | | | | | | | +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ | | |.----+----.| |.----+----.| |.----+----.| |.----+----.| | | || || || || || || || HIL 4 || |

| || HIL 1 || || HIL 2 || || HIL 3 || |`----+----'| | | || || |`----+----'| |`----+----'| | | | | | |`----+----'| | | | | | | | | +--+--+ +--+--+ | | |.----+----.| | | | | | | | | | | | || || |.----+----.| |.----+--+-.| |.-+--+----.| || || || || || || || || || HAL 2 || || || || || || || || || || HAL 3 || || HAL 4 || || HAL 1 || |`----+----'| || || || || || || | | | || || || || || || | | | |`----+----'| |`----+----'| |`----+----'| |.----+----.| | | | | | | | | | || || |.----+----.| | | | |.----+----.| || HPL 2 || || || |.----+----.| || HPL 1 || || || || HPL 3 || || HPL 4 || |`----+----'| |`----+----'| |`----+----'| |`----+----'| +-----+-----+ +-----+-----+ +-----+-----+ +-----+-----+ HW/SW | | | | boundary ************************************************************************ +------+-----+ +-----+-----+ +-----+-----+ +-----+-----+ |HW Plat 1 | |HW Plat 2 | |HW Plat 3 | |HW Plat 4 | +------------+ +-----------+ +-----------+ +-----------+ Fig.1: The proposed Hardware Abstraction Architecture

和传统的例如 WindowCE 集成系统开发的步骤相比,这样的设计增加了从脱 离某些平台的抽象化以及平台升降级的易用性。通过这样的方式,基于某些平台 的应用程序可以围绕着 HIL 组件设计并且连接到 HAL 的界面以达到最大性能, HAL 的界面则提供了对硬件模块的高性能访问能力 接下来进一步讨论每个组件层的使用功能。

(三)Hardware Presentation Layer (HPL) 硬件表示层
属于 HPL 的组件被直接放置在软硬件的接口上。 他们的主要任务是表示硬件 的能力。他们通过内存或者映射到输入输出设备的接口来访问。相反,硬件可以 发出中断信号来要求服务。通过内部的信息枢纽,HPL 隐藏了复杂的硬件接口并 且输出了可读性更强的界面。 HPL 组件是完全收到硬件模块能力的控制。这种和硬件的紧密结合降低了组 件设计和应用上的自由度。 尽管每个 HPL 组件都是唯一独特的。但是他们都有一 个类似的结构。为了更完美的和剩下的结构结合在一起,每个 HPL 都应该有: - 为了满足更有效的电源管理,用来初始化,开始以及结束硬件模块的命令

- 为控制硬件操作的寄存器提供"get"和"set"的命令 - 单独的命令用于最通常的标示设置和测试操作, 命令的命名遵循最大描述 性的原则 - 用于设置和取消硬件模块产生的中断的命令 - 硬件模块产生中断的服务程序 在 HPL 组件的中断服务程序只被用在时间临界操作中,例如复制一个变量, 清空一些标示, 这些中断服务程序分配余下的处理能力给更高等级的处理外部系 统状态的组件。 上述 HPL 结构简化操纵硬件。代替利用隐蔽宏和注册的名称,其定义是深藏 在头文件的编译图书馆,程序员现在可以访问硬件通过熟悉的界面 本 HPL 没有提供任何实质性的抽象的硬件超越自动化常用命令序列。然而, 它隐藏最依赖硬件的代码并开辟了一种开发更高层次的抽象组件方式。 这些较高 的抽象可用于同一类的不同的 HPL 硬件模块。例如,许多使用现有 sensornet 平台的微控制器上有两个 USART 模块的串行通信。它们具有相同的存取功能,但 略有不同的注册使用的名称并产生不同的中断向量。HPL 的组成部分可以隐藏这 些小分歧而取得一致的界面, 使更高一级的抽象资源独立。程序员可以切换不同 USART 模块通过简单的重新连接 HPL 组件,而不任何改变执行代码。

(四)Hardware Adaptation Layer (HAL) 硬件适配层
适应层的组件代表了架构的核心组成部分。 他们使用由 HPL 提供的接口建立 有效的抽象,隐藏与硬件资源复杂性。与 HPL 的组件相反,他们被允许保持可用 于执行仲裁和资源控制所使用的状态。 由于传感器网络的效率要求,HAL 层的抽象是针对具体的设备类别和平台。 除了不隐瞒硬件背后通用模型的特点,HAL 接口提供“最好”的可能抽象简化应 用开发,同时保持资源的有效利用。 例如,除了不对所有的设备使用一个单一的 “类似文件”的抽象,我们建 议的具体模式域像*报警* , *模数转换器*, *频道* , *EEPROM*。 根据模型, HAL 组件应提供通过丰富的, 定制的接口到抽象的访问,而不是通过标准通过隐藏几 个重载命令后面的所有功能来缩小这些访问。 这也提供了更加有效的对抽象接口 使用错误的编译时间检测。

(五)Hardware Interface Layer (HIL) 硬件接口层
最后一个构造等级是由 HIL 组件,HIL 组件作为 HAL 所提供的平台专用的抽 象, 并将他们转换为硬件独立接口所使用的跨平台应用。这些接口提供了一个建 立于简化软件开发硬件的平台独立抽象,隐藏硬件的差异。此 API 应该反映出* 典型*的硬件服务,需要在 sensornet 应用的。 HIL 组件的复杂性主要取决于相对于平台独立接口的抽象组件的能力。当硬 件的能力超过目前的 API 的合同,HIL 就会“降级”HAL 所提供的基于平台的抽 象化直到在选择的标准接口上平稳运行。因此,当基本的硬件降级后,底层硬件 差,HIL 可能倾向于失踪硬件能力的软件仿真。随着更新和更强大的平台推出, 就会慢慢取代 API 的传导。 当性能要求超过了稳定接口自身优点,就会分立跳转 将作出的重新调整的 API 提供的抽象在新的 HAL。演变的平台独立界面将迫使受 影响的 HIL 硬件得到再利用。对于新的平台, HIL 将变得简单得多,因为 API 的传导和他们的 HAL 抽象变得更加紧密。在另一个方面,提升软件能力的在旧平 台伤的成本也增加了。 由于我们期待设计出编个 HIL 接口的新硬件, 我们必须确定何时软件仿真硬 件的功能不能再持续下去。在这一点上,我们介绍 HIL 接口的版本管理。指定一 个版本编号给每一次迭代的 HIL 接口, 我们可以设计应用程序中使用的传统界面 符合先前部署设备。 这是非常重要的传感器网络,因为它们执行长期运行的应用 程序,并可能被部署多年。

(六)Horizontal decomposition
除了 HAA 的*纵向*分解, 一个横向分解可以促进不同平台上的重用硬件资源 抽象。为此目的,TinyOS 2.0 引入了芯片的概念 ,一种自载某一硬件芯片的抽 象: 微控制器, 无线芯片, 闪存芯片等, 每个芯片的分解按照 HAA 模型进行, HIL 作为最高的组成部分。平台然后组成不同芯片组件的形式建立( Fig.3_ )
.. _Fig.3: :: +----------------------------------------------------+ | AppC | | /Application Component/ | +------+--------------------------------------+------+ | | |Millisecond Timer | Communication +------+------+ +---------+------+ | TimerMilliC | | ActiveMessageC | | | | | | /Platform | | /Platform | | Component/ | | Component/ |

+------+------+ +---------+------+ | | +------+------+ +------+------+ | | 32kHz Timer | | | | +--------------+ | | | Atmega128 | | CC2420AlarmC | | CC2420 | | +----+ +----+ | | Timer Stack | | /Platform | | Radio Stack | | | | Component/ | | | | /Chip | +--------------+ | /Chip | | Component/ | | Component/ | +-------------+ +-------------+ Fig.3: The CC2420 software depends on a physical and dedicated timer. The micaZ platform code maps this to a specific Atmega128 timer.

一些共用硬件模块连接到微控制器使用一个标准的总线接口: SPI , I2C 的 总线, UART 接口。分享硬件驱动程序在不同平台的问题对抽象的互连已得到解 决。显然,最大的可移植性和再利用将实现使用通用总线抽象一样 NetBSD 的 [ NetBSD 的] _.该模型的文摘不同的总线协议在一个通用的总线接入方案。在 这这样, 它的提取分离的芯片从抽象的互连,可能允许在同一个芯片是抽象使用 不同的连接协议在不同的平台。然而,这一概括是在成本高的性能。这个可负担 得起的桌面操作系统,但具有高度次优的具体传感器网络应用平台。 TinyOS 2.0 需要较少的通用方法,提供硬件在环 HIL 水平,微控制器的独 立抽象的主要总线协议喜欢的 I2C 及 SPI , UART 和引脚的 I / O 这一区别使得 协议的具体优化,例如,将 SPI 抽象概念不需要处理客户端的地址,I2C 接口抽 象可以做。 此外, 程序员可以选择直接进入自来水芯片具体* HAL 的*-级别部分, 这可能进一步提高性能允许使用微调芯片的具体配置选项。 TinyOS 2.0 总线的抽象, 再加上那些低级引脚 I / O 和引脚中断 (见[ TEP117 ] _ ) 使某一芯片抽象都可以重复使用任何平台上的需要, , 支持总线协议。 CC2420 无线电的,例如,都可以使用的 Telos 和 micaZ 平台,因为抽象的串行模块的 MSP430 和 Atmega128 微控制器支持 SPI 总线抽象统一,这是用相同的 CC2420 无线电堆栈执行。 共享芯片提出了在总线上跨平台争夺资源的问题,当多个芯片连接到它时。 例如,在在 micaZ 的 CC2420 是连接到一个专用的 SPI 总线,而在 Telos 平台是 一个 SPI 总线之间共享 CC2420 电台和闪存芯片。化解冲突的资源预留机制拟议 中的[ TEP108_ ]适用于:每个芯片抽象使用总线协议必须使用``资源``接口, 以获得进入总线资源。通过这种方式,该芯片可以放心使用在专门的情景,以及 在一些情况下还多芯片连接到同一物理总线互连。

(七)CPU abstraction CPU 抽象
在 TinyOS 大部分变异的处理单元之间是隐藏的操作系统仅仅用 nesC / c 根据编程语言有一个共同的编译器套件(海合会)。例如,标准库分布式的编译 器创造了必要的启动代码初始化全局变量,堆栈指针和中断矢量表,屏蔽操作系 统从这些任务。统一的事情此外,TinyOS 提供共同构建宣布折返和非折返中断 服务例程和关键代码部分。 HAA 不是目前所使用的摘要的特点不同的 CPU 。目前支持的微控制器,结合 编译器套件支持和低级别的 I / O 是足够了。然而,如果新的内核截然不同架构 必须在未来辅之以 TinyOS,这部分硬件抽象的功能将得到明确处理。

(八)HIL alignment HIL 调整
虽然 HAA 要求提供完整的硬件独立( `强/实时半实物仿真` _ ),一些抽 象可能只部分达到这一目标( `弱半实物仿真` _ )。本节介绍几个方面描述不 同程度的配合概念的硬件在环*. *它还使用以下区别: - 平台定义 X: X 是定义在所有平台上,但 定义可能有所不同 - 平台特定的 X :X 是确定的只是一个平台 Strong/Real HILs 强/实时 HIL 强/实时 HIL 意味着“代码可以使用这些抽象合理预期的行为同样对所有的 实施。这个匹配的原始定义的硬件在环*一级*根据高空飞艇的* *.例子包括硬件 在环* *的定时器 TimerMilliC , [ TEP102 ] _ ) , ( 对发光二极管 LedsC ) , ( 积极的讯息( ActiveMessageC , [ TEP116 ] _ ,如果不使用任何无线电中继 至少) ,传感器包装( DemoSensorC , [ TEP109 ] _ )或存储( [ TEP103 ] _ ) 。强 HIL 可以使用平台定义的类型,如果它们还提供了业务操作他们(即, 他们的平台定义的抽象数据类型) ,为例如, TinyOS 2.x 的消息缓冲区抽象, `` message_t ( [ TEP111 ] _ ) Weak HILs 弱 HIL 弱 HIL*意味着一个“可以编写可移植代码对这些抽象的,但任何利用这些 涉及平台的具体行为 。 “ 虽然这种平台的具体行为可能-至少在最起码的语法水 平-是由一个平台无关的应用, 需要的语义知识特定平台。 例如, 需要抽象的 ADC 平台的具体配置和返回的数据必须解释鉴于此配置。 ADC 配置暴露在所有平台 该 上通过“ AdcConfigure ”界面,考虑了一个平台定义类型( adc_config_t ) 作为参数。但是,返回的 ADC 数据可能被处理的平台独立的方式,例如,通过计 算最大/分钟或意味着多模数转换器读数。 有利于从弱 HIL 是一个可以写入便携式工具代码,例如,一个重复的一个 ADC 采样顶部的数据路径。虽然使用这些抽象的代码可能无法完全便携式,它将

仍然是比较容易的港口比代码基础上的 HAL ,因为弱*半实物仿真*涉及一些指 导如何揭露一些功能,这应有助于程序员并为其提供指导平台开发商。

(九) Hardware Independent Interfaces (HII) 硬件独立接 口
?

硬件无关接口( HII ) * ,是一个界面定义用于跨多个平台。

例子包括的 SID 接口,引脚接口由[ TEP117 ]提供,该报警器/计数器/等接口由 [ TEP102 ]提供. Utility components 实物组件
?

实用组件*的件明确便携式代码(通常通用组件),这是不是暴露了自成 一体的服务。

例子包括部件的 TOS / lib 目录/定时器和 ArbitratedRead *组件。这些规定和 使用 HIIs 。

六、ADCs(TEP101)
(一)Abstract
This TEP proposes a hardware abstraction for analog-to-digital converters (ADCs) in TinyOS 2.x, which is aligned to the three-layer Hardware Abstraction Architecture (HAA) specified in [TEP2]. It describes some design principles and documents the set of hardware-independent interfaces to an ADC.

(二) Introduction
Analog-to-digital converters (ADCs) are devices that convert analog input signals to discrete digital output signals, typically voltage to a binary number. The interested reader can refer to Appendix A for a brief overview of the ADC hardware on some current TinyOS platforms. In earlier versions of TinyOS, the distinction between a sensor and an ADC were blurred: this led components that had nothing to do with an ADC to still resemble one programatically, even though the semantics and forms of operation were completely different. To compensate for the difference non-ADC sensors introduced additional interfaces, such as ADCError, that were tightly bound to sensor acquisition but separate in wiring. The separation between the ADC and ADCError interface is bug prone and problematic, as is the equation of a sensor and an ADC. TinyOS 2.x separates the structure and interfaces of an ADC from those of sensor drivers (which may be on top of an ADC stack, but this fact is hidden from

higher level components). This TEP presents how TinyOS 2.x structures ADC software. [TEP109] (Sensor Boards) shows how a platform can present actual named sensors. As can be seen in Appendix A the ADC hardware used on TinyOS platforms differ in many respects, which makes it difficult to find a chip independent representation for an ADC. Even if there were such a representation, the configuration details of an ADC would still depend on the actual device producing the input signal (sensor). Neither a platform independent application nor the ADC hardware stack itself has access to this information, as it can only be determined on a platform or sensorboard level. For example, determining which ADC port a sensor is attached to and how conversion results need to be interpreted is a platform specific determination. Although the actual configuration details may be different the procedure of configuring an ADC can be unified on all ADCs with the help of hardware independent interfaces: in a similar way as the Read interface definition does not predefine the type or semantics of the exchanged data (see [TEP114]), a configuration interface definition can abstract from the data type and semantics of the involved configuration settings. For example, like a component can provide a Read<uint8_t> or Read<uint16_t> interface, it can also provide a AdcConfigure<atm128_adc_config_t> or AdcConfigure<msp430adc12_channel_config_t> interface depending on what ADC it represents. This TEP proposes the (typed) AdcConfigure interface as the standard interface for configuring an ADC in TinyOS 2.x. In spite of their hardware differences, one aspect represents a common denominator of ADCs: they all produce conversion results. To facilitate sensor software development conversion results are returned by the ADC stack through the interfaces Read, ReadStream and ReadNow (see 2. Interfaces and [TEP114]). Conversion results are returned as uninterpreted values and translating them to engineering units can only be done with the configuration knowledge of the respective platform, for example, the reference voltage or the resistance of a reference resistor in ratiometric measurements. Translating uninterpreted values to engineering units may be performed by components located on top of the ADC stack and is out of the scope of this TEP. The top layer of abstraction of an ADC - the Hardware Interface Layer (HIL) - thus provides the interfaces Read, ReadNow and ReadStream and uses the AdcConfigure interface for hardware configuration (why it uses and does not provide AdcConfigure is explained below). Since the type and semantics of the parameters passed through these interfaces is dependent on the actual ADC implementation, it is only a "weak" HIL (see [TEP2]). Following the principles of the HAA [TEP2] the Hardware Adaptation Layer (HAL, which resides below the HIL) of an ADC should expose all the chip-specific capabilities of the chip. For example, the ADC12 on the MSP430 MCU supports a "Repeat-Sequence-of-Channels Mode" and therefore this function should be

accessible on the HAL of the MSP430 ADC12 hardware abstraction. Other ADCs might not exhibit such functionality and might therefore - on the level of HAL provide only an interface to perform single conversions. Since all ADCs have the same HIL representation it may be necessary to perform some degree of software emulation in the HIL implementation. For example, a ReadStream command can be emulated by multiple single conversion commands. Below the HAL resides the Hardware Presentation Layer (HPL), a stateless component that provides access to the hardware registers (see [TEP2]). The general structure (without virtualization) of the ADC stack is as follows
^ | | | | Read, AdcConfigure ReadNow (+ Resource), | ReadStream | | | V +----------------------------------+ | Hardware Interface Layer (HIL) | | (chip-specific implementation) | +----------------------------------+ | | chip-specific interface(s) + Resource (e.g. Msp430Adc12SingleChannel + Resource) | V +----------------------------------+ | Hardware Adaptation Layer (HAL) | | (chip-specific implementation) | +----------------------------------+ | | chip-specific interface(s) (e.g. HplAdc12) | V +----------------------------------+ | Hardware Presentation Layer (HPL)| | (chip-specific implementation) | +----------------------------------+

The rest of this TEP specifies:

? ? ? ? ?

the set of standard TinyOS interfaces for collecting ADC conversion results and for configuring an ADC (2. Interfaces) guidelines on how an ADC's HAL should expose chip-specific interfaces (3. HAL guidelines) what components an ADC's HIL MUST implement (4. HIL requirements) guidelines on how the HIL should be implemented (5. HIL guidelines) a section pointing to current implementations (6. Implementation)

This TEP ends with appendices documenting, as an example, the ADC implementation for the TI MSP430 MCU.

(三)Interfaces
This TEP proposes the AdcConfigure interface for ADC hardware configuration and the Read, ReadStream and ReadNow interfaces to acquire conversion results. The Read and ReadStream interfaces are documented in [TEP114] and the ReadNow interface is documented in this TEP. A Read[Now|Stream] interface is always provided in conjunction with a AdcConfigure interface.

(1)Interface for configuring the ADC hardware
The AdcConfigure interface is defined as follows:
interface AdcConfigure< config_type > { async command config_type getConfiguration(); }

This interface is used by the ADC stack to retrieve the hardware configuration of an ADC HIL client. config_type is a chip-specific data type (simple or structured) that contains all information necessary to configure the respective ADC hardware. For example, on the ADC12 of the MSP430 the AdcConfigure interface will be instantiated with the const msp430adc12_channel_config_t* data type. A client MUST always return the same configuration through a AdcConfigure interface and, if configuration data is passed as a pointer, the HIL component (see 4. HIL requirements) MUST NOT reference it after the return of the getConfiguration() command. If a client wants to use the ADC with different configurations it must provide multiple instances of the AdcConfigure interface. Note that the AdcConfigure interface is provided by an ADC HIL client and it is used by the ADC HIL implementation. Therefore an ADC HIL client cannot initiate the configuration of the ADC hardware itself. Instead it is the ADC HIL implementation that can "pull" the client's ADC configuration just before it initates a

conversion based on the respective client's configuration. The rationale is that the ADC HIL implementation does not have to store an ADC configuration per client instead the ADC client can, for example, store its configuration in program memory.

(2)Interfaces for acquiring conversion results
This TEP proposes to adopt the following two source-independent data collection interfaces from [TEP114] for the collection of ADC conversion results on the level of HIL:
interface Read< size_type > interface ReadStream< size_type >

In addition it proposes the following data collection interface for low-latency reading of conversion results:
interface ReadNow< size_type >

Every data collection interface is associated with an AdcConfigure interface (how this association is realized is explained in Section 4. HIL requirements). As the resolution of conversion results is chip-specific, the size_type parameter reflects an upper bound for the chip-specific resolution of the conversion results - the actual resolution may be smaller (e.g. uint16_t for a 12-bit ADC).

(2.1)Read
The Read interface can be used to sample an ADC channel once and return a single conversion result as an uninterpreted value. The Read interface is documented in [TEP114].

(2.2)ReadStream
The ReadStream interface can be used to sample an ADC channel multiple times with a specified sampling period. The ReadStream interface is documented in [TEP114] .

(2.3)ReadNow
The ReadNow interface is intended for split-phase low-latency reading of small values:
interface ReadNow<val_t> { async command error_t read();

async event void readDone( error_t result, val_t val ); }

This interface is similar to the Read interface, but works in asynchronous context. A successful call to ReadNow.read() means that the ADC hardware has started the sampling process and that ReadNow.readDone() will be signalled once it has finished (note that the asynchronous ReadNow.readDone() might be signalled even before the call to ReadNow.read() has returned). Due to its timing constraints the ReadNow interface is always provided in conjunction with an instance of the Resource interface and a client must reserve the ADC through the Resource interface before the client may call ReadNow.read(). Please refer to [TEP108] on how the Resource interface should be used by a client component.

(四) HAL guidelines
As explained in 1. Introduction the HAL exposes the full capabilities of the ADC hardware. Therefore only chip- and platform-dependent clients can wire to the HAL. Although the HAL is chip-specific, both, in terms of implementation and representation, its design should follow the guidelines described in this section to facilitate the mapping to the HIL representation. Appendix B shows the signature of the HAL for the MSP430.

(1)Resource reservation
As the ADC hardware is a shared resource that is usually multiplexed between several clients some form of access arbitration is necessary. The HAL should therefore provide a parameterized Resource interface, instantiate a standard arbiter component and connect the Resource interface to the arbiter as described in [TEP108]. To ensure fair and uniform arbitration on all platforms the standard round robin arbiter is recommended. Resource arbiters and the Resource interface are the topic of [TEP108].

(2)Configuration and sampling
As the ADC hardware is a shared resource the HAL should support hardware configuration and sampling per client (although per-port configuration is possible, it is not recommended, because it forces all clients to use the same configuration for a given port). Therefore the HAL should provide sampling interfaces parameterized by a client identifier. A HAL client can use its instance of the sampling interface to configure the ADC hardware, start the sampling process and acquire conversion results. It wires to a sampling interface using a unique client identifier (this may be hidden by a virtualization component). All commands and events in the sampling

interface should be 'async' to reflect the potential timing requirements of clients on the level of HAL. A HAL may provide multiple different parameterized sampling interfaces, depending on the hardware capabilities. This allows to differentiate/group ADC functionality, for example single vs. repeated sampling, single channel vs. multiple channels or low-frequency vs. high-frequency sampling. Every sampling interface should allow the client to individually configure the ADC hardware, for example by including the configuration data as parameters in the sampling commands. However, if configuration data is passed as a pointer, the HAL component MUST NOT reference it after the return of the respective command. Appendix B shows the HAL interfaces for the MSP430.

(3)HAL virtualization
In order to hide wiring complexities and/or export only a subset of all ADC functions generic ADC wrapper components may be provided on the level of HAL. Such components can also be used to ensure that a sampling interface is always provided with a Resource interface and both are instantiated with the same client ID if this is required by the HAL implementation.

(五) HIL requirements
The following generic components MUST be provided on all platforms that have an ADC:
AdcReadClientC AdcReadNowClientC AdcReadStreamClientC

These components provide virtualized access to the HIL of an ADC. They are instantiated by an ADC client and provide/use the four interfaces described in Section 2. Interfaces. An ADC client may instantiate multiple such components. The following paragraphs describe their signatures. Note that this TEP does not address the issue of how to deal with multiple ADCs on the same platform (the question of how to deal with multiple devices of the same class is a general one in TinyOS 2.x). Appendix C shows the AdcReadClientC for the MSP430.

(1)AdcReadClientC
generic configuration AdcReadClientC() { provides { interface Read< size_type >; }

uses { interface AdcConfigure< config_type >; } }

The AdcReadClientC component provides a Read interface for acquiring single conversion results. The associated ADC channel (port) and further configuration details are returned by the AdcConfigure.getConfiguration() command. It is the task of the client to wire this interface to a component that provides the client's ADC configuration. The HIL implementation will use the AdcConfigure interface to dynamically "pull" the client's ADC settings when it translates the Read.read() command to a chip-specific sampling command. Note that both, size_type and config_type, are only placeholders and will be instantiated by the respective HIL implementation (for an example, see the AdcReadClientC for the MSP430 in Appendix C).

(2)AdcReadNowClientC
generic configuration AdcReadNowClientC() { provides { interface Resource; interface ReadNow< size_type >; } uses { interface AdcConfigure< config_type >; } }

The AdcReadNowClientC component provides a ReadNow interface for acquiring single conversion results. In contrast to Read.read() when a call to ReadNow.read() succeeds, the ADC starts to sample the channel immediately (a successful Read.read() command may not have this implication, see [TEP114] and 2. Interfaces). A client MUST reserve the ADC through the Resource interface before the client may call ReadNow.read() and it MUST release the ADC through the Resource interface when it no longer needs to access it (for more details on how to use the Resource interface please refer to [TEP108]). The associated ADC channel (port) and further configuration details are returned by the AdcConfigure.getConfiguration() command. It is the task of the client to wire this interface to a component that provides the client's ADC configuration. The HIL implementation will use the AdcConfigure interface to dynamically "pull" the client's ADC settings when it translates the ReadNow.read() command to a chip-specific sampling command. Note that both, size_type and config_type, are only placeholders and will be instantiated by the respective HIL implementation (for an example how this is done for the AdcReadClientC see Appendix C).

(3)AdcReadStreamClientC
generic configuration AdcReadStreamClientC() { provides { interface ReadStream< size_type >; } uses { interface AdcConfigure< config_type>; } }

The AdcReadStreamClientC component provides a ReadStream interface for acquiring multiple conversion results at once. The ReadStream interface is explained in [TEP114] and 2. Interfaces. The AdcConfigure interface is used in the same way as described in the section on the AdcReadClientC. Note that both, size_type and config_type, are only placeholders and will be instantiated by the respective HIL implementation (for an example how this is done for the AdcReadClientC see Appendix C).

(六)HIL guidelines
The HIL implementation of an ADC stack has two main tasks: it translates a Read, ReadNow or ReadStream request to a chip-specific HAL sampling command and it abstracts from the Resource interface (the latter only for the AdcReadClientC and AdcReadStreamClientC). The first task is solved with the help of the AdcConfigure interface which is used by the HIL implementation to retrieve a client's ADC configuration. The second task MAY be performed by the following library components: ArbitratedReadC, and ArbitratedReadStreamC (in tinyos-2.x/tos/system) - please refer to the Atmel Atmega 128 HAL implementation (in tinyos-2.x/tos/chips/atm128/adc) for an example. Note that since the ReadNow interface is always provided in conjunction with a Resource interface the HIL implementation does not have to perform the ADC resource reservation for an AdcReadNowClientC, but may simply forward an instance of the Resource interface from the HAL to the AdcReadNowClientC. The typical sequence of events is as follows: when a client requests data through the Read or ReadStream interface the HIL will request access to the HAL using the Resource interface. After the HIL has been granted access, it will "pull" the client's ADC configuration using the AdcConfigure interface and translate the client's Read or ReadStream command to a chip-specific HAL command. Once the HIL is signalled the conversion result(s) from the HAL it releases the ADC through the Resource interface and signals the conversion result(s) to the client though the Read or ReadStream interface. When a client requests data through the ReadNow interface

the HIL translates the client's command to the chip-specific HAL command without using the Resource interface (it may check ownership of the client through the ArbiterInfo interface - this check can also be done in the HAL implementation). Once the HIL is signalled the conversion result(s) it forwards it to the respective ReadNow client.

(七) Implementation (1)TestAdc application
An ADC HIL test application can be found in tinyos-2.x/apps/tests/TestAdc. Note that this application instantiates generic DemoSensorC, DemoSensorStreamC and DemoSensorNowC components (see [TEP114]) and assumes that these components are actually wired to an ADC HIL. Please refer to tinyos-2.x/apps/tests/TestAdc/README.txt for more information.

(2)HAA on the MSP430 and Atmega 128
The implementation of the ADC12 stack on the MSP430 can be found in tinyos-2.x/tos/chips/msp430/adc12:
? ? ? ? ?

HplAdc12P.nc is the HPL implementation Msp430Adc12P.nc is the HAL implementation AdcP.nc is the HIL implementation AdcReadClientC.nc, AdcReadNowClientC.nc and AdcReadStreamClientC.nc provide virtualized access to the HIL the use of DMA or the reference voltage generator and the HAL virtualization components are explained in README.txt

The Atmel Atmega 128 ADC implementation can be found in tinyos-2.x/tos/chips/atm128/adc:
? ? ?

?

HplAtm128AdcC.nc is the HPL implementation Atm128AdcP.nc is the HAL implementation AdcP.nc, WireAdcP.nc and the library components for arbitrating 'Read', 'ReadNow' and 'ReadStream', ArbitratedReadC and ArbitratedReadStreamC (in tinyos-2.x/tos/system), realize the HIL AdcReadClientC.nc, AdcReadNowClientC.nc and AdcReadStreamClientC.nc provide virtualized access to the HIL

( 八 ) Appendix A: Hardware differences between platforms
The following table compares the characteristics of two microcontrollers commonly used in TinyOS platforms:
Atmel Atmega 128 Resolution channels 10-bit
? ? ?

TI MSP430 ADC12 12-bit

8 multiplexed external channels 16 differential voltage input combinations 2 differential inputs with gain amplification

?

?

8 individually configurable external channels internal channels (AVcc, temperature, reference voltages)

internal voltage

reference

2.56V
? ?

1.5V or 2.5V positive terminal: AVcc or 2.56V or AREF (external) negative terminal: GND individually channel:
? ? ? ? ? ?

conversion reference

selectable

per

AVcc and AVss Vref+ and AVss Veref+ and AVss AVcc and (Vref- or Veref-) AVref+ and (Vref- or Veref-) Veref+ and (Vref- or Veref-) single conversion mode repeat single conversion mode sequence mode (sequence <= 16 channels) repeat sequence mode

conversion modes

? ?

single channel conversion mode free running mode (channels and reference voltages can be switched between samples)

? ? ?

?

conversion

clock

clkADC with prescaler

ACLK, MCLK, SMCLK or

Atmel Atmega 128 source sample-hold-time conversion triggering conversion during sleep mode possible interrupts 1.5 clock cycles (fixed) by software yes after each conversion

TI MSP430 ADC12 ADC-oscillator (5MHz) prescaler respectively with

selectable values from 4 to 1024 clock cycles by software or timers yes after single conversion or sequence

(九)Appendix B: a HAL representation: MSP430 ADC12
This section shows the HAL signature for the ADC12 of the TI MSP430 MCU. It reflects the four MSP430 ADC12 conversion modes as it lets a client sample an ADC channel once ("Single-channel-single-conversion") or repeatedly ("Repeat-single-channel"), multiple times ("Sequence-of-channels") or multiple times repeatedly ("Repeat-sequence-of-channels"). In contrast to the single channel conversion modes the sequence conversion modes trigger a single interrupt after multiple samples and thus enable high-frequency sampling. The DMAExtension interface is used to reset the state machine when the DMA is responsible for data transfer (managed in an exterior component):
configuration { provides { interface interface interface } } Msp430Adc12P

Resource[uint8_t id]; Msp430Adc12SingleChannel as SingleChannel[uint8_t id]; AsyncStdControl as DMAExtension[uint8_t id];

interface Msp430Adc12SingleChannel { async command error_t configureSingle(const msp430adc12_channel_config_t *config); async command error_t configureSingleRepeat(const msp430adc12_channel_config_t *config, uint16_t jiffies); async command error_t configureMultiple( const msp430adc12_channel_config_t *config, uint16_t buffer[], uint16_t numSamples, uint16_t jiffies);

async command error_t configureMultipleRepeat(const msp430adc12_channel_config_t *config, uint16_t buffer[], uint8_t numSamples, uint16_t jiffies); async command error_t getData(); async event error_t singleDataReady(uint16_t data); async event uint16_t* multipleDataReady(uint16_t buffer[], uint16_t numSamples); } typedef struct { unsigned int inch: 4; unsigned int sref: 3; unsigned int ref2_5v: 1; unsigned int adc12ssel: 2; unsigned int adc12div: 3; unsigned int sht: 4; unsigned int sampcon_ssel: 2; unsigned int sampcon_id: 2; } msp430adc12_channel_config_t;

// // // // // // // //

input channel reference voltage reference voltage level clock source sample-hold-time clock divider sample-hold-time sample-hold-time clock source sampcon signal clock divider sampcon signal

(十)Appendix C: a HIL representation: MSP430 ADC12
The signature of the AdcReadClientC component for the MSP430 ADC12 is as follows:
generic configuration AdcReadClientC() { provides interface Read<uint16_t>; uses interface AdcConfigure<const msp430adc12_channel_config_t*>; } [TEP1] [TEP2] [TEP108] [TEP109] [TEP114] TEP 1: TEP Structure and Keywords. (1, 2, 3, 4) TEP 2: Hardware Abstraction Architecture. (1, 2, 3, 4) TEP 108: Resource Arbitration. TEP 109: Sensor Boards. (1, 2, 3, 4, 5, 6, 7, 8, 9) TEP 114: SIDs: Source and Sink Independent Drivers.

七、Timers(TEP102)
(一)Abstract
This TEP proposes a Timer design that supports common timing requirements both in precision and width across common hardware configurations. This TEP focuses on aligning the Timer abstraction with the three-layer Hardware Abstraction Architecture (HAA).

(二) Introduction
Most microcontrollers offer a rich timer system, with features like:
? ? ?

several counters, possibly of different widths, with multiple clocking options one or more compare registers for each counter, which can trigger interrupts, changes to output pins and changes to the counter value capture of the time of input pin changes

The interested reader can refer to Appendix A for a brief overview of the timer hardware on some current TinyOS platforms. TinyOS does not attempt to capture all this diversity in a platform-independent fashion. Instead, following the principles of the HAA[_tep2], each microcontroller should expose all this functionality via components and interfaces at the HPL and, where appropriate, HAL levels. However, two aspects of timers are sufficiently common and important that they should be made available in a well-defined way: measuring time, and triggering (possibly repeating) events at specific times. The rest of this TEP specifies:
? ? ? ?

a set of platform-independent interfaces for counting time and triggering events (2. Interfaces) guidelines on how each microcontroller's HAL SHOULD expose its timer hardware in terms of the above interfaces (3. HAL guidelines) what components a microcontroller's timer HIL MUST implement (4. HIL requirements) a set of utility components whose use simplifies building the components specified by the HAL guidelines and HIL requirements (5. Utility components)

This TEP ends with appendices documenting, as an example, the mica2 timer subsystem implementation.

(三) Interfaces
Before presenting the interfaces (2.2), we start with a general discussion of the issues of precision, width and accuracy in timer interfaces (2.1).

(1) Precision, Width and Accuracy
Three fundamental properties of timers are precision, width and accuracy. Examples of precision are millisecond, a cycle of a 32kHz clock, and microseconds. All precisions presented in this TEP are in "binary" units with respect to one second. That is, one second contains 1024 binary milliseconds, 32768 32kHz ticks, or 1048576 microseconds. This TEP emphasizes millisecond and 32kHz tick precisions while reasonably accommodating other precisions. The use of "binary" units is motivated by the common availability of hardware clocks driven by a 32768Hz crystal. Examples of widths are 8-bit, 16-bit, 32-bit, and 64-bit. The width for timer interfaces and components SHOULD be 32-bits. This TEP emphasizes 32-bit widths while reasonably accommodating other widths - a particular platform may have good reasons not to expose a 32-bit interface. Accuracy reflects how closely a component conforms to the precision it claims to provide. Accuracy is affected by issues such as clock drift (much higher for internal vs crystal oscillators) and hardware limitations. As an example of hardware limitations, a mica2 clocked at 7.37MHz cannot offer an exact binary microsecond timer -- the closest it can come is 7.37MHz/8. Rather than introduce a plethora of precisions, we believe it is often best to pick the existing precision closest to what can be provided, along with appropriate documentation. However, the accuracy MUST remain reasonable: for instance, it would be inappropriate to claim that a millisecond timer is a 32kHz timer. This TEP parameterizes all interfaces by precision and some interfaces by width. This intentionally makes similar timer interfaces with different precision or width mutually incompatible. It also allows user code to clearly express and understand the precision and width for a given timer interface. Accuracy is not reflected in the interface type. Precision is expressed as a dummy type -- TMilli, T32khz, and TMicro -- written in the standard Timer.h header like this:
typedef struct { int notUsed; } TMilli; // 1024 ticks per second typedef struct { int notUsed; } T32khz; // 32768 ticks per second typedef struct { int notUsed; } TMicro; // 1048576 ticks per second

Note that the precision names are expressed as either frequency or period, whichever is convenient.

(2) Timer interfaces
This TEP proposes these timer interfaces:
interface interface interface interface interface Counter< precision_tag, size_type > Alarm< precision_tag, size_type > BusyWait< precision_tag, size_type > LocalTime< precision_tag > Timer< precision_tag >

The LocalTime and Timer interfaces are used primarily by user applications and use a fixed width of 32-bits. The Alarm, BusyWait, and Counter interfaces are used by the TinyOS timer system and advanced user components.

(2.1)Counter
The Counter interface returns the current time and provides commands and an event for managing overflow conditions. These overflow commands and events are necessary for properly deriving larger width Counters from smaller widths.
interface Counter<precision_tag,size_type> { async command size_type get(); async command bool isOverflowPending(); async command void clearOverflow(); async event void overflow(); } get() return the current time. isOverflowPending() return TRUE if the overflow flag is set for this counter, i.e., if and only if an overflow event will occur after the outermost atomic block exits. Return FALSE otherwise. This command only returns the state of the overflow flag and causes no side effect. clearOverflow() cancel the pending overflow event clearing the overflow flag. overflow() signals that an overflow in the current time. That is, the current time has wrapped around from its maximum value to zero.

(2.2)Alarm
Alarm components are extensions of Counters that signal an event when their compare register detects the alarm time has been hit. All commands and events of the Alarm interface are asynchronous (or in "interrupt context"). The Alarm interface provides a set of "basic" commands for common usage and provides a set of "extended" commands for advanced use.
interface Alarm<precision_tag,size_type> { // basic interface async command void start( size_type dt ); async command void stop(); async event void fired(); // extended interface async command bool isRunning(); async command void startAt( size_type t0, size_type dt ); async command size_type getNow(); async command size_type getAlarm(); } start(dt) cancel any previously running alarm and set to fire in dt time units from the time of invocation. The alarm will only fire once then stop. stop() cancel any previously running alarm. fired() signals that the alarm has expired. isRunning() return TRUE if the alarm has been started and has not been cancelled or has not yet fired. FALSE is returned otherwise.

startAt(t0,dt) cancel any previously running alarm and set to fire at time t1 = t0+dt. This form allows a delay to be anchored to some time t0 taken before the invocation of startAt. The timer subsystem uses this form internally, to be able to use of the full width of an alarm while also detecting when a short alarm elapses prematurely. The time t0 is always assumed to be in the past. A value of t0 numerically greater than the current time (returned by getNow()) represents a time from before the last wraparound.

getNow() return the current time in the precision and width of the alarm. getAlarm() return the time the currently running alarm will fire or the time that the previously running alarm was set to fire. getAlarm can be used with startAt to set an alarm from the previous alarm time, as in startAt(getAlarm(),dt). This pattern is used within the fired() event to construct periodic alarms.

(2.3)BusyWait
The BusyWait interface allows for very short synchronous delays. BusyWait should be used sparingly and when an Alarm would not be reasonably efficient or accurate. The BusyWait interface replaces the TOSH_uwait macro from TinyOS 1.x. BusyWait blocks for no less than the specified amount of time. No explicit upper bound is imposed on the enacted delay, though it is expected that the underlying implementation spins in a busy loop until the specified amount of time has elapsed.
interface BusyWait<precision_tag,size_type> { async command void wait( size_type dt ); } wait(dt) block until at least dt time units have elapsed

(2.4)LocalTime
The LocalTime interface exposes a 32-bit counter without overflow utilities. This is primarily for application code that does not care about overflow conditions.
interface LocalTime<precision_tag> { async command uint32_t get(); } get() return the current time.

(2.5)Timer
All commands and events of the Timer interface are synchronous (or in "task context"). The Timer interface provides a set of "basic" commands for common usage

and provides a set of "extended" commands for advanced use. The Timer interface allows for periodic events.
interface Timer<precision_tag> { // basic interface command void startPeriodic( uint32_t dt ); command void startOneShot( uint32_t dt ); command void stop(); event void fired(); // extended interface 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(dt) cancel any previously running timer and set to fire in dt time units from the time of invocation. The timer will fire periodically every dt time units until stopped. startOneShot(dt) cancel any previously running timer and set to fire in dt time units from the time of invocation. The timer will only fire once then stop. stop() cancel any previously running timer. fired() signals that the timer has expired (one-shot) or repeated (periodic). isRunning() return TRUE if the timer has been started and has not been cancelled and has not fired for the case of one-shot timers. Once a periodic timer is started, isRunning will return TRUE until it is cancelled. isOneShot() return TRUE if the timer is a one-shot timer. Return FALSE otherwise if the timer is a periodic timer. startPeriodicAt(t0,dt)

cancel any previously running timer and set to fire at time t1 = t0+dt. The timer will fire periodically every dt time units until stopped.

As with alarms, the time t0 is always assumed to be in the past. A value of t0 numerically greater than the current time (returned by getNow()) represents a time from before the last wraparound.
startOneShotAt(t0,dt)

cancel any previously running timer and set to fire at time t1 = t0+dt. The timer will fire once then stop. t0 is as in startPeriodicAt.
getNow() return the current time in the precision and width of the timer. gett0() return the time anchor for the previously started timer or the time of the previous event for periodic timers. getdt() return the delay or period for the previously started timer.

(四) HAL guidelines
Platforms SHOULD expose their relevant timing capabilities using standard Alarm and Counter interfaces. The design pattern presented here defines a component naming convention to allow platform independent access to particular Alarms and Counters if they exist and to cause compile errors if they do not. A platform specific hardware timer with precision ${P} and width ${W} SHOULD be exposed as these two conventional Counter and Alarm components:
configuration Counter${P}${W}C { provides interface Counter< T${P}, uint${W}_t >; } generic configuration Alarm${P}${W}C() { provides interface Alarm< T${P}, uint${W}_t >; }

Instantiating an Alarm${P}${W}C component provides a new and independent Alarm. If the platform presents a limited number of Alarm resources, then allocating more Alarms in an application than are available for the platform SHOULD produce a compile-time error. See Appendices B and C for an example of how to make allocatable Alarms that are each implemented on independent hardware timers.

For example, if a platform has an 8-bit 32kHz counter and three 8-bit 32kHz alarms, then the Counter and Alarm interfaces for ${P}=32khz and ${W}=16 are:
configuration Counter32khz8C { provides interface Counter< T32khz, uint8_t >; } generic configuration Alarm32khz8C() { provides interface Alarm< T32khz, uint8_t >; }

This pattern MAY be used to define components for the platform that are mutually incompatible in a single application. Incompatible components SHOULD produce compile-time errors when compiled together.

(五) HIL requirements
The following component MUST be provided on all platforms
HilTimerMilliC BusyWaitMicroC

Both of these components use "binary" units, i.e., 1/1024s for HilTimerMilliC and 1/1048576s for BusyWaitMicroC. Components using other precisions (e.g., regular, non-binary milliseconds) MAY also be provided.

(1)HilTimerMilliC
configuration HilTimerMilliC { provides interface Init; provides interface Timer<TMilli> as TimerMilli[ uint8_t num ]; provides interface LocalTime<TMilli>; }

A new timer is allocated using unique(UQ_TIMER_MILLI) to obtain a new unique timer number. This timer number is used to index the TimerMilli parameterised interface. UQ_TIMER_MILLI is defined in Timer.h. HilTimerMilliC is used by the LocalTimeMilliC component and the TimerMilliC generic component, both found in tos/system/

(2)BusyWaitMicroC
configuration BusyWaitMicroC { provides interface BusyWait<TMicro,uint16_t>; }

BusyWaitMicroC allows applications to busy-wait for a number of microseconds. Its use should be restricted to situations where the delay is small and setting a timer or alarm would be impractical, inefficient or insufficiently precise.

(六) Utility components
A number of platform independent generic components are provided to help implementers and advanced users of the TinyOS timer system:
? ? ? ? ? ?

AlarmToTimerC BusyWaitCounterC CounterToLocalTimeC TransformAlarmC TransformCounterC VirtualizeTimerC

Appendices B and C show how these can be used to help implement the timer HAL and HIL.

(1)AlarmToTimerC
AlarmToTimerC converts a 32-bit Alarm to a Timer.
generic component AlarmToTimerC( typedef precision_tag ) { provides interface Timer<precision_tag>; uses interface Alarm<precision_tag,uint32_t>; }

(2)BusyWaitCounterC
BusyWaitCounterC uses a Counter to block until a specified amount of time elapses.
generic component BusyWaitC( typedef precision_tag, typedef size_type @integer() )

{ provides interface BusyWait<precision_tag,size_type>; uses interface Counter<precision_tag,size_type>; }

(3)CounterToLocalTimeC
CounterToLocalTimeC converts from a 32-bit Counter to LocalTime.
generic component CounterToLocalTimeC( precision_tag ) { provides interface LocalTime<precision_tag>; uses interface Counter<precision_tag,uint32_t>; }

(4)TransformAlarmC
TransformAlarmC decreases precision and/or widens an Alarm. An already widened Counter component is used to help.
generic component TransformAlarmC( typedef to_precision_tag, typedef to_size_type @integer(), typedef from_precision_tag, typedef from_size_type @integer(), uint8_t bit_shift_right ) { provides interface Alarm<to_precision_tag,to_size_type> as Alarm; uses interface Counter<to_precision_tag,to_size_type> as Counter; uses interface Alarm<from_precision_tag,from_size_type> as AlarmFrom; }

to_precision_tag and to_size_type describe the final precision and final width for the provided Alarm. from_precision_tag and from_size_type describe the precision and width for the source AlarmFrom. bit_shift_right describes the bit-shift necessary to convert from the used precision to the provided precision. For instance to convert from an Alarm<T32khz,uint16_t> to an Alarm<TMilli,uint32_t>, the following TransformAlarmC would be created:
new TransformAlarmC( TMilli, uint32_t, T32khz, uint16_t, 5 )

It is the exclusive responsibility of the developer using TransformAlarmC to ensure that all five of its arguments are self consistent. No compile errors are generated if the parameters passed to TransformAlarmC are inconsistent.

(5)TransformCounterC
TransformCounterC decreases precision and/or widens a Counter.
generic component TransformCounterC( typedef to_precision_tag, typedef to_size_type @integer(), typedef from_precision_tag, typedef from_size_type @integer(), uint8_t bit_shift_right, typedef upper_count_type @integer() ) { provides interface Counter<to_precision_tag,to_size_type> as Counter; uses interface Counter<from_precision_tag,from_size_type> as CounterFrom; }

to_precision_tag and to_size_type describe the final precision and final width for the provided Counter. from_precision_tag and from_size_type describe the precision and width for the source CounterFrom. bit_shift_right describes the bit-shift necessary to convert from the used precision to the provided precision. upper_count_type describes the numeric type used to store the additional counter bits. upper_count_type MUST be a type with width greater than or equal to the additional bits in to_size_type plus bit_shift_right. For instance to convert from a Counter<T32khz,uint16_t> to a Counter<TMilli,uint32_t>, the following TransformCounterC would be created:
new TransformCounterC( TMilli, uint32_t, T32khz, uint16_t, 5, uint32_t )

(6)VirtualizeTimerC
VirtualizeTimerC uses a single Timer to create up to 255 virtual timers.
generic component VirtualizeTimerC( typedef precision_tag, int max_timers ) { provides interface Init; provides interface Timer<precision_tag> as Timer[ uint8_t num ];

uses interface Timer<precision_tag> as TimerFrom; }

(七) Implementation
The definition of the HIL interfaces are found in tinyos-2.x/tos/lib/timer:
? ? ? ? ? ?

Alarm.nc BusyWait.nc Counter.nc LocalTime.nc Timer.h defines precision tags and strings for unique() Timer.nc

The implementation of the utility components are also found in tinyos-2.x/tos/lib/timer:
? ? ? ? ? ? ?

AlarmToTimerC.nc BusyWaitCounterC.nc CounterToLocalTimeC.nc TransformAlarmC.nc TransformCounterC.nc VirtualizeAlarmC.nc VirtualizeTimerC.nc

The implementation of timers for the MSP430 is in tinyos-2.x/tos/chips/msp430/timer:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

Alarm32khz16C.nc is generic and provides a new Alarm<T32khz,uint16_t> Alarm32khz32C.nc is generic and provides a new Alarm<T32khz,uint32_t> AlarmMilli16C.nc is generic and provides a new Alarm<TMilli,uint16_t> AlarmMilli32C.nc is generic and provides a new Alarm<TMilli,uint32_t> BusyWait32khzC.nc provides BusyWait<T32khz,uint16_t> BusyWaitMicroC.nc provides BusyWait<TMicro,uint16_t> Counter32khz16C.nc provides Counter<T32khz,uint16_t> Counter32khz32C.nc provides Counter<T32khz,uint32_t> CounterMilli16C.nc provides Counter<TMilli,uint16_t> CounterMilli32C.nc provides Counter<TMilli,uint32_t> GpioCaptureC.nc HilTimerMilliC.nc provides LocalTime<TMilli> and Timer<TMilli> as TimerMilli[uint8_t num] Msp430AlarmC.nc is generic and converts an MSP430 timer to a 16-bit Alarm Msp430Capture.nc HPL interface definition for MSP430 timer captures Msp430ClockC.nc exposes MSP430 hardware clock initialization

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

Msp430ClockInit.nc HPL interface definition for hardware clock initialization Msp430ClockP.nc implements MSP430 hardware clock initialization and calibration and startup Msp430Compare.nc HPL interface definition for MSP430 timer compares Msp430Counter32khzC.nc provides Counter<T32khz,uint16_t> based on MSP430 TimerB Msp430CounterC.nc is generic and converts an Msp430Timer to a Counter Msp430CounterMicroC.nc provides Counter<TMicro,uint16_t> based on MSP430 TimerA Msp430Timer.h defines additional MSP430 timer bitmasks and structs Msp430Timer.nc HPL interface definition Msp430Timer32khzC.nc is generic and allocates a new 32khz hardware timer Msp430Timer32khzMapC.nc exposes the MSP430 hardware timers as a parameterized interface allocatable using Msp430Timer32khzC Msp430TimerC.nc exposes the MSP430 hardware timers Msp430TimerCapComP.nc is generic and implements the HPL for MSP430 capture/compare special function registers Msp430TimerCommonP.nc maps the MSP430 timer interrupts to Msp430TimerEvents Msp430TimerControl.nc HPL interface definition Msp430TimerEvent.nc HPL interface definition Msp430TimerP.nc is generic and implements the HPL for MSP430 timer special function registers

Implementation of timers for the ATmega128 and PXA27x may be found in tinyos-2.x/tos/chips/atm128/timer and tinyos-2.x/tos/chips/pxa27x/timer respectively.

( 八 ) Appendix A: Timer hardware on various microcontrollers
a. i.
? ? ?

Atmega128 Two 8-bit timers, each allowing 7 prescaler values (division by different powers of 2) Timer 0 can use an external 32768Hz crystal One compare register, with many compare actions (change output pin, clear counter, generate interrupt, etc) Two 16-bit timers, each with 5 prescaler values External and software clocking options

ii.
? ?

? ?

Three compare registers (again with many actions) Input capture

b. MSP430 i.
? ? ? ?

Two 16-bit timers with One with three compare registers One with eight compare registers Each from distinct clock source Each with limited prescalers Intel PXA27x One fixed rate (3.25MHz) 32-bit timer with
? ?

c. i.

4 compare registers Watchdog functionality 8 variable rate 32-bit timers with 1 associated compare register each Individually selectable rates: 1/32768s, 1ms, 1s, 1us Individually selectable sources: (32.768 external osc, 13 Mhz internal clock) Periodic & one-shot capability Two external sync events

ii.
? ? ?

iii. iv.

(九)Appendix B: a microcontroller: Atmega 128 timer subsystem
The Atmega128 exposes its four timers through a common set of interfaces:
? ? ?

HplTimer<width> - get/set current time, overflow event, control, init HplCompare<width> - get/set compare time, fired event, control HplCapture<width> - get/set capture time, captured event, control, config

Parameterising these interfaces by width allows reusing the same interfaces for the 8 and 16-bit timers. This simplifies building reusable higher level components which are independent of timer width.
interface HplAtm128Timer<timer_size> { /// Timer value register: Direct access

async command timer_size get(); async command void set( timer_size t ); /// Interrupt signals async event void overflow(); interrupt /// Interrupt async command async command async command async command async command flag void void void bool bool utilites: Bit reset(); //<! start(); //<! stop(); //<! test(); //<! isOn(); //<!

//<! Signalled on overflow

level set/clr Clear the overflow interrupt flag Enable the overflow interrupt Turn off overflow interrupts Did overflow interrupt occur? Is overflow interrupt on?

/// Clock initialization interface async command void off(); //<! Turn off the clock async command void setScale( uint8_t scale); //<! Turn on the clock async command uint8_t getScale(); //<! Get prescaler setting } interface HplAtm128Compare<size_type> { /// Compare value register: Direct access async command size_type get(); async command void set(size_type t); /// Interrupt signals async event void fired(); /// Interrupt async command flag async command async command async command async command }

//<! Signalled on compare interrupt

flag utilites: Bit level set/clr void reset(); //<! Clear the compare interrupt void start(); void stop(); bool test(); bool isOn(); //<! Enable the compare interrupt //<! Turn off comparee interrupts //<! Did compare interrupt occur? //<! Is compare interrupt on?

interface HplAtm128Capture<size_type> { /// Capture value register: Direct access async command size_type get();

async command void

set(size_type t);

/// Interrupt signals async event void captured(size_type t); //<! Signalled on capture int /// Interrupt flag utilites: Bit level set/clr async command void reset(); //<! Clear the capture interrupt flag async command void start(); //<! Enable the capture interrupt async command void stop(); //<! Turn off capture interrupts async command bool test(); //<! Did capture interrupt occur? async command bool isOn(); //<! Is capture interrupt on? async command void setEdge(bool up); //<! True = detect rising edge }

These interfaces are provided by four components, corresponding to each hardware timer: HplAtm128Timer0AsyncC, and HplAtm128Timer0C through HplAtm128Timer3C. Timers 1 and 3 have three compare registers, so offer a parameterised HplAtm128Compare interface:
configuration { provides { // 16-bit interface interface interface interface } } ... HplAtm128Timer1C

Timers HplAtm128Timer<uint16_t> HplAtm128TimerCtrl16 HplAtm128Capture<uint16_t> HplAtm128Compare<uint16_t>

as as as as

Timer; TimerCtrl; Capture; Compare[uint8_t id];

where the id corresponds to the compare register number. The parameterised interface is only connected for id equal to 0, 1 or 2. Attempts to use another value cause a compile-time error. This is achieved as follows (code from the implementation of HplAtm128Timer1C)
Compare[0] = HplAtm128Timer1P.CompareA; Compare[1] = HplAtm128Timer1P.CompareB; Compare[2] = HplAtm128Timer1P.CompareC;

The Atmega128 chip components do not define a HAL, as the timer configuration choices (frequencies, use of input capture or compare output, etc) are platform-specific. Instead, it provides a few generic components for converting the

HPL interfaces into platform-independent interfaces. These generic components include appropriate configuration parameters (e.g., prescaler values):
generic module Atm128AlarmC(typedef frequency_tag, typedef timer_size @integer(), uint8_t prescaler, int mindt) { provides interface Init; provides interface Alarm<frequency_tag, timer_size> as Alarm; uses interface HplTimer<timer_size>; uses interface HplCompare<timer_size>; } ... generic module Atm128CounterC(typedef frequency_tag, typedef timer_size @integer()) { provides interface Counter<frequency_tag,timer_size> as Counter; uses interface HplTimer<timer_size> as Timer; } ...

As a result of issues arising from using timer 0 in asynchronous mode, the HAL also offers the following component:
generic configuration Atm128AlarmAsyncC(typedef precision, int divider) { provides { interface Init @atleastonce(); interface Alarm<precision, uint32_t>; interface Counter<precision, uint32_t>; } } ...

which builds a 32-bit alarm and timer over timer 0. divider is used to initialise the timer0 scaling factor.

(十)Appendix C: a mote: Mica family timer subsystem
Members of the mica family (mica2, mica2dot, micaz) use the Atmega128 microprocessor and have external crystals at 4 or 7.37MHz. Additionally, they can be run from an internal oscillator at 1, 2, 4, or 8 MHz. The internal oscillator is less precise, but allows for much faster startup from power-down and power-save modes

(6 clocks vs 16000 clocks). Finally, power consumption is lower at the lower frequencies. The mica family members support operation at all these frequencies via a MHZ preprocessor symbol, which can be defined to 1, 2, 4, or 8. If undefined, it defaults to a platform-dependent value (4 for mica2dot, 8 for mica2 and micaz). The mica family configures its four timers in part based on the value of this MHZ symbol:
?

Timer 0: uses Atm128AlarmAsyncC to divide the external 32768Hz crystal by 32, creating a 32-bit alarm and counter. This alarm and counter is used to build HilTimerMilliC, using the AlarmToTimerC, VirtualizeTimerC and CounterToLocalTimeC utility components. Timing accuracy is as good as the external crystal.

?

Timer 1: the 16-bit hardware timer 1 is set to run at 1MHz if possible. However, the set of dividers for timer 1 is limited to 1, 8, 64, 256 and 1024. So, when clocked at 2 or 4MHz, a divider of 1 is selected and timer 1 runs at 2 or 4MHz. To reflect this fact, the HAL components exposing timer 1 are named CounterOne16C and AlarmOne16C (rather than the CounterMicro16C AlarmMicro16C as suggested in Section 3). 32-bit microsecond Counters and Alarms, named CounterMicro32C and AlarmMicro32C, are created from CounterOne16C and AlarmOne16C using the TransformAlarmC and TransformCounterC utility components. Three compare registers are available on timer1, so up to three instances of AlarmOne16C and/or AlarmMicro32C can be created. The timing accuracy depends on how the mote is clocked:
o o o

internal clock: depends on how well the clock is calibrated external 7.37MHz crystal: times will be off by ~8.6% external 4MHz crystal: times will be as accurate as the crystal

? ?

Timer 2: this timer is not currently exposed by the HAL. Timer 3: the 16-bit hardware timer 3 is set to run at a rate close to 32768Hz, if possible. As with timer 1, the limited set of dividers makes this impossible at some clock frequencies, so the 16-bit timer 3 HAL components are named CounterThree16C and AlarmThree16C. As with timer 1, the rate of timer 3 is adjusted in software to build 32-bit counter and 32-bit alarms, giving components Counter32khz32C and Alarm32khz32C. As with timer 1, three compare registers, hence up to three instances of Alarm32khz32C and/or AlarmThree16C are available.

At 1, 2, 4 and 8MHz, Counter32khz32C and Alarm32khz32C run at 31.25kHz (plus clock rate inaccuracy). At 7.37MHz, they run at ~28.8kHz. The automatic allocation of compare registers to alarms (and corresponding compile-time error when too many compare registers are used) is achieved as follows. The implementations of AlarmOne16C and AlarmThree16C use the Atm128AlarmC generic component and wire it, using unique, to one of the compare registers offered by HplAtm128Timer1C and HplAtm128Timer3C:
generic configuration AlarmOne16C() { provides interface Alarm<TOne, uint16_t>; } implementation { components HplAtm128Timer1C, InitOneP, new Atm128AlarmC(TOne, uint16_t, 3) as NAlarm; Alarm = NAlarm; NAlarm.HplAtm128Timer -> HplAtm128Timer1C.Timer; NAlarm.HplAtm128Compare -> HplAtm128Timer1C.Compare[unique(UQ_TIMER1_COMPARE)]; }

On the fourth creation of an AlarmOne16C, unique(UQ_TIMER1_COMPARE) will return 3, causing a compile-time error as discussed in Appendix B (HplAtm128Timer1C's Compare interface is only defined for values from 0 to 2). When an Atmega128 is in any power-saving mode, hardware timers 1, 2 and 3 stop counting. The default Atmega128 power management will enter these power-saving modes even when timers 1 and 3 are enabled, so time as measured by timers 1 and 3 does not represent real time. However, if any alarms built on timers 1 or 3 are active, the Atmega128 power management will not enter power-saving modes. The mica family HIL components are built as follows:
? ?

HilTimerMilliC: built as described above from hardware timer 0. BusyWaitMicroC: implemented using a simple software busy-wait loop which waits for MHZ cycles per requested microsecond. Accuracy is the same as Timer 1.

Finally, the mica family motes measure their clock rate at boot time, based on the external 32768Hz crystal. The results of this clock rate measurement are made available via the cyclesPerJiffy command of the Atm128Calibrate interface of the MeasureClockC component. This command reports the number of cycles per

1/32768s. Please see this interface definition for other useful commands for more accurate timing.

八、任务调度(TEP106)
(一)介绍
TinyOS 有二个基本的计算抽象:异步事件和任务。Tinyos 早些版本提供单 一的类型任务, 没有参数且只能 FIFO 调度。将任务调度表现成 TINYOS 组件更容 易制定,将任务表现成 TINYOS 接口可扩展任务类型。TINYOS2.0 采用这二种方 法,这份文本记录了其是如何以简单的机制来提高系统可靠性。

(二)TinyOS1.x 任务调度
TinyOS 中的任务是可延迟的调用过程 DPC,可以使某程序延迟计算或操作。 TOS 任务一次运行完毕,任务间不可抢占。这二个约束条件意味着任务代码是同 步的。也就是说,任务是原子性的。在 tinyos1.x 中,nesC 语言通过二种机制 支持任务,任务声明和任务发布表达 post task void computeTask() { // Code here } result_t rval = post computeTask(); TinyOS1.x 提供单一的任务类型, 无参数函数及单一 FIFO 的调度策略。 Post 语句可返回 FAIL,表明 TinyOS 发布任务失败。可发布任务多次。例如,如果某 一任务连续发布了二次,第一次成功但第二次失败,此任务将会被运行一次。因 为这样,虽然一次发布失败,但任务仍可能运行。 Tinyos1.x 调度器由 sched.c 文件中的 C 函数集实现的。若要修改调度器则 需替代或修改此文件。 另外, 因为任务仅通过 nesC 中的 task 关键字声明和 post 关键字支持,假设是无参数函数,不能修改语句或任务功能。 Tinyos1.x 的任 务队列是由固定大小的函数指针类型的循环缓冲实现。 发布任务就是将此任务的 函数指针放入下个空缓冲区中。如果没有空的缓冲区,发布任务将返回失败。这 类模型有几个问题: 1) 某些组件针对发布任务失败没有合适的响应 2) 某给出的任务能发布 多次,这将占用多个缓冲区 3) 所有组件的所有任务共享单一资源:某个有问 题的组件可能导致其他组件发布任务失败。从根本上来,为了使组件 A 在发布任 务失败后重新发布任务, 另外一个组件 B 必须调用 A 的函数 (命令或事件) ??。 例如组件 A 必须调用定时器,或从客户端重试。然而,系统中许多组件也依靠此 任务(如定时器),任务队列溢出有可能导致整个系统失效。

以上三个问题组合意味着某个问题组件会导致 tinos 整个系统挂起。例如: 这种情况(在 Telos 平台上真实遇到的问题)。 ?基于数据包的硬件收发,只在发送完一个数据包后触发中断 ?networking 组件处理中断来发布任务进而发 singal SendMsg.sendDone. 事件。 ?sensing 组件处理 ADC.dataReady 事件时,发布任务 ?application 组件发送数据包,然后 ADC 采样频率设置得太高 发生这种情况,sensing 组件将以更快的速率处理事件。将开始发布任务来 处理接收到的数据,直到队列满。在某时,射频发送数据包完成并 singal 其中 断。Networking 组件不能发布任务来 signal SendMsg.sendDone(),事件丢失。 Application 组件不会重发另外的数据包直到知道其发送完成(因此可重用缓冲 区)。因 sendDone()事件丢失。不会再发送其他数据包,应用程序停止网络通 信。 如果发布失败,解决 TinyOS 1.x 中这类特殊问题的方法是在无线发送中断 程序中完成 signal sendDone()事件:这将与 sync/async 界限冲突,但合理之 处是极少见的资源冲突相对某一错误而言好些。 另一种解决方法是使用中断源来 周期性的重新发布任务。这不违反 sync/async,直到发布成功系统不能再发送 数据包。The TinyOS 1.x model prevents it from doing any better.。因 tinyos1.x 模型限制没有其他更好的办法。

(三)tinyos2.x 任务
2.x 任务机制与 1.x 不同。这是针对 1.x 模型中出现的限制和运行错误而 制定的改变。在 tiny2.x 中,发布任务 post 失败,只有在任务已经发布且还没 运行的情况下。 任务可以经常运行,但任何时候只能发布一次。 2.x 中通过为每个任务分配一个字节来表示任务状态,(假设系统任务最多 255 个)实现这机制。若任务太多,这将占用太大开销,也就没多大意义。若某 组件要多次发布某任务,任务逻辑最后可根据需要报告自己情况。例如: post processTask(); ... task void processTask() { // do work if (moreToProcess) { post processTask(); } } 这个方式有好几个问题,比如当任务队列满时,不能发信号通知分相 split-phase 事件完成,初始化时任务队列溢出,组件多次发布某任务导致分配 任务不公平 unfair task allocation。

TinyOS2.x 中采用的方式是:任务基本操作要保持简单容易,但也允许在任 务基本使用的情况下引入新的任务类型。tinyOS 实现的方法是:在基本任务中 使用 post 和 task 关键字,为增加的任务引入任务接口。 任务接口允许用户扩展任务语法和语义。一般,任务接口有 async 命令,发 布和事件,run。这些函数的确切形式取决于接口中定义。例如:某一任务接口 参数是整数类型。 interface TaskParameter { async error_t command postTask(uint16_t param); event void runTask(uint16_t param); } 使用这个任务接口,组件需发布带有 uint16_t 参数的任务。当调度器运行 任务时,其中包含任务逻辑,通过传递参数 signal 通知 runTask 事件。注意这 并不保存 RAM 信息:调度器必须为参数分配 RAM 空间。另外,在任何时候只能运 行一个任务,即可在组件中简单的储存变化。例如,而不是: call TaskParameter.postTask(34); ... event void TaskParameter.runTask(uint16_t param) { ... } one can: uint16_t param; ... param = 34; post parameterTask(); ... task void parameterTask() { // use param } 这二个模型代码的主要区别是如果组件发布任务二次,将在 TaskParameter 例子中的使用旧参数,但在 basic task 例子中使用新参数。若组件想用最先的 参数(oldest)可用如下方式: if (post myTask() == SUCCESS) { param = 34; }

(四)tinyos2.x 调度
调度器是 TinyOS 组件。 每个调度器必须支持 nesC 任务。也可支持多个传统 任务接口。 调度器组件可协调调度各种类型任务如最早截止时间型任务、具有优 先级任务。 Tinyos2.x 普通的任务是不带参数的 FIFO 任务。任务执行和发布仍支持 nesC,即声明接口和连接到调度器组件都用 nesC 语言。附录 A 描述了这些 nesC 语言缩写配置方式。 若调度器提供的任务接口是带参数的,则每个任务连接到接 口时,需要一个 unique()函数来获取惟一的标识符,调度器通过标识符来调度 任务。 module SchedulerBasicP { provides interface Scheduler; provides interface TaskBasic[uint8_t taskID]; uses interface McuSleep; } 调度器 必须提供带参数的 TaskBasic 接口,调用 TaskBasic.postTask()返回成功 时,调度器终会有任务运行,而不必关心饥饿没任务运行。当是第一次调用 TaskBasic.postTask()时,调度器必需返回 SUCCESS 到 TaskBasic.postTask(), 若不是第一次则勿须返回,因为已经有信号通知过 TaskBasic.runTask()事件 了。McuSleep 接口用于微处理器能源管理。其工作方式在 TEP112 有详细说明。

调度器必须提供调度接口。调度接口 Scheduler inerface 中有用于初始化 和运行任务的命令集。 interface Scheduler { command void init(); command bool runNextTask(bool sleep); command void taskLoop(); } init()命令函数用于初始化任务队列和调度器数据结构。runNextTask()须 一次执行完成, 不管理调度器所调用的下一个任务是何任务:返回值表明是否在 运行任务。bool sleep 参数表明当没有任务运行时,调度器应该干嘛。当 sleep=FALSE,runNextTask()命令函数将立刻返回 FALSE 值,当 sleep=TRUE, runNextTask()命令函数直到有任务执行时才返回,将使 CPU 进行休眠直到有新 任务到达, 调用 runNextTask(FALSE)可能返回 TRUE 或 FALSE, runNextTask(TRUE) 总返回 TRUE。taskLoop()命令通知调度器进入无限任务运行循环,当处理器空 闲无任务时,MCU 进入低功耗状态:没有返回值。 为高效利用电源,调度主要职责是将处理器进入休眠状态 sleep,通过调用 休眠可提高任务循环效率, TaskBasic interface: interface TaskBasic { async command error_t postTask(); void event runTask(); } 当组件声明任务时用在 nesC 中 task 关键字,也是寓味着使用了 TaskBasic 接口的实例,任务主体是 runTask 事件。当组件使用 post 关键词寓 味着调用了 postTask 命令。每个 TaskBasic 连接到调度器组件时必须带有参数 (惟一标识),这个标识符是从 unique 函数获取而来,这是 "TinySchedulerC.TaskBasic"一大关键含义。 SchedulerBasicP 将从 unique 函数得到的标识符作为任务队列元素。当 tinyos 通知调度器运行任务时,将取出队列的下个标识,并在调用带参数的 TaskBasic 接口时使用。 当 tinyos 默认使用 FIFO 调度策略时,Tinyos 组件不能采取 FIFO 策略。如 果二个任务必须以特定顺序运行,这需在前个任务发布下个任务。

(五)替换调度器
Tinyos 调度器是以 TinySchedulerC.组件实现的。默认的 tinyos 调度器实 现是 SchedulerBasicP 模块,默认的调度器组件是某一配置(configuration), 可连接到 SchedulerBasicP。 因特殊应用要替换调度器,开发者需将 TinySchedulerC 配置放入应用程序 目录:这将取代默认。调度器组件提供了配线方法来实现所需的调度方式。所有 的调度器实现必须提供带参数的 TaskBasic 接口,像 SchedulerBasicP 一样;也 要支持 nesC 中 post 语句和任务声明,使能 tinyos 内核操作。总之,Tinyos 不 要修改内核代码来实现新的调度方法。所有的调度器实现都要提供调度接口。

例如,假设某一调度器提供最早截止时间任务,由 TaskEdf 接口提供。 interface TaskEdf { async command error_t postTask(uint16_t deadlineMs); event void runTask(); } 这个调度器命名为:SchedulerEdfP,并同时提供 TaskBasic 和 TaskEdf 二个接口。 module SchedulerEdfP { provides interface Scheduler; provides interface TaskBasic[uint8_t taskID]; provides interface TaskEdf[uint8_t taskID]; } 应用程序想将 SchedulerEdfP 代替 SchedulerBasicP 及 TinySchedulerC,配置,可以输出所有的 SchedulerEdfP 接 口。 configuration TinySchedulerC { provides interface Scheduler; provides interface TaskBasic[uint8_t taskID]; provides interface TaskEdf[uint8_t taskID]; } implementation { components SchedulerEdfP; Scheduler = SchedulerEdf; TaskBasic = SchedulerEdfP; TaskEDF = SchedulerEdfP; } 当模块有最早截止时间任务时,需使用 TaskEdf 接口,其配置需连接到 TinySchedulerC。重要的是取得任务惟一标识符是语句 “TinySched-ulerC.TaskInterface”, TaskInterface 是由调度器提供的一个 新任务接口。通常为使字符一致,使用#define。例如:TaskEDF.nc 可能包括: 1. define UQ_TASK_EDF "TinySchedulerC.TaskEdf" 下面这个例子包含二个 EDF 任务: configuration SomethingP { ... } implementation { components SomethingP, TinySchedulerC; SomethingP.SendTask -> TinySchedulerC.TaskEdf[unique(UQ_TASK_EDF)]; SomethingP.SenseTask -> TinySchedulerC.TaskEdf[unique(UQ_TASK_EDF)]; } 模块 SomethingP 也有普通任务。nesC 编译器自动将 task 关键字转换成 BasicTask interfaces 并完成相关配线连接 wire。因此,作为普通任务,组件 用户可以使用 task 和 post 关键字,也可使用 BasicTask interface。只要有可 能组件应该使用关键字,但不能在 given task 中混淆这二个语句。(???) 在普通任务中使用关键字实现 SomethingP 实例: module SomethingP { uses interface TaskEdf as SendTask uses interface TaskEdf as SenseTask } implementation { // The TaskBasic, written with keywords task void cleanupTask() { ... some logic ... } event void SendTask.runTask() { ... some logic ... } event void SenseTask.runTask() { ... some logic ... } void internal_function() { call SenseTask.postTask(20); call SendTask.postTask(100); post cleanupTask(); } } 为保证普通任务不会处于饥饿状态, 调度器支持 EDF 任务必须保证普通任务 最终能运行,虽然有无数截止时间短的任务执行。最终到底是多久很难确定,近 似约为 MCU 周期的 1%。如果调度器提供同一任务的二个实例,通过 as 声明了二 个不同的接口名。例如:调度器提供了二个 TaskBasic 实例:标准 tasks 及高优 先级任务。 调度器通常选择高优先级队列中的任务, 再选择标准任务队列中任务。 configuration TinySchedulerC { provides interface Scheduler; provides interface TaskBasic[uint8_t taskID]; provides interface TaskBasic[uint8_t taskID] as TaskHighPriority; } 不能总是选择优先级高

的任务因为这将导致普通任务饥饿得不到运行。 组件使用优先级高的任务需通过 关键字 “TinySchedulerC.TaskHighPriority” 连接 TaskHighPriority。 configuration SomethingElseC {} implementation { components TinySchedulerC as Sched, SomethingElseP; SomethingElseP.RetransmitTask - > Sched.TaskHighPriority[unique("TinySchedulerC.TaskHighPriority")]; }

(六)实现方法
调度相关文件有: SchedulerBasicP.nc:主要的 tinyos 调度器,提供带参 数的 TaskBasic 接口 TinySchedulerC.nc:默认的调度配置即将 SchedulerBasicP->McuSleepC 调度器原形是支持 EDF 任务 Earliest DeadlineFirst

(七)附件 A
改变调度方法 nesC 编译器可改变 nesC 接口中的 post 任务和任务关键字、 连接和调用。 module a { ... } implementation { task x() { ... post x(); } } is e?ectively: module a { ... provides interface TaskBasic as x; } implementation { event void x.runTask() { ... call x.postTask(); } } -fnesc-scheduler=TinySchedulerC,TinySchedulerC.TaskBasic,TaskBasic,Ta skBasic,runTask,postTask 因任务转化成接口是在 nesC 编译器中建立的。 每个 平台的.platform 文件中通过选项-fnesc-scheduler 可修改具体名称。 6 个词汇解释: 1) 调度组件接口名:TinySchedulerC 2) 调度组件中带 参数接口(带惟一字符):TinySchedulerC.TaskBasic 3) 调度组件接口名: TaskBasic 4) 接口类型名:TaskBasic 5) 运行任务事件函数名:runTask 6) 发布任务命令函数名:postTask

九、TinyOS 启动顺序(TEP 107)
(一) 概述
在 tinyos 启动顺序中,有一系列调用语句规定。Tinyos 早些版本使用接口 “StdControl” 来进行系统初始化并启动所需的软件系统。在多个硬件平台上实 践发现“StdControl”接口不能满足要求,因为其只能提供同步接口。另外, “StdControl” 只在启动时绑定初始化概念,同时包括能源管理和服务控制。 TinyOS 2.x 可解决这些问题, 通过将 StdControl 分成三个独立的接口:初始化、

启动和停止组件、 通知 mote 已经启动。本文记录了 tinyos 启动顺序和其语义原 由。

(二) TinyOS 1.x Boot Sequence
大多 mote 平台中,TinyOS 1.x 启动顺序是一样的(TOSSIM 启动顺序完全不 同,因为是 PC 中的项目)。RealMain 模块实现 main()。
module RealMain { uses { command result_t hardwareInit(); interface StdControl; interface Pot; } } Main()函数是 nesC 和 C 混合。 int main() __attribute__ ((C, spontaneous)) { call hardwareInit(); call Pot.init(10); TOSH_sched_init(); call StdControl.init(); call StdControl.start(); __nesc_enable_interrupt(); while(1) { TOSH_run_task(); } }

存在好几个问题。这些调用只是用于旧平台上:Pot 组件涉及到 mica 可变 电位器计, 用于控制转发能量, 在其他平台是 stub component 调用-- TOSH sched init and TOSH run task –都是 C 函数,可在其他组件中实现,通过 include

命令自动包括其所在文件。从 nesC 组件模型分离依赖这些函数比平常的 tinyos 更困难。 更重要的是,初始化顺序有几个限制。组件 HPLInit 实现 hardwareInit 命 令函数(连接到 main 组件):硬件初始化有可能不是纯粹的 HPL 层的一部分。 硬件件初始化后是调度器初始化,意味着即使需要硬件初始化也不会发布任务。 StdControl 接口包括组件初始化(init())和激活(start()/stop())。如果某组 件需要 RealMain 初始化,也需要启动。将这二个步骤分开使能源管理更灵活, 并从能量管理的高层组件(比如 application)辨别出必须一直运行的低层组件 (比如时钟 timer)。最后,某些组件需要 main 以常启动,比如射频,并不是 按照同步 start/stop 模式。这种情况下,某些组件直到射频启动后才能正常操 作,但 main 中并没有等待射频启动完成的事件。

(三) TinyOS 2.x Boot Interfaces
TinyOS 2.x Boot 顺序有使用三个接口: ?初始化:组件和硬件初始化 ?调度器:任务初始化和运行 ?启动:通知系统已经成功启动 Init interface 有单一命令函数 init() interface Init { command error_t init(); } Init 提供的是同步接口,规定了初始化顺序。与正常的运行不同,大多组 件操作需要高效的交错运行, 初始化是顺序的同步操作:初始化完成前没有组件 启动。 如果某特别的组件初始化时需要等待中断或其他同步事件,其就一直等待 (比如通过自循环),直到完成前不能返回。否则系统可能在初始化完成前就启 动了。 Scheduler 调度器接口是用于任务初始化和任务调度。其在 TEP106 有详细 说明。 Boot 启动接口有一事件 booted()。当启动完成时发送信号。 interface Boot { event void booted(); }

(四) TinyOS 2.x Boot Sequence
RealMainP 模块实现 TinyOS 2.x 启动顺序。MainC 配件中将 RealMainP 中的 某些接口 wire 连线到组件(用于实现标准抽象和据特定应用的输出)。HIL 层 以上的代码应用连接到 MainC 组件 而不 RealMainP 组件:
module RealMainP { provides interface Booted; uses { interface Scheduler; interface Init as PlatformInit; interface Init as SoftwareInit; } } implementation { int main() __attribute__ ((C, spontaneous)) { atomic { platform_bootstrap(); call Scheduler.init(); call PlatformInit.init(); while (call Scheduler.runNextTask()); call SoftwareInit.init(); while (call Scheduler.runNextTask()); } __nesc_enable_interrupt(); signal Boot.booted(); call Scheduler.taskLoop();

return -1; } default command error_t PlatformInit.init() { return SUCCESS; } default command error_t SoftwareInit.init() { return SUCCESS; } default event void Boot.booted() { } }

(4.1) Initialization
启动顺序的第一步就是初始化。
atomic { platform_bootstrap(); call Scheduler.init(); call PlatformInit.init(); while (call Scheduler.runNextTask()); call SoftwareInit.init(); while (call Scheduler.runNextTask()); }

第一次调用 platform bootstrap(),是最基本的函数,用于将系统置于运 行状态。这函数不包括需要后来执行代码(further code)的操作,如调度器初 始化。 platform bootstrap()操作例子是配置内存系统和设定处理器模式。 通常, platform bootstrap()是空函数。TINYOS 顶层包括文件,tos.h,包括这个函数 的默认实现方法, 即不做任何事。如果平台需要将这个默认替换成其他操作则应 该通过#de?ne 语句.将其置于平台的 platform.h 文件。tos.h 支持的方法是:
/* This platform_bootstrap macro exists in accordance with TEP 107. A platform may override this through a platform.h file. */ 1. include <platform.h> 1. ifndef platform_bootstrap 1. define platform_bootstrap() {} 1. endif

启动顺序有三个独立的初始化:Scheduler 调度器初始化, PlatformInit 平台初始化, and SoftwareInit 软件初始化.boot 启动配件(MainC)自动边线 头二个初始化
configuration MainC { provides interface Boot; uses interface Init as SoftwareInit; } implementation { components PlatformC, RealMainP, TinySchedulerC; RealMainP.Scheduler -> TinySchedulerC; RealMainP.PlatformInit -> PlatformC; // Export the SoftwareInit and Booted for applications SoftwareInit = RealMainP.SoftwareInit; Boot = RealMainP; }

MainC 为应用程序提供 Boot and SoftwareInit 接口。TinySchedulerC 是 TINYOS 调度器名字。因为初始化次序需运行任务,boot sequence 启动次序程序 首先对它进行初始化。初始化的第二步是调用 PlatformInit.init()即完成 MainC 连接到 PlatformC 组件。PlatformInit 必须依照某特定的次序来隐藏依 赖关系比如作为整个硬件平台操作的一部分。时钟校准就是其中初始化实例。因 为 PlatformInit 调用组件 PlatformC,每个平台都有不同的初始化次序。因为 是根据硬件特性来隐藏其依赖关系,所是次序是与平台相关的。Tinyos 移植到 一个新硬件平台时必须包含组件 PlatformC 来提供一个(仅一个)Init 接口实 例。 通过 PlatformC 调用初始化需要满足以下某些或所有条件: 1、 初始化要求配置硬件资源。这暗示代码是基于平台的。 2、 初始化应用经常执行 3、 初始化是系统能进行普通服务的先决条件 属于 PlatformInit 的三个操作是:I/O 口设置、时钟校准和 LED 配置。I/O 口设置能满足前二条要求, (不管 OS 使用何组件)都要执行 IO 设置,为了满足

低功耗:错误的 IO 设置,将导致电流增加,并妨碍 MCU 进入最低休眠状态。时 钟校准满足所有三条。LED 配置是个特例:其名义是满足三个要求,最重要的是 第三条:SoftwareInit 初始化过程中需要 LED 指示,在调用 SoftwareInit 前需 设置 LED。 注意, 并不是所有满足以上条件的代码都能通过 PlatformC 进行配线。特别 是,条件 1 是基本要求,但并不充分。例如,时钟系统设置溢出和捕获、UART 堆栈中设置波特率和转发操作时需配线连接到 SoftwareInit。这是封装抽象直 到发生 boot 事件,都不能被调用或 start 开始,仅当系统包含其函数时才能被 配置。 组件初始化并不直接依赖于硬件资源与 MainC.SoftwareInit 配线连接。如 果组件需要一个特定的初始化次序,则其负责确定次序。依照 Init 原理,很少 发生这种情况;组件不应该引入初始化依赖关系 introduce initialization dependencies 除非其需要。 通常的方法是通过配件 configuration 来将其内部组件初始化例程自动配 线。这配件不能提供 Init 接口。虚拟服务通常采用这种方法,比如服务器需要 这种初始化,而不是客户端。例如,标准的时钟虚拟 Timer virtualization, 将 TimerMilliC 配线到 TimerMilliP,这种简单的配件采用底层的实现方法 (TimerMilliC, wires to TimerMilliP,),并将其配线连接到 MainC:
configuration TimerMilliP { provides interface Timer<TMilli> as Timinitialization in ordererMilli[uint8_t id]; } implementation { components HilTimerMilliC, MainC; MainC.SoftwareInit -> HilTimerMilliC; TimerMilli = HilTimerMilliC; }

而不是自动在应用程序中将 wire HilTimerMilliC to MainC, TimerMilliP。 当组件实例化 TimerMilliC 后取名 TimerMilliP,即当 TINYOS 启动时会自动确 认时钟系统已经初始化。

(4.2) Interrupts in Initialization
当调用 Init.int 的所有函数得到返回后,才会使能中断。如果组件初始化 需处理中断,他将做以下三件事之一。 1) 如果有中断状态标志,Init.init()实现方法中需使用自循环 spin loop 来测试中断的发生。 2) 如果没有中断状态标志,Init.init()实现方法中,如果不会导致其他 组件来处理中断,会临时使能中断。也就是说,如果某组件不能使能(在中断处 理程序中全调用其它组件的中断。而且,当退出 Init.init(),中断必须关闭。 3) 如果没有中断状态标志位,且没有隔离所调用的中断处理程序的方法, 则组件需依赖 Init 顺序外的机制,如 SplitControl。 4) 目前为止, 启动顺序主要为第一种方式。 有些情况下, 组件需处理中断, 比如硬件限制(没有挂起的 flag)或为获取简单的边缘 to catch a brief edge transition(意思不明??)。这些情况下,启动顺序中组件可以处理中断,但 前提是不能导致其他组件来处理中断。因为在进行写操作时规定直到 Init 完毕 才可使能中断,但当有组件处理中断时,将会导致失败。 依硬件能力,有好几个方法来满足以上要求。最简单的是在 main 启动顺序 外处理这些初始化临界情况(initialization edge case??)比如:进入 SplitControl。第二种方法是重定位中断向量表,如果 MCU 支持这种方法,不管 选择何种机制,要特别小心不能干扰其他组件的操作。 除非是 HAA 硬件抽象结构的一部分,the Init.init()命令函数决不能相当 然的认为其他没有初始化的组件初始化了, 且决不能调用任何组件中那些会产生 资源共享或与共享资源交互的接口函数。 组件也许会调用子系统内部的其他组件 函数。例如:网络层可调用队列操作来初始化其队列,但链路层不能在 SPI 总线 上发送指令。HAA 组件可能会使用其他调用来初始化硬件状态。若不是 HAA 中的 组件, 不能通过其他组件调用 Init.init(), 除非其需要将初始化顺序临时改变。 如果组件 A 依赖于组件 B(需初始化),则 A 应 wire 配线连接到 B 初始化 来直接进入启动次序 boot sequence,除非其需要将初始化次序临时改变。这种 规定的目的是用来简化初始化及初始化次序

(五) Implementation
tinyos-2.x/tos/system 文件中有 tinyos 启动次序的相关文件 ? RealMainP.nc 模块包含 main 函数

? MainC.nc 配件即将 RealMainP 连接到 PlatformC and TinySchedulerC

十、Sensors and Sensor Boards(TEP109)
(一)Abstract
This memo documents how sensor drivers are organized in TinyOS and how sets of sensor drivers are combined into sensor boards and sensor platforms, along with general principles followed by the components that provide access to sensors.

(二) Principles
This section describes the basic organization principles for sensor drivers in TinyOS. For background, a sensor can be attached to the microcontroller on a TinyOS platform through a few different types of connections:
? ? ? ? ?

Included within the microcontroller itself Connected to general-purpose IO pins for level/edge detection Connected to an ADC in the microcontroller for voltage sampling Connected to general-purpose IO pins for digital communication Connected through a standard digital bus protocol (1-Wire, I2C, SPI)

Physically, these connections can also be decoupled by attaching the sensors to a sensor board, which can be removed from the TinyOS platform, and could attach to multiple different TinyOS platforms. The capabilities of a physical sensor are made available to a TinyOS application through a sensor driver. According to the HAA [TEP2], TinyOS devices SHOULD provide both simple hardware-independent interfaces for common-case use (HIL) and rich hardware-dependent interfaces for special-case use (HAL). Sensor drivers SHOULD follow this spirit as well. TinyOS 2.x represents each sensor as an individual component. This allows the compilation process to minimize the amount of code included. A sensor board containing multiple sensors SHOULD be represented as a collection of components, one for each sensor, contained within a sensor board directory.

Sensors, being physical devices that can be shared, can benefit from virtualization and arbitration. This document describes a design pattern for sensor virtualization that SHOULD be followed by sensor drivers. The same physical sensor can be attached to multiple different TinyOS platforms, through platform-dependent interconnections. The common logic of sensor driver SHOULD be factored into chip-dependent, platform-independent components, and those components SHOULD be bound to the hardware resources on a platform by platform-dependent components, and to the hardware resources on a sensor board by sensorboard-dependent components. A physical sensor has a general class and a specific set of performance characteristics, captured by the make and model of the sensor itself. The naming of the sensor driver components SHOULD reflect the specifc name of the sensor, and MAY provide a component with a generic name for application authors who only care about the general class of the sensor. This document requires that sensor components specify the range (in bits) of values returned by sensor drivers, but takes no position on the meaning of these values. They MAY be raw uninterpreted values or they MAY have some physical meaning. If a driver returns uninterpreted values, the driver MAY provide additional interfaces that would allow higher-level clients to obtain information (e.g. calibration coefficients) needed to properly interpret the value.

(三) Sensor HIL Components
A sensor HIL component MUST provide:
?

One or more SID interfaces [TEP114], for reading data.

A sensor HIL component MAY provide:
?

One or more SID interfaces [TEP114], for reading or writing calibration coefficients or control registers.

A sensor device driver SHOULD be a generic component that virtualizes access to the sensor. A sensor device driver can provide such virtualization for itself by defining a nesC generic client component. When a client component is being used, a call to a top-level SID interface SHOULD be delayed when the device is busy, rather than failing. Using one of the system arbiters can make the implementation of this requirement easier to accomplish. For example:

generic configuration SensirionSht11C() { provides interface Read<uint16_t> as Temperature; provides interface ReadStream<uint16_t> as TemperatureStream; provides interface DeviceMetadata as TemperatureDeviceMetadata; provides interface Read<uint16_t> as Humidity; provides interface ReadStream<uint16_t> as HumidityStream; provides interface DeviceMetadata as HumidityDeviceMetadata; } implementation { // connect to the ADC HIL, GPIO HAL, or sensor's HAL }

When a HIL component is being used, the sensor MUST initialize itself, either by including the MainC component and wiring to the SoftwareInit interface, or by allowing a lower-level component (like an ADC) to initialize itself. In addition, the HIL sensor driver MUST start the physical sensor automatically. For sensors without a constant power draw, the sensor MAY be started once at boot time by wiring to the MainC.Boot interface. Sensors that draw appreciable power MUST be started in response to a call to one of the top-level SID interfaces, and stopped some time after that call completes. Using one of the power-management components described in [TEP115] can make this implementation easier. Generally, simple types are made up of octets. However, sensor values often have levels of precision besides a multiple of 8. To account for such cases, each device MUST specify the precision of each one of its interfaces by providing the DeviceMetadata interface:
interface DeviceMetadata { command uint8_t getSignificantBits(); }

The name of the instance of DeviceMetadata MUST clearly indicate which interface it corresponds to. The getSignificantBits() call MUST return the number of significant bits in the reading. For example, a sensor reading taken from a 12-bit ADC would typically return the value 12 (it might return less if, e.g., physical constraints limit the maximum A/D result to 10-bits). Sensor driver components SHOULD be named according to the make and model of the sensing device being presented. Using specific names gives the developer the option to bind to a particular sensor, which provides compile-time detection of missing sensors. However, wrapper components using "common" names MAY also

be provided by the driver author, to support application developers who are only concerned with the particular type of the sensor and not its make, model, or detailed performance characteristics. A "common" naming layer atop a HIL might look like this:
generic configuration TemperatureC() { provides interface Read<uint16_t>; provides interface ReadStream<uint16_t>; provides interface DeviceMetadata; } implementation { components new SensirionSht11C(); Read = SensirionSht11C.Temperature; ReadStream = SensirionSht11C.TemperatureStream; DeviceMetadata = SensirionSht11C.TemperatureDeviceMetadata; } generic configuration HumidityC() { provides interface Read<uint16_t>; provides interface ReadStream<uint16_t>; provides interface DeviceMetadata; } implementation { components new SensirionSht11C(); Read = SensirionSht11C.Humidity; ReadStream = SensirionSht11C.HumidityStream; DeviceMetadata = SensirionSht11C.HumidityDeviceMetadata; }

(四) Sensor HAL Components
Sensors with a richer interface than would be supported by the SID interfaces MAY provide a HAL component in addition to a HIL component. A sensor HAL component MUST provide:
?

A SID-based interface or a specific hardware-dependent interface with commands for sampling and controlling the sensor device.

A sensor HAL component MAY need to provide:
?

A StdControl or SplitControl interface for manual power management by the user, following the conventions described in [TEP115].

?

?

A Resource interface for requesting access to the device and possibly performing automated power management, following the conventions described in [TEP108] and [TEP115]. Any other interfaces needed to control the device, e.g., to read or write calibration coefficients.

For example:
configuration SensirionSht11DeviceC { provides interface Resource[ uint8_t client ]; provides interface SensirionSht11[ uint8_t client ]; } implementation { // connect to the sensor's platform-dependent HPL here }

( 五 ) Sensor Component Organization and Compiler Interaction Guidelines
Sensors are associated either with a particular sensor board or with a particular platform. Both sensors and sensor boards MUST have unique names. Case is significant, but two sensor (or sensor board) names MUST differ in more than case. This is necessary to support platforms where filename case differences are not significant. Each sensor board MUST have its own directory whose name is the sensor board's unique name (referred to as <sensorboard> in the rest of this section). Default TinyOS 2.x sensor boards are placed in tos/sensorboards/<sensorboard>, but sensor board directories can be placed anywhere as long as the nesC compiler receives a -I directive pointing to the sensor board's directory. Each sensor board directory MUST contain a .sensor file (described below). If the sensor board wishes to define any C types or constants, it SHOULD place these in a file named <sensorboard>.h in the sensor board's directory. A sensor board MAY contain components that override the default TinyOS demo sensors. This allows the sensor board to easily be used with TinyOS sample applications that use the demo sensors. If a sensor board wishes to override the default demo sensor:
?

It MUST provide a generic component named DemoSensorC with the following signature:
? ?

provides interface Read<uint16_t>; provides interface DeviceMetadata;

?

It MAY provide a generic component named DemoSensorNowC with the following signature:
? ?

provides interface ReadNow<uint16_t>; provides interface DeviceMetadata;

This component SHOULD sample the same sensor as DemoSensorC.
?

It MAY provide a generic component named DemoSensorStreamC with the following signature:
? ?

provides interface ReadStream<uint16_t>; provides interface DeviceMetadata;

This component SHOULD sample the same sensor as DemoSensorC. These components MUST be an alias for one of the sensor board's usual sensors, though they change the precision of the sensor if necessary. For instance, if DemoSensorC is an alias for a 20-bit sensor that provides a Read<uint32_t> interface, DemoSensorC would still provide Read<uint16_t> and would include code to reduce the precision of the aliased sensor.

(1) Compiler Interaction
When the ncc nesC compiler frontend is passed a -board=X option, it executes the .sensor file found in the sensor board directory X. This file is a perl script which can add or modify any compile-time options necessary for the sensor board. It MAY modify the following perl variables, and MUST NOT modify any others:
?

?

@includes: This array contains the TinyOS search path, i.e., the directories which will be passed to nescc (the TinyOS-agnostic nesC compiler) as -I arguments. You MUST add to @includes any directories needed to compile this sensor board's components. For instance, if your sensor boards depends on support code found in tos/chips/sht11, you would add "%T/chips/sht11" to @includes. @new_args: This is the array of arguments which will be passed to nescc. You MUST add any arguments other than -I that are necessary to compile your sensor board components to @new_args.

If a sensor is associated with a platform P rather than a sensor board, then that platform MUST ensure that, when compiling for platform P, all directories needed to compile that sensor's component are added to the TinyOS search path (see [TEP131] for information on how to set up a TinyOS platform).

(2) Sensor Components
A particular sensor is typically supported by many components, including the HIL and HAL components from Sections 2 and 3, A/D conversion components (for analog sensors), digital bus components (e.g., SPI, for digital sensors), system services (timers, resource and power management, ...), glue components (to connect sensors, sensor boards and platforms), etc. These components can be divided into three classes: sensorboard-dependent, platform-dependent and platform-independent. The sensorboard and platform MUST ensure (Section 4.1) that all these components can be found at compile-time. Because the same physical sensor can be used on many platforms or sensor boards, and attached in many different ways, to maximize code reuse the organization of sensor drivers SHOULD reflect the distinction between sensor and sensor interconnect. The sensor components SHOULD be platform-independent, while the sensor interconnect components are typically sensorboard or platform-dependent. However, some sensors (e.g. analong sensors) will not have a sufficiently large amount of platform-independent logic to justify creating platform-independent components. The following guidelines specify how to organize sensor and sensor interconnect components within TinyOS's directory hierarchy. These guidelines are only relevant to components that are part of the core source tree. The string <sensor> SHOULD reflect the make and model of the sensor device.
?

? ? ?

Platform-independent sensor components that exist as part of a larger chip, like a MCU internal voltage sensor, SHOULD be placed in a subdirectory of the chip's directory tos/<chip>/sensors/<sensor>. Other platform-independent sensor components SHOULD be placed in tos/chips/<sensor>. Sensorboard-dependent sensor and sensor interconnect components SHOULD be placed either in the <sensorboard> directory or in a <sensorboard>/chips/<sensor> directory. Platform-dependent sensor and sensor interconnect components SHOULD be placed in tos/<platform>/chips/<sensor>.

(六) Citations
[TEP2] [TEP108] [TEP114] TEP 2: Hardware Abstraction Architecture TEP 108: Resource Arbitration (1, 2) TEP 114: SIDs: Source and Sink Indepedent Drivers

[TEP115] [TEP131]

(1, 2, 3) TEP 115: Power Management of Non-Virtualized Devices TEP 131: Creating a New Platform for TinyOS 2.x

(七)Appendix A: Sensor Driver Examples (1) Analog ADC-Connected Sensor
The Analog sensor requires two components
? ?

a component to present the sensor itself (HamamatsuS1087ParC) a component to select the appropriate hardware resources, such as ADC port 4, reference voltage 1.5V, and a slow sample and hold time (HamamatsuS1087ParP).

The AdcReadClientC component and underlying machinery handles all of the arbitration and access to the ADC.
tos/platforms/telosa/chips/s1087/HamamatsuS1087ParC.nc // HIL for the HamamatsuS1087 analog photodiode sensor generic configuration HamamatsuS1087ParC() { provides interface Read<uint16_t>; provides interface ReadStream<uint16_t>; provides interface DeviceMetadata; } implementation { // Create a new A/D client and connect it to the Hamamatsu S1087 A/D // parameters components new AdcReadClientC(); Read = AdcReadClientC; components new AdcReadStreamClientC(); ReadStream = AdcReadStreamClientC; components HamamatsuS1087ParP; DeviceMetadata = HamamatsuS1087ParP; AdcReadClientC.AdcConfigure -> HamamatsuS1087ParP; AdcReadStreamClientC.AdcConfigure -> HamamatsuS1087ParP; } tos/platforms/telosa/chips/s1087/HamamatsuS1087ParP.nc #include "Msp430Adc12.h"

// A/D parameters for the Hamamatsu - see the MSP430 A/D converter manual, // Hamamatsu specification, Telos hardware schematic and TinyOS MSP430 // A/D converter component specifications for the explanation of these // parameters module HamamatsuS1087ParP { provides interface AdcConfigure<const msp430adc12_channel_config_t*>; provides interface DeviceMetadata; } implementation { msp430adc12_channel_config_t config = { inch: INPUT_CHANNEL_A4, sref: REFERENCE_VREFplus_AVss, ref2_5v: REFVOLT_LEVEL_1_5, adc12ssel: SHT_SOURCE_ACLK, adc12div: SHT_CLOCK_DIV_1, sht: SAMPLE_HOLD_4_CYCLES, sampcon_ssel: SAMPCON_SOURCE_SMCLK, sampcon_id: SAMPCON_CLOCK_DIV_1 }; async command const msp430adc12_channel_config_t* AdcConfigure.getConfiguration() { return &config; } command uint8_t DeviceMetadata.getSignificantBits() { return 12; } }

(2) Binary Pin-Connected Sensor
The Binary sensor gets a bit more complex, because it has three components:
? ? ?

one to present the sensor (UserButtonC) one to execute the driver logic (UserButtonLogicP) one to select the appropriate hardware resources, such as MSP430 Port 27 (HplUserButtonC).

Note that the presentation of this sensor is not arbitrated because none of the operations are split-phase.
tos/platforms/telosa/UserButtonC.nc // HIL for the user button sensor on Telos-family motes

configuration UserButtonC { provides interface Get<bool>; // Get button status provides interface Notify<bool>; // Get button-press notifications provides interface DeviceMetadata; } implementation { // Simply connect the button logic to the button HPL components UserButtonLogicP; Get = UserButtonLogicP; Notify = UserButtonLogicP; DeviceMetadata = UserButtonLogicP; components HplUserButtonC; UserButtonLogicP.GpioInterrupt -> HplUserButtonC.GpioInterrupt; UserButtonLogicP.GeneralIO -> HplUserButtonC.GeneralIO; } tos/platforms/telosa/UserButtonLogicP.nc // Transform the low-level (GeneralIO and GpioInterrupt) interface to the // button to high-level SID interfaces module UserButtonLogicP { provides interface Get<bool>; provides interface Notify<bool>; provides interface DeviceMetadata; uses interface GeneralIO; uses interface GpioInterrupt; } implementation { norace bool m_pinHigh; task void sendEvent(); command bool Get.get() { return call GeneralIO.get(); } command error_t Notify.enable() { call GeneralIO.makeInput(); // If the pin is high, we need to trigger on falling edge interrupt, and // vice-versa if ( call GeneralIO.get() ) { m_pinHigh = TRUE;

return call GpioInterrupt.enableFallingEdge(); } else { m_pinHigh = FALSE; return call GpioInterrupt.enableRisingEdge(); } } command error_t Notify.disable() { return call GpioInterrupt.disable(); } // Button changed, signal user (in a task) and update interrupt detection async event void GpioInterrupt.fired() { call GpioInterrupt.disable(); m_pinHigh = !m_pinHigh; post sendEvent(); } task void sendEvent() { bool pinHigh; pinHigh = m_pinHigh; signal Notify.notify( pinHigh ); if ( pinHigh ) { call GpioInterrupt.enableFallingEdge(); } else { call GpioInterrupt.enableRisingEdge(); } } command uint8_t DeviceMetadata.getSignificantBits() { return 1; } } tos/platforms/telosa/HplUserButtonC.nc // HPL for the user button sensor on Telos-family motes - just provides // access to the I/O and interrupt control for the pin to which the // button is connected configuration HplUserButtonC { provides interface GeneralIO; provides interface GpioInterrupt; }

implementation { components HplMsp430GeneralIOC as GeneralIOC; components new Msp430GpioC() as UserButtonC; UserButtonC -> GeneralIOC.Port27; GeneralIO = UserButtonC; components HplMsp430InterruptC as InterruptC; components new Msp430InterruptC() as InterruptUserButtonC; InterruptUserButtonC.HplInterrupt -> InterruptC.Port27; GpioInterrupt = InterruptUserButtonC.Interrupt; }

(3) Digital Bus-Connected Sensor
The Digital sensor is the most complex out of the set, and includes six components:
? ? ? ? ? ?

one to present the sensor (SensirionSht11C) one to request arbitrated access and to transform the sensor HAL into the sensor HIL (SensirionSht11P) one to present the sensor HAL (HalSensirionSht11C) one to perform the driver logic needed to support the HAL, which twiddles pins according to a sensor-specific protocol (SensirionSht11LogicP). one to select the appropriate hardware resources, such as the clock, data, and power pins, and to provide an arbiter for the sensor (HplSensirionSht11C). one to perform the power control logic needed to support the power manager associated with the arbiter (HplSensirionSht11P).

This bus-connected sensor is overly complex because it does not rely on a shared framework of bus manipulation components. A sensor built on top of the I2C or SPI bus would likely require fewer components.
tos/platforms/telosa/chips/sht11/SensirionSht11C.nc // HIL interface to Sensirion SHT11 temperature and humidity sensor generic configuration SensirionSht11C() { provides interface Read<uint16_t> as Temperature; provides interface DeviceMetadata as TemperatureDeviceMetadata; provides interface Read<uint16_t> as Humidity; provides interface DeviceMetadata as HumidityDeviceMetadata; } implementation {

// Instantiate the module providing the HIL interfaces components new SensirionSht11ReaderP(); Temperature = SensirionSht11ReaderP.Temperature; TemperatureDeviceMetadata = SensirionSht11ReaderP.TemperatureDeviceMetadata; Humidity = SensirionSht11ReaderP.Humidity; HumidityDeviceMetadata = SensirionSht11ReaderP.HumidityDeviceMetadata; // And connect it to the HAL component for the Sensirion SHT11 components HalSensirionSht11C; enum { TEMP_KEY = unique("Sht11.Resource") }; enum { HUM_KEY = unique("Sht11.Resource") }; SensirionSht11ReaderP.TempResource -> HalSensirionSht11C.Resource[ TEMP_KEY ]; SensirionSht11ReaderP.Sht11Temp -> HalSensirionSht11C.SensirionSht11[ TEMP_KEY ]; SensirionSht11ReaderP.HumResource -> HalSensirionSht11C.Resource[ HUM_KEY ]; SensirionSht11ReaderP.Sht11Hum -> HalSensirionSht11C.SensirionSht11[ HUM_KEY ]; } tos/chips/sht11/SensirionSht11ReaderP.nc // Convert Sensirion SHT11 HAL to HIL interfaces for a single // client, performing automatic resource arbitration generic module SensirionSht11ReaderP() { provides interface Read<uint16_t> as Temperature; provides interface DeviceMetadata as TemperatureDeviceMetadata; provides interface Read<uint16_t> as Humidity; provides interface DeviceMetadata as HumidityDeviceMetadata; // Using separate resource interfaces for temperature and humidity allows // temperature and humidity measurements to be requested simultaneously // (if a single Resource interface was used, a request for temperature would // prevent any humidity requests until the temperature measurement was complete) uses interface Resource as TempResource; uses interface Resource as HumResource;

uses interface SensirionSht11 as Sht11Temp; uses interface SensirionSht11 as Sht11Hum; } implementation { command error_t Temperature.read() { // Start by requesting access to the SHT11 return call TempResource.request(); } event void TempResource.granted() { error_t result; // If the HAL measurement fails, release the SHT11 and signal failure if ((result = call Sht11Temp.measureTemperature()) != SUCCESS) { call TempResource.release(); signal Temperature.readDone( result, 0 ); } } event void Sht11Temp.measureTemperatureDone( error_t result, uint16_t val ) { // Release the SHT11 and signal the result call TempResource.release(); signal Temperature.readDone( result, val ); } command uint8_t TemperatureDeviceMetadata.getSignificantBits() { return 14; } command error_t Humidity.read() { // Start by requesting access to the SHT11 return call HumResource.request(); } event void HumResource.granted() { error_t result; // If the HAL measurement fails, release the SHT11 and signal failure if ((result = call Sht11Hum.measureHumidity()) != SUCCESS) { call HumResource.release(); signal Humidity.readDone( result, 0 ); } } event void Sht11Hum.measureHumidityDone( error_t result, uint16_t val ) {

// Release the SHT11 and signal the result call HumResource.release(); signal Humidity.readDone( result, val ); } command uint8_t HumidityDeviceMetadata.getSignificantBits() { return 12; } // Dummy handlers for unused portions of the HAL interface event void Sht11Temp.resetDone( error_t result ) { } event void Sht11Temp.measureHumidityDone( error_t result, uint16_t val ) { } event void Sht11Temp.readStatusRegDone( error_t result, uint8_t val ) { } event void Sht11Temp.writeStatusRegDone( error_t result ) { } event event val ) { event { } event void Sht11Hum.resetDone( error_t result ) { } void Sht11Hum.measureTemperatureDone( error_t result, uint16_t } void Sht11Hum.readStatusRegDone( error_t result, uint8_t val ) void Sht11Hum.writeStatusRegDone( error_t result ) { }

// We need default handlers as a client may wire to only the Temperature // sensor or only the Humidity sensor default event void Temperature.readDone( error_t result, uint16_t val ) { } default event void Humidity.readDone( error_t result, uint16_t val ) { } } tos/platforms/telosa/chips/sht11/HalSensirionSht11C.nc // HAL interface to Sensirion SHT11 temperature and humidity sensor configuration HalSensirionSht11C { // The SHT11 HAL uses resource arbitration to allow the sensor to shared // between multiple clients and for automatic power management (the SHT11 // is switched off when no clients are waiting to use it) provides interface Resource[ uint8_t client ]; provides interface SensirionSht11[ uint8_t client ]; } implementation { // The HAL implementation logic components new SensirionSht11LogicP();

SensirionSht11 = SensirionSht11LogicP; // And it's wiring to the SHT11 HPL - the actual resource management is // provided at the HPL layer components HplSensirionSht11C; Resource = HplSensirionSht11C.Resource; SensirionSht11LogicP.DATA -> HplSensirionSht11C.DATA; SensirionSht11LogicP.CLOCK -> HplSensirionSht11C.SCK; SensirionSht11LogicP.InterruptDATA -> HplSensirionSht11C.InterruptDATA; components new TimerMilliC(); SensirionSht11LogicP.Timer -> TimerMilliC; components LedsC; SensirionSht11LogicP.Leds -> LedsC; } tos/chips/sht11/SensirionSht11LogicP.nc generic module SensirionSht11LogicP() { provides interface SensirionSht11[ uint8_t client ]; uses interface GeneralIO as DATA; uses interface GeneralIO as CLOCK; uses interface GpioInterrupt as InterruptDATA; uses interface Timer<TMilli>; uses interface Leds; } implementation { ... bus protocol details omitted for brevity ... } tos/platforms/telosa/chips/sht11/HplSensirionSht11C.nc // Low-level, platform-specific glue-code to access the SHT11 sensor found // on telos-family motes - here the HPL just provides resource management // and access to the SHT11 data, clock and interrupt pins configuration HplSensirionSht11C { provides interface Resource[ uint8_t id ]; provides interface GeneralIO as DATA;

provides interface GeneralIO as SCK; provides interface GpioInterrupt as InterruptDATA; } implementation { // Pins used to access the SHT11 components HplMsp430GeneralIOC; components new Msp430GpioC() as DATAM; DATAM -> HplMsp430GeneralIOC.Port15; DATA = DATAM; components new Msp430GpioC() as SCKM; SCKM -> HplMsp430GeneralIOC.Port16; SCK = SCKM; components new Msp430GpioC() as PWRM; PWRM -> HplMsp430GeneralIOC.Port17; // HPL logic for switching the SHT11 on and off components HplSensirionSht11P; HplSensirionSht11P.PWR -> PWRM; HplSensirionSht11P.DATA -> DATAM; HplSensirionSht11P.SCK -> SCKM; components new TimerMilliC(); HplSensirionSht11P.Timer -> TimerMilliC; components HplMsp430InterruptC; components new Msp430InterruptC() as InterruptDATAC; InterruptDATAC.HplInterrupt -> HplMsp430InterruptC.Port15; InterruptDATA = InterruptDATAC.Interrupt; // The arbiter and power manager for the SHT11 components new FcfsArbiterC( "Sht11.Resource" ) as Arbiter; Resource = Arbiter; components new SplitControlPowerManagerC(); SplitControlPowerManagerC.SplitControl -> HplSensirionSht11P; SplitControlPowerManagerC.ArbiterInit -> Arbiter.Init; SplitControlPowerManagerC.ArbiterInfo -> Arbiter.ArbiterInfo; SplitControlPowerManagerC.ResourceDefaultOwner -> Arbiter.ResourceDefaultOwner; } tos/platforms/telosa/chips/sht11/HplSensirionSht11P.nc

// Switch the SHT11 on and off, and handle the 11ms warmup delay module HplSensirionSht11P { // The SplitControl interface powers the SHT11 on or off (it's automatically // called by the SHT11 power manager, see HplSensirionSht11C) // We use a SplitControl interface as we need to wait 11ms for the sensor to // warm up provides interface SplitControl; uses interface Timer<TMilli>; uses interface GeneralIO as PWR; uses interface GeneralIO as DATA; uses interface GeneralIO as SCK; } implementation { task void stopTask(); command error_t SplitControl.start() { // Power SHT11 on and wait for 11ms call PWR.makeOutput(); call PWR.set(); call Timer.startOneShot( 11 ); return SUCCESS; } event void Timer.fired() { signal SplitControl.startDone( SUCCESS ); } command error_t SplitControl.stop() { // Power the SHT11 off call SCK.makeInput(); call SCK.clr(); call DATA.makeInput(); call DATA.clr(); call PWR.clr(); post stopTask(); return SUCCESS; } task void stopTask() { signal SplitControl.stopDone( SUCCESS ); }

}

(4)MDA100 Sensor Board Directory Organization
Here we show the organization of the sensor board directory for the mica-family Xbow MDA100CA and MDA100CB sensor boards, which have temperature and light sensors. It is found in tos/sensorboards/mda100:
./tos/sensorboards/mda100: .sensor ArbitratedPhotoDeviceP.nc component ArbitratedTempDeviceP.nc support component DemoSensorC.nc default sensor PhotoC.nc PhotoImplP.nc component PhotoTempConfigC.nc component PhotoTempConfigP.nc component SharedAnalogDeviceC.nc component SharedAnalogDeviceP.nc component TempC.nc ca/TempImplP.nc support component cb/TempImplP.nc support component mda100.h

# Compiler configuration # Light sensor support # Temperature sensor # Override TinyOS's # Light sensor HIL # Light sensor support # Shared support # Shared support # Shared support # Shared support # Temperature Sensor HIL # Temperature sensor # (MDA100CA board) # Temperature sensor # (MDA100CB board) # Header file for mda100

This sensor board provides only a HIL (PhotoC and TempC components), and overrides the TinyOS demo sensor (DemoSensorC). The demo sensor is an alias for PhotoC. The two forms of the mda100 differ only by the wiring of the temperature sensor. The user has to specify which form of the sensor board is in use by providing a -I%T/sensorboards/mda100/ca or -I%T/sensorboards/mda100/cb compiler option.

This sensor board relies on a platform-provided MicaBusC component that specifies how the mica-family sensor board bus is connected to the microcontroller.

十一、微控制器电源管理(TEP 112)
(一)简介
微控制器通常有几个电源状态,它们具有不同的电源功率、唤醒延迟和外围 支持。 微控制器在能满足程序需求的前提下,应当一直处于能耗尽量低的电源状 态。 为了准确地决断处理器该处于哪个状态,需要有大量外围子系统和外围设备 的电源状态信息。另外,状态转换是很常见的。每当微控制器处理一个中断时, 它从低功耗状态切换到激活状态;当 TinyOS 调度器发现任务队列为空时,它回 到低功耗状态。TinyOS 2.x 使用三种机制来决定让处理器处于哪种状态:状态 和控制寄存器,脏位,电源状态覆盖(override)。本文档讲述了这些机制以及 子系统电源管理的基本概念。

(二)背景
TinyOS 的调度器在任务队列为空时让处理器进入睡眠状态。然而,处理器 可能有多个电源状态。比如 MSP430 有一个激活状态(发射指令)和 5 个低功耗 状态。低功耗状态有 LPM0?LPM4。LPM0 只禁用 CPU 和主系统时钟,LPM4 禁用 CPU、所有的时钟和震荡器,只留下唤醒用的外部中断源。各种低功耗模式的差 异可能有 350 倍或更多(LPM0 为 75uA/3V,LPM4 为 0.2uA/3V)。正确地选择 微控制器的低功耗状态可以大幅度地提高系统的生存时间。 TinyOS 1.x 用几种不同的方法管理 MCU 的功耗,但是各种方法之间有共同 之处。比如在 mica 平台上有一个叫 HPLPowerManagement 的组件,它拥有启用 和禁用低功耗模式的命令,还有一个 adjustPower() 命令用于根据各种控制寄 存器和状态寄存器计算低功耗状态,并把结果存储在 Atmega128 的 MCU 控制寄 存器中。当 TinyOS 要求 MCU 睡眠时,它使用控制寄存器决定转移到哪个状态。 基于 MSP430 的平台如 Telos 和 eyes 在每次调度器要求系统进入睡眠时计算低 功耗状态。 这两种方法有各自的长处和缺点。1.x 中 mica 使用的方法是效率较高的, 因为它只在被要求计算的时候才计算低功耗状态。 然而这就把何时计算的时机让 于其它组件决定, 这很容易引入 Bug。由于 1.x 硬件抽象架构定义的不够完善从 而使这个问题更加严重。MSP430 的方法更简单,系统不需要外部的干涉可以自 动进入正确的电源状态。 然而这种方法开销较大, 每个中断都会引入了 40-60 个 周期用于唤醒系统,这可能会是系统中断处理速率的瓶颈。

两种方法都假设 TinyOS 可以通过检查控制寄存器和状态寄存器决定正确 的低功耗状态。比如 MSP430 默认运行在低功耗模式 3,当它检测到定时器 A、 USART 或 ADC 被激活时,它转到低功耗模式 1(LPM1)。外围设备和子系统可能 在 MCU 睡眠时唤醒节点或继续工作,以这样的观点来看这是正确的行为。但是 电源模式转换会引入唤醒延迟, 这可能是高层组件所关心的因素。在功率非常低 的 MCU 中唤醒延迟并不是一个严重的问题,如 Atmega128 和 MSP430,但在如 Xscale(imote2 平台的基础)等更强力的处理器上可能会使唤醒延迟长达 5ms。 在一些应用领域中,这样的延迟是严重的问题。高层的组件应当根据需求向 TinyOS 电源控制器提供适当的信息,用于计算正确的低功耗状态。

(三)微控制器电源管理
TinyOS 2.x 使用 3 种基本机制管理和控制 MCU 电源状态:一个脏位,一个 针对芯片的低功耗状态计算函数和电源状态覆盖函数。脏位告诉 TinyOS 何时需 要计算新的低功耗状态, 由低功耗状态计算函数作计算,覆盖函数允许高层组件 根据需要引入额外的需求。 这三种机制都在 TinyOS 核心调度循环中运作, TEP 在 106(调度器与任务)中有介绍。该循环在启动过程中调用,这在 TEP 107(启 动顺序)中有讲述。当启用 MCU 休眠时,调用的命令是 Scheduler.taskLoop()。 若在任务队列为空时调用该命令,则 TinyOS 调度器会通过 McuSleep 接口 让 MCU 进入睡眠状态: interface McuSleep { async command void sleep(); } McuSleep.sleep() 使 MCU 进入低功耗睡眠状态,可以由中断唤醒。该命令 与 TinyOS 1.x 中的__nesc_atomic_sleep()不同。注意,1.x 中的调用意味着 让 MCU 睡眠必须具有某种原子的属性。该命令应在 atomic 区域中调用,必须原 子地重新启用中断再进入睡眠。 问题是系统中重新启用中断和进入睡眠这两者之 间的时间发生了中断: 该中断可能会 post 一个任务,但是该任务必须在 mcu 被 唤醒后才能执行。 MCU 通常用硬件机制要解决这个问题。比如 Atmega128 中的 sei 指令在指 令发射两个周期后才重新启用中断(因此指令序列 sei sleep 会原子的执行)。 McuSleepC 组件提供 McuSleep 接口,TinySchedulerC 必须原子地把它连接到 调度器实现上。McuSleepC 是芯片或平台相关的组件,它的头部中必须包含以下 接口: component McuSleepC { provides interface McuSleep; provides interface PowerState; uses interface PowerOverride; }

interface McuPowerState { async command void update(); } interface McuPowerOverride { async command mcu power t lowestState(); } McuSleepC 还可以用其它的接口。

(四)脏位
每当硬件表示层(HPL,见 TEP 2:硬件抽象结构)组件改变了一部分硬件 配置时,这可能会改变 MCU 可用的低功耗状态,它必调用 McuPowerState.update()。这是电源管理的第一种机制──脏位。如果 McuPowerState.update() 被调用,则 McuSleepC 必须在调用 McuSleep.sleep() 进入睡眠之前重新计算低功耗状态。

(五)低功耗状态计算
McuSleepC 负责计算最低的功率状态,同时保证 TinyOS 各子系统能安全正 确地工作。 McuSleepC 应当使这种计算次数尽量少,因为它是原子性的计算, 如果计算过于频繁会导致相当大的开销和抖动。 MCU 电源状态必须在标准芯片的头文件中用 enum 类型表示。该文件必须定 义 mcu power t 和一个结合函数将两个电源状态值合并为一个并返回,以提供 它们功能的结合。 假设有一种 MCU 具有 3 个低功耗状态(LPM0,LPM1,LPM2)和两种硬件资 源如时钟(HR0,HR1)。在 LPM0 中,HR0 和 HR1 都处于激活状态。在 LPM1 中, HR0 是关闭的而 HR1 是激活的。在 LPM2 中,HR0 和 HR1 是关闭的。这面的表 描述了合适的结合函数(essentially a MAX):
L L L L L L L

PM0 PM1 PM2 PM0 PM0 PM0 PM0 L L L L PM1 PM0 PM1 PM1 L L L L PM2 PM0 PM1 PM2

LPM2 中,HR0 是激活的而 HR1 是关闭的,结合函数如下:

L L L L L L L

L L L L

L L L L

PM0 PM1 PM2 PM0 PM0 PM0 PM0 PM1 PM0 PM1 PM0 PM2 PM0 PM0 PM2

(六)电源状态覆盖
当 McuSleepC 计算最佳最功耗状态时,它必须调用 PowerOverride.lowestState()。McuSleepC 应当有该命令的一种默认实现,返 回 MCU 可用的最低功耗状态。该命令的返回值应当是 mcu_power_t 类型。 McuSleepC 必须考虑该调用的返回值,并恰当地与它计算出的低功耗状态相结 合。 PowerOverride 功能的存在是因为高层的组件可能有硬件状态或配置寄存 器不能捕获的信息, 如可允许的最大唤醒延迟。 它可以覆盖所有系统的保留机制, 因此要谨慎行事。由于它是在核心调度器循环的 atomic 区域中调用的, PowerOverride.lowestState() 的实现应当是一个高效的函数,不能长于 20 ? 30 个周期。该实现应当是一个预存值的简单返回。随意地连接到这个命令很容 易引起 TinyOS 出现不正常的行为。mcu_power_t 具有一个结合函数意味着这个 命令可以有扇出调用。 第 5 节描述了在 Atmega128 的定时器栈中使用 McuPowerOverride 的一个例 子。

(七)外围设备和子系统
在 HIL 层中,TinyOS 子系统通常有一个简单、强制的电源管理接口。根据 引入的延时, 这些接口可以是 StdControl, SplitControl 或 AsyncStdControl。 这些接口保证如果有组件调用另一个组件的 stop 命令,它能使该组件所代表的 子系统进入非激活、低功耗状态。 从 MCU 电源管理的角度来看,这种转换会引起状态寄存器和控制寄存器的 变化(比如禁用某个时钟)。根据 3.1 小节所述,在有较大的变化发生时,MCU 电源管理子系统会被告知,并在进入睡眠之前做适当的动作。TEP 115 描述了非 虚拟化设备的电源管理,TEP 108 描述了 TinyOS 可以自动将电源管理放入共享 非虚拟化设备中。

(八)实现
McuSleepC 的实现可以在 tinyos-2.x/tos/chips/atm128, tinyos-2.x/tos/chips/msp430, tinyos-2.x/tos/chips/px27ax 中找到。 使用 McuPowerOverride 的一个例子可以在 atmega128 定时器系统中找到。 由于一部分低功耗状态唤醒延迟比其它的要长, 定时器系统不允许在定时器即将 触发前有较长的的延迟。该实现可以在 tinyos-2.x/tos/chips/atm128/timer/HplAtm128Timer0AsyncP.nc 中找到, tinyos-2.x/tos/chips/atm128/timer/HplAtm128Timer0AsyncC.nc 会自动连接 到 McuSleepC。

十二、串口通信(TEP113)
(一)摘要
本文档描述了 TinyOS 2.x 中节点与 PC 串口数据通信系统的结构与标准实 现。该系统可分为三层(编码、装帧与分派),从而便于修改或替换某个层。同 时,它也处理多种包格式。与 1.x 不同,2.x 的串口数据包并没有与射频数据包 格式绑定。另外,数据包的格式是与平台无关的,因此 PC 端的程序可以与任意 节点通信。

(二)简介
用户需要从 TinyOS 网络中读出数据,最常用的方法是通过线缆将将节点连 到 PC 或笔记本上。虽然 PC 端可能有串口、USB 或网卡等接口,但是节点通常使 用串口(UART)。在 TinyOS1.x 中,UART 包的格式是与平台有关的,这就增加 了设计通信协议和 PC 端工具的难度,需要了解平台间的差异。TinyOS 2.0 引入 了包格式分派的概念,因此节点可以同时支持多种 UART 数据包。这就允许透明 桥接 (如 802.15.4 基站) 与平台无关通信共存, 从而简化了 PC 端工具链的开发。 本文档描述了 TinyOS 2.x 串口协议栈的结构。

(三)串口协议栈结构
在 TinyOS 2.x 串口协议栈分为 4 个组件,自底向上分别是
? 原始 UART ? 编码器/装帧器

? 协议 ? 分派器

结构如下图所示: _____________________ | | | Dispatcher | |_____________________| _____________________ | | | Protocol | |_____________________| _____________________ | | | Encoder/Framer | |_____________________| _____________________ | | | Raw UART | |_____________________| Packet formatting.

Acknowledgements, CRC computation, windowing.

Translating raw bytes into frame delimiters, escape bytes.

Platform code for reading/writing bytes over the serial connection.

下三层提供了一个字节接口,而分派器提供了包级接口。上三层是平台无关 的,只有 UART 部分是平台有关的。 协议栈的最下一层是原始 UART 组件,HIL 层组件提供了配置 UART(速率, 停止位等)、收发字节、冲刷 UART 缓冲区的功能。 Encoder/Framer 组件建于原始 UART 层之上。该组件将原始字节数据转换为 串口协议数据包格式。 编码/装帧器假设有两种类型的字节,分隔符和数据字节, 分别用不同的事件向上层组件发出信号。 Protocol 组件处理数据和分隔符事件。它负责读入并发送所有的协议控制 包。 如果 Protocol 组开始接收数据包,则它会向 Dispatcher 组件发送信号并附 上接收到的数据字节作为参数。当数据包接收完毕时,Protocol 组件通知 Dispatcher 组件包已接收完成,同时也告诉它 CRC 校验是否通过。 Dispatcher 组件处理数据包字节与分隔符。它负责将数据读入 message_t 并告知上层组件数据包已接收完毕。Dispatcher 组件支持多种包格式,基于 message_t 的工作方式(见 TEP111), 这就需要知道数据包的包头大小以计算出数 据的偏移。3.4 小节将讲述 TinyOS 2.x 中的实现 SerialDispatcherC。

(四)2.x 中串口协议栈的实现
第 2 节中讲述了 TinyOS 2.x 串口协议栈的基本结构。本节将讲述它的实现, 包括 SerialActiveMessageC,它构建于 Dispatcher 组件的上层。除了 UartC, 所有与串口相关的部分都在'tos/lib/serial'目录下。

(1)原始 UART: UartC
UART 的 HIL 组件为 UartC,它为串口通信提供了字节级的接口 SerialByteComm: interface async async async } SerialByteComm { command error_t put(uint8_t data); event void get(uint8_t data); event void putDone();

同时,UartC 也可以提供多字节的 UartStream 接口,详见 TEP 低级 I/OTEP117。 另外,UartC 提供 split-phase 接口以便在 UART 空闲时发出通知信号。有 时我们需要知道发送到串口的数据已全部被传输 (比如与串口数据通路相连接无 线收发机需要从发送转换到接收状态时),但问题是如果使用的 MCU 用的是双缓 冲 UART 通信机制,那么 putDone 事件只意味着它已经准备好接收下一字节,而 不是 UART 空闲。 SerialFlush { command void flush(); event void flushDone(); } 该接口提供另一种配置串口的方法,但在本 TEP 不作讲述。

(2)Encoder/Framer:HdlcTranslaterC
HdlcTranslaterC 是串口协议栈中的编码和解码组件,它使用 SerialByteComm 接口,提供 SerialFrameComm 接口。 interface async async async SerialFrameComm { command error_t putDelimiter(); command error_t putData(uint8_t data); command void resetSend();

async async async async }

command void resetReceive(); event void delimiterReceived(); event void dataReceived(uint8_t data); event void putDone();

它使用 HDLC 协议作为编码方式,0x7e 作为帧分隔符,0x7d 作为转义符。 HdlcTranslaterC 中维护了 10 位的状态码。对于接收和发送通路各使用了一位 状态码用于表示是否使用了转义符, 另外传输路径上还有一个字节用于决定何时 发送转义符。 当 HdlcTranslaterC 收到分隔符时,它设置 receiveEscape 位为 true,当 它收到接收到其它字节时,先检测 receiveEscape 位是否已设置。若是,则数据 字节与 0x20 异或并清除 receiveEscape 位,同时发出 dateReceived()信号以及 收到的数据字节。 而转义符最见的使用情况是实际传输的字节与分隔符或转义符 相同,此时,0x7e 将被编码为 0x7d 0x5e。 HdlcTranslaterC 在发送端的行为与接收端类似。当需要传输一个与分隔符 或转义符相同的字节时,它设置 transmitEscape 标识位为 true,同时存储数据 字节与 0x20 异或的值,然后发送转义符。当转义符发送完成后,接着发送刚才 存储的数据字节。

(3)Protocol: SerialP
SerialP 组件用类似于 PPP/HDLC 的帧结构实现了串口协议(RFC1662)。而消 息分派和缓冲区管理就留给了上层去处理。主机到节点的通信使用的停等的方 式,而节点到主机用尽力传输的方式。 SerialP 提供了两个面向字节的接口用于收发数据包,分别是 SendBytePacket 和 ReceiveBytePacket。 在发送方, SerialP 负责封装上层的数据包。 上层组件如 SerialDispatcherC 调用 startSend()初始化包的发送并传递第一个要发送的字节。SerialP 使用 nextByte()信号收集接下来要发送的包。在 nextByte 运行期间或者在调用 nextByte()的间隙,协议上层要通过调用 completeSend()表明数据包的结尾。 如果 completeSend 是在 nextByte()运行之间被调用的,SerialP 会忽略调用 nextByte()的返回值。 interface SendBytePacket { async command error_t startSend(uint8_t first_byte); async command error_t completeSend(); async event uint8_t nextByte(); async event void sendCompleted(error_t error); }

SerialP 维护了一个小的窗口用于存放上层接收的字节以及还没有被发送 到 UART 的字节。窗口的大小取决于 UART 的时间需求,同时通过反复的调用 nextByte()以保持窗口填充。 SerialP 使用 SerialFrameComm 来发送帧间的分 隔符、串行级别类型字段、数据包字节和两字节的帧循环冗余校验(CRC)码。同 时, 为了实现节点和主机之间的间隙检测和稳定链接, 还可以发送一个序列号 (默 认实现中无此功能)。一帧发送结束并且接收到底层的最后一个 putDone()事件 后,SerialP 调用 sendCompleted()表明此帧发送成功与否。 SerialP 还负责数 据包的接收工作,并且向高层提供 ReceiveBytePacket 接口: interface ReceiveBytePacket { async event error_t startPacket(); async event void byteReceived(uint8_t b); async event void endPacket(error_t result); } 一旦接收到帧间分隔符和一个新帧的帧头,SerialP 向高层发出信息表明一 个数据包已经到达。每收到一个字节,SerialP 调用一次 byteReceived()。当收 到一个完整的帧时,SerialP 调用 endPacket 并返回 SUCCESS。反之,若在接收 过程中失去同步,SerialP 调用 endPacket 并返回 FAIL。 SerialP 会对收到的 帧发出确认信息。确认信息的优先级比数据传输高,因此,数据帧会被稍微延迟 发送。然而,确认信息存储在与数据缓冲区不同的队列中,因此当 SerialP 正发 送确认信息时,待发送的数据包开始进入 SerialP 的后台运行。仅有 PC 到 mote 方向的通信才支持确认功能。SeriaP 不需要获得 PC 的确认信息,原因有两个: 首先,确认机制不是很可靠,在 PC 到节点的通信中使用确认机制是为了将可靠 性提高到可用级别。 PC 到节点方向的通信路径中,典型的 UART 接收缓存仅有 在 一个字节大小,因此高负荷中断很容易丢失字节。PC 的接收缓存则要大得多, 并且不用处理溢出问题。 其次, 增加确认机制会增加串口通信栈的代码长度和复 杂度。这样的结果就是以消耗宝贵的代码空间来换取很少需要的功能。当然,任 何对可靠性要求较高的应用程序都会将其任务计划基于串口协议栈顶。 确认协议 采用停等机制以最少占用节点的缓存资源。在 PC 到节点方向的通信过程中,停 等机制在存储受限的设备上要比增加吞吐量更加重要, 绝大多数的应用程序仅偶 尔进行传输控制的时候才使用后者。

(4)Dispatcher: SerialDispatcherC
SerialDispatcherC 负责处理协议组件接收的数据包。它使用 SendBytePacket 和 ReceiveBytePacket 接口,提供带参数的 Send 和 Receive 接 口。 Send 和 Receive 接口的参数(uart_id_t)决定了 message_t 中数据包的格式。 SerialDispatcherC 在通过 SerialP 发送和接收的数据包中放置了一字节的包头 和包格式标识符。通过使用带参数的 SerialPacketInfo 接口, SerialDispatcherC 能够处理各种数据包格式。 interface SerialPacketInfo { async command uint8_t offset();

async command uint8_t dataLinkLength(message_t* msg, uint8_t upperLen); async command uint8_t upperLength(message_t* msg, uint8_t dataLinkLen); } SerialDispatcherC 收到由 SerialP 发出的第一个字节后,先将其存储为数 据包类型,同时调用 offset()以在 message_t 中确定偏移量。然后将数据字节 导入并填充到 message_t 的缓冲区中。发送过程与此类似,SerialDispatcherC 先发送类型字节并导出数据字节,数据字节的起始位置由调用 offest()函数得 到的索引值确定。SerialDispatcherC 使用了两个长度命令:dataLinkLength() 和 upperLength()以在两种数据包类型中进行转换。向上,长度为不含数据包头 部的有效载荷长度;向下,长度则为包含了数据包头的长度。在使用了串口端口 参数: uart_id_t U 提供通信服务的组件中,必须在组件实现中通过 uart_id_t U 将 SerialPacketInfo 配线 SerialDispatcherC。目前,仅有平台独立的活动消 息(TOS_SERIAL_ACTIVE_MESSAGE_ID,在 3.5 节中有详细描述)、802.15.4 的 活动消息(TOS_SERIAL_802_15_4_ID)、mica2 平台的 CC1000 数据包 (TOS_SERIAL_CC1000_ID)和错误代码 TOS_SERIAL_UNKNOWN_ID 是保留标识符, 新的数据包格式绝对不可以使用任何保留标识符。

(5)SerialActiveMessageC
SerialActiveMessgaeC 是平台独立的活动消息层组件,工作于串口通信栈 的顶端的配置文件,它将 SerialActiveMessageP 配线到带 uart_id_t 参数的 SerialDispatcherC,同时 将 SerialPackerInfoActiveMessgaP 配线到带参数 uart_id_tTOS_SERIAL_ACTIVE_MESSAGE_ID 的 SerialDispatcherC。 includes Serial;`` configuration SerialActiveMessageC { provides { interface Init; interface AMSend[am_id_t id]; interface Receive[am_id_t id]; interface Packet; interface AMPacket; } uses interface Leds; } implementation { components new SerialActiveMessageP() as AM, SerialDispatcherC; components SerialPacketInfoActiveMessageP as Info; Init = SerialDispatcherC; Leds = SerialDispatcherC;

AMSend = AM; Receive = AM; Packet = AM; AMPacket = AM; AM.SubSend -> SerialDispatcherC.Send[TOS_SERIAL_ACTIVE_MESSAGE_ID]; AM.SubReceive -> SerialDispatcherC.Receive[TOS_SERIAL_ACTIVE_MESSAGE_ID]; SerialDispatcherC.SerialPacketInfo[TOS_SERIAL_ACTIVE_MESSAGE_ID] -> Info; } SerialActiveMessageP 是一个通用组件,因此可用于各种数据包级的通信 层 顶端。它不以目的地址或组号过滤数据包,而是假设从串口端口接收的数据 包目的地址就是本节点。 这样 PC 端的工具就不用再寻找和考虑节点的 ID 和组号 了。平台无关的活动消息没有 CRC(通常假设其串口栈的 CRC 是足够的),其头 部格式如下: typedef nx_struct SerialAMHeader { nx_am_addr_t addr; nx_uint8_t length; nx_am_group_t group; nx_am_id_t type; } SerialAMHeader;

(6)Packet Format
TinyOS3.x 的串口栈里,数据包在配线过程中有如下的格式。各协议字段分 别和特定的组件对应。 ____________________________________________ | | | | | | | | | | | | | | | | |_|_|_|_|_______________________________|__|_| F P S D Payload CR F F P S D = = = = Framing byte, denoting start of packet: HdlcTranslateC Protocol byte: SerialP Sequence number byte: SerialP Packet format dispatch byte: SerialDispatcherC

Payload = Data payload (stored in SerialDispatcherC): SerialDispatcherC CR = Two-byte CRC over S to end of Payload: SerialP F = Framing byte denoting end of packet: HdlcTranslateC Payload 是由 SerialDispatcherC 读入的连续数据包。需要注意:payload 总任何值为 0x7e 和 0x7d 的字节都将被相应转化为 0x7d 0x5e 或者 0x7d 0x5d。 例如,一个目标地址为 0xbeef,类型为 6,组号为 0x7d,长度为 5 的平台独立 活动数据包为:7e 40 09 00 be ef 05 7d 5d 06 01 02 03 04 05 7e 注意:组 号 0x7d 被转化为 0x7d 0x5d。 协议字段 (P) 值为 0x40 (64), SERIAL_PROTO_ACK 与 一致(SERIAL_PROTO_ACK 在 Serial.h 文件中有说明)。

(五)Access Abstractions
SerialAMSenderC 和 SerialAMReceiverC 连接到 SerialActiveMessageC 以 提供虚拟的方式访问串口栈。每个 SerialAMSenderC 的实例都有自己的深度为 1 的队列空间。底层的实现使用一些公平调度排队的形式来调度这些队列。 SerialAMReceiverC 为接收过程提供了虚拟抽象。这些和 TinyOS 的广播抽象 (AMSenderC 和 AMReceiverC)非常类似。更多内容参考第 4 部分的[TEP116]。 与 TEP 116 中提供的服务部同,串口通信虚拟不提供监听功能。

十三、Low-Level I/O 低级 I/O(TEP117)
(一)Abstract
The memo documents the TinyOS 2.x interfaces used for controlling digital IO functionality and digital interfaces.

(二)Introduction
The canonical TinyOS device is likely to have a variety of digital interfaces. These interfaces may be divided into two broad categories. The first are general purpose digital I/O lines (pins) for individual digital signals at physical pins on a chip or platform. The second are digital I/O interfaces that have predefined communication protocol formats. The three buses covered in this document are the Serial Peripheral Interface (SPI), the Inter-Integrated Circuit (I2C) or Two-Wire interface, and the Universal Asynchronous Receiver/Transmitter (UART) interface. While there are

likely other bus formats, we presume SPI, I2C, and UART to have the largest coverage. This memo documents the interfaces used for pins and the three buses.

(三)Pins
General Purpose I/O (GPIO) pins are single, versatile digital I/O signals individually controllable on a particular chip or platform. Each GPIO can be placed into either an input mode or an output mode. On some platforms a third 'tri-state' mode may exist, but this functionality is platform specific and will not be covered in this document. On many platforms, a physical pin may function as either a digital GPIO or another special function I/O such. Examples include ADC I/O or a bus I/O. Interfaces to configure the specific function of a pin are platform specific. The objective of the interfaces described here is not to attempt to cover all possibilities of GPIO functionality and features, but to distill down to a basis that may be expected on most platforms.
In input mode, we assume the following capabilities:
? The ability to arbitrarily sample the pin ? The ability to generate an interrupt/event from either a rising edge or falling edge

digital signal. In output mode, we assume the following capabilities:
? An I/O may be individually cleared (low) or set (hi)

Platform that provide GPIO capabilities MUST provide the following HIL interfaces:
? ?

GeneralIO GpioInterrupt

Platforms MAY provide the following capture interface.
?

GpioCapture

(1)GeneralIO
The GeneralIO HIL interface is the fundamental mechanism for controlling a GPIO pin. The interface provides a mechanism for setting the pin mode and reading/setting

the pin value. The toggle function switches the output state to the opposite of what it currently is. Platforms with GPIO functionality MUST provide this interface. It SHOULD be provided in a component named GeneralIOC, but MAY be provided in other components as needed.
interface GeneralIO { async command void async command void async command void async command bool async command void async command bool async command void async command bool }

set(); clr(); toggle(); get(); makeInput(); isInput(); makeOutput(); isOutput();

(2)GpioInterrupt
The GPIO Interrupt HIL interface provides baseline event control for a GPIO pin. It provides a mechanism to detect a rising edge OR a falling edge. Note that calls to enableRisingEdge and enableFallingEdge are NOT cumulative and only one edge may be detected at a time. There may be other edge events supported by the platform which MAY be exported through a platform specific HAL interface.
interface GpioInterrupt { async async async async async async } command error_t enableRisingEdge(); command bool isRisingEdgeEnabled(); command error_t enableFallingEdge(); command bool isFallingEdgeEnabled(); command error_t disable(); event void fired();

(3)GpioCapture
The GpioCapture interface provides a means of associating a timestamp with a GPIO event. Platforms MAY provide this interface.

Some platforms may have hardware support for such a feature. Other platforms may emulate this capability using the SoftCaptureC component. The interface makes not declaration of the precision or accuracy of the timestamp with respect to the associated GPIO event.
interface GpioCapture { async async async async async async } command error_t captureRisingEdge(); command bool isRisingEdgeEnabled(); command error_t captureFallingEdge(); command bool isFallingEdgeEnabled(); event void captured(uint16_t time); command void disable();

(四)Buses
Bus operations may be divided into two categories: data and control. The control operations of a particular bus controller are platform specific and not covered here. Instead, we focus on the data interfaces at the HIL level that are expected to be provided.

(1)Serial Peripheral Interface
The Serial Peripheral Interface (SPI) is part of a larger class of Synchronous Serial Protocols. The term SPI typically refers to the Motorola SPI protocols. Other protocols include the National Semiconductor Microwire, the TI Synchronous Serial Protocol and the Programmable Serial Protocol. The dataside interfaces here were developed for the Motorola SPI format, but may work for others. Platforms supporting SPI MUST provide these interfaces. Of note, the interfaces DO NOT define the behavior of any chip select or framing signals. These SHOULD determined by platform specific HAL interfaces and implementations. The interface is split into a synchronous byte level and an asynchronous packet level interface. The byte level interface is intended for short transactions (3-4 bytes) on the SPI bus.
interface SpiByte { async command uint8_t write( uint8_t tx );

}

The packet level interface is for larger bus transactions. The pointer/length interface permits use of hardware assist such as DMA.
interface SpiPacket { async command error_t send( uint8_t* txBuf, uint8_t* rxBuf, uint16_t len ); async event void sendDone( uint8_t* txBuf, uint8_t* rxBuf, uint16_t len, error_t error ); }

(2)I2C
The Inter-Integrated Circuit (I2C) interface is another type of digital bus that is often used for chip-to-chip communication. It is also known as a two-wire interface. The I2CPacket interface provides for asynchronous Master mode communication on an I2C with application framed packets. Individual I2C START-STOP events are controllable which allows the using component to do multiple calls within a single I2C transaction and permits multiple START sequences Platforms providing I2C capability MUST provide this interface.
interface I2CPacket<addr_size> { async command error_t read(i2c_flags_t flags, uint16_t addr, uint8_t length, u int8_t* data); async event void readDone(error_t error, uint16_t addr, uint8_t length, uint8_t* data); async command error_t write(i2c_flags_t flags, uint16_t addr, uint8_t length, uint8_t* data); async event void writeDone(error_t error, uint16_t addr, uint8_t length, uint8_t* data) }

The interface is typed according to the addressing space the underlying implementation supports. Valid type values are below.
TI2CExtdAddr - Interfaces uses the extended (10-bit) addressing mode. TI2CBasicAddr - Interfaces uses the basic (7-bit) addressing mode.

The i2c_flags_t values are defined below. The flags define the behavior of the operation for the call being made. These values may be ORed together.

I2C_START I2C_STOP be used I2C_ACK_END valid

- Transmit an I2C STOP at the beginning of the operation. - Transmit an I2C STOP at the end of the operation. Cannot with the I2C_ACK_END flag. - ACK the last byte sent from the buffer. This flags is only a write operation. Cannot be used with the I2C_STOP flag.

(3)UART
The Universal Asynchronous Receiver/Transmitter (UART) interface is a type of serial interconnect. The interface is "asynchronous" since it recovers timing from the data stream itself, rather than a separate control stream. The interface is split into an asynchronous multi-byte level interface and a synchronous single-byte level interface. The multi-byte level interface, UartStream, provides a split-phase interface for sending and receiving one or more bytes at a time. When receiving bytes, a byte-level interrupt can be enabled or an interrupt can be generated after receiving one or more bytes. The latter is intended to support use cases where the number of bytes to receive is already known. If the byte-level receive interrupt is enabled, the receive command MUST return FAIL. If a multi-byte receive interrupt is enabled, the enableReceiveInterrupt command MUST return FAIL.
interface UartStream { async command error_t send( uint8_t* buf, uint16_t len ); async event void sendDone( uint8_t* buf, uint16_t len, error_t error ); async command error_t enableReceiveInterrupt(); async command error_t disableReceiveInterrupt(); async event void receivedByte( uint8_t byte ); async command error_t receive( uint8_t* buf, uint8_t len ); async event void receiveDone( uint8_t* buf, uint16_t len, error_t error ); }

The single-byte level interface, UartByte, provides a synchronous interface for sending and receiving a single byte. This interface is intended to support use cases with short transactions. Because UART is asynchronous, the receive command takes a timeout which represents units in byte-times, after which the command returns with an error. Note that use of this interface is discouraged if the UART baud rate is low.
interface UartByte { async command error_t send( uint8_t byte );

async command error_t receive( uint8_t* byte, uint8_t timeout ); }

(五)Implementation
Example implementations of the pin interfaces can be found in tos/chips/msp430/pins, tos/chips/atm128/pins, and tos/chips/pxa27x/gpio. Example implementations of the SPI interfaces can be found in tos/chips/msp430/usart, tos/chips/atm128/spi, and tos/chips/pxa27x/ssp. Example implementations of the I2C interfaces can be found in tos/chips/msp430/usart, tos/chips/atm128/i2c, and tos/chips/pxa27x/i2c. Example implementations of the UART interfaces can be found in tos/chips/msp430/usart, tos/chips/atm128/uart/ and tos/chips/pxa27x/uart.

十四、Towards TinyOS for 8051(TEP121)
(一)Abstract
This TEP covers our effort of porting TinyOS to the nRF24E1 platform. We ported the basic modules of TinyOS: Timer, UART, ADC and LEDS.

(二)Project Outline
The original 8 bit 8051 chip is a member of the mcs51 family and was developed in 1980 by Intel. It is still to this date one of the most widely used microcontrollers. Porting TinyOS to the 8051 System on chip architecture makes perfect sense - the mcs51 family has been thoroughly tested, it is relatively cheap and it has a reasonable small footprint - which makes it ideal for embedded solutions and sensor networks. For this work, we use a Nordic Semiconductor VLSI nRF24E1 evaluation-board [NSC]. The board contains an Intel 8051 compatible MCU with 4KB program memory, a 16 MHz clock, 3 different Timers (one being 8052 compatible), a 2.4 GHz wireless RF transceiver and 9 input 10 bit ADC, SPI and a RS232 Serial interface. The nRF24E1 board was chosen because the radio component matches the radio on the specially designed DIKU/DTU HogthrobV0 [HOG] boards used for research purposes at DIKU [PEH].

We ported a subset of TinyOS for the 8051 platform consisting of the Timer, UART, ADC and LED modules. We did not port the radio module and the underlying SPI-bus code. This works attacks the two most immediate problems when porting TinyOS to 8051: the toolchain and the hardware abstraction components. The first problem when porting TinyOS to 8051-based platforms concerns the toolchain. The Gcc compiler does not support 8051. This is a major issue as the code generated by the NesC preprocessor is tailored for gcc. The second problem concerns the hardware abstraction components that must be specialized to the 8051 idiosyncracies. In a perfect world, such a specialization should not require to modify any interface. Unfortunately, we needed to modify some of the interfaces to accomodate the 8051 features. This work was done under the supervision of Martin Leopold at University of Copenhagen.

(三)Project Approach
The approach to the porting project has been pragmatic. The focus has been on producing working code, so testing and debugging have been key elements of our work. The process has been to implement new functionality in small iterative steps and do testing simultaneously. To bootstrap the development without a JTAG module or alike, we built a small LED expansion board attachable to the port logic. The LEDs was an easy way to get instant low level test output. We also built a small stimulator based on a potentiometer (variable resistor) to get valid input from the ADC pins.
The following TinyOS application programs have been written and tested:
? Empty - test of port logic and tool chain ? mcsatomic - test of atomic and interrupts ? mcsBlink - test of LEDs ? mcsBlinkTimer - test of Timers using LEDs ? mcsSerialTest - test of UART code, simple input/output one char ? mcsSerialTest2 - test of multiple byte output ? mcsTimerSerialTest - test of UART controlled by Timer interrupts ? mcsADC - test of ADC code with Timer and LEDs

(四) Development Environment and Tool Chain
The following subsections describe the different development tools, their selection and interconnection.

(1) Selection of Development Tools/Compilers
A large number of 8051 compilers exist primarily for the DOS and Windows platforms. We have focused on two popular and regularly updated compilers: KEIL and the Small Device C Compiler (SDCC). SDCC is an open source project hosted on the Sourceforge website, whereas the KEIL C51 compiler is a commercial compiler and Integrated Development Environment (IDE). The debugger for SDCC (SDCDB) is still fairly experimental. The KEIL suite runs on the Windows platform, and has a good interactive debugger and simulator. KEIL and SDCC accepts roughly the same syntactical dialect, which eases the work of moving between the two compilers. During our work with SDCC and SDCDB we encountered numerous problems and bugs. SDCC 2.4.0 suddenly returned 'fatal compiler errors' with no apparent reason. After an update of SDCC from version 2.4.0 to 2.5.0 (most recent release) the error disappeared, the code compiled, but it still did not work correctly on the board. We also discovered a serious problem regarding sign bits in SDCC 2.5.0. SDCC made a type error when reading a 32 bit signed value. Apparently SDCC did not interpret the sign bit correctly, so a very small negative number was interpreted as a very large positive number as if the value was unsigned. KEIL however interprets the value correctly. The bug was submitted by us and fixed by the SDCC development team in just two days, but the timer module still does not work using SDCC. Our attempts using SDCC's debugger/simulator (SDCDB) was equally troublesome. SDCDB simply stopped at address 0, and running or stepping through the code returned us to the UNIX prompt with no error message. Without SDCDB, we had no debug possibility and we were forced to rethink the tool chain. We decided to substitute SDCC with the KEIL development kit. This gave us a working debug environment - with minimal change to the already produced code.

(1.1) Our Recommendation
In our experience the SDCC compiler and associated tools are not yet mature enough to support our development. We recommend pursuing other alternatives such as KEIL or other compiler suites.

We continue to mention SDCC in the remaining text, because we encourage the use of open source software and cross-platform development. We hope SDCC will prove an reliable alternative in the future.

(2)Tool Chain Overview
The following figure and sections are an overview of the current tool chain. The tool chain is based on TinyOS 1.x, NesC 1.1.3, avr-gcc 3.4.3, PERL v. 5.8.6 and SDCC 2.5.4 or KEIL C51 version 7.20. Each step in the tool chain will be explained in the section below.
MangleTinyOS script -----> app.c -----> app_mangled.c --------> app.hex ------> nRF24E1 NesC PERL SDCC/KEIL nRFPROG

(3)Description of the Tool Chain
The compilation of the TinyOS test program outputs two files, a 'main.exe' file and an 'app.c' file. The 'app.c' file contains all the needed code to run the TinyOS application. However the C code produced by NesC cannot be compiled for the 8051 platform directly. One solution could be to alter the syntax NesC produces for this specific platform, by modifying the source for NesC. However as a first step we chose not to make changes to NesC, but instead changed the content of the NesC output file 'app.c'. We inserted an extra step in the tool chain in the form of a mangle script. The mangle script works as the rope, tying the output from NesC to the input of SDCC or KEIL. After running the mangling script on the 'app.c' file we obtain an 'app_mangled.c' file which can be compiled by either SDCC or KEIL. This produces a hex file that is transferred to the chip by the nRFPROG software.

(4) Description of the Mangling Script
The mangling script is written in PERL, a commonly used general purpose scripting language with powerful pattern matching capabilities and extensive handling of regular expressions. The mangle script handles all currently known problems, and it can easily be expanded to handle additional alterations. To run the mangle script use the following syntax:

"./sdccMangleAppC.pl -KEIL -file build/mcs51/app.c > build/mcs51/app_mangled.c"

or
"./sdccMangleAppC.pl -SDCC -file build/mcs51/app.c > build/mcs51/app_mangled.c"

The 'sdccMangleAppC.pl' script handles a number of needed alterations:
? ? ? ? ? ? ?

it alters the SFR and SBIT declarations for SDCC and KEIL respectively it convert 64 bit data types to 32 bit it alters the reserved SDCC keyword data to _data it removes inlining directives it removes preprocessor line numbering it alters $ in identifiers to underscore it alters GCC interrupt declaration to SDCC syntax

Each of these alterations will be explained in the sections below.

(4.1) SFR and SBIT Declarations
In order to make TinyOS accept the 8051 special function registers (SFR) and special bit variables (SBIT), we have included them into the TinyOS 8051 platform folder as a 8051.h file. SFRs are located on an address dividable by 8, whereas an SBIT addresses a specific bit within these SFR. In order to make TinyOS accept the SFRs we have type defined them in the NesC code as:
typedef int sfr; sfr P0 __attribute((x80));

which is altered to
//typedef int sfr; sfr at 0x80 P0;

for the SDCC compiler in the mangle script and
sfr P0 = 0x80;

for the KEIL compiler and similar for the SBIT declarations.

NOTE: The SDCC website refers to a PERL script (keil2sdcc.pl - last updated June 2003) for translating SFR and SBIT declarations from KEIL to SDCC, but it produces code with illegal syntax, so either do not use it, or alter it to produce code with the right syntax.

(4.2) SDCC/KEIL Data Types
TinyOS and SDCC/KEIL do not support the same data types, so some alterations were needed to compile the code with SDCC and KEIL.
SDCC/KEIL supports the following data types:
? char (8 bits, 1 byte) ? short (16 bits, 2 bytes) ? int (16 bits, 2 bytes) ? long (32 bit, 4 bytes) ? float (32 bit, 4 bytes).

TinyOS supports an extra data type - 64 bit long long (u)int64_t. Since we are working with software that does not support this data type, on a very small hardware memory model, we decided to change the NesC 64 bit data types to 32 bit. This is done in the mangling script.

(4.3) Reserved Keywords in SDCC
A number of keywords are reserved in SDCC. Half of them represent a directive to the compiler, defining which memory segment on the nRF24E1 the specific lines of code refer to. To ensure that the developer does not break code by unintentionally and unaware of their effect use them fx. as a variable name in the NesC code, they need to be replaced or altered to something else, e.g. data to _data, before compiling for SDCC. Right now the mangle script only handles the reserved keyword data. None of the other keywords except SFR, SBIT and interrupt are currently in use in the code. This might pose as a problem to future work, but the mangle script can easily be expanded to handle misuse of the other keywords. However, if the code size increases significantly in the future, it might be nessecary to insert the keywords into the code, to support the architectures segmented memory model. Right now, everything is stored in the directly addressable memory segment, which is quite small.
The reserved keywords are:
? data / near

? xdata / far ? idata / pdata ? code ? bit ? SFR / SBIT ? interrupt ? critical

Variables declared with keyword storage class data/near will be allocated in the directly addressable portion of the internal RAM. This is the default option for the small memory model. Variables declared with storage class xdata/far will be placed in external RAM, which is default for the large memory model. Variables declared with keyword storage class idata will be allocated in the indirectly addressable portion of the internal RAM. The first 128 byte of idata physically access the same RAM as the data memory. The original 8051 had 128 byte idata, but nowadays most devices have 256 byte idata memory. Paged xdata access (pdata) is just as straightforward. The different memory segments that the keywords apply to, can be seen in the figure below. nRF24E1 Internal Data Memory Structure Overview:
IRAM SFR +---------------------+---------------------+ | | | FFh | Accessible by | Accessible by | | indirect | direct | | addressing only | addressing only | | | | 80h +---------------------+---------------------+ | | | Addressable by | | direct and | | indirect addressing | | | +---------------------+

FFh Upper 128 bytes 80h 7Fh Lower 128 bytes 00h

The prefered memory model can be defined in SDCC through CLI option --model-small or --model-large - the small memory model is default. In KEIL it can be changed through selecting it in the options pane for the target.

(4.4) Removal of inlining
NesC assumes that GCC is being used for the final compilation. GCC supports inline functions and can be made to optimize code quite aggressively, so the code generated by NesC does not need to be very efficient. Unfortunately SDCC does not support code inlining, so the inline statements have to be removed, when compiling for SDCC. Lines with the following format are affected: static inline void TOSH_sleep(void ); static __inline void TOSH_SET_RED_LED_PIN(void); __inline void__nesc_enable_interrupt(void); Lines with the noinline attribute is substituted with the #pragma NO_INLINE.

(4.5) Removal of Preprocessor Line Numbering
Also NesC produce preprocessor line number meta data, to allow the compiler to report error messages referring to the original code. We do not really need them for anything, so we filter them out to minimize the code size. It also eases the code reading significantly. If needed for debug purposes the regular expression in the mangle script which remove them can be commented out.

(4.6)Change $ in Identifiers
The SDCC compiler is very strict when it comes to valid symbols in identifiers. NesC produce GCC-code which inserts $ as a separator character in identifiers. We mangle the $ to two underscores in order to enable SDCC/KEIL to compile.

(4.7)Interrupt Vectors
The syntax for declaration of interrupt vectors are different in GCC and SDCC/KEIL. So we mangle the interrupt declaration: From: void __attribute((interrupt)) __vector_5(void) To: void __vector_5(void) interrupt 5 Additionally KEIL does not understand that the interrupt vector is defined previous to its use. So we remove the forward declaration of the vectors in the mangle script, when compiling for KEIL.

(五)TinyOS Modifications
TinyOS is based on modules with different levels of hardware abstraction. When porting TinyOS to a new platform, you change the underlying hardware dependencies in TinyOS, and you have to rebuild the modules bottom up. Hence, it has been necessary to modify a number of modules in TinyOS. The figure below shows the topological hierarchy of the TinyOS modules we have focused on. By far, most of the work has been done in the Hardware Presentation Layer (HPL), but certain changes also affected the higher abstractions, such as changes in interfaces and interrupt handling. Modified TinyOS modules overview:
+------------------------------------------------------------+ | TinyOS Application | +------------------------------------------------------------+ \/ /\ \/ /\ \/ /\ \/ /\ +----------+ +----------+ +---------+ +--------+ | Timer | | UART | | ADC | | LEDs | +----------+ +----------+ +---------+ +--------+ \/ /\ \/ /\ \/ /\ +----------+ +---------------------+ +---------+ | HPLClock | | HPLUART | | HPLADC | \/ +----------+ +---------------------+ +---------+ \/ /\ \/ \/ /\ \/ /\ +----------+ +--------+ +--------+ +---------+ +--------+ | Timer2 | | Timer1 | > | Serial | | Sensors | | Port | +----------+ +--------+ | Port | +---------+ +--------+ +--------+

App ----HAL ----HPL ----HW

The following sections describe the changes to the four groups of modules.

(1) HPLClock and related modules
The 8051 chip has three independent timer/counter circuits: Timer0, Timer1 and Timer2, which can run in various modes. Each timer/counter consists of a 16-bit register that is accessible to software as three SFRs (TL0/TH0, TL1/TH1 and TL2/TH2). Timer0 and Timer1 can be used as 13 or 16 bit timer/counter or as 8 bit counters with auto-reload. Timer2 is only capable of running as a 16 bit timer/counter, but can remain as such even in auto-reload mode. Reload is desirable for loading the clock with a start value.

We have chosen to use Timer2 for the clock module, since it gives our design a maximum clock interval of 49.15 ms at a 16 MHz system clock. Using a different timer circuit would limit the interval to 0.192 ms, which would result in a great deal of interrupts and consume processing power for administrational overhead.

(1.1)Timer
The Timer module (HAL) uses the HPLClock module to handle the hardware timing. These two modules communicate through the clock interface. However, the standard TinyOS clock interface is designed for an MCU with a more flexible prescaler, then the 8051 chip is equipped with. The 8051 is limited to a prescaler with a factor of 1/4 or 1/12 of the CPU clock frequency, whereas the TinyOS clock interface currently uses an 8 bit prescaler and an 8 bit timer. Because of the 8051s limited prescaler options, and the possibility to use a 16 bit timer/counter to compensate for the reduced prescaler options, we decided to widen the clock interface from 8 to 16 bit. We are using the factor 1/4 for the prescaler. The interface change has affected the following methods: result_t setRate(uint16_t interval, char scale) void setInterval(uint16_t value) void setNextInterval(uint16_t value) uint16_t getInterval() result_t setIntervalAndScale(uint16_t interval, uint8_t scale) uint16_t readCounter() void setCounter(uint16_t n)
See: Clock.h Clock.nc HPLClock.nc TimerM.nc TimerC.nc 8051.h

(2)HPLUART
The UART is depending on a timer to generate a baud rate for the serial port. The architecture only allows two of the three timers (Timer1 or Timer2), to act as such. Since Timer2 is already used by the clock module, this leaves only Timer1 available for the UART module. When using Timer1 as the baud rate generator, 5 different baud rates can be obtained: 1.20 KiB/s, 2.4 KiB/s, 4.8 KiB/s, 9.6 KiB/s or 19.2 KiB/s. We chose to use a baud rate of 19.2 KiB/s with 8 data bits, no parity and one stop bit, since this speed is commonly used and the fastest speed possible using this timer. We have also expanded the HPLUART interface to include a put2 method. This method is able to send more than one byte, by taking two pointers as arguments. These pointers refer to the first and last bytes to be sent. The HPLUART interrupt handler was also modified to take the multiple byte data into account.
See:

8051.h HPLUART.nc HPLUARTC.nc HPLUARTM.nc

(3)HPLADC
The TinyOS standard ADC interface was developed for the AVR which includes hardware functionality for repetitive sampling at a given interval. Implementing this functionality on the 8051, which does not support this in hardware, would require use of the last timer. We chose not to implement repetitive sampling, therefore the setSampleRate method currently has no use.
See: 8051.h ADCM.nc HPLADCC.nc HPLADCM.nc

(4)LEDS
TinyOS features three standard LEDs (Red, Green and Yellow), but the nRF24E1 evaluation board is not equipped with programmable LEDs so we used the general purpose ports (GPIO). The standard 8051 platform features four 8 bit GPIO, however the nRF24E1 evaluation board is only equipped with two ports: Port0 and Port1, where Port0 has eight bits and Port1 has only three bits. Intuitivel

相关文章:
Tinyos移植 0414
Tinyos移植 0414 Tinyos移植Tinyos移植隐藏>> 一、 NECS 语言学习一种支持组件化的编程语言,把组件化/模块化和基于事件驱动的执行模型 结合起来,这提高了应用开发的...
更多相关标签:
tinyos移植到msp430 | tinyos | tinyos教程 | tinyos官网 | tinyos安装教程 | tinyos tep 中文版 | tinyos视频教程 | ubuntu tinyos |