1. 程式人生 > >CC2530基礎實驗三 定時器

CC2530基礎實驗三 定時器

一、任務要求

使用CC2530微控制器內部定時/計數器來控制LED1進行週期性閃爍,具體閃爍效果要求如下:

①通電後LED1每隔2秒閃爍一次。
②LED1每次閃爍點亮時間為0.5秒

1.定時/計數器介紹
(1)定時/計數器的概念
定時/計數器是一種能夠對時鐘訊號或外部輸入訊號進行計數,當計數值達到設定要求時便向CPU提出處理請求,從而實現定時或計數功能的外設。在微控制器中,一般使用Timer表示定時計數器。
(2)定時/計數器的作用
定時/計數器的基本功能是實現定時和計數,且在整個工作過程中不需要CPU進行過多參與,它的出現將CPU從相關任務中解放出來,提高了CPU的使用效率。例如我們之前實現LED燈閃爍時採用的是軟體延時方法,在延時過程中CPU通過執行迴圈指令來消耗時間,在整個延時過程中會一直佔用CPU,降低了CPU的工作效率。若使用定時/計數器來實現延時,則在延時過程中CPU可以去執行其他工作任務。CPU與定時/計數器之間的互動關係可用圖4-1來進行表示。
這裡寫圖片描述

微控制器中的定時/計數器一般具有以下功能:

1)定時器功能
對規定時間間隔的輸入訊號的個數進行計數,當計數值達到指定值時,說明定時時間已到。這是定時/計數器的常用功能,可用來實現延時或定時控制,其輸入訊號一般使用微控制器內部的時鐘訊號。
2)計數器功能
對任意時間間隔的輸入訊號的個數進行計數。一般用來對外界事件進行計數,其輸入訊號一般來自微控制器外部開關型感測器,可用於生產線產品計數、訊號數量統計和轉速測量等方面。
3)捕獲功能
對規定時間間隔的輸入訊號的個數進行計數,當外界輸入有效訊號時,捕獲計數器的計數值。通常用來測量外界輸入脈衝的脈寬或頻率,需要在外界輸入訊號的上升沿和下降沿進行兩次捕獲,通過計算兩次捕獲值的差值可以計算出脈寬或週期等資訊。
4)比較功能
當計數值與需要進行比較的值相同時向CPU提出中斷請求或改變I/O口輸出電平等操作。一般用於控制訊號輸出。
5)PWM輸出功能
對規定時間間隔的輸入訊號的個數進行計數,根據設定的週期和佔空比從I/O口輸出控制訊號。一般用來控制LED燈亮度或電機轉速。

(3)定時/計數器基本工作原理
無論使用定時/計數器的哪種功能,其最基本的工作原理是進行計數。定時/計數器的核心是一個計數器,可以進行加1(或減1)計數,每出現一個計數訊號,計數器就自動加1(或自動減1),當計數值從最大值變成0(或從0變成最大值)溢位時定時/計數器便向CPU提出中斷請求。計數訊號的來源可選擇週期性的內部時鐘訊號(如定時功能)或非週期性的外界輸入訊號(如計數功能)。
這裡寫圖片描述

二、CC2530的定時/計數器

CC2530中共包含了5個定時/計數器,分別是定時器1、定時器2、定時器3、定時器4和睡眠定時器。
(1)定時器1

定時器1是一個16位定時器,主要具有以下功能:
支援輸入捕獲功能,可選擇上升沿、下降沿或任何邊沿進行輸入捕獲。
支援輸出比較功能,輸出可選擇設定、清除或切換。
支援PWM功能。
具有5個獨立的捕獲/比較通道,每個通道使用一個I/O引腳。
具有自由執行、模、正計數/倒計數三種不同工作模式。
具有可被1、8、32或128整除的時鐘分頻器,為計數器提供計數訊號。
能在每個捕獲/比較和最終計數上產生中斷請求。
能觸發DMA功能。
定時器1是CC2530中功能最全的一個定時/計數器,是在應用中被優先選用的物件。

CC2530定時/計數器的工作模式

CC2530的定時器1、定時器3和定時器4雖然使用的計數器計數位數不同,但它們都具備“自由執行”、“模”和“正計數/倒計數”三種不同的工作模式,定時器3和定時器4還具有單獨的倒計數模式。此處以定時器1為例進行介紹。
(1)自由執行模式
在自由執行模式下,計數器從0x0000開始,在每個活動時鐘邊沿增加1,當計數器達到0xFFFF時溢位,計數器重新載入0x0000並開始新一輪遞增計數
這裡寫圖片描述

