Blogs

MSP430 Launchpad教程-第2部分-中断和计时器

恩里科·加兰特(Enrico Garante)2013年6月17日42条留言

什么是“打断这是一个信号,通知我们的MCU某个事件已经发生,从而导致主程序正常流程的中断和“中断例程”的执行,该例程处理该事件并采取指定的措施。

快速链接

中断对于避免在轮询循环中浪费处理器的宝贵时间,等待外部事件至关重要(实际上,它们用于实时操作系统, 实时操作系统)。

本文以PDF格式提供,便于打印

在MSP430架构中,有几种类型的中断:定时器中断,端口中断,ADC中断等等。它们中的每一个都需要启用并配置为工作,并且每个中断都有一个单独的“服务例程”。

在本教程中,我们将看到如何使用计时器和端口中断来刷新某些LED,在下一个教程中,我们将保留ADC中断。因此,让我们编写一些代码!

#包括"msp430g2231.h" 
void main(void)
{
  WDTCTL = WDTPW + WDTHOLD;                 // Stop WDT  

您应该认识到这些行,我们在上一教程中使用了它们来为我们的MCU添加定义文件,声明主要功能并停止看门狗定时器。

  CCTL0 = CCIE; //使能CCR0中断
  TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK / 8,向上模式  
  CCR0 =  10000;                           // 12.5 Hz   

这是一些有趣的东西。这些行配置计时器中断。我们首先通过将CCTL0寄存器中的CCIE位置1来使能它。

然后我们在TimerA控制寄存器中设置定时器模块的时钟。

如果查看msp430g2231.h文件,可以看到:

  • TASSEL_2选择SMCLK(由内部DCO提供,该内部DCO的运行频率约为1 MHz)。
  • MC_1选择“ UP模式”,定时器递增到存储在CCR0寄存器中的数字;
  • ID_3为提供的时钟选择一个内部8x分频器(在我们的情况下,我们为SMCLK / 8)。

最后,我们设置CCR0寄存器。我们将TimerA模块配置为在溢出和触发中断之前最多计数该寄存器中存储的数量。

通过将其设置为10000,我们得到的溢出频率为12,5 Hz。实际上,我们有(SMCLK / 8)/ 10000 = 12,5。

您可以通过更改此数字来获得多个频率(请记住,MSP430具有16位定时器,因此存储在CCR0寄存器中的值不得大于65535),更改分频器或使用计数器添加if-else块在中断程序中。让我们继续。

  
  P1OUT &= 0x00; //关闭所有内容
  P1DIR &= 0x00;               
  P1DIR |= BIT0 + BIT6;       // P1.0 and P1.6 pins output the rest are input 
  P1REN |= BIT3;                 // Enable internal pull-up/down resistors
  P1OUT |= BIT3;                 //Select pull-up mode for P1.3

这些行也应该很熟悉,但是有一些补充:首先,我们清除PORT1输出和方向寄存器。然后,我们将P1.0和P1.6引脚设置为输出,其余设置为输入。最后两行启用开关(BIT3)上的上拉电阻,以便正常状态(未按下按钮)将为“ 1”。

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

使用这些代码行,我们首先告诉MCU监听P1.3引脚的逻辑状态变化(有效地启用该特定引脚上的中断)。

然后我们在中断产生时选择边沿(从高到低或从低到高)。请记住,LaunchPad上的按钮在按下时将输入引脚连接到GND,而在未按下时将其连接到VCC。因此,我们选择高/低沿。

最后,我们清除该引脚的中断标志。中断标志程序P1IFG在中断发生时进行报告,应在中断服务程序结束时将其清除。

  _BIS_SR(CPUOFF + GIE);        // Enter LPM0 w/ interrupt   
  while(1)                      //Loop forever, we do  everything with interrupts!
  {}
}

您可能还记得,通过这条线,我们在保持中断使能的同时关闭了CPU,以节省一些电源。然后我们进入一个循环,以确保MCU在执行中断工作时不执行其他操作。

//定时器A0中断服务程序
#pragma vector = TIMERA0_VECTOR
__interrupt void Timer_A(void)
{
  P1OUT ^= BIT0;                            // Toggle P1.0
}

