找回密码
 注册会员
更新自动建库工具PCB Footprint Expert 2024.04 Pro / Library Expert 破解版

[电源技术] CC2430的睡眠功能及唤醒方法

[复制链接]
admin 发表于 2014-4-2 09:32:31 | 显示全部楼层 |阅读模式

本文包含原理图、PCB、源代码、封装库、中英文PDF等资源

您需要 登录 才可以下载或查看,没有账号?注册会员

×
  一、承上启下
     这一篇,我们来讨论一下CC2430的睡眠功能及唤醒方法。在实际运用中的CC2430节点一般是靠电池来供电,因此对其功耗的控制显得至关重要。
     下面是摘自CC2430中文手册对CC2430的4种功耗模式的介绍:
   
     查看原图(大图)
     从上表中可看出,CC2430共有4种电源模式:PM0(完全清醒),PM1(有点瞌睡)、PM2(半醒半睡)、PM3(睡的很死)。越靠后,被关闭的功能越多,功耗也越来越低。它们之间的转化关系如下:
    20121112052541102341500.jpg   
     把 PM1、PM2 唤醒到PM0,有三种方式:复位、外部中断、睡眠定时器中断;但把 PM3 唤醒到 PM0,只有两种方式:复位、外部中断(这是因为在 PM3 下,所有振荡器均停止工作,睡眠定时器当然也熄火啦~)
     下面我们通过一个小实验,来介绍如何进入睡眠模式,以及如何唤醒到 PM0 状态。
     二、系统睡眠及中断唤醒实验
     (1)实验简介
     系统初始化,处于PM0
     → 进入PM1
     → 1s后被睡眠定时器唤醒为PM0
     → 进入PM2
     → 2s后被睡眠定时器唤醒为PM0
   
     → 进入PM3
     → 等待按键S1按下,触发外部中断,被唤醒为PM0 
     (2)程序流程图
    20121112052541133601501.jpg
     查看原图(大图)
     (注:上图中的圆角框表示系统的运行状况)
     (3)实验源码及剖析(下面的框框是可以点的~)
     头文件及宏定义
   /*
     实验说明:中断唤醒睡眠实验,分别介绍三种睡眠模式下的唤醒
   */
   #include 
   #define LED_ON 0
   #define LED_OFF 1
   #define led1 P1_0     
   #define led2 P1_1     
   #define led3 P1_2     
   #define led4 P1_3  
     子函数
   /*系统时钟初始化
   -------------------------------------------------------*/
   void xtal_init(void)
   {
    SLEEP &= ~0x04;       //都上电
    while(!(SLEEP & 0x40));   //晶体振荡器开启且稳定
    CLKCON &= ~0x47;     //选择32MHz 晶体振荡器
    SLEEP |= 0x04;
   }
   /*LED初始化
   -------------------------------------------------------*/
   void led_init(void)
   {
    P1SEL = 0x00;     //P1为普通 I/O 口
    P1DIR |= 0x0F;     //P1.0 P1.1 P1.2 P1.3 输出
    led1 = LED_OFF;     //关闭所有LED
    led2 = LED_OFF;
    led3 = LED_OFF;
    led4 = LED_OFF;
   }
   /*外部中断初始化
   -------------------------------------------------------*/
   void io_init(void)
   {
     P0INP &= ~0X02;  //P0.1有上拉、下拉
     EA = 1;      //总中断允许
     IEN1 |= 0X20;  // P0IE = 1,P0中断使能
     PICTL |= 0X09;  //P0.1允许中断,下降沿触发
     P0IFG &= ~0x02;  //P0.1中断标志清0
   }
   /*睡眠定时器中断初始化
   -------------------------------------------------------*/
   void sleepTimer_init(void)
   {
    STIF=0;  //睡眠定时器中断标志清0
    STIE=1;  //开睡眠定时器中断
    EA=1;   //开总中断
   }
   /*设置睡眠定时器的定时间隔
   -------------------------------------------------------*/
   void setSleepTimer(unsigned int sec)
   {
    unsigned long sleepTimer = 0;
    sleepTimer |= ST0;           //取得目前的睡眠定时器的计数值
    sleepTimer |= (unsigned long)ST1 &lt;< 8;
    sleepTimer |= (unsigned long)ST2 << 16;
    sleepTimer += ((unsigned long)sec * (unsigned long)32768);  //加上所需要的定时时长
    ST2 = (unsigned char)(sleepTimer >> 16);  //设置睡眠定时器的比较值
    ST1 = (unsigned char)(sleepTimer >> 8); 
    ST0 = (unsigned char)sleepTimer;
   }
   /*选择电源模式
   -------------------------------------------------------*/
   void PowerMode(unsigned char mode)
   {
    if(mode<4)
    {
     SLEEP &= 0xfc;    //将SLEEP.MODE清0
     SLEEP |= mode;    //选择电源模式
     PCON |= 0x01;    //启用此电源模式
    }
   }
   /*延时函数
   -------------------------------------------------------*/
   void Delay(unsigned int n)
   {
    unsigned int i,j;
    for(i=0;i
     for(j=0;j<1000;j++);
   }
   
   主函数
   /*主函数
   -------------------------------------------------------*/
   void main(void)
   { 
    xtal_init();     
    led_init();     
    //PM0状态,亮灯并延时
    led1 = LED_ON;     //亮LED1,表示统在PM0模式工作
    Delay(10);
    //PM1状态,灭灯
    setSleepTimer(1);   //设置睡眠定时器的定时间隔为1s
    sleepTimer_init();   //开睡眠定时器中断
    led1 = LED_OFF;
    PowerMode(1);     //设置电源模式为PM1
    //1s后,由PM1进入PM0,亮灯并延时
    led1 = LED_ON;
    Delay(50);
    //PM2,灭灯
    setSleepTimer(2);   //设置睡眠定时器的定时间隔为2s
    led1 = LED_OFF;
    PowerMode(2);     //设置电源模式为PM2
    //2s后,由PM2进入PM0,亮灯并延时
    led1=0;
    Delay(50); 
    //PM3,灭灯 
    io_init();       //初始化外部中断
    led1 = LED_OFF;
    PowerMode(3);     //设置电源模式为PM3
    //当外部中断发生时,由PM3进入PM0,亮灯
    led1 = LED_ON;
    while(1);
   }
     中断服务程序
   /*外部中断服务程序
   -------------------------------------------------------*/
   #pragma vector = P0INT_VECTOR
   __interrupt void P0_ISR(void)
   {
    EA = 0;             //关中断
    Delay(50);
    if((P0IFG & 0x02 ) >0 )     //按键中断
    {
     P0IFG &= ~0x02;        //P0.1中断标志清0
    }
    P0IF = 0;            //P0中断标志清0
    EA = 1;             //开中断
   }
   /*睡眠定时器中断服务程序
   -------------------------------------------------------*/
   #pragma vector= ST_VECTOR
   __interrupt void sleepTimer_IRQ(void)
   {
    EA=0;   //关中断
    STIF=0;  //睡眠定时器中断标志清0
    EA=1;   //开中断
   }
   
     关于如何使用睡眠定时器来唤醒系统,可以总结为如下流程:开睡眠定时器中断 → 设置睡眠定时器的定时间隔 → 设置电源模式
     (注:“设置睡眠定时器的定时间隔”这一步一定要在“设置电源模式”之前,因为进入睡眠后系统就不会继续执行程序了)
     接下来,我们重点关注一下设置睡眠定时器定时间隔的子函数:setSleepTimer
     首先对睡眠定时器简单的介绍一下:它是运行于32.768kHz的24位定时器,当系统运行在除了PM3之外的所有的电源模式下,睡眠定时器都会不间断运行。
     睡眠定时器使用的寄存器有:ST0,ST1,ST2。下面是摘自CC2430中文手册对其功能的详细介绍:
    20121112052541180491502.jpg
     查看原图(大图)
    20121112052541211751503.jpg
     查看原图(大图)
    20121112052541243011504.jpg
     查看原图(大图)
   
     可以看出,它们的功能包括两方面:读,写。
     读:用于读取当前定时器的计数值,读的顺序必须遵循:读ST0 → 读ST1 → 读ST2
     写:用于设置定时器的比较值(当定时器的计数值=比较值时,产生中断),写的顺序必须遵循:写ST2 → 写ST1 → 写ST0
     OK,接下来我们结合源码来讲解:
     (1)首先,定义一个unsigned long型变量(32位)sleepTimer,用于接收睡眠定时器的当前计数值:
    unsigned long sleepTimer = 0;
    sleepTimer |= ST0;           //取得目前的睡眠定时器的计数值
    sleepTimer |= (unsigned long)ST1 << 8;
    sleepTimer |= (unsigned long)ST2 << 16;
     (2)然后加上所需要的定时间隔:
    sleepTimer += ((unsigned long)sec * (unsigned long)32768);  //加上所需要的定时时长
     此处需要稍微解释一下:
     为什么1s就代表着32768?因为定时器是工作在32.768kHz之下,所以定时器每加1,需耗时1/32768 s;加32768,就需要1s;
     (3)最后将sleepTimer的值作为定时器的比较值:
    ST2 = (unsigned char)(sleepTimer >> 16);  //设置睡眠定时器的比较值
    ST1 = (unsigned char)(sleepTimer >> 8); 
    ST0 = (unsigned char)sleepTimer;
     这样,就可成功设置定时器的定时周期啦~
     (注:至于源码的其他部分,相信结合着详细的注释,大家可以轻松看懂,在此不作赘述)
     (4)实验结果
     运行程序,观察LED1,现象为:LED1闪烁(即亮->灭1次),1s后再次闪烁,2s后再次闪烁,然后保持熄灭状态,然后按下S1,LED1亮。
   
     实验现象和预期完全吻合,Over~
     三、结语
     吁~ 抽出2天的课余时间,终于搞定了这篇日志。真的发现写博,特别是写一篇“读者友好”的博文,的确是一项体力活:严谨性、美观性、逻辑性...都是要考虑的事儿。
     每次贴代码都嫌太长,但又不太愿意使用博客园自带的折叠工具。因此在本篇博文中,笔者试探性的加入了一些JQuery元素,实现了代码的平滑折叠,还是有小小的成就感嘀,呵呵(JQuery菜鸟,高手勿笑~)。但当局者迷,我并不知这样做是否真正增强了文章的可读性,欢迎读者朋友作出评论 :)
     这一个月,笔者真正决定在博客园扎下根来,于是花费了大量的课余时间在博文的写作上。初次写博,虽然评论很少,但大部分日志都有500以上的点击率,也算是对我的小小的鼓励!在博客园发表关于单片机的内容,的确需要勇气,不过我会坚持写下去的~
     从开始到现在的九篇博文,重点是CC2430芯片上的基本硬件模块的运用。到此为止,我们基本上把CC2430上的大部分外设都过了一遍,但是还有比如Flash存取、随机数发生器、AES协处理器、射频通信等,还没涉及到。不过Zigbee之旅并未结束,笔者打算在下一个主题(Z-Stack 协议的实现)中,再来有选择性的把这些遗漏之处补齐。
     下一篇博文,笔者打算以一个稍带综合性与扩展性的小实验--“温度监测系统”来结束Zigbee的首次旅行,讲解一下如何去综合运用前面学到的知识点。
     其实根本没有资格称“讲解”,作为一个初学者,笔者只希望在写博的过程中与读者互勉,共同进步!
fds 发表于 2014-9-27 14:41:01 | 显示全部楼层
{:soso_e179:}
*滑块验证:
您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

QQ|手机版|MCU资讯论坛 ( 京ICP备18035221号-2 )|网站地图

GMT+8, 2024-12-22 01:51 , Processed in 0.059930 second(s), 11 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表