引言
“预定睡眠-随机唤醒”是嵌入式系统设计中常用的方法。例如,在完成规定的数据发送或接收任务后,MCU立即进入休眠以节约电力,这就是“预定睡眠”的含义;当有新的数据发送和接收任务时,通过各种方式唤醒MCU执行前述任务,这就是“随机唤醒”的含义。在实际工程中,完成该功能主要使用软件,首先整合系统功能,执行完功能模块后马上执行预定的睡眠指令,而唤醒功能则直接在中断服务程序中体现。很多教科书、论坛中广泛介绍的也都是这种方法,该方法能够满足一般工程设计的需要。但“预定睡眠”在某些工程中并不适合使用,例如在无线传感网络构成的系统中,某个节点或分系统的睡眠时机不是由自己决定的,而是远程随机控制的。在这种情况下,睡眠指令在节点或分系统执行自己任务的过程中随机发生,同样,唤醒指令发出也是随机的,通常为节省系统的硬件开支并提高其可靠性,发出睡眠和唤醒指令使用同一个信号,所以系统软件的睡眠指令就不能固定在系统功能程序的预定位置,甚至不能在功能主程序及其调用的函数中出现。
1 目前Arduino睡眠与唤醒的方法
在Arduino IDE中控制Arduino睡眠的工具主要有Enerlib和LowPower两种开源类库,前一种已被Arduino开源库收录,这两种类库专门完成 Arduino的睡眠控制。Arduino的睡眠效果和典型应用在参考文献中有详细的介绍,但也未涉及到随机进入睡眠的方法。
1.1 使用Enerlib类库的实现方法
Enerlib类库提供的Example代码说明了如何使用该类库的功能函数,为阅读方便起见,笔者添加了行号标识和中文注释,如下所列:
可以看出,进入睡眠的代码在仅执行一次的初始化模块void setup()中,表明系统启动后做完初始化工作即进入睡眠。代码的19~23行给出了5种睡眠方式,在实际应用中选择一种适合的方式即可。系统的唤醒在 Int0外部中断服务程序中,Enerlib类库提供了一个只能在中断服务程序中使用的WasSleeping()函数,以判定系统目前的“睡眠/醒着” 的状态,该程序执行一次睡眠,然后随机唤醒后不能再次进入睡眠状态。这是一个典型的“预定睡眠一随机唤醒”实例。
1.2 使用LowPower类库的实现方法
LowPower类库提供的实例代码如下(行号和中文注释由笔者添加):
可以看出,进入睡眠的代码在主程序循环体void loop()内,系统启动后即进入休眠状态,一旦有外部中断即唤醒,唤醒后执行完主程序功能后再次进入睡眠,这也是典型的“预定睡眠-随机唤醒”实例。
因ATMega单片机在外部中断模式下会忽略引起中断引脚的数据方向,所以该例程第10行“pinMode(wake UpPin,INPUT);”没有必要。
以上两种睡眠与唤醒实例均实现不了随机进入睡眠的功能要求。
2 随机睡眠与唤醒的方法
如果可以实现在系统工作过程中随时中止工作进入睡眠(如为了节省电力而远程控制终止节点的监测),然后在适当的时机再唤醒系统继续工作,就是典型的“随机睡
眠-随机唤醒”功能。
2.1 随机进入睡眠与唤醒的方法
在以上两个实例中,一个是将睡眠指令放于初始化模块,实现一次性主动睡眠;另一个是将睡眠指令放在主程序循环体中,在执行完预定功能后主动进入睡眠,二者均有外部随机唤醒。如果要实现通过外部指令(中断)随机进入睡眠,睡眠指令首先不能放在初始化模块void setup()中,因为这样仅会引起一次睡眠;如果放到主程序循环体void loop()中,则必须在由“睡眠/唤醒”指令引起的中断服务程序中放置相应的标志,然后再在主程序中判断这个标志,势必增加相应的软件开支,而且程序的结构也不明晰。
因此,实现随机进入睡眠的最简单的方法就是将睡眠指令直接置于中断服务程序当中,当睡眠指令发出后,执行中断服务程序进入睡眠;当唤醒指令发出后,只要进入了中断服务程序即可唤醒。但是在同一个中断服务程序中实现该功能,需要透彻地了解相应单片机的中断处理机制以及所用的库函数对中断的处理方法。
2.2 Arduino的外部中断
构成Arduino的核心处理器主要是Atmel的AVR ATmega系列MCU(如ATmega328),常用的实验平台有UNO、NANO、Pro、Pro Mini、Micro、Lilypad、Leonardo、Duemilanove等,因此Arduino的睡眠与唤醒的控制实质是对MCU的控制,也就是控制ATnaega328睡眠与唤醒。
实现随机睡眠与唤醒的实验平台是Arduino Pro Mini3.3 V、8 MHz/ATInega328,ATrnega328的外部中断机制主要有:①RESET具有最高的优先级,第二个为INT0;②任一中断发生时全局中断使能位被清零,从而禁止了所有其他的中断;③退出中断后,总是回到主程序并至少执行一条指令才可以去执行其他被挂起的中断;④如果选择了边沿触发方式或电平变化触发方式,那么持续时间大于一个时钟周期的脉冲将触发中断,过短的脉冲则不能保证触发中断,如果选择低电平触发方式,那么低电平必须保持到当前指令执行完成;⑤外部中断通过引脚INT0、INT1与INT2触发,只要使能中断且电平发生了合适的变化,即使引脚INT0~2配置为输出,中断也会触发;⑥若要求INT0与INT1在信号下降沿或上升沿触发,则I/O时钟必须工作;⑦通过电平方式触发中断,在将MCU从掉电模式唤醒时,要保证电平保持一定的时间,以降低MCU对噪声的敏感程度;⑧中断响应时间最少为4个时钟周期,若中断发生时MCU处于休眠模式,中断响应时间还需增加4个时钟周期,此外还要考虑到不同的休眠模式所需要的启动时间。
2.3 Enerlib和LowPower类库的中断处理方式
两个类库中,Enerlib的5种睡眠指令为PowerDown()、Standby()、PowerSave()、SleepADC()、Idle(),LowPower的5种睡眠方式为ADC_OFF、BOD_OFF、IDLE_OFF、STANDBY EXT_STANDBY,除了对各种不同设备进行关断外,对系统中断的处理方式均相同,即睡眠前后均不对中断的使能进行处理,而且均提供了打开和关闭终端使能的函数。这样,打开和关闭中断的时机就完全交给了开发者。
3 随机睡眠与唤醒的实现与结果
使用Arduino自带的LED(Pin13驱动)来检验随机睡眠与唤醒,睡眠发生时LED闪烁0.1 s,表示接收到睡眠命令,然后熄灭LED进入睡眠;唤醒发生时LED闪烁0.1 s,表示接收到唤醒命令,然后LED以1 Hz频率闪烁工作。以下代码笔者添加了行号标识和中文注释。
将Arduino Pro Mini的Pin2(D2)通过一个10 KΩ电阻连接到Vcc,然后在Pin2对地连接一个按键开关,下载完程序后上电,系统LED进入1 Hz闪亮模式,按一下按键,LED闪烁0.1 s,然后进入睡眠;再按一下按键,LED闪烁0.1 s,然后系统LED进入1 Hz闪烁模式,如此重复。
结语
Arduino的核心--ATmega系列单片机,以其RISC的可靠结构、主控板、外围模块等硬件的规范设计,具有较高的可靠性。Arduino之所以风靡全球,除了开源的硬件之外,更重要的是配套了开发软件的IDE以及众多的类库,同时IDE集成、定义了编程方法,底层的Bootloader就像是个微型操作系统,而不像使用Keil C开发单片机软件那样自己来组织程序的结构、编写驱动程序,Arduino的软件和硬件设计使得编程、供电、下载一体化。由于其遵循开源共享的宗旨,相关类库会越来越丰富,也正是这个IDE将软件开发工具和实际硬件隔离,使得软件开发变得简单、快速,开发者可以专注于方案的实施。当然也由于软件底层与功能模块硬件的隔离,使得开发者越来越依赖类库,这一循环导致开发者过分依赖类库而不去探究问题的实质,最后导致问题得不到解决或者退而更换主控系统硬件,从而妨碍了Arduino更大范围的应用。本文所叙述的例程可以根据实际工程要求做适当修改而直接应用,具有一定的参考价值。