TI - MCU - MSP430使用指南13 -> Timer定时器模块

定时器,作为MCU最重要也是最基本的功能集成在每一个MCU中。随着MCU功能的日渐强大,定时器的功能也越来越强大,因此配置和使用起来也就比较麻烦,下面我们针对MSP430的Timer模块进行详细讲解,配合多种可以直接使用的例程,方便用户直接移植和深入理解。

首先,普及一下定时器知识:本质上就是一个计数器,可以由用户自定义计数的值,同时到达计数值后可以执行相应的动作,因此可以时间周期性动作,采集捕捉动作等,用于实时控制及多种功能实现。

MSP430 MCU中有三种定时器:Timer_A   Timer_B   Timer_D.

首先 Timer_D定时器基本没有集成在MCU中,很少很少使用,是一个高分辨率的定时器,因此我们不做讲解(内部寄存器和使用与Timer_A/B很是相似,如果有用户用到,可以直接参考,如果有人需要可以留言,我会开贴再次单独讲解Timer_D)。

因此本次内容主要讲解一下Timer_A和Timer_B定时/计数器。首先先说一下MSP430 MCU内部Timer的主要功能:

  • 16位定时/计数器,支持四种工作模式/计数模式:STOP  UP Continuous UP/DOWN
  • 输入捕捉功能
  • 输出比较功能,即PWM波功能

主要就这三种功能,用户在使用过程中也就是这三种功能,然后Timer有一些自己的特点:

  • 时钟源可以选择
  • 异步输入,输出所存
  • 中断向量寄存器等

那么为什么MSP430会有两种定时器呢:A和B,有什么区别呢:

  • B可以配置成8,10,12,16位模式,A只能用16位模式
  • B的CCRn寄存器是双缓冲,即Double-buffered,可以形成组
  • B的每一个输出口都可配置成高阻态
  • B中没有SCCI位

什么?不懂?没事,等我讲解完Timer所有功能后你就了解了,如果有疑问请接着往后看,最后在讲解完之后,我会重新详细分析一下Timer_A和Timer_B的区别。

首先先了解一下Timer_A的内部结构图,从下图可以看到,内部的定时器位数是16位,同时支持TAxCLK,ACLK,SMCLK,INCLK作为时钟源,后可以进行进一步的分频使用,同时拥有7个比较输出模块,可用于输出PWM波(注意 一个Timer只能输出6个PWM波,因为有一路计数器需要作为base使用:在可以调节PWM频率和占空比的情况下)

内部结构图决定的是模块的实现方式,因此可以清晰地看出实现的原理和寄存器中每个位的作用,因此最好养成观看内部结构图的习惯,这样有利于高效地调试自己的程序,当然现在刚开始看起来有点懵,不太理解也比较正常,但当学习完整个Timer后,对比着程序来看就会恍然大悟。

好啦,下面开始进入Timer地核心内容区域:使用模式。

简单来说就是如何使用它,怎么使用,有几种使用方法。对于MSP430 Timer_A定时器来说,总共有四种工作模式,如下图所示,由寄存器TAxCLT中MC位决定,如下图所示:

  • 模式0:默认模式,即Stop,此模式下定时器内部不会开始计数,处于停止状态。
  • 模式1:Up模式,此模式下定时器从0开始计数,一直计数到TAxCCR0寄存器中地值为止产生中断(如果使能了中断)并重新回到0,再次开始计数,如下图所示:

              在使用这种模式下,中断在什么时候产生呢?这时候就要弄清楚一个问题:Timer_A模块  有两种中断:TAxIFG(定时器中断)和TAxCCRx(捕捉/比较中断),TAxIFG时定时器的基本中断,一个定时器模块只有一个,就是当内部计数到设定值后就会产生,TAxCCRx时输入/比较中断,其实内部就一个定时器在计数,但是有不同通道的几个值可以设定,也就是TAxCCRx寄存器,因此当相对应的通道设定了值之后,则会触发相应的中断。

触发中断的时间如下图所示:

TAxIFG是在计数完CCR0后会产生的,TAxCCR0中断时在计数完CCR0-1后产生的,两者相差一个时钟位。不过在使用过程中,基本可以忽略了,只是在理论上了解一下更好。

