zigbee的低功耗、近距离无线传输的特点使得其在一众近距离无线传输方案中备受青睐。而zigbee低功耗优势是通过根据不同工况选择运行在不同的运行模式(供电模式)实现的,因此,掌握zigbee的系统电源管理与睡眠唤醒的相关知识,是对zigbee进行优势开发的重中之重。本文将根据开发经验和网络相关资料进行详解解读,也欢迎指正。(声明:本文为总结性文章,内容有大量引用其他博主的内容,末尾有参考链接声明)
一、zigbee的电源管理模式
zigbee cc2530是一种集成了Zigbee通信协议的芯片,通过不同的运行模式(电源模式)可以实现低功耗的操作。运行模式有主动模式、空闲模式和供电模式 1、2 和 3(PM1-PM3),各自特点如下图:
(1)主动模式:即完全功能的运行模式,也是复位设备默认运行模式,数字稳压器为开启状态,CPU、外设和 RF 收发器都是活动的,高 频 晶 振 ( 16M 或 者 32M ) 和 低 频 晶 振 ( 32.768KRCOSC/XOSC )全部工作, 数字处理模块正常工作。在该模式下cc2530可以充当Zigbee网络中的协调器(Coordinator)、路由器(Router)或终端设备(End Device)。
(2)空闲模式:除了 CPU 内核停止运行(即空闲),其他和主动模式一样。在主动模式(SLEEPCMD.MODE = 0x00)通过使能 PCON.IDLE 位,CPU 内核就停止运行,进入空闲模式。所有其他外设将正常工作,且 CPU 内核将被任何使能的中断唤醒(从空闲模式转换到主动模式)。
(3)PM1模式:低功耗模式1(Power Mode 1)。在PM1模式下,稳压器的数字部分开启。32 MHz XOSC 和 16 MHz RCOSC 都不运行。32 kHz RCOSC 或 32 kHz XOSC运行。复位、外部中断或睡眠定时器过期时系统将转到主动模式。该模式下cc2530的主要功能是接收和处理数据,但其无线电部分处于关闭状态,以降低功耗。这种模式适用于需要在较短时间内接收数据的应用场景。
(4)PM2模式:低功耗模式2(Power Mode 2)。在PM2模式下,稳压器的数字内核关闭。32 MHz XOSC 和 16 MHz RCOSC 都不运行。32kHz RCOSC 或 32 kHz XOSC运行。复位、外部中断或睡眠定时器过期时系统将转到主动模式。该模式下cc2530,I/O 引脚保留在进入PM2 之前设置的I/O 模式和输出值,无线电部分处于关闭状态,同时大部分的功能也被禁用,以进一步减少功耗。这种模式适用于需要长时间处于待机状态,但偶尔需要唤醒进行一些处理的应用场景。
(5)PM3模式:休眠模式(Power Mode 3)。在PM3模式下,获得最运行低功耗,稳压器的数字内核关闭,所有的振荡器都不运行,复位或外部中断时系统将转到主动模式。该模式下cc2530,I/O 引脚保留进入PM3 之前设置的I/O 模式和输出值,其他所有功能都被禁用,包括无线电部分。在这种模式下,cc2530需要外部触发来唤醒并重新启动其功能。这种模式适用于需要极低功耗且只偶尔需要进行通信的应用场景。
二、如何切换电源管理
上一节,我们已经了解zigbee cc2530具有五中工作模式,各自拥有不同的特点,根据运行工况进行选择切换,实现低功耗方案设计。那么如何进行电源模式的切换管理,供电模式通过使用 SLEEPCMD 控制寄存器的 MODE 位和 PCON寄存器的IDLE 位来选择。
1、控制寄存器
首先,我们认识系统电源管理的两个寄存器:
2、切换电源模式与唤醒
进行电源模式的切换管理很简单,分两步走:
(1)首先,通过使用SLEEPCMD 控制寄存器的MODE 位选择系统电源模式;
(2)使能SFR 寄存器的PCON.IDLE 位(设置为1),进入SLEEPCMD.MODE 所选的模式。
SLEEPCMD |= i; // 设置系统睡眠模式, i=0,1,2,3
PCON = 0x01; // 进入睡眠模式 ,通过中断打断
PCON = 0x00; // 系统唤醒 ,通过中断打断
示例函数:
void SysPowerMode(char mode)
{
if((mode > 0)&&(mode < 4))
{
SLEEPCMD |= mode; //设置系统睡眠模式
PCON = 0x01; //进入睡眠模式 ,通过中断唤醒
}
else
PCON = 0x00; //通过中断唤醒系统
}
三、案例讲解
1、中断唤醒
初始设置系统为PM3电源模式,然后通过外部中断的方式,使得电源模式恢复(唤醒)主动模式。实验现象:LED1闪烁3次后进入睡眠状态,通过按下按键S1产生外部中断进行唤醒,重新进入主动模式,LED1闪烁3次后又进入睡眠状态。
//该代码基于作者的zigbee设备编写
//移植使用注意修改LED和按键的对应节点
#include <iocc2530.h>
#define LED1 P1_0
// 初始化LED1对应P1_0
void initLED1_P1_0()
{
P1SEL &= ~0x01;
P1DIR |= 0x01;
P1_0 = 1;
}
//初始化一个按键KEY1中断
void initInterrupt()
{
P0SEL &= 0xfd; //中断口是P0_0
P0DIR &= 0xfd;
P0INP &= 0xfd;
P2INP &= 0xdf;
EA = 1;
P0IE = 1;
P0IEN |= 0x02;
PICTL |= 0x01;
}
//延时函数
void delayms( unsigned int a ) //@16MHz
{
unsigned char i, j;
for( int s = 0; s < a; s++ )
{
i = 3;
j = 148;
do
{
while( --j );
}
while( --i );
}
}
// 设置系统工作模式函数
void SysPowerMode( char mode )
{
if( ( mode > 0 ) && ( mode < 4 ) )
{
SLEEPCMD |= mode; //设置系统睡眠模式
PCON = 0x01; //进入睡眠模式 ,通过中断唤醒
}
else
{
PCON = 0x00; //通过中断唤醒系统
}
}
#pragma vector=P0INT_VECTOR
__interrupt void P0_INT( void )
{
if( P0IFG & 0x02 )
{
SysPowerMode( 4 );
}
P0IFG = 0;
P0IF = 0;
}
void main()
{
char count = 0;
initInterrupt(); //初始化一个按键中断
initLED1_P1_0(); // 初始化一个LED
while( 1 )
{
if( count > 10 ) {
count = 0;
}
LED1 = ~LED1;
if( ++count >= 6 )
{
count = 0;
SysPowerMode( 3 ); //3次闪烁后进入睡眠状态PM3,等待按键S1中断唤醒
}
delayms( 800 );
}
}
2、睡眠定时中断唤醒
电源模式恢复(唤醒)为主动模式的另一种方法是定时中断唤醒,需要使用到睡眠定时器。我们先来了解睡眠定时器。这里简单只讲解睡眠定时器的比较模式用法,更详细讲解阅读参考链接(3)。
2.1 睡眠定时器
睡眠定时器用于设置系统进入和退出低功耗睡眠模式之间的周期。还用于当系统进入低功耗模式后,维持 MAC 定时器(T2)的定时。其特性如下:
(1)长达 24 位定时计数器,运行在 32.768KHZ 的工作频率;
(2)24 位的比较器具有中断和 DMA 触发功能在 PM2 低功耗模式下运行(PM3模式下振荡器全部被关闭,无法使用睡眠定时器唤醒);
(3)定时器的当前值可以从 SFR 寄存器 ST2:ST1:ST0 中读取
2.2 睡眠定时器寄存器
睡眠定时器(T2)工作模式仍然有捕获和比较两种。通过如下寄存器控制。
2.3 睡眠定时器比较模式
睡眠定时器在复位之后立即启动,如果没有中断就继续运行。当定时器的值等于 24 位比较器的值,就发生一次定时器比较。通过写入寄存器 ST2:ST1:ST0 来设置比较值。
寄存器 ST0、ST1、ST2分别对应着定时器的值等于 24 位比较器的低、中、高位,如何写值?定义一个32位的变量,通过移位的方式进行,如下:
//在给STx赋值是都是需要最后给ST0赋值,在读取STx值时都是要最先读取ST0的值
void Set_ST_Period(uint sec)
{
UINT32 sleepTimer = 0; //定义一个32位的变量
//注意:读取ST2:ST1:ST0的值,建议读取顺序ST0、ST1、ST2
sleepTimer |= ST0; //读取ST0的值,写入sleepTimer的[0:7]位
sleepTimer |= (UINT32)ST1 << 8; //读取ST1的值,写入sleepTimer的[8:15]位
sleepTimer |= (UINT32)ST2 << 16; //读取ST2的值,写入sleepTimer的[16:24]位
sleepTimer += ((UINT32)sec * (UINT32)32768);
//当 STLOAD.LDRDY 是 1 写入 ST0 后,会发起加载最新的比较值,ST2、ST1 和 ST0 寄存器的最新的值。
//注意:写入ST2:ST1:ST0的值,写入顺序必须为ST2、ST1、ST0
ST2 = (UINT8)(sleepTimer >> 16); //将sleepTimer的[16:24]位的值,写入ST2
ST1 = (UINT8)(sleepTimer >> 8); //将sleepTimer的[8:15]位的值,写入ST1
ST0 = (UINT8) sleepTimer; //将sleepTimer的[0:7]位的值,写入ST0
}
2.4睡眠定时器唤醒
通过设置定时器在特定时间内进行唤醒,重新进入工作模式,每次唤醒LED2闪烁3下。
#include <ioCC2530.h>
#define uint unsigned int
#define uchar unsigned char
#define UINT8 unsigned char
#define UINT16 unsigned int
#define UINT32 unsigned long
//定义控制LED灯的端口
#define LED1 P1_0 //定义LED2为P1.1口控制
//函数声明
void Delayms(uint); //延时函数
void InitLed(void); //初始化P1口
void SysPowerMode(uchar sel); //系统工作模式
/****************************
//延时函数
*****************************/
void Delayms(uint xms) //i=xms 即延时i毫秒
{
uint i,j;
for(i=xms;i>0;i--)
for(j=587;j>0;j--);
}
/****************************
//初始化LED1对应P1_0
*****************************/
void initLED1_P1_0()
{
P1SEL &= ~0x01;
P1DIR |= 0x01;
P1_0 = 1;
}
/****************************************************************
系统工作模式选择函数
* para1 0 1 2 3
* mode PM0 PM1 PM2 PM3
****************************************************************/
void SysPowerMode(uchar mode)
{
uchar i,j;
i = mode;
if(mode<4)
{
SLEEPCMD |= i; // 设置系统睡眠模式
for(j=0;j<4;j++);
PCON = 0x01; // 进入睡眠模式 ,通过中断打断
}
else
{
PCON = 0x00; // 系统唤醒 ,通过中断打断
}
}
/*****************************************
//初始化 Sleep Timer (设定后经过指定时间自行唤醒)
*****************************************/
void Init_SLEEP_TIMER(void)
{
ST2 = 0X00;
ST1 = 0X00;
ST0 = 0X00;
EA = 1; //总开中断
STIE = 1; //睡眠定时器中断使能
STIF = 0; //睡眠定时器中断标志清零
}
/*********************************************************************
//设置睡眠时间
*********************************************************************/
void Set_ST_Period(uint sec)
{
UINT32 sleepTimer = 0;
sleepTimer |= ST0;
sleepTimer |= (UINT32)ST1 << 8;
sleepTimer |= (UINT32)ST2 << 16;
sleepTimer += ((UINT32)sec * (UINT32)32768);
ST2 = (UINT8)(sleepTimer >> 16);
ST1 = (UINT8)(sleepTimer >> 8);
ST0 = (UINT8) sleepTimer;
}
/***************************
//主函数
***************************/
void main(void)
{
uchar i;
initLED1_P1_0(); //调用初始化LED1函数
Init_SLEEP_TIMER(); //初始化SLEEPTIMER
while(1)
{
for(i=0;i<6;i++) //闪烁3下
{
LED1=~LED1;
Delayms(200);
}
Set_ST_Period(3); //重新进入睡眠模式
SysPowerMode(2); //进入PM2低频晶振模式
}
}
//中断唤醒
#pragma vector = ST_VECTOR
__interrupt void ST_ISR(void)
{
STIF = 0; //清标志位
SysPowerMode(4); //进入正常工作模式
}
main函数开始初始化LED和睡眠定时器,然后在while大循环里面先LED闪烁3次,然后调用睡眠周期设置设置3s,此时睡眠定时器开始计时,而main函数由于执行到设置系统进入PM2模式而休眠,当5s后触发休眠定时器中断,在中断中设置系统进入active模式。
参考链接:
(1)lesson4(Zigbee补充2)CC2530睡眠唤醒(详解)_cc2530休眠与唤醒-CSDN博客
(2)ZigBee基础实验(九)--定时器二_zigbee看门狗的唤醒定时器唤醒定时器彭20控制-CSDN博客
(3)ZigBee基础实验(八)--定时器一_zigbee定时器实验代码-CSDN博客