这是TimerA中断服务程序。每当TimerA溢出时,都会执行此例程中插入的代码(请注意特殊声明)。如您所见,我们仅切换P1.0引脚(LaunchPad上的红色指示灯),然后返回正常执行。

//端口1中断服务程序
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
{    
   P1OUT ^= BIT6;                        // Toggle P1.6   
   P1IFG &=~BIT3;                        // P1.3 IFG cleared    
} 

这是Port1中断服务程序。每次我们按下P1.3按钮时,都会执行此例程中插入的代码(请注意特殊声明)。我们切换P1.6引脚(在LaunchPad上变为绿色),清除P1.3中断标志(非常重要),然后返回正常执行。

编译并编程LaunchPad时,按P1.3按钮时,应该看到红色的LED闪烁,绿色的LED切换。

这是完整的代码,请尽情享受!

#包括"msp430g2231.h"  
  
void main(void)
{
  WDTCTL = WDTPW + WDTHOLD; //停止WDT  
  CCTL0 = CCIE; //使能CCR0中断
  TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK / 8,向上模式
  CCR0 = 10000; // 12.5赫兹   
  P1OUT &= 0x00; //关闭所有内容
  P1DIR &= 0x00;               
  P1DIR | = BIT0 + BIT6; // P1.0和P1.6引脚输出,其余输入 
  P1REN | = BIT3; //启用内部上拉/下拉电阻 
  P1OUT | = BIT3; //为P1.3选择上拉模式
  P1IE | = BIT3; //启用P1.3中断
  P1IES | = BIT3; // P1.3高/低边缘
  P1IFG &=〜BIT3; // P1.3 IFG已清除
  _BIS_SR(CPUOFF + GIE); //输入带有中断的LPM0 
  while(1)//永远循环,我们处理中断!
  {}
} 
  
//定时器A0中断服务程序 
#pragma vector = TIMERA0_VECTOR 
__interrupt void Timer_A(void) 
{   
   P1OUT ^ = BIT0; //切换P1.0 
} 
//端口1中断服务程序
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
{    
   P1OUT ^ = BIT6; //切换P1.6
   P1IFG &=〜BIT3; // P1.3 IFG已清除 
}

[-]
评论者 Tomi892015年7月22日
你好!
我将示例转换为MSP-EXP430F5529LP,希望对您有所帮助!

#包括“ msp430F5529.h” //包含寄存器和内置函数的定义
#包括

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停止WDT

TA0CCTL0 = CCIE; //使能CCR0中断
TA0CTL = TASSEL_2 | MC_1 | ID_3; // SMCLK / 8,向上模式
TA0CCR0 = 10000; //我必须计算出来,但是我很懒

P1OUT &= 0x00; //关闭P1上的引脚
P1DIR &= 0x00; //将P1引脚设置为输出
P1DIR | = BIT0; // P1.0引脚设置为输出,其余输入

P4OUT &= 0x00; //关闭P4上的引脚
P4DIR &= 0x00; //将P4引脚设置为输出
P4DIR | = BIT7; // P4.7引脚设置为输出,其余输入

P2REN | = BIT1; //为P2启用内部上拉/下拉电阻
P2OUT | = BIT1; //为P2.1选择上拉模式

P2IE | = BIT1; //启用P2.1中断
P2IES | = BIT1; // P2.1高/低边缘
P2IFG &=〜BIT1; // P2.1 IFG已清除

_BIS_SR(CPUOFF + GIE); //输入带有中断的LPM0
while(1)//永远循环,我们处理中断!
{}
}

//定时器A0中断服务程序
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A0(void)
{
P1OUT ^ = BIT0; //切换P1.0

}

//端口2中断服务程序
#pragma vector = PORT2_VECTOR
__interrupt void Port_2(void)
{
静态uint8_t反跳= 0;

while(debounce <= 100)
{
if (~P2IN & BIT1) debounce++;
else debounce = 0;
}
P4OUT ^ = BIT7; //切换P4.7
debounce = 0;

P2IFG &=〜BIT1; // P2.1 IFG已清除
}
[-]
评论者 农民丹尼斯2015年12月29日
基本相同的概念,但对于开关“ S2”

