一、承上啟下
這一篇,我們來討論一下CC2430的睡眠功能及喚醒方法。在實際運(yùn)用中的CC2430節(jié)點(diǎn)一般是靠電池來供電,因此對其功耗的控制顯得至關(guān)重要。
下面是摘自CC2430中文手冊對CC2430的4種功耗模式的介紹:
查看原圖(大圖)
從上表中可看出,CC2430共有4種電源模式:PM0(完全清醒),PM1(有點(diǎn)瞌睡)、PM2(半醒半睡)、PM3(睡的很死)。越靠后,被關(guān)閉的功能越多,功耗也越來越低。它們之間的轉(zhuǎn)化關(guān)系如下:
把 PM1、PM2 喚醒到PM0,有三種方式:復(fù)位、外部中斷、睡眠定時器中斷;但把 PM3 喚醒到 PM0,只有兩種方式:復(fù)位、外部中斷(這是因為在 PM3 下,所有振蕩器均停止工作,睡眠定時器當(dāng)然也熄火啦~)
下面我們通過一個小實驗,來介紹如何進(jìn)入睡眠模式,以及如何喚醒到 PM0 狀態(tài)。
二、系統(tǒng)睡眠及中斷喚醒實驗
?。?)實驗簡介
系統(tǒng)初始化,處于PM0
→ 進(jìn)入PM1
→ 1s后被睡眠定時器喚醒為PM0
→ 進(jìn)入PM2
→ 2s后被睡眠定時器喚醒為PM0
→ 進(jìn)入PM3
→ 等待按鍵S1按下,觸發(fā)外部中斷,被喚醒為PM0
?。?)程序流程圖
查看原圖(大圖)
(注:上圖中的圓角框表示系統(tǒng)的運(yùn)行狀況)
?。?)實驗源碼及剖析(下面的框框是可以點(diǎn)的~)
頭文件及宏定義
/*
實驗說明:中斷喚醒睡眠實驗,分別介紹三種睡眠模式下的喚醒
*/
#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
子函數(shù)
/*系統(tǒng)時鐘初始化
-------------------------------------------------------*/
void xtal_init(void)
{
SLEEP &= ~0x04; //都上電
while(!(SLEEP & 0x40)); //晶體振蕩器開啟且穩(wěn)定
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; //關(guān)閉所有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允許中斷,下降沿觸發(fā)
P0IFG &= ~0x02; //P0.1中斷標(biāo)志清0
}
/*睡眠定時器中斷初始化
-------------------------------------------------------*/
void sleepTimer_init(void)
{
STIF=0; //睡眠定時器中斷標(biāo)志清0
STIE=1; //開睡眠定時器中斷
EA=1; //開總中斷
}
/*設(shè)置睡眠定時器的定時間隔
-------------------------------------------------------*/
void setSleepTimer(unsigned int sec)
{
unsigned long sleepTimer = 0;
sleepTimer |= ST0; //取得目前的睡眠定時器的計數(shù)值
sleepTimer |= (unsigned long)ST1 << 8;
sleepTimer |= (unsigned long)ST2 << 16;
sleepTimer += ((unsigned long)sec * (unsigned long)32768); //加上所需要的定時時長
ST2 = (unsigned char)(sleepTimer >> 16); //設(shè)置睡眠定時器的比較值
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; //啟用此電源模式
}
}
/*延時函數(shù)
-------------------------------------------------------*/
void Delay(unsigned int n)
{
unsigned int i,j;
for(i=0;i
for(j=0;j<1000;j++);
}
主函數(shù)
/*主函數(shù)
-------------------------------------------------------*/
void main(void)
{
xtal_init();
led_init();
//PM0狀態(tài),亮燈并延時
led1 = LED_ON; //亮LED1,表示統(tǒng)在PM0模式工作
Delay(10);
//PM1狀態(tài),滅燈
setSleepTimer(1); //設(shè)置睡眠定時器的定時間隔為1s
sleepTimer_init(); //開睡眠定時器中斷
led1 = LED_OFF;
PowerMode(1); //設(shè)置電源模式為PM1
//1s后,由PM1進(jìn)入PM0,亮燈并延時
led1 = LED_ON;
Delay(50);
//PM2,滅燈
setSleepTimer(2); //設(shè)置睡眠定時器的定時間隔為2s
led1 = LED_OFF;
PowerMode(2); //設(shè)置電源模式為PM2
//2s后,由PM2進(jìn)入PM0,亮燈并延時
led1=0;
Delay(50);
//PM3,滅燈
io_init(); //初始化外部中斷
led1 = LED_OFF;
PowerMode(3); //設(shè)置電源模式為PM3
//當(dāng)外部中斷發(fā)生時,由PM3進(jìn)入PM0,亮燈
led1 = LED_ON;
while(1);
}
中斷服務(wù)程序
/*外部中斷服務(wù)程序
-------------------------------------------------------*/
#pragma vector = P0INT_VECTOR
__interrupt void P0_ISR(void)
{
EA = 0; //關(guān)中斷
Delay(50);
if((P0IFG & 0x02 ) >0 ) //按鍵中斷
{
P0IFG &= ~0x02; //P0.1中斷標(biāo)志清0
}
P0IF = 0; //P0中斷標(biāo)志清0
EA = 1; //開中斷
}
/*睡眠定時器中斷服務(wù)程序
-------------------------------------------------------*/
#pragma vector= ST_VECTOR
__interrupt void sleepTimer_IRQ(void)
{
EA=0; //關(guān)中斷
STIF=0; //睡眠定時器中斷標(biāo)志清0
EA=1; //開中斷
}
關(guān)于如何使用睡眠定時器來喚醒系統(tǒng),可以總結(jié)為如下流程:開睡眠定時器中斷 → 設(shè)置睡眠定時器的定時間隔 → 設(shè)置電源模式
(注:“設(shè)置睡眠定時器的定時間隔”這一步一定要在“設(shè)置電源模式”之前,因為進(jìn)入睡眠后系統(tǒng)就不會繼續(xù)執(zhí)行程序了)
接下來,我們重點(diǎn)關(guān)注一下設(shè)置睡眠定時器定時間隔的子函數(shù):setSleepTimer
首先對睡眠定時器簡單的介紹一下:它是運(yùn)行于32.768kHz的24位定時器,當(dāng)系統(tǒng)運(yùn)行在除了PM3之外的所有的電源模式下,睡眠定時器都會不間斷運(yùn)行。
睡眠定時器使用的寄存器有:ST0,ST1,ST2。下面是摘自CC2430中文手冊對其功能的詳細(xì)介紹:
查看原圖(大圖)
查看原圖(大圖)
查看原圖(大圖)
可以看出,它們的功能包括兩方面:讀,寫。
讀:用于讀取當(dāng)前定時器的計數(shù)值,讀的順序必須遵循:讀ST0 → 讀ST1 → 讀ST2
寫:用于設(shè)置定時器的比較值(當(dāng)定時器的計數(shù)值=比較值時,產(chǎn)生中斷),寫的順序必須遵循:寫ST2 → 寫ST1 → 寫ST0
OK,接下來我們結(jié)合源碼來講解:
(1)首先,定義一個unsigned long型變量(32位)sleepTimer,用于接收睡眠定時器的當(dāng)前計數(shù)值:
unsigned long sleepTimer = 0;
sleepTimer |= ST0; //取得目前的睡眠定時器的計數(shù)值
sleepTimer |= (unsigned long)ST1 << 8;
sleepTimer |= (unsigned long)ST2 << 16;
?。?)然后加上所需要的定時間隔:
sleepTimer += ((unsigned long)sec * (unsigned long)32768); //加上所需要的定時時長
此處需要稍微解釋一下:
為什么1s就代表著32768?因為定時器是工作在32.768kHz之下,所以定時器每加1,需耗時1/32768 s;加32768,就需要1s;
?。?)最后將sleepTimer的值作為定時器的比較值:
ST2 = (unsigned char)(sleepTimer >> 16); //設(shè)置睡眠定時器的比較值
ST1 = (unsigned char)(sleepTimer >> 8);
ST0 = (unsigned char)sleepTimer;
這樣,就可成功設(shè)置定時器的定時周期啦~
?。ㄗⅲ褐劣谠创a的其他部分,相信結(jié)合著詳細(xì)的注釋,大家可以輕松看懂,在此不作贅述)
(4)實驗結(jié)果
運(yùn)行程序,觀察LED1,現(xiàn)象為:LED1閃爍(即亮->滅1次),1s后再次閃爍,2s后再次閃爍,然后保持熄滅狀態(tài),然后按下S1,LED1亮。
實驗現(xiàn)象和預(yù)期完全吻合,Over~
三、結(jié)語
吁~ 抽出2天的課余時間,終于搞定了這篇日志。真的發(fā)現(xiàn)寫博,特別是寫一篇“讀者友好”的博文,的確是一項體力活:嚴(yán)謹(jǐn)性、美觀性、邏輯性...都是要考慮的事兒。
每次貼代碼都嫌太長,但又不太愿意使用博客園自帶的折疊工具。因此在本篇博文中,筆者試探性的加入了一些JQuery元素,實現(xiàn)了代碼的平滑折疊,還是有小小的成就感嘀,呵呵(JQuery菜鳥,高手勿笑~)。但當(dāng)局者迷,我并不知這樣做是否真正增強(qiáng)了文章的可讀性,歡迎讀者朋友作出評論 :)
這一個月,筆者真正決定在博客園扎下根來,于是花費(fèi)了大量的課余時間在博文的寫作上。初次寫博,雖然評論很少,但大部分日志都有500以上的點(diǎn)擊率,也算是對我的小小的鼓勵!在博客園發(fā)表關(guān)于單片機(jī)的內(nèi)容,的確需要勇氣,不過我會堅持寫下去的~
從開始到現(xiàn)在的九篇博文,重點(diǎn)是CC2430芯片上的基本硬件模塊的運(yùn)用。到此為止,我們基本上把CC2430上的大部分外設(shè)都過了一遍,但是還有比如Flash存取、隨機(jī)數(shù)發(fā)生器、AES協(xié)處理器、射頻通信等,還沒涉及到。不過Zigbee之旅并未結(jié)束,筆者打算在下一個主題(Z-Stack 協(xié)議的實現(xiàn))中,再來有選擇性的把這些遺漏之處補(bǔ)齊。
下一篇博文,筆者打算以一個稍帶綜合性與擴(kuò)展性的小實驗——“溫度監(jiān)測系統(tǒng)”來結(jié)束Zigbee的首次旅行,講解一下如何去綜合運(yùn)用前面學(xué)到的知識點(diǎn)。
其實根本沒有資格稱“講解”,作為一個初學(xué)者,筆者只希望在寫博的過程中與讀者互勉,共同進(jìn)步!