[MSP-EXP430G2ET] [MSP430G2 LaunchPad入门系列] 第4篇 - MSP430中断

  [复制链接]

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
楼主
查看: 16671回复: 18 发表于 2019-1-24 14:03:51   只看该作者
中断是单片机中一个重要的功能,它可以大大解放CPU资源。MSP430中多个外设均带有中断功能,本章中将介绍MSP430的中断,并练习如何编写一个带有中断函数的程序。


本章目录如下:
章节 题目
4.1 什么是中断
4.2 中断的工作过程
4.3 中断优先级和中断向量表
4.4 如何写一个中断函数
4.5 中断函数练习


MSP430G2 LaunchPad 相关资源链接:

本章内容PDF下载:
第四章-MSP430中断.pdf (826.86 KB, 下载次数: 572)


本系列其他章节:

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
推荐
发表于 2019-1-24 16:08:36   只看该作者
本帖最后由 Shuyang 于 2019-1-24 16:18 编辑

4.5 中断函数练习

下面我们以P1端口的中断为例,练习一下如何编写一个完整的带有中断的程序。练习中我们要实现的功能是使用I/O口中断来读取按键状态,并根据按键状态来控制LED灯。在这个练习中涉及到的中断相关知识点包括:

  • 设置中断向量
  • 使能中断
  • 创建中断服 务函数(ISR)


下面我们来进入具体的练习。

练习目标:利用中断读取MSP430G2 LaunchPad上按键S2的状态,并根据该状态控制绿色LED灯。每按下S2一次,LED改变一次亮灭状态。

在第二章中,我们曾经介绍过MSP430G2 LaunchPad的引脚定义。在本练习中,我们需要检测按键S2的状态,S2与MSP430G2553的P1.3端口相连;另外我们还要控制绿色的LED2,它与P1.6端口相连。


MSP430G2LaunchPad上的按键S2是低电平有效,也就是说当按键被按下时,P1.3为低电平;而当按键没有被按下时,P1.3为高电平。我们要检测按键按下的动作,实际上就是检测P1.3端口上的电平变化。当P1.3端口上出现一个下降沿(即电平从高变低)时,就可以认为出现了按键被按下的动作。

本练习的程序可以分为3个部分,首先需要对GPIO模块做初始化,以配置相应的输入输出功能,并打开中断使能位;其次在主程序中还是要写一个无限循环,不过由于现在按键的检测是通过中断完成的,因此循环中CPU其实什么事情也不用做;最后还要写一个中断服 务函数,在里面设定中断事件发生后要执行的任务。


1)     初始化

初始化时,我们需要完成下列步骤:

        i)      将P1.6设为输出,P1.3设为输入;
        ii)     使能P1.3的内部上拉电阻;
        iii)    使能P1.3引脚中断;
        iv)    将P1.3中断设为下降沿中断;
        v)     复位P1.3的中断标志位

其中第ii步是为了实现按键S2的低电平有效,GPIO的初始化和上拉电阻设置在第2章都有介绍过,可以复习第二章的对应部分。

完成GPIO初始化后,请不要忘记使用_BIS_SR(GIE)指令来使能全局中断,这样才能使CPU对中断信号做出反应。

初始化的具体代码如下:
  1. int main(void)
  2. {
  3.   WDTCTL = WDTPW + WDTHOLD;                // Stop watchdog timer

  4.   P1DIR = BIT6;                                                // P1.6 output, else input
  5.   P1OUT = BIT3;                                              // P1.3 set, else reset
  6.   P1REN |= BIT3;                                             // P1.3 pullup
  7.   P1IE |= BIT3;                                                // P1.3 interrupt enabled
  8.   P1IES |= BIT3;                                              // P1.3 Hi-low edge
  9.   P1IFG &= ~BIT3;                                         // P1.3 IFG cleared

  10.   _BIS_SR(GIE);                                               // Enable global interrupt
复制代码
如果想进一步了解GPIO中断的寄存器设置,请参考“MSP430x2xxFamily User's guide”的8.2.7节。


2)     while循环