#包括

/ *
* main.c
*/
int main(void){
WDTCTL = WDTPW | WDTHOLD; //停止看门狗定时器

TA0CCTL0 = CCIE; / * TA0CCTL0寄存器:如果您打算使用定时器,则需要设置的第一个寄存器是CCTL0。它是一个16位寄存器,该寄存器的设置会影响我们如何使用该寄存器。为了我们的目的,我们只是告诉它使用* /启用中断。
TA0CTL = TASSEL_2 + MC_1 + ID_3; / * TASSEL_2:使用SMCLK MC_1:递增ID_3:除以8 * /
TA0CCR0 = 10000; //最多10000

P1OUT = 0;
P1DIR = BIT0; // P0作为输出
P1REN | = BIT1; //为P1启用上拉/下拉
P1OUT | = BIT1; //拉出P1的分辨率

P4OUT = 0;
P4DIR = 0;
P4DIR= BIT7;

P1IE |= BIT1; //P1.0 interrupt enable
P1IES | = BIT1; //高/低边
P1IFG &=〜BIT1; //清除标志

_BIS_SR(CPUOFF + GIE); //睡觉直到中断

while(1) {}

return 0;
}

//定时器A中断
#pragma vector = TIMER0_A0_VECTOR
__interrupt void TIMER0_A0_ISR(void)
{
P4OUT ^= BIT7;
}

#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^= BIT0;
P1IFG &= ~BIT1;
}
[-]
评论者 emihackr972014年5月22日
嗨,我在Energia IDE中使用了您的代码,它可以完美编译并上载,但是只有按钮中断有效,而定时器中断无效,这可能是什么问题?顺便说一句,我正在使用MSP430G2553。

非常感谢!
[-]
评论者 朱利奥·索扎(Julio Souza)2014年7月15日
看看前面的评论
[-]
评论者 chintan_gaurav82016年8月30日
你有解决方案了吗?当我运行相同的程序时,TAR寄存器不会递增,因此永远无法达到所需的值,并且执行保持在while(1)循环上。是否存在时钟问题或其他问题?
[-]
评论者 Naresh142015年5月10日
你好,

不错的教程。我可以知道你从哪里得到材料吗?我的意思是指令集。谢谢!
[-]
评论者 Jed642016年2月23日
感谢您的分享!
我有关于清除中断标志的问题。
为什么我们不需要在Timer的中断服务程序中清除中断标志,而需要在Port1中断服务程序中清除中断标志?
[-]
评论者 托尼2016年2月24日
在端口IRQ上具有IV的MSP系列,您无需手动清除该标志,因为只需读取寄存器即可清除导致该标志的标志,并且一次只能设置一个标志,因此可以在其中使用开关/大小写。好像发生了两个IRQ,他们将排在队列中。
430x2xxx没有IV,并且一次可以设置多个IFG标志,并且如果仅通过读取就清除了所有标志,那么如果对IFG寄存器进行位测试,就不能有多个标志,因此您需要手动清除该位。
[-]
评论者 奥多里卡·亚伯拉罕九月26,2016
在第一个教程中我对ISR的结束有疑问,请使我们的LED闪烁:
#pragma vector = PORT1_VECTOR
__interrupt void Port_1(void)
{
P1OUT ^ = BIT6; //切换P1.6
P1IFG &=〜BIT3; // P1.3 IFG已清除
}
如果按下按钮1.3,则进入ISR,因为为此配置了P1IE和P1IES,然后在例程P1OUT XOR BIT6,P1IFG中将程序返回到“ main()”?我的意思是,ISR本身会结束并自动返回吗?如果很严格,我想如果再按一次该按钮,程序将再次进入ISR。我是说。

但是,当TimerA告诉MCU“去ISR_TimerA例程?_TimerA开始计数,并且在计数结束时,程序将转到ISR_TimerA?如果是的话,哪一行开始对TimerA进行计数。我们如何停止”例如,ISR_TimerA ...在5次相同的中断之后。

