摘要
本文档介绍TinyOS 如何管理微控制器的低功耗状态。
简介
微控制器通常有几个电源状态,它们具有不同的电源功率、唤醒延迟和外围支持。微
控制器在能满足程序需求的前提下,应当一直处于能耗尽量低的电源状态。为了准确地决
断处理器该处于哪个状态,需要有大量外围子系统和外围设备的电源状态信息。另外,状
态转换是很常见的。每当微控制器处理一个中断时,它从低功耗状态切换到激活状态;当
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):
| LPM0
| LPM1
| LPM2
|
LPM0
| LPM0
| LPM0
| LPM0
|
LPM1
| LPM0
| LPM1
| LPM1
|
LPM2
| LPM0
| LPM1
| LPM2
|
LPM2 中,HR0 是激活的而HR1 是关闭的,结合函数如下:
| LPM0
| LPM1
| LPM2
|
LPM0
| LPM0
| LPM0
| LPM0
|
LPM1
| LPM0
| LPM1
| LPM0
|
LPM2
| LPM0
| LPM0
| LPM2
|
电源状态覆盖
当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。
|