初始化完成后,需要写一个while循环,由于现在按键检测是通过中断功能自动完成的,因此在这个循环中CPU其实没有做任何事情,只是简单地等待中断事件的发生。当按键按下的一刹那(即P1.3的下降沿),CPU将检测到中断事件,并自动进入对应的中断服 务函数。
  1. int main(void)
  2. {
  3.   ......
  4.   while (1) {
  5.     __no_operation();                                 // Do nothing
  6.   }
  7. }
复制代码


3)     中断服 务函数

下面我们要编写P1.3对应的中断服 务函数,来告诉CPU中断事件(即按键被按下)发生后需要执行的任务。

编写中断服 务函数时,首先要找到对应的中断向量。本练习中我们使用的是P1端口的中断,其中断向量名为PORT1_VECTOR。接下来按照4.4节中的说明编写中断服 务函数。

中断服 务函数里具体要做的事情包括:

        i)       翻转P1.6的电平;
        ii)      清除P1.3中断标志位。

这样,当每一次按下S2按键时,LaunchPad上的绿色LED灯就会改变状态。
  1. #pragma vector=PORT1_VECTOR
  2. __interrupt void Port_1(void)
  3. {
  4.   P1OUT ^= BIT6;                            // P1.6 toggled
  5.   P1IFG &= ~BIT3;                           // P1.3 IFG cleared
  6. }
复制代码


完整代码

把上述步骤连起来,就完成了一个完整的GPIO中断程序。完整代码如下:
  1. #include <msp430.h>

  2. int main(void)
  3. {
  4.   WDTCTL = WDTPW + WDTHOLD;                // Stop watchdog timer

  5.   P1DIR = BIT6;                                                // P1.6 output, else input
  6.   P1OUT = BIT3;                                              // P1.3 set, else reset
  7.   P1REN |= BIT3;                                             // P1.3 pullup
  8.   P1IE |= BIT3;                                                // P1.3 interrupt enabled
  9.   P1IES |= BIT3;                                              // P1.3 Hi-low edge
  10.   P1IFG &= ~BIT3;                                         // P1.3 IFG cleared

  11.   _BIS_SR(GIE);                                               // Enable global interrupt

  12.   while (1)
  13.   {
  14.     __no_operation();                                  // Do nothing
  15.   }
  16. }

  17. // Port 1 interrupt ser-vice routine
  18. #pragma vector=PORT1_VECTOR
  19. __interrupt void Port_1(void)
  20. {
  21.   P1OUT ^= BIT6;                            // P1.6 toggled
  22.   P1IFG &= ~BIT3;                           // P1.3 IFG cleared
  23. }
复制代码


17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
板凳
发表于 2019-1-24 14:08:26   只看该作者
本帖最后由 Shuyang 于 2019-1-24 14:14 编辑

4.1 什么是中断

中断是单片机内非常重要的一个功能。在单片机内部,CPU就像是主心骨,能力强责任大,最重要的任务都需要它来完成。同时正是因为CPU性能较强,因此CPU运行时要消耗的功耗也是很大的。而中断就像是CPU的一个助理,当出现一些特定事件时助理会主动提醒CPU,让CPU及时知道并做出反应。有了中断,CPU不仅可以一心多用,同时处理多个任务,而且在不是必须工作时CPU可以解放出来,这样可以大大节省单片机的功耗。

以下是几个常见的中断应用场景:

  • GPIO输入状态的检测:如果没有GPIO中断,CPU必须不断通过读取GPIO引脚状态来知道目前的输入是什么。有了GPIO中断,CPU就可以解放出来,执行其他更重要的任务,或是进入休眠状态,直到GPIO中断信号产生时再唤醒CPU去对GPIO输入状态的改变做出响应;
  • 利用定时器中断实现延时:之前章节中我们需要延时的话,都是通过执行一个空循环来实现的,这种方式无疑是对CPU资源的“浪费”。而有了定时器中断,我们就可以给定时器设定一个固定的时间间隔,在到达设定时间之后通知CPU进入定时器中断。这就像一个倒计时的闹钟一样,在闹钟响起之前,CPU无需消耗资源。