自由執行模式最大為FFFF也就是65535

自由執行模式的計數週期是固定值0xFFFF,當計數器達到最終計數值0xFFFF時,系統自動設定標誌位IRCON.T1IF和T1STAT.OVFIF,如果使用者設定了相應的中斷遮蔽位TIMIF.T1OVFIM和IEN1.T1EN,將產生一箇中斷請求。
(2)模模式
在模模式下,計數器從0x0000開始,在每個活動時鐘邊沿增加1,當計數器達到T1CC0暫存器儲存的值時溢位,計數器將復位到0x0000並開始新一輪遞增計數
這裡寫圖片描述

計數溢位後,將置位相應標誌位,同時如果設定了相應的中斷使能則會產生一箇中斷請求。T1CC0由2個8位暫存器T1CC0H和T1CC0L構成,分別用來儲存最終計數值的高8位和低8位。模模式的計數週期不是固定值,可由使用者自行設定,以便獲取不同時長的定時時間。
定時器3和定時器4的倒計數模式類似與模模式,只不過計數值是從最大計數值向0x00倒序計數。
(3)正計數/倒計數模式
在正計數/倒計數模式下,計數器反覆從0x0000開始,正計數到T1CC0儲存的最終計數值,然後再倒計時回0x0000

這裡寫圖片描述
正計數/倒計數模式下,計數器在到達最終計數值時溢位,並置位相關標誌位,若使用者已使能相關中斷,則會產生中斷請求。這種工作模式在用來進行PWM控制時可以實現中心對齊的PWM輸出。

定時器設定流程

這裡寫圖片描述

執行流程

這裡寫圖片描述

三、CC2530時鐘源

時鐘源
1、內部RC震盪器(32KHz、16MHz)
2、外部石英晶振(32.768KHz、32MHz)
注意:外部石英晶振比較穩定,在無線收發中採用外部石英晶振

時鐘源的切換
CLKCONSTA
用於判斷時鐘源是否切換成功

這裡寫圖片描述

CLKCONCMD
用於設定時鐘源

這裡寫圖片描述

用命令暫存器(CLKCONCMD)來改變系統時鐘源,用狀態暫存器 (CLKCONSTA) 來判斷改變後的暫存器是否穩定了。

這裡寫圖片描述

四、定時器

這裡寫圖片描述

定時器設定

定時器時間:1/CLKCONCMD*T1CTL分頻*工作模式*溢位次數

CLKCONCMD -->TI計數器(T1CTL模式選擇)-->TISTAT.OVFIF(標誌位)-->TIMIF.OVFIM-->T1IF-->EA-->T1IE

定時器初始化

T1CTL 設定分頻 與模式
T1CCOH T1CCOL 高低八位
T1CCTL0|=0x04 通道0 比較模式

T1IF=0 清除TIMER1中斷標誌
T1STAT &=~0x01; 清除通道0中斷標誌
IEN1|=0X02 使能定時器1的中斷
EA=1

T1定時器中斷條件

1、設定時鐘源
CLKCONCMD &= ~0X40;
  while(CLKCONSTA & 0x40);
  CLKCONCMD &= ~0X47;
2、設定T1定時器的工作模式和分頻
T1CTL = 0X05;
3、開啟T1中斷和中斷總開關
  IEN1 |= 0X02;
EA = 1;
4、寫中斷函式
  #pragma vector = T1_VECTOR
__interrupt void ti()
{
    counter++;
    if(counter == 50)
    {
      T1IF = 0;   //清空T1溢位中斷標誌位
      counter = 0;
/***************
程式碼
**************/
    }
}

按鍵控制定時器T1

1、初始化按鍵中斷暫存器,開啟中斷總開關
  IEN2 |= 0X10;
  P1IEN |= 0X04;
  PICTL &= ~0X02;
EA = 1;
2、修改32MHz時鐘源
  CLKCONCMD &= ~0X40;
  while(CLKCONSTA & 0x40);
  CLKCONCMD &= ~0X07;
3、按鍵中斷函式,並設定定時器的工作模式和分頻,開啟T1中斷
  #pragma vector = P1INT_VECTOR