那么还有一个问题:当前的计数值是在寄存器TAxR(看内部结构框图可以发现)中的,TAxCCR0只是设定一个值,那么当模式从Stop开始到Up模式时,TAxR的值不一定为0,那么设定就会出现两个情况:

  1. 当设定CCR0值时,TAxR值小于CCR0的值,那么定时器从当前值(TAxR)开始计数,计数到CCR0值后产生中断,回到0,从新开始计数。。。。。
  2. 当设定CCR0值时,TAxR值大于CCR0的值,那么计数器会直接回到0,从0开始计数。

因此这个时候就会出现两种情况,然而你在设定时如果不去检查TAxR的值,那么可能对于第一个周期,就会出现不同的结果。为了避免这个情况,建议在设定CCR0值的时候,将MC设置成Stop模式(TI建议),其实这时候不仅仅应该设置成Stop模式,最重要的是要将TACLR设置成1(注意这个位是TAxR清除位,直接设置成1后会自动清0,去读的话 也都是0)。

  • 模式2:Continuous模式,此模式下定时器从0开始计数,一直计数到0xFFFF为止产生中断(如果使能了中断)并重新回到0,和Up模式地区别就是Up是计数到TAxCCR0(用户自己设定)值,而Continuous模式是直接计数到0xFFFF,这个值是固定的,不能设置,这样的话,定时器溢出的之间只能由计数频率决定了。如下图所示:

这种模式下,仅有TAxIFG中断会用到,因为没有设定CCR寄存器,也不需要这个寄存器,产生中断的时刻如下图所示:

在完成0xFFFF计数后才会产生中断。那么当工作在Continuous模式下 CCRx就不能使用了吗?

NO,也可以使用,而且可以根据客户的需求来定义不同占空比 不同形状的波形,如下图所示,使用两个CCRx时可以产生的中断(加上TAxIFG中断会更多,灵活使用):

上图可以看到,在CCR0和CCR1处都会产生中断,同时TAxIFG也是存在的。这种模式下在设定CCRx值时就仅有一种情况了,因为TAxR一直在计数,因此设定CCRx值时并不影响计数,只是看什么时候会产证中断而已,如果当前计数到10000,你设定10001,那么下一个时钟就会中断,如果当前10000,你设定9999,那么需要等下一次TAxR值等于9999时才能产生中断了,与Up模式不同的时,设定CCRx的值,不会改变或影响TAxR的值。

  • 模式3:Up/down模式,这个模式下和Up比较类似,唯一的区别是Up模式下计数到TAxCCR0时会产生中断并再次从0开始,但是Up/down模式下则不会从0开始,而是从现在值开始往下计数(减计数),如下图所示:

触发中断的时间如下图所示:

TAxIFG会在计数到0(计完1)后触发,CCIFG会在计数完CCR0-1后触发。

那么改变CCRx值时是什么状况呢?

  1. 当设定CCR0值时,TAxR值小于CCR0的值,那么定时器从当前值(TAxR)开始计数,不影响TAxR的值。
  2. 当设定CCR0值时,TAxR值大于CCR0的值,那么计数器会立刻开始递减,进行减计数,减到0后再开始递增计数,最后正常工作。

因此此时设定CCRx值时,推荐和Up模式下保持一致。

上图显示的是在Up/down模式下CCRx使用方式,当然 这个也是PWM的输出方法,由CCR0通道作为base计数,用于调节PWM的频率,CCRx则用来调节占空比,而且还支持多种输出模式。这个模式的区别,后面会详细讲解。

到此处位置,Timer_A的四种工作模式,其实就三种(除去Stop)已经全部讲解完成,本质上也就是每个定时器模块内有一个计数器,一直在计数,然后有一些CCRx寄存器,可用于用户设定相应的值,当计数到这个值时产生相应的中断,去执行相应的操作。那么如何使用描述的这些功能呢? 我认为需要了解五块内容,会和你的使用息息相关:

中断方式,寄存器,输入捕捉,输出比较(PWM),例程。

下面按照这种方法来进行讲解(比较乱,但是我认为这种方式比较合理,先搞清楚定时器原理及模式,在进一步细看寄存器,中断实现方式,在面向使用去了解输入捕捉和输出比较,最后进行例程的学习)。

  1. Timer_A定时器的中断

在细看完上面的讲解后,你可能会有个印象,Timer_A模块有两种中断:TAxIFG和TAxCCRx中断,那么中断向量如何呢?(中断向量是中断的入口)