我认为我的问题不是那么荒谬。
[-]
评论者 Vic862015年6月28日
嗨,我正在用计时器打扰一下,但我需要将SMCLK的频率更改为8MHz,您能帮我吗?
[-]
评论者 Tomi892015年7月22日
第二个#include是stdint.h之间<>
我不知道为什么它不显示。
[-]
评论者 Yara222015年10月31日
您的教程很棒!非常感谢。
[-]
评论者 尼克门2016年3月15日


我正在尝试获取外部中断并使LED闪烁。但是我收到类似“ pragma vector =接受数字参数或“ unused_interrupts”而不是PORT_VECTOR的错误”

int main(){
//停止看门狗定时器
WDTCTL = WDTPW + WDTHOLD;
P1DIR&=〜BIT4; //配置为输入
P1输出|= BIT4;
P3DIR | = BIT4;
P3OUT|= BIT4;
P1IES = 0x01;
P1IE = 0x01; // 1b =启用相应的端口中断
P1IFG= 0x01; // PxIFG标志设置为高到低过渡
P1REN = 0x01; //启用上拉或下拉
}

#pragma vector = PORT_VECTOR
__interrupt void Port_1(void){

如果((P1IN& BIT4)==0 ){
//将输入读取为GND
P3OUT|= BIT4;
}
否则if((P1IN& BIT4)==1 ){
P3OUT&= ~BIT4;

}

}
[-]
评论者 迪潘卡潘达2016年6月19日
嗨,我正在使用MSP430F1491。编译时,它在使能电阻上拉/下拉线(P1REN | = BIT3;)上给出错误。表明它无效。但是在使用MSP430F2419进行编译时,它可以完美地进行编译。背后的原因是什么?
[-]
评论者 chintan_gaurav82016年8月30日
当我运行相同的程序时,TAR寄存器不会递增,因此永远无法达到所需的值,并且执行保持在while(1)循环上。是否存在时钟问题或其他问题?
[-]
评论者 chintan_gaurav82016年8月30日
当我运行相同的程序时,TAR寄存器不会递增,因此永远无法达到所需的值,并且执行保持在while(1)循环上。是否存在时钟问题或其他问题?
[-]
评论者 迪潘卡潘达50九月12,2016
你好,
上面的例子效果很好。现在,如果我想使用timer_A以不同的频率闪烁2个LED,我该怎么办?
我已将TACCRO = 10000和TACCR1 = 60000
因此,通过TACCTLO = CCIE和TACCTL1 = CCIE允许中断

还提出了两个ISR:
#pragma vector = TIMERA0_VECTOR
__interrupt void ISR1(无效)
{ }
和#pragma vector = TIMERA1_VECTOR
__interrupt void ISR2(无效)
{ }

但它不能正常工作。一个LED指示灯完美闪烁,而另一个则不闪烁。任何人都可以建议。提前致谢。
[-]
评论者 trmittal242017年5月12日

什么是#语用 vector = PORT1_VECTOR是什么意思?


另外,如果我将2个交换机连接到端口1的2个不同的引脚,并希望为它们执行不同的例程,该如何实现?

[-]
评论者 opipi6282018年3月12日

大家好, 

我为使FR5994的代码工作有点挣扎,所以我认为这可以帮助其他人使用基于fr5994的启动板


// **************************************************** *******************************************

// MSP430闪烁LED演示-软件切换P1.0

//

// MSP430x5994

//-----------------

// / / \\ |辛|-

// | | | |

//--| RST XOUT |-

// | | |

// | | P1.0 |->LED1

// P1.1 |->LED2

// P5.5 |<--P5.5

//

//德州仪器(TI)

// 2013年7月

// **************************************************** *******************************************

#包括<msp430.h>

void main(void)

