在研究定時器與捕捉比較器之前,我們可以先讨論一下,他們能做什麼?
1.定時,産生固定頻率的波形,或者使LED等按照固定時間閃亮
2.産生Timer0定時中斷,在一定的時間間隔執行某些功能,例如超音波傳感器的使用,可以設定超音波測量距離的測量頻率
3.可以測量脈沖或PWM波的的高低電平時間或頻率
4.脈沖計數
5.捕捉比較模式可以産生PWM波
6.可以産生PPM波
7.可以測量PPM波的各通道占空比
是以定時器是單片機中使用頻率很高的資源,不要随便使用定時器的IO口直接做輸入輸出使用,這樣有點浪費。
先對MSP430G2553進行分析,通過使用者手冊和Datasheet,我們可以知道G2553隻有定時器A,沒有定時器B,并且沒有定時器A2,定時器A隻有捕捉比較器0(TA0.CCI0A 引腳P1.1),捕捉比較器1(TA0.CCI1A 引腳P1.2)等資源,Timer_A為16為定時器,也就是說最高可以計數到65536,當定時到實踐或者滿足捕獲比較條件時可以出發定時器A中斷。
在這裡對中斷進行一定的介紹,中斷使暫停CPU正在運作的程式,轉去執行相應的中斷服務程式,中斷完畢後傳回被中斷的程式并且繼續運作的現象和技術,中斷的存在是很必要的,可以很好地處理突發事件,并且不與主程式内容沖突。這些解釋或許不便于了解,我現在舉一個例子來說明中斷的必要性。例如,我現在要用MSP430系列的單片機做飛控控制四軸飛行器,首先他需要不斷産生200HZ的四路PWM波,還需要不斷的讀來自MPU6050傳遞過來的飛信器加速度角速度等資料,還需要擷取超音波傳回的距離資訊以避障,還需要進行四元素融合計算歐拉角以及進行PID疊代,這麼多的操作要同時執行,假如說沒有中斷,我們産生200HZ的PWM波的方式或許會采用延時,也就是寫一些延時程式延時到5ms則取反則能産生200HZ的pwm波,但是這樣你的程式需要不斷執行延時程式,并且不能被打斷,因為一旦被打斷,你産生的波形的周期也就變了,這樣我們将不能加入MPU6050等傳感器。但是使用中斷可以很好地解決這一問題。我們的主程式不斷執行的就是PID疊代這個操作,然後再5ms的計時周期到了之後,進入定時器中斷并且産生pwm波,序列槽中斷到了之後進入序列槽讀MPU6050的資料,echo信号的高電平到了之後進入超音波對應的定時器讀高電平,在這些中斷結束後繼續在主函數進行PID疊代,這樣就可以不斷地更新資訊,産生波形,并且不影響主函數的執行。是以學會使用中斷使很重要的。中斷的來源有内部中斷和外部中斷,并且可以設定優先級,要能夠進入中斷也必須先在相關的寄存器中設定中斷使能,這些内容大家可以參照代碼和一些資料自己學習,現在我來講幾個定時器中斷的典型例子:
#include
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關閉看門狗定時器
P1DIR |= 0x01; // 設定P1.0為輸出
CCTL0 = CCIE; // TA0CCR0定時器使能,這裡的CCTL0在宏定義中其實就是TACCTL0
CCR0 = 1000-1; //設定計數為1000
TACTL = TASSEL_1 + MC_1; // 使用ACLK=32768HZ 上數模式
_BIS_SR(LPM3_bits + GIE); // 進入LPM3中斷并且中斷使能
}
// 定時器A0的捕捉比較器0中斷
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A (void)
{
P1OUT ^= 0x01; // P1.0口取反進而産生方波
}
/*
* 産生方波的周期為:32768hz/1000*2=16HZ
*/
上面這個例子采用的是捕捉比較器A0的中斷,A0和其他捕捉比較器的中斷使不同的,A0的中斷格式如這個程式所示,其他捕捉比較器的中斷我們之後再說,上面的程式基本有注釋,很容易看懂,其中要注意的就是定時器計數的四種模式,分别是停止模式(不計數),增計數模式(從0計數到TAxCCR0),連續計數模式(從0計數到0FFFFh),增減計數模式(從0計數到TAxCCR0之後減計數到0循環往複),是以通過增計數或者增減計數模式可以通過改變TAxCCR0改變計數周期,也就是改變要生成的波形周期。此外還有輸入輸出的模式定義,對應可以生成不同需求的波形,例如pwm波等等,這個可以詳細看使用者手冊了解。接下來看第二個例子:
#include
int main(void) {
WDTCTL = WDTPW | WDTHOLD; // 關閉看門狗計時器
P1DIR|=0X01;
TACTL=TASSEL_2+MC_2+TAIE; //SMCLK=1.048576Mhz定時器為16位,溢出計數為65536
_BIS_SR(LPM0_bits+GIE); //f=1.048576MHZ/65536*2=8hz
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
switch(TA0IV)
{
case 2:break;
case 4:break;
case 10:P1OUT^=0x01;
break;
}
}
/*
* 定時器A有兩個不同的中斷向量位址,一個是CCR0的定時器溢出中斷,是定時或計數周期時間到了之後進入該中斷
* 程式如下:
* #pragma vector=TIMER0_A0_VECTOR
* __interrupt void Timer_A0(void)
* {
* }
* 另一個是CCR1/CCR2以及TAR計數溢出中斷
* 程式如本程式所示
* 其中case2是CCR1産生的中斷,case2是CCR2産生的中斷,case10是定時器TAR溢出中斷
*/
這個例子使用的是A1的中斷,像注釋中所說,其中case2是CCR1産生的中斷,case4是CCR2産生的下降沿中斷,case10是定時器TAR溢出中斷,是以你想要在8hz時P1.0取反,則在case10時寫這句話,當然你也可以設定在CCR1或者CCR2的計數時間到時取反,如下方代碼所示:
#include
int main(void) {
WDTCTL = WDTPW +WDTHOLD; // Stop watchdog timer
P1DIR|=0x01;
CCTL1=CCIE;
CCR1=50000;
TACTL=TASSEL_2+MC_2; //SMCLK=1.048576MHZ 連續計數模式 計數100000個,是以頻率為10hz左右
_BIS_SR(LPM0_bits+GIE);
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A(void)
{
switch(TA0IV)
{
case 2:
{
P1OUT^=0x01; //捕獲比較器1觸發
CCR1+=50000;
}
break;
case 4:break;
case 10:break;
}
}
/*
* f=10.89hz
* 使用捕獲比較器1
*/
#include
void main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1SEL|=BIT1+BIT2;
P1DIR|=BIT0+BIT1+BIT2;
CCTL0=OUTMOD_4+CCIE;
CCTL1=OUTMOD_4+CCIE;
TACTL=TASSEL_2+MC_2+TAIE;
_BIS_SR(LPM0_bits+GIE);
}
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{
CCR0+=200;
}
#pragma vector=TIMER0_A1_VECTOR
__interrupt void Timer_A1(void)
{
switch(TA0IV)
{
case 2:CCR1+=1000;
break;
case 10:P1OUT^=0x01;
break;
}
}
/*
* 對于P1.0 f=1M/65536*2=8HZ
* 對于P1.1即CCR0 f=1M/2*200=2500hz
* 對于P1.2即CCR1 f=1M/2*1000=500HZ
*/
以上的代碼大緻是定時器中斷的介紹,相信大家不難了解,但是到這裡會有一個問題,就是假如不用中斷,能不能産生一定周期的信号呢,答案是可以的,可以配置CCR1和CCR0的引腳為比較輸出模式,便可以産生一定頻率的方波。這裡比較簡單,不再仔細講,具體代碼和注釋如下:
#include
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關閉看門狗計時器
P1DIR |= 0x02; // P1.1 輸出
P1SEL |= 0x02; // P1.1 第二功能選擇
CCTL0 = OUTMOD_4; // CCR0 比較輸出模式4
CCR0 = 500-1; //計數為500,是以周期為SMCLK/1000
TACTL = TASSEL_2 + MC_1; // SMCLK為時鐘,上數模式
_BIS_SR(CPUOFF); // 關閉CPU進入休眠,P1.1頻率為SMCLK/1000
}
下面這個例子是不用中斷生成pwm波波形的例子,用中斷生成pwm波的例子我之後會專門寫文章說明
#include
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關狗
P1DIR |= 0x0C; // P1.2 P1.3 設定為輸出
P1SEL |= 0x0C; // P1.2 P1.3 選擇TA1/2 功能
CCR0 = 512-1; // 設定PWM波周期
CCTL1 = OUTMOD_7; // CCR1 模式7
CCR1 = 384; // 設定CCR1占空比75%
TACTL = TASSEL_2 + MC_1; // SMCLK為時鐘,上數模式
_BIS_SR(CPUOFF); // 進入休眠
}
以上基本是MSP430G2553的定時器A的說明,還有一些其他的例程可能配置時鐘為ACLK,或者産生不同的占空比,或者使用不同的引腳,不過原理都大同小異,大家要注意的就是選擇哪個時鐘,哪種計數模式,便能很好地運用定時器A。 同樣我們來看一下MSP430F5336的定時器使用,F5336的定時器資源相對來說就豐富的多,定時器有定時器A0,定時器A1,定時器A2,定時器B四個不同的定時器子產品,定時器A0有CCI0-CCI4五個捕捉比較器,還有CCI1B和CCI2B做為選擇,是管教P1.1-P1.7的第二功能;定時器A1和定時器A2都是分别有CCI0-CCI2三個捕捉比較器,在引腳P3上;定時器B有CCI0-CCI6七個捕捉比較器,在引腳P4上;具體的原理和G2553差不多,不過寄存器略有差別,我不詳細講了,直接貼代碼: 定時器A:
#include
/*
* 定時器A的寄存器情況基本與G2553一緻,可以參考中文資料
* P1頻率為10hz,是以48ms發生一次中斷
* SMCLK為1M左右,是以為50000us相當于50ms
*/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關閉看門狗
P1DIR |= 0x01; // P1.0設為輸出
TA0CCTL0 = CCIE; // CCR0中斷使能
TA0CCR0 = 50000;
TA0CTL = TASSEL_2 + MC_1 + TACLR; // SMCLK, 增計數模式, 清除TAR計數器
__bis_SR_register(LPM0_bits + GIE); // 進入LPM0,使能中斷
}
// TA0中斷服務程式
#pragma vector=TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void)
{
P1OUT ^= 0x01; // 反轉P1.0口輸出狀态
}
#include
/*
* 定時器A的寄存器情況基本與G2553一緻,可以參考中文資料
* 65536一次溢出
* SMCLK為1M左右,是以為65535us相當于65ms
*/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關閉看門狗
P1DIR |= 0x01; // P1.0設為輸出
TA1CTL = TASSEL_2 + MC_2 + TACLR + TAIE;
// SMCLK,連續計數模式,清除TAR,并使能TAIFG中斷
__bis_SR_register(LPM0_bits + GIE); // 進入LPM0, 并啟動中斷
}
// TA1中斷服務程式
#pragma vector=TIMER1_A1_VECTOR
__interrupt void TIMER1_A1_ISR(void)
{
switch(__even_in_range(TA1IV,14))
{
case 0: break; // 無中斷
case 2: break; // TA1CCR1 CCIFG中斷
case 4: break; // TA1CCR2 CCIFG中斷
case 6: break; // TA1CCR3 CCIFG中斷
case 8: break; // TA1CCR4 CCIFG中斷
case 10: break; // TA1CCR5 CCIFG中斷
case 12: break; // TA1CCR6 CCIFG中斷
case 14: P1OUT ^= 0x01; // TAIFG中斷
break;
default: break;
}
}
#include
/*
* 使得P1.2(TA0CCR1)和P1.3(TA0CCR2)和P1.4(TA0CCR3)和P1.5(TA0CCR4)分别輸出占空比為20和%40和%60和%80的波形
* 頻率為980000/TA0CCR0=400hz
* 可以用作操控電機
*/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 關閉看門狗
P1DIR |= BIT2+BIT3+BIT4+BIT5; // P1.2和P1.3設為輸出
P1SEL |= BIT2+BIT3+BIT4+BIT5; // P1.2和P1.3引腳功能選為定時器輸出
TA0CCR0 = 2500; // PWM周期定義
TA0CCTL1 = OUTMOD_7; // CCR1比較輸出模式7:複位/置位
TA0CCR1 = 500; // CCR1 PWM 占空比定義
TA0CCTL2 = OUTMOD_7; // CCR2 比較輸出模式7:複位/置位
TA0CCR2 = 1000; // CCR2 PWM 占空比定義
TA0CCTL3 = OUTMOD_7; // CCR1比較輸出模式7:複位/置位
TA0CCR3 = 1500; // CCR1 PWM 占空比定義
TA0CCTL4 = OUTMOD_7; // CCR2 比較輸出模式7:複位/置位
TA0CCR4 = 2000; // CCR2 PWM 占空比定義
TA0CTL = TASSEL_2 + MC_1 + TACLR; // ACLK,增計數模式,清除TAR計數器
__bis_SR_register(LPM3_bits); // 進入LPM3
}
定時器A1,A2,B與之類似不再複述了,以上基本是定時器的原理和使用方法。 ——達達的馬蹄 ——2015-8-21