如果拆开看有8个中断:TAxIFG,CCR0IFG-CCR6IFG,每个都分配一个中断向量? 太浪费了吧! 因此MSP430只分配了两个中断向量:

TAxCCR0自己单独一个中断向量,名字叫做:TIMER0_A0 (TimerA0),这个不需要记,去看工程下的cmd文件即可。

剩下的TAxIFG和TAxCCRx共同享用一个中断向量:TIMER0_A1 (TimerA0),因此你会发现每个定时器仅有两个中断向量,也就够使用了,那么如何用呢?

对于CCR0中断:

// Timer0_A0 interrupt service routine
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0_ISR (void)
{
  P1OUT ^= BIT0;
}

对于其他中断,进入中断后需要进行中断类型判断:

// Timer0_A1 Interrupt Vector (TAIV) handler
#pragma vector=TIMER0_A1_VECTOR
__interrupt void TIMER0_A1_ISR(void)
{
  switch(__even_in_range(TA0IV, TA0IV_TAIFG))
  {
    case TA0IV_NONE:   break;               // No interrupt
    case TA0IV_TACCR1: break;               // CCR1 not used
    case TA0IV_TACCR2: break;               // CCR2 not used
    case TA0IV_TACCR3: break;               // reserved
    case TA0IV_TACCR4: break;               // reserved
    case TA0IV_TACCR5: break;               // reserved
    case TA0IV_TACCR6: break;               // reserved
    case TA0IV_TAIFG:                       // overflow
      P1OUT ^= BIT0;
      break;
    default: break;
  }
}

注意:并不是所有的Timer_A都有6个CCR寄存器,具体有几个看每个芯片的datasheet。

  1. Timer_A寄存器

先看一下总共有多少寄存器:18个寄存器,但是只有六类寄存器,其他都是相同的。

 

  • TAxCTL寄存器

 

  • TASSEL :  时钟源选择,如上图所示,有四种时钟源。
  • ID : 时钟源分频,对选中的时钟源进行分频后再输入Timer用于计数。
  • MC : 定时器模式选择,设定上述所讲的四种模式。
  • TACLR : 计数器清零位,写1会清除当前计数值,会自动复位。
  • TAIE : Timer_A中断使能位。
  • TAIFG : Timer_A中断标志位,即使中断不使能,当产生中断信号时,标志位也会置位。

 

  • TAxR寄存器

  • TAxR : 计数器当前计数的值,可以进行读写操作。

 

  • TAxCCTLn寄存器

 

 

  • CM : 捕捉边沿类型,这个用于捕捉外部的信号,可以选择上升沿,下降沿,上升/下降沿。
  • CCIS :  捕捉输入通道选择,可以选择成A, B, GND, VCC,每个通道对应的GPIO口不同,具体看datasheet。
  • SCS : 同步捕捉还是异步捕捉,是否将输入信号与时钟信号进行同步,看结构图可以看到内部就是使用了一个与门,可以实现时钟信号与输入信号的同步,这个根据客户需要进行选择,一般来说两种模式都可以,具体看客户使用场景,在学习是注意一下有这个点即可,当遇到问题时,如果可能是时序问题的话,查看一下这个。
  • SCCI : 同步输入信号加载紧EQU失能位选择,就是给EQU多一个输入源CCI。
  • CAP : 输入捕捉模式还是输出比较模式选择。
  • OUTMOD : 输出模式类型,总共有7中模式,也就是再不同中断情况下进行不同的操作,可能是置位,也可能是复位,或者反转等,很多模式都可以输出PWM波,用户自己看就可以。

给出一个再Up模式下不同模式的波形图:

模式1(001) :   CCR1中断时直接置位。

模式2(010)  :     CCR1中断时反转,CCR0中断时复位。

模式3(011)  :     CCR1中断时置位,CCR0中断时复位。

模式4(100)  :     CCR1中断时反转。

模式5(101)  :     CCR1中断时复位。

模式6 (110):      CCR1中断时反转,CCR0中断时置位。