总之中断起到的作用就是提醒,就像接电话的过程一样,正常情况下CPU按照程序一步一步的执行,当有电话进来时(中断事件发生时),CPU会暂时放下手里的工作,处理电话中的事情,处理完之后再继续投入之前的工作。


轮询 vs 中断

要让CPU检测到一件事情发生有2种方法,一种叫“轮询”,一种叫“中断”。就好像收快递,如果没有短信或电话提醒,那只能不断的到快递点去询问“快递到了吗?”,这种情况就是轮询。而中断就是提醒短信,只要收到短信后再到快递点去取件就可以了。


这样一对比就发现,中断比轮询要省力得多。对于单片机也是一样,有了中断之后平时单片机可以去处理其他任务,甚至进入休眠状态,只要等中断事件发生时再醒来处理就可以了,处理完了还可以继续睡大觉。

一个例子就是我们第二章学过的按键状态检测,当时我们用的就是轮询的方式,CPU反复的读取I/O口的状态,判断按键是否被按下。这种方式下CPU一直被占用。而本章中我们就要学会使用中断来处理按键,CPU占用率会大大降低。



上图左边就是轮询方式检测按键,右边则是中断方式来实现。请注意右边的中断服 务函数,中断服 务函数里面就是单片机收到中断信号后具体要做的事情。MSP430的中断服 务函数有固定的格式。

  • #pragma关键字后面是中断向量,中断向量决定了这个中断函数响应的是哪一个中断源。例如这里的PORT1_VECTOR就代表来自P1 I/O口的中断。
  • __interrupt(注意有两个下划线)关键字会告诉编译器这个函数是一个中断服 务函数,后面的函数名(rx)是编程者可以自行定义的。

另外,要想使用中断,必须开启中断使能位,同时在对应外设中设置好中断功能(例如将GPIO引脚设为中断输入引脚)。

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
4#
发表于 2019-1-24 14:21:00   只看该作者
本帖最后由 Shuyang 于 2019-1-24 14:30 编辑

4.2 中断的工作过程

要想学会使用中断,需要先了解一下中断的工作过程。一个中断过程可以分为4个阶段。


1)   中断事件发生

首先,中断事件的发生是一个中断的起点。中断事件指的是可以导致CPU进入中断的外部事件,MSP430中有许多外设可以产生中断事件,例如串口、GPIO、定时器、ADC等等。


以GPIO为例,I/O口上的电平变化就可以是一个中断事件,如果I/O口外接按键,那么按键按下或抬起所造成的电平变化就可以导致中断事件发生。MSP430系列大部分型号允许两组I/O口(P1和P2)作为中断输入。


2)   中断标志位被置位

当中断事件被单片机探测到之后,对应的中断标志位(interrupt flag,简称IFG)就会被自动置位(置为1)。置位之后除非进行复位,否则中断标志位会一直保持置位状态。这样做的好处是当有些中断事件发生的时间很短时,中断信号不会被CPU所忽略。MSP430的外设中有该外设中断所对应的IFG寄存器,中断事件发生时这些寄存器就会被改写。


中断信号是如何到达CPU的呢?中断事件发生以后,对应的外设IFG寄存器被置位,但这并不意味着CPU能够接收到中断信号,要想CPU能接收到中断信号,必须将中断使能位(Interruptenable bits,简称IE)置为1。


中断使能位的存在是使得CPU能够自由选择对哪个中断事件作出响应。在单片机工作过程中,可能会产生多个不同外设的中断事件,例如定时器溢出、串口接到数据、I/O口电平变化等,但单片机CPU并不一定想对所有这些中断事件作出响应。有了中断使能位,就像有了开关,CPU就可选择只响应那些需要响应的中断事件。

