1. 程式人生 > >Zigbee之旅(九):幾個重要的CC2430基礎實驗——系統睡眠及中斷喚醒

Zigbee之旅(九):幾個重要的CC2430基礎實驗——系統睡眠及中斷喚醒

一、承上啟下

  這一篇,我們來討論一下CC2430的睡眠功能及喚醒方法。在實際運用中的CC2430節點一般是靠電池來供電,因此對其功耗的控制顯得至關重要。

  下面是摘自CC2430中文手冊對CC2430的4種功耗模式的介紹:

  從上表中可看出,CC2430共有4種電源模式:PM0(完全清醒),PM1(有點瞌睡)、PM2(半醒半睡)、PM3(睡的很死)。越靠後,被關閉的功能越多,功耗也越來越低。它們之間的轉化關係如下:

  

  把 PM1、PM2 喚醒到PM0,有三種方式:復位、外部中斷、睡眠定時器中斷;但把 PM3 喚醒到 PM0,只有兩種方式:復位、外部中斷(這是因為在 PM3 下,所有振盪器均停止工作,睡眠定時器當然也熄火啦~)

  下面我們通過一個小實驗,來介紹如何進入睡眠模式,以及如何喚醒到 PM0 狀態。

二、系統睡眠及中斷喚醒實驗

(1)實驗簡介

  系統初始化,處於PM0   → 進入PM1   → 1s後被睡眠定時器喚醒為PM0   → 進入PM2   → 2s後被睡眠定時器喚醒為PM0   → 進入PM3   → 等待按鍵S1按下,觸發外部中斷,被喚醒為PM0 

(2)程式流程圖

  (注:上圖中的圓角框表示系統的執行狀況)

(3)實驗原始碼及剖析(下面的框框是可以點的~

標頭檔案及巨集定義 複製程式碼 /*
實驗說明:中斷喚醒睡眠實驗,分別介紹三種睡眠模式下的喚醒
*/

#include
<ioCC2430.h
> LED_ON 0#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 <<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<n;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.768kHz24位定時器,當系統執行在除了PM3之外的所有的電源模式下,睡眠定時器都會不間斷執行

  睡眠定時器使用的暫存器有:ST0ST1ST2。下面是摘自CC2430中文手冊對其功能的詳細介紹:

  可以看出,它們的功能包括兩方面:

  :用於讀取當前定時器的計數值,讀的順序必須遵循:讀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的首次旅行,講解一下如何去綜合運用前面學到的知識點。