{

  //CLK SETUP

  WDTCTL = WDTPW + WDTHOLD; //停止WDT

  PM5CTL0 &=〜LOCKLPM5; //禁用GPIO上电默认的高阻抗模式

  TA0CCTL0 = CCIE; //使能CCR0中断

  TA0CTL = TASSEL_1 + MC_1 + ID_3; // ACLK = 32.768kHz / 8,升频

  TA0CCR0 = 4096; // 1Hz赫兹

  P1OUT &= 0x00; //关闭所有内容

  P1DIR &= 0x00;

  P1DIR | = BIT0 + BIT1; // P1.0和P1.1引脚输出,其余为输入。输出用于两个LED

  P5REN | = BIT5; //启用内部上拉/下拉电阻

  P5OUT | = BIT5; //需要设置上拉高电阻

  P5IE | = BIT5; //启用P5.5中断

  P5IES | = BIT5; // P5.5高/低边缘

  P5IFG &=〜BIT5; //已清除P5.5 IFG

  _BIS_SR(CPUOFF + GIE); //输入带有中断的LPM0

  while(1)//永远循环,我们处理中断!

  {}

}

//定时器A0中断服务程序

#pragma vector = TIMER0_A0_VECTOR

__interrupt void Timer_A0(void)

{

   P1OUT ^ = 0x01; //切换P1.0

   //TA0CTL &= ~TAIFG;

}

//端口5中断服务程序

#pragma vector = PORT5_VECTOR

__interrupt void Port_1(void)

{

   P1OUT ^ = 0x02; //切换P1.1

   P5IFG &=〜BIT5; //已清除P5.5 IFG

}

[-]
评论者 Unalfaruk十月19,2018

谢谢,将时钟设置为1 Hz非常重要。我想在开发之前,我们应该先看一下时钟源。

[-]
评论者 Qubic2十月28,2019

你好,恩里科。非常好的系列文章。似乎很多人都在错误: #2580-D pragma vector =接受数字参数或“ unused_interrupts”,但 PORT2_Vector”,因为他们尝试使用中断。我在许多论坛上都看过,而且没有一种建议的解决方案似乎真正解决了这个问题。我只是在尝试将端口2的一个引脚拉低时产生一个中断。我已经多次重写代码以尝试不同的端口和不同的引脚,但始终会收到相同的警告。


关于解决方案有什么建议吗?我正在使用CCS 9和MPS430 FR2355评估板。


真诚的,鲍勃

[-]
评论者 斯蒂芬布十月28,2019

鲍勃,你应该尝试 我们的论坛.

[-]
评论者 Qubic2十月28,2019

谢谢。我会试一试。

[-]
评论者 HK652013年7月21日
你能告诉我为什么你的初始化代码中有P1OUT | = BIT3吗?我不明白为什么它在那里,但是没有它,代码将无法工作。
[-]
评论者 埃加兰特2013年7月31日
如《 MSP430x2xx系列用户指南》中所述:

“每个PxREN寄存器中的每个位均启用或禁用相应I / O引脚的上拉/下拉电阻。
PxOUT寄存器中的相应位选择是将引脚上拉(1)还是下拉(0)。”


我在代码中添加了注释,以使其更加清晰,感谢您的举报!
[-]
评论者 DJC2013年8月13日
您可以在每个端口上声明多个中断引脚,并为每个中断信号运行单独的代码吗?
[-]
评论者 埃加兰特2013年8月15日
当然!
您只需使用P1IE和P1IES寄存器启用引脚中断即可。然后,在PORT1中断例程中,可以使用P1IFG寄存器上的if语句为每个引脚执行不同的代码,这将使您知道已按下了哪个引脚。
[-]
评论者 比什瓦瓦尔丹2013年9月7日
感谢您的文章,这非常有帮助
[-]
评论者 Prasadganna2013年9月16日
嗨,大家好,

我正在使用MPS430g2553控制器,我想从具有5个按键的小键盘上设置时间,它的作用是什么?

[-]
评论者 卡洛斯802013年9月30日
再次伟大,但不适用于2553,我无法修复它!
[-]
评论者 吉皮2013年10月15日
对于MSP430G2553,必须使用
#pragma vector = TIMER0_A0_VECTOR
代替
#pragma vector = TIMERA0_VECTOR
定时器中断服务程序,它应该工作。我在10分钟前测试过!
[-]
评论者 日尼提九月22,2017

你好,

我想了解这行 #pragma vector = TIMER0_A0_VECTOR does when compiling? 