默认情况下,所有中断使能位都是关闭的(除了看门狗以外)。我们需要通过编程来打开需要的中断使能位。例如想要检测P1.3口所连接的按键,就要将P1端口的中断使能寄存器(P1IE)中的BIT3置为1。

除了上述中断使能位以外,CPU中还有一个全局中断使能位,它相当于一个总开关。MSP430的全局中断使能位称为GIE,它在SR寄存器中。


3)   CPU响应中断

现在我们假设一个中断事件已经发生,IFG也已被置位,对应的中断使能位也已经打开,这样我们确保中断信号能够顺利到达CPU。那么CPU接收到中断信号之后如何做出反应呢?

CPU接收到中断信号之后,会暂停正在执行的工作,并执行中断服 务函数(interrupt ser-vice routine,简称ISR)。这句话说起来简单,但实际上CPU为了保证执行完中断服 务函数之后能够顺利的回到之前的工作,需要做一系列的准备工作,如下图中第3项所述。


在执行完当前一条指令后,CPU会保存当前程序计数器(PC)的值,这样就记录下现在程序执行到的位置,以便中断结束后能返回主程序继续执行。同时状态寄存器SR的值也会被保存起来。接下来CPU会将SR寄存器清零,这将使单片机退出所有低功耗状态以便执行中断程序。另外SR寄存器中的全局中断使能位GIE也将被禁用,这样就避免了在执行中断服 务函数时CPU再响应其他中断。退出中断后CPU会重新取回SR寄存器的值,程序回到进入中断之前的位置继续执行。

接下来中断向量的地址会被加载到程序计数器(PC)中,这样就会进入到中断服 务函数的入口开始执行中断程序。CPU之所以能识别出中断的源头,是因为每一个中断标志位(IFG)是与一个中断服 务函数(ISR)一一对应的,这个对应关系存在一个表里面,这个表叫做中断向量表。例如P1端口的中断就与中断向量PORT1_VECTOR对应,因此检测到P1口的中断信号后就会自动进入PORT1_VECTOR对应的中断服 务函数。更多关于中断向量表的知识将在后续章节中详细介绍。


4)  执行中断服 务函数

中断服 务函数(ISR)里的内容是由用户编程的,想要单片机在中断时执行什么任务,就在中断服 务函数中编写相应指令。例如想要实现按键按下时LED灯亮起,那么就在GPIO中断函数中加入LED控制的语句。


中断服 务函数所做的事情由上图第4项描述,其中最关键的就是红色的“Runyour interrupt’s code”。不过需要注意在此之前,还需要判断是否为“grouped interrupt”。由于MSP430的中断资源是很宝贵的,很多中断会共用一个中断向量入口。例如前面提到的P1端口中断,当P1口的8个I/O口中的任何一个检测到中断事件时,都会进入中断服 务函数。这时就需要在中断服 务函数中先查询中断标志位,以判断究竟是哪个I/O口“出事”了。

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
5#
发表于 2019-1-24 14:34:49   只看该作者
本帖最后由 Shuyang 于 2019-1-24 14:37 编辑

4.3 中断优先级和中断向量表

MSP430的很多外设都能触发中断,那么如果多个中断同时被触发会怎么样呢?之前在介绍中断工作过程时提到过,MSP430在进入中断时会自动关闭全局中断使能位(GIE),所以如果MSP430已经在一个中断里,是不能再进入另一个中断的,这时另一个(或多个)中断排队等候,等到当前中断结束后再进入下一个中断。

如果有多个中断同时在等候,需要注意它们的优先级并不是由时间顺序决定的,而是由MSP430本身规定的中断优先级来决定,这个优先级在中断向量表里可以看到。所以在当前中断结束之后,CPU会自动按照中断优先级来依次执行。

说了这么多,下面我们就来见一见中断向量表的“真身”,中断向量表在MSP430G2553 datasheet的“Interrupt VectorAddresses”部分可以找到。


表中第一列是中断事件来源的名称;第二列是对应的中断标志位;第三列表示中断的性质,其中non-maskable是无法关掉的中断,即不受GIE控制的中断,maskable是可以被GIE关掉的中断;第四列是中断向量在存储器中的位置;最后一列就是中断优先级。

