在研究定时器与捕捉比较器之前,我们可以先讨论一下,他们能做什么?
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
*/
#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溢出中断
*/
#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
*/
#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
}
#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); // 进入休眠
}
#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
}