__interrupt void f1()
{
  EA = 0;
  if(P1IFG & 0X04)
  {
    while(P1_2 == 0);
    delay(100);
    while(P1_2 == 0);

    if(k == 1)
    {
      k = 0;
      IEN1 |= 0X02;
      T1CTL = 0X05;
    }
    else if(k == 0)
    {
      k = 1;
      IEN1 &= ~0X02;
      T1CTL = 0;
    }
    P1IFG = 0;
    P1IF = 0;
  }
  EA = 1;
}

4、定時器中斷函式

  #pragma vector = T1_VECTOR
__interrupt void t1()
{
  counter++;
  if(counter == 30)
  {
    T1IF = 0;
    counter = 0;
    /***************
    程式碼
    ******************/
      }
}


暫存器
T1CTL
設定T1定時器操作模式和分頻

這裡寫圖片描述

T1STAT

這裡寫圖片描述

IRCON
溢位中斷標誌定時器計數滿後需要清楚標誌位
當定時器T1產生中斷時也可以寫作  
IRCON &= 0X02;    T1IF = 0;

這裡寫圖片描述

T1CCTL0
設定TIMER1通道0比較模式      T1CCTL0  |= 0X04;

這裡寫圖片描述

T1CC0L、T1CC0H

這裡寫圖片描述

正計數/倒計數器工作模式從0x0000~T1CC0,再從T1CC0~0x0000需設定

這裡寫圖片描述

TIMIF
不產生定時器T1的溢位中斷
常用設定
TIMIF &= ~0X40;

這裡寫圖片描述

IEN1
用於設定中斷使能
常用設定
    IEN1 |= 0X02;

這裡寫圖片描述

五、具體程式碼

#include "ioCC2530.h" //引用CC2530標頭檔案

#define LED1 (P1_0)     //LED1埠巨集定義
#define LED2 (P1_1)     //LED1埠巨集定義

unsigned char flag_Pause=0;  //流水燈執行標誌位,為1暫停,為0執行。

/**************************************************************
函式名稱:delay
功    能:軟體延時
入口引數:time--延時迴圈執行次數
出口引數:無
返 回 值:無
**************************************************************/
void delay(unsigned int time)
{
    unsigned int i;
    unsigned char j;
    for(i = 0;i < time;i++)
        for(j = 0;j < 240;j++)
        {
            asm("NOP");//asm用來在C程式碼中嵌入組合語言操作,匯
            asm("NOP");//編命令nop是空操作,消耗1個指令週期。
            asm("NOP");
            while(flag_Pause);//根據flag_Pause的值確定是否在此迴圈
        }
}

/**************************************************************
函式名稱:main
功    能:程式主函式
入口引數:無
出口引數:無
返 回 值:無
**************************************************************/
void main(void)
{
    P1SEL &= ~0x03;         //設定P1_0口和P1_1口為普通I/O口
    P1DIR |= 0x03;          //設定P1_0口和P1_1口為輸出口

    LED1 = 0;               //熄滅LED1
    LED2 = 0;               //熄滅LED2

    /*************新增外部中斷初始化部分****************/
    IEN2 |= 0x10;           //使能P1口中斷
    P1IEN |= 0x04;          //使能P1_2口中斷
    PICTL |= 0x02;          //P1_3到P1_0口下降沿觸發中斷
    EA = 1;                 //使能總中斷
    /***************************************************/

    while(1)//程式主迴圈
    {
        delay(1000);        //延時
        LED1 = 1;           //點亮LED1
        delay(1000);        //延時
        LED2 = 1;           //點亮LED2
        delay(1000);        //延時
        LED1 = 0;           //熄滅LED1
        delay(1000);        //延時
        LED2 = 0;           //熄滅LED2
    }
}

/**************************************************************
函式名稱:P1_INT
功    能:P1口外部中斷服務函式
入口引數:無
出口引數:無
返 回 值:無
**************************************************************/
#pragma  vector = P1INT_VECTOR
__interrupt void P1_INT(void)
{
    if(P1IFG & 0x04)       //如果P1_2口中斷標誌位置位
    {
        if(flag_Pause == 0)
        {
            flag_Pause = 1;
        }
        else
        {
            flag_Pause = 0;
        }
        P1IFG &= ~0x04;    //清除P1_2口中斷標誌位
    }
    P1IF = 0;              //清除P1口中斷標誌位
}