中断向量表中大多数中断属于“grouped interrupt”,即多个中断源共用一个中断向量入口。例如P1.0-P1.7中断都使用P1中断这一个入口。要识别中断具体来自哪一个I/O口,需要查看P1IFG寄存器,P1IFG共8位,分别对应P1.0-P1.7,当某个引脚出现中断事件时对应的P1IFG.x就会被置位,在退出中断之前一定不要忘记手动将P1IFG.x复位。另外P1IE是P1口的中断使能寄存器,也有8位,可以分别控制每个I/O口的中断使能。

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
6#
发表于 2019-1-24 15:06:04   只看该作者
本帖最后由 Shuyang 于 2019-1-24 15:09 编辑

4.4 如何写一个中断函数

要写一个带有中断的程序,需要做下面几件事情:

1)     配置外设中和中断有关的寄存器,例如I/O口中断是上升沿触发还是下降沿触发,定时器中断的计数方式和定时值等等。
2)     依照中断服 务函数的模板写中断服 务函数,添加中断后要干什么的代码。
3)     使能外设的中断,使能全局中断(GIE)
4)     一旦中断发生,CPU停下主函数中的任务,并标记位置,进入中断服 务函数,执行完中断服 务函数之后回到主函数标记位置处继续运行。



中断服 务函数

我们想要单片机在中断里执行什么任务,都是由中断服 务函数决定的。中断服 务函数的编写和一般的函数略有不同,我们以P1端口的中断服 务函数来做一个说明:
  1. // Port 1 interrupt ser-vice routine
  2. #pragma vector=PORT1_VECTOR
  3. __interrupt void Port_1(void)
  4. {
  5.   P1OUT ^= 0x01;                            // P1.0 = toggle
  6.   P1IFG &= ~0x08;                           // Clear P1.3 IFG
  7. }
复制代码

1) #pragma vector=PORT1_VECTOR

#Pragma是编译器指令,是告诉编译器将函数与中断向量连接起来。“vector=”后面是中断向量地址的宏定义,例如P1口中断就是PORT1_VECTOR,定时器中断就是TIMER0_A1_VECTOR。

不同外设的中断向量名在哪里找呢?打开CCS的安装文件夹,例如C:\ti\ccsv7\ccs_base\msp430\include,在这个文件夹中搜索芯片的具体型号,如msp430g2553,可以找到一个名为msp430g2553.h的头文件。这个头文件中包含了所有寄存器位的宏定义,包括中断向量的宏定义。在msp430g2553.h中找到“Interrupt Vectors”这个部分(如下图),其中包含了所有中断向量的名称。例如P1端口的中断向量名就是PORT1_VECTOR。



2) __interrupt void Port_1(void)

__interrupt关键字表明这是一个中断服 务函数,CPU见到这个关键字以后就会去做中断之前的准备工作。Port_1是用户自己取的函数名称,这个名称可以任意命名。


3)  中断服 务函数的具体内容

中断服 务函数的内容依据中断的不同种类有所差别。退出中断前一定不要忘记将中断标志位复位。

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
7#
发表于 2019-3-5 12:23:33   只看该作者
本章参考资料:     
  
1.     《从零开启大学生电子设计之路--基于MSP430 LaunchPad口袋实验平台》,杨艳,傅强编著,北京航空航天大学出版社,2014年8月第1版。
2.     “MSP Design Workshop” by TexasInstruments, Version 4.60, May 2016, http://processors.wiki.ti.com/index.php/MSP_Design_Workshop
3.     “Getting Started with the MSP430G2553Value-Line LaunchPad Workshop” by Texas Instruments, Version 2.22, July 2013, http://processors.wiki.ti.com/in ... _LaunchPad_Workshop
4.     MSP430G2553 datasheet (Rev.J), May2013, http://www.ti.com/mcu/docs/litab ... 2&familyId=1937
5.     MSP430x2xxFamily User's Guide, SLAU144J, http://www.ti.com/cn/lit/pdf/slau144
6.     MSP-EXP430G2LaunchPad Evaluation Kit User's Guide, SLAU318G, March 2016, http://www.ti.com/cn/lit/pdf/slau318