模式7 (111):      CCR1中断时复位,CCR0中断时置位。

 

 

  • CCIE : 输入捕捉或输出比较使能。
  • CCI : 只读寄存器,可以读取被选中通道的点评状态。
  • OUT : 输出比较模式时初始输出状态。当OUTMOD=000时,就直接一致输出这个状态。
  • COV : 输入捕捉overflow产生,即溢出了,当输入的数大于0xFFFF时会溢出,这一位必须软件复位。注意,在计算输入捕捉到的脉冲数量时,需要检测这一位,否则会出现溢出情况导致捕捉到的脉冲是负数或者差一个0xFFFF。
  • CCIFG : 输入捕捉/输出比较中断标志位。

 

  • TAxCCRn寄存器

  • TAxCCRn : 输出比较时使设定值,输入捕捉时是当收到捕捉信号时定时器的值。

 

  • TAxIV寄存器

 

  • TAIV : 只读寄存器,中断类型标志位,用来识别是哪个中断源引起的中断(因为除了TAxCCR0,其他中断源共享一个中断向量)

 

  • TAxEX0寄存器

  • TAxIDEX : 输入时钟分频寄存器,和TAxCTL寄存器中ID位效果一致,用来对所选的时钟进行分频后再输入给Timer_A。
  1. Timer_A输入捕捉

首先看,什么叫输入捕捉,用在什么地方:主要用于测量脉冲数量,监控外部脉冲等功能,其实最主要的就是连接增量式编码器来获取固定时间内的脉冲数量从而计算电机的转速,这个是最最常用的功能。

如下是MSP430 输入捕捉功能的工作流程图:

 

那么如何设定输入捕捉呢?

这个主要再TAxCCTLn寄存器,首先你需要将CAP设置成1,代表使用输入捕捉模式,其次根据你的硬件连接选择A或者B通道,然后设定同步位,捕捉类型等,最后依旧是要设定最基本的定时器功能,因为定时器要工作,开始计数嘛!

当然设置完这些后输入捕捉就可以工作了,但是捕捉到信号之后怎么办呢? 需要开中断嘛,这时进中断,将这个时刻计数器的值给记下来,用于脉冲频率等的测量。

后续如何使用此项功能,在后面会给出详细的Example Code.

  1. Timer_A输出比较

输出比较最常用的就是输出PWM波了,这个电机控制,LED控制等中非常常用,原理上前面已经描述清除,就是靠Base计数器和CCRx计数器,当计数到CCRx寄存器时会进行相应的动作,从而实现PWM功能的输出,通过CCR0调节PWM频率,CCRx调节占空比。

  1. Timer_A例程

好啦,上面讲了一大堆最最主要的还是程序应该怎么写,怎么使用每个功能,下面给出几种常用的程序。(例程使用MSP430FR5969,别问为什么 主要因为刚好有这个板子。。。其实MSP430 内部Timer_A模块都是一样的,因此程序基本通用,可能仅仅有一些寄存器不一样或者功能有点区别而已,总的来说大同小异。)

  • 基本定时功能(20ms中断)
#include <msp430.h>

int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT
  // Configure GPIO
  P1DIR |= BIT0;
  P1OUT |= BIT0;

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;
  TA0CCTL0 = CCIE;                          // TACCR0 interrupt enabled
  TA0CCR0 = 20000;                          // SMCLK default 1MHz,
                                     // Set 20000 : 20000/1000000 = 20ms
  TA0CTL = TASSEL__SMCLK | MC__UP;          // SMCLK, UP mode

  __bis_SR_register(LPM0_bits + GIE);       // Enter LPM0 w/ interrupt
  __no_operation();                         // For debugger
}

// Timer0_A0 interrupt service routine
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer0_A0_ISR (void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer0_A0_ISR (void)
#else
#error Compiler not supported!
#endif
{
  P1OUT ^= BIT0;
}

  • 输入捕捉功能 (开始捕捉中断,捕捉到20个脉冲后反转P1.1口,同时记录下每个脉冲时刻的计数器的值)
#include "msp430.h"

#define NUMBER_TIMER_CAPTURES       20
volatile unsigned int timerAcaptureValues[NUMBER_TIMER_CAPTURES];
unsigned int timerAcapturePointer = 0;

int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                 // Stop watchdog timer

  // Configure GPIO
  P1OUT &= ~0x01;                           // Clear P1.0 output
  P1DIR |= 0x01;                            // Set P1.0 to output direction

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  // Clock System Setup
  CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
  CSCTL2 &= ~SELA_7;
  CSCTL2 |= SELA__VLOCLK;                   // Select ACLK=VLOCLK
  CSCTL0_H = 0x00;                        // Lock CS module (use byte mode to upper byte)

  __delay_cycles(1000);                     // Allow clock system to settle

  // Timer0_A3 Setup
  TA0CCTL2 = CM_1 | CCIS_1 | SCS | CAP | CCIE;
                                            // Capture rising edge,
                                            // Use CCI2B=ACLK,
                                            // Synchronous capture,
                                            // Enable capture mode,
                                            // Enable capture interrupt
  TA0CTL = TASSEL__SMCLK | MC__CONTINUOUS;  // Use SMCLK as clock source,
                                            // Start timer in continuous mode

  __bis_SR_register(LPM0_bits | GIE);
  __no_operation();
}

