[MSP430] 2.中断和计时器

在这一部分中我们将会初步了解到中断的概念及其作用, 我们会尝试使用计时器中断和 I/O 中断操作 LED 灯,让我们开始吧!

什么是中断?我们可以将它理解为一个约定的信号,来告知单片 机特定的事件发生了,引起程序从正常运行的主函数中断开,转而 执行中断处理程序,处理特定的事件。
中断是一个非常重要的概念,它可以让处理器免于执行冗余的轮 询操作等待特定的外部事件的发生。在 MSP430 的架构中,有许 多种类的中断:计时器中断,I/O 中断,ADC 中断等等。每一种中 断在使用前都要使能和配置,每一种中断又分别有中断处理程序 (Service Routine)。
下面就让我们尝试写一个小程序,实现使用计时器中断和 I/O 中 断操作 LED 灯。

#include "msp430g2553.h" void main(void)
{
  WDTCTL = WDTPW + WDTHOLD; // Stop WDT
按照惯例,首先包含 g2553 的头文件,关闭看门狗。每次写程序的时候你总会用到它们。

  CCTL0 = CCIE;  // CCR0 interrupt enabled
  TACTL = TASSEL_2 + MC_1 + ID_3;  // SMCLK/8, upmode
  TACCR0 = 10000;  // 12.5 Hz

这几行简单配置了计时器中断。

  CCTL0 = CCIE;  // CCR0 interrupt enabled
我们首先通过置 CCTL0(Timer_A capture/compare control 0)寄存器的 CCIE 位(Capture/compare interrupt enable)使能了计时器中断。

  TACTL = TASSEL_2 + MC_1 + ID_3;  // SMCLK/8, up mode
然后我们通过 TACTL(Timer_A control)寄存器配置了计时器的时钟。如果查阅一下 MSP430 的手册,你会看到之后几位分别表示什么含义:
TASSEL_2 选择了 SMCLK 时钟(由内部 DCO 支持,默认频 率大约为 1MHz);
MC_1 选择了上升模式(up mode),即计时器计数的时候由小 至大,计数上限由 TACCR0(Timer_A capture/compare 0)寄存器决定

由此就有

  TACCR0 = 10000;  // 12.5 Hz
你一定猜到了配置的结果 12.5Hz 是怎么得来的了吧, 1M/8/10000=12.5Hz,这就是产生计时器中断的频率。
通过选择不同的时钟源,不同的时钟分频,不同的计数上限,你几乎可以配置出任何你想要的频率,需要注意的是,MSP430的寄存器都是16 位的,所以 TACCR0 的上限是 65535。

我们继续完善我们的程序。

  P1OUT &= 0x00;  // Shut down everything P1DIR &= 0x00;
  P1DIR |= BIT0 + BIT6;  // P1.0 and P1.6 pins output 
  P1REN |= BIT3; // Enable internal pull-up/down resistors
  P1OUT |= BIT3;  // Select pull-up mode for P1.3

这几行代码我们应该已经熟悉了。我们首先清空了 PORT1 的输 出寄存器和方向寄存器,然后配置板上两个 LED 所对应引脚为输出, 为按键对应的引脚配置上拉电阻。

  P1IE |= BIT3;  // P1.3 interrupt enabled
  P1IES |= BIT3;  // P1.3 Falling edge
  P1IFG &= ~BIT3;  // P1.3 IFG cleared

这几行代码中,我们首先使能了 P1.3 引脚的中断功能,然后我 们选择了下降沿触发中断(高电平到低电平触发),Launchpad 上的按键在不按下的时候连接着 VCC,而按下的时候连接的是 GND,因此我们选择下降沿触发。最后,我们要清除相应的中断标 志位。中断标志位通知单片机一个中断的产生,因此在每次中断处 理程序结束后,如果我们希望下次产生事件的时候依然有中断,我 们应当清除中断标志位。

  _EINT(); // Enable all interrupts
  while(1) // Loop forever, we work with interrupts! 
    {}

打开所有中断,轻松加愉快。接下来就是中断的事儿了。

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) {
  P1OUT ^= BIT0;  // Toggle P1.0
}
这是 TimerA 的中断处理程序。每当计数器溢出的时候,中断触 发,程序便会执行这段代码,翻转 P1.0 的输出,对应地,LED1 会 出现闪烁的效果。每次翻转之后,程序便会回到触发中断的地方, 在本例中,回到 While(1)。

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR __interrupt void Port_1(void) {
  P1OUT ^= BIT6;  // Toggle P1.6
  P1IFG &= ~BIT3;  // P1.3 IFG cleared
}

这是PORT1的中断处理程序,每当我们按下P1.3对应的按键时,中断触发,程序便会执行这段代码,效果如同在前一节中演示的一样

烧代码看效果吧

以下是这一节的完整代码:

#include "msp430g2553.h" 
void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;
  CCTL0 = CCIE;  // CCR0 interrupt enabled
  TACTL = TASSEL_2 + MC_1 + ID_3;  // SMCLK/8, upmode
  TACCR0 = 10000;  // 12.5 Hz
  P1OUT &= 0x00;  // Shut down everything
  P1DIR &= 0x00;
  P1DIR |= BIT0 + BIT6;  // P1.0 and P1.6 pins output
  P1REN |= BIT3;  // Enable internal pull-up/down resistors
  P1OUT |= BIT3;  // Select pull-up mode for P1.3
  P1IE |= BIT3;  // P1.3 interrupt enabled
  P1IES |= BIT3;  // P1.3 Falling edge
  P1IFG &= ~BIT3;  // P1.3 IFG cleared
  _EINT();  // Enable all interrupts
  while(1)  // Loop forever, we work with interrupts!
  {}
}

// Timer A0 interrupt service routine
#pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A0 (void) {
  P1OUT ^= BIT0;  // Toggle P1.0
}

// Port 1 interrupt service routine
#pragma vector=PORT1_VECTOR __interrupt void Port_1(void) {
  P1OUT ^= BIT6;  // Toggle P1.6
  P1IFG &= ~BIT3;  // P1.3 IFG cleared
}









你可能感兴趣的:(MSP430)