0

主题

7

帖子

4

积分

一粒轻沙

Rank: 1

积分
4
8#
发表于 2019-4-19 15:59:53   只看该作者
正在与中断做斗争

0

主题

14

帖子

53

积分

一粒轻沙

Rank: 1

积分
53
9#
发表于 2019-4-20 18:40:33   只看该作者
好!谢谢老师!

0

主题

3

帖子

16

积分

一粒轻沙

Rank: 1

积分
16
10#
发表于 2019-6-28 19:48:31   只看该作者
给力,说的很清楚了

0

主题

15

帖子

138

积分

二氧化硅

Rank: 2

积分
138
11#
发表于 2019-7-7 11:16:01   只看该作者
谢谢老师

0

主题

2

帖子

61

积分

一粒轻沙

Rank: 1

积分
61
12#
发表于 2019-7-7 19:06:23   只看该作者
请问老师,如果碰到要使用多个中断时,应该要注意什么?

17

主题

137

帖子

553

积分

单晶硅锭

Rank: 3Rank: 3

积分
553
13#
发表于 2019-7-8 10:29:22   只看该作者
hfy 发表于 2019-7-7 19:06
请问老师,如果碰到要使用多个中断时,应该要注意什么?

430是没有中断嵌套的,所以一个中断如果正在进行,是无法被打断的,后来的中断只能排队等待前面的中断结束

如果一个中断正在发生时同时有多个中断在排队,那么这些中断将按照中断向量表中的优先级来决定哪个先执行,当然前提还是要等到正在执行的中断结束

如果需要在两个中断里同时改写同一个变量或者是寄存器,那么就要小心了,因为单片机在写入时实际上都是“读-改-写”的顺序(如果不理解,网上有很多关于这个的文章),因此如果在主程序和中断里同时去写一个寄存器或是变量的话,是有可能出现最终的值和预想值不同的情况的

这种情况的解决方法是可以先用另外一个变量做下中转,出了中断之后再对最终目标赋值,尽量避免在中断和主程序里同时对一个变量进行改写

0

主题

2

帖子

61

积分

一粒轻沙

Rank: 1

积分
61
14#
发表于 2019-7-10 09:50:13   只看该作者
Shuyang 发表于 2019-7-8 10:29
430是没有中断嵌套的,所以一个中断如果正在进行,是无法被打断的,后来的中断只能排队等待前面的中断结 ...

谢谢老师!

0

主题

24

帖子

20

积分

一粒轻沙

Rank: 1

积分
20
15#
发表于 2019-7-10 20:03:03   只看该作者
太棒了,好看

0

主题

14

帖子

6

积分

一粒轻沙

Rank: 1

积分
6
16#
发表于 2019-7-24 10:22:56   只看该作者
谢谢分享

0

主题

13

帖子

15

积分

一粒轻沙

Rank: 1

积分
15
17#
发表于 2020-2-15 10:21:44   只看该作者
Shuyang 发表于 2019-1-24 15:06
4.4 如何写一个中断函数

要写一个带有中断的程序,需要做下面几件事情:

解决了很大的疑惑,谢谢

0

主题

1

帖子

61

积分

一粒轻沙

Rank: 1

积分
61
18#
发表于 2020-9-28 16:06:26   只看该作者
Shuyang 发表于 2019-1-24 16:08
4.5 中断函数练习

下面我们以P1端口的中断为例,练习一下如何编写一个完整的带有中断的程序。练习中我们要 ...

找了好久资料 学习了 感谢分享!

0

主题

6

帖子

4

积分

一粒轻沙

Rank: 1

积分
4
19#
发表于 2020-12-6 02:41:45   只看该作者
谢谢分享
快速回复 返回顶部 返回列表