// Timer0_A3 CC1-4, TA Interrupt Handler
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector = TIMER0_A1_VECTOR
__interrupt void Timer0_A1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(TIMER0_A1_VECTOR))) Timer0_A1_ISR (void)
#else
#error Compiler not supported!
#endif
{
  switch (__even_in_range(TA0IV, TA0IV_TAIFG)) {
    case TA0IV_TA0CCR1:
      break;
    case TA0IV_TA0CCR2:
      timerAcaptureValues[timerAcapturePointer++] = TA0CCR2;
      if (timerAcapturePointer >= 20) {
        while (1) {
          P1OUT ^= 0x01;                    // Toggle P1.0 (LED)
          __delay_cycles(100000);
        }
      }
      break;
    case TA0IV_TA0IFG:
      break;
    default:
      break;
  }
}

  • 输出比较功能(输出两路PWM波,频率1KHz,占空比分别为25%和75%)
#include <msp430.h>

int main(void)
{
  WDTCTL = WDTPW | WDTHOLD;                 // Stop WDT

  // Configure GPIO
  P1DIR |= BIT0 | BIT1;                     // P1.0 and P1.1 output
  P1SEL0 |= BIT0 | BIT1;                    // P1.0 and P1.1 options select

  // Disable the GPIO power-on default high-impedance mode to activate
  // previously configured port settings
  PM5CTL0 &= ~LOCKLPM5;

  CSCTL0_H = CSKEY >> 8;                    // Unlock CS registers
  CSCTL1 = DCOFSEL_6;                       // Set DCO = 8MHz
  CSCTL2 = SELA__VLOCLK | SELS__DCOCLK | SELM__DCOCLK;// Set ACLK=VLO SMCLK=DCO
  CSCTL3 = DIVA__8 | DIVS__8 | DIVM__8;     // Set all dividers
  CSCTL0_H = 0;                             // Lock CS registers

  // Configure Timer0_A
  TA0CCR0 = 1000-1;                         // PWM Period 1KHz
  TA0CCTL1 = OUTMOD_7;                      // CCR1 reset/set
  TA0CCR1 = 750;                            // CCR1 PWM duty cycle
  TA0CCTL2 = OUTMOD_7;                      // CCR2 reset/set
  TA0CCR2 = 250;                            // CCR2 PWM duty cycle
  TA0CTL = TASSEL__SMCLK | MC__UP | TACLR;  // SMCLK, up mode, clear TAR

  __bis_SR_register(LPM0_bits);             // Enter LPM0
  __no_operation();                         // For debugger
}

 

下面看一下Timer_B,和Timer_A基本都没区别,功能,使用方法等,那么回归最初的问题,区别在那些地方呢:
B可以配置成8,10,12,16位模式,A只能用16位模式

  • B的CCRn寄存器是双缓冲,即Double-buffered,可以形成组
  • B的每一个输出口都可配置成高阻态
  • B中没有SCCI位

 

先看一下结构图的区别:B可以成组,因此多了TBCLGRP,其次可以设置成8,10,12,16位模式,因此多了CNTL位,没有SCCI位,故CCI不能到EQU模块。

先说第一条:B可以设定成8,10,12,16位模式,也就是说计数的最大值不同,这个在TBxCTL中你会发现多了CNT位(12-11),用于选择定时器位数:

 

其次没有SCCI,因此也就没有这个设定位了。

最后就是Group,看下面寄存器截图:

这个功能就是在输出比较模式下,这几个通道可以同时成组的去更新数据,用于多个输出比较形成同步的功能。

  • 4
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值