[-]
评论者 桑杰·卡格拉2013年11月18日
请告诉我有关使用msp430的相量计算的信息
[-]
评论者 NIKHIL_EMBEDDED2014年2月27日
先生,我想将密钥与P1.0交互。作为输出,我想以1%的时间间隔以1%的步长将PWM波的占空比百分比从1%增加到99%。
[-]
评论者 穆罕默德922014年5月24日
您为什么不考虑按钮防抖的20毫秒?
[-]
评论者 H2SO42014年8月29日
很棒的向导,完美的解释!非常感谢。
[-]
评论者 阿拉文2014年11月6日
你好,
感谢您的教程。我们对代码的解释非常好,而且很容易将代码分解为更简单的理解方式,非常感谢。

我将行“ P1IES | = BIT3;”更改为“ P1IES&=〜BIT3; ,即从上升沿到下降沿。
它按预期工作,..

同时我也尝试过
我将“ P1OUT | = BIT3;”行更改为“ P1OUT&= ~BIT3; ".
如果我是正确的,这是清除位,即。将该位设置为下拉电阻器模式。
输出没有变化。
它应该不起作用,对.. ???!
[-]
评论者 Akash12015年4月1日
最初,P1.3被拉 上...那个 is,it is high.
现在,当按下按钮时,它变低,在轮询中,它用(〜BIT2&P1IN)。使用中断编码如何完成?
[-]
评论者 SL122015年4月2日
当您将此(按下按钮的过程)定义为中断时,则在等待程序时,程序还会继续寻找中断。现在,当您按下按钮时,将设置一个中断标志,并调用一个子例程……
[-]
评论者 SL122015年4月2日
你好,
我正在g2553上使用此端口和计时器中断程序。
它显示错误为:“ Port”未识别为内部或外部命令。
我还按照g2553.h更改了中断名称,但仍然是相同的错误……
为了 参考...这里 is the code :::

#包括“ msp430g2553.h”

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //停止WDT
CCTL0 = CCIE; //使能CCR0中断
TACTL = TASSEL_2 + MC_1 + ID_3; // SMCLK / 8,向上模式
CCR0 = 10000; // 12.5赫兹
P1OUT &= 0x00; //关闭所有内容
P1DIR &= 0x00;
P1DIR | = BIT0 + BIT6; // P1.0和P1.6引脚输出,其余输入
P1REN | = BIT3; //启用内部上拉/下拉电阻
P1OUT | = BIT3; //为P1.3选择上拉模式
P1IE | = BIT3; //启用P1.3中断
P1IES | = BIT3; // P1.3高/低边缘
P1IFG &=〜BIT3; // P1.3 IFG已清除
_BIS_SR(CPUOFF + GIE); //输入带有中断的LPM0
while(1)//永远循环,我们处理中断!
{}
}

//定时器A0中断服务程序
#pragma vector = TIMER0_A0_VECTOR
__interrupt void Timer_A(void)
{
P1OUT ^ = BIT0; //切换P1.0
}
//端口1中断服务程序
#pragma vector = PORT1_VECTOR
__interrupt void PORT1_ISR(无效)
{
P1OUT ^ = BIT6; //切换P1.6
P1IFG &=〜BIT3; // P1.3 IFG已清除
}
[-]
评论者 金香料2015年6月16日
对于G2553,您必须使用:
TA0CTL = TASSEL_2 + MC_1 + ID_3;
TA0CCR0 = 10000;
TA0CCTL0 = CCIE
#pragma vector = TIMER0_A0_VECTOR
[-]
评论者 greenthumb792015年4月11日
可能的问题是ISR函数名称-您使用void PORT1_ISR(void),但更常用的是void Port_1(void)。

要发布对评论的回复,请单击每个评论所附的“回复”按钮。要发布新评论(而不是回复评论),请查看评论顶部的“写评论”标签。

注册后,您可以参加所有相关网站上的论坛,并获得所有pdf下载的访问权限。

注册

我同意 使用条款隐私政策.

试试我们偶尔但很受欢迎的时事通讯。非常容易退订。
或登录