四轴飞行器DIY活动征集报名中!更有现金奉送哦~→ 立即报名 ←
电子产品世界 » 论坛首页 » 高校专区 » 坤创E-Geek/天科大新电社 » 3队MSP-EXP430G2ET学习(五)

共2条 1/1 1 跳转至

3队MSP-EXP430G2ET学习(五)

菜鸟
2019-07-14 12:02:16    评分

MSP-EXP430G2ET中断系统的整理笔记

      写在前面:学习这个中断是时候出现了一些问题,当时一直没理解明白再加上其他学习任务所以就当误到了现在。今天学习的中断我是跟以前学习的中断进行对比性学习的,然后找出它们的各自优缺点,能在以后制作作品时有所比较。接下的写的前面都是找来的具体解释有些繁琐,其实最重要的也就后面那几步。

    一,什么是中断?

        1.中断是一个过程,如果学过51,32单片机的都应该很熟悉下面的介绍吧;

    CPU在处理某一事件A时,发生了另一事件B请求CPU迅速去处理(中断发生);

    CPU暂时中断当前的工作,转去处理事件B(中断响应和中断服务);

    待CPU将事件B处理完毕后,再回到原来事件A被中断的地方继续处理事件A(中断返回),这一过程称为中断 。

         2.而MSP-EXP430G2ET这块板子主要优势就在于低功耗!以前的板子它们的CPU使用率太过频繁,功耗太大,所以MSP-EXP430G2ET就注重在这一点上,接下来是MSP-EXP430G2ET对中断的定义:

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

        归根到底它们只是中断的过程不一样,但是原理还是一样的。

   二,什么时候用到中断,中断常见的场景

      GPIO 输入状态的检测:如果没有 GPIO 中断, CPU 必须不断通过读取 GPIO 引脚状态来知道目前的输入是什么。有了 GPIO 中断, CPU 就可以解放出来,执行其他更重要的任务,或是进入休眠状态, 直到 GPIO 中断信号产生时再唤醒 CPU 去对 GPIO 输入状态的改变做出响应;

      利用定时器中断实现延时:之前章节中我们需要延时的话,都是通过执行一个空循环来实现的,这种方式无疑是对 CPU 资源的“浪费”。而有了定时器中断, 我们就可以给定时器设定一个固定的时间间隔,在到达设定时间之后通知 CPU 进入定时器中断。 这就像一个倒计时的闹钟一样,在闹钟响起之前, CPU 无需消耗资源。

      按键使用,其实也是属于GPIO输入,可以说按键实验简直是完美的解释了中断程序,还记的之前在介绍GPIO口时写的一个按键程序吗?实现内容是首先点亮绿色的LED,也就是输出状态,然后就是用开发板上的按键输入来改变红色的LED亮灭。这就是中断,红色LED本是灭的状态,当按键按下,CPU就执行按下后的现象,这是部分实验程序如下:

 while(1)  
   {  
    volatile unsigned  int i;  
     P1OUT ^= 0X01;//点亮绿色的led  
      if((P1IN&0X08)==0X00)  
      {  
        i=2000;  
        while(i--);//消除抖动  
        if((P1IN&0X08)==0X00)  
         {  
          P1OUT ^= 0X40;  
          }  
        while(!(P1IN&0X08));//检测按键是否松开  
      }  
  }

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

   三,中断的固有格式和工作过程

   1.MSP430 的中断服务函数有固定的格式。
   #pragma 关键字后面是中断向量, 中断向量决定了这个中断函数响应的是哪一个中断源。例如这里的 PORT1_VECTOR 就代表来自 P1 I/O 口的中断。

   __interrupt(注意有两个下划线)关键字会告诉编译器这个函数是一个中断服务函数,后面的函数名(rx)是编程者可以自行定义的。

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

   2.中断的工作过程

    1) 中断事件发生

     首先,中断事件的发生是一个中断的起点。中断事件指的是可以导致 CPU 进入中断的外部事件, MSP430 中有许多外设可以产生中断事件,例如串口、 GPIO、定时器、 ADC 等等。GPIO 为例, I/O 口上的电平变化就可以是一个中断事件,如果 I/O 口外接按键,那么按键按下或抬起所造成的电平变化就可以导致中断事件发生。 MSP430 系列大部分型号允许两组 I/O口(P1 P2)作为中断输入。

    2) 中断标志位被置位

    当中断事件被单片机探测到之后,对应的中断标志位(interrupt flag,简称 IFG)就会被自动置位。 置位之后除非进行复位,否则中断标志位会一直保持置位状态。这样做的好处是当有些中断事件发生的时间很短时,中断信号不会被 CPU 所忽略。 MSP430 的外设中有该外设中断所对应的 IFG 寄存器,中断事件发生时这些寄存器就会被改写。中断信号是如何到达 CPU 的呢?中断事件发生以后,对应的外设 IFG 寄存器被置位,但这并不意味着 CPU 能够接收到中断信号,要想 CPU 能接收到中断信号,必须将中断使能位(Interrupt enable bits,简称 IE)打开。

1563072037469097.png

        中断使能位的存在是使得 CPU 能够自由选择对哪个中断事件作出响应。在单片机工作过程中,可能会产生多个不同外设的中断事件,例如定时器溢出、串口接到数据、 I/O 口电平变化等,但单片机 CPU 并不一定想对所有这些中断事件作出响应。有了中断使能位,就像有了开关,CPU 就可选择只响应那些需要响应的中断事件。 默认情况下, 所有中断使能位都是关闭的(除了看门狗以外)。我们需要通过编程来打开需
要的中断使能位。例如想要检测
P1.3 口所连接的按键,就打开 P1 端口的中断使能位。
       除了上述中断使能位以外,
CPU 中还有一个全局中断使能位,它相当于一个总开关。MSP430 的全局中断使能位称为 GIE,它在 SR 寄存器中。

    3) CPU 响应中断
      现在我们假设一个中断事件已经发生,
IFG 也已被置位,对应的中断使能位也已经打开,这样我们确保中断信号能够顺利到达 CPU。 那么 CPU 接收到中断信号之后如何做出反应呢?CPU 接收到中断信号之后,会暂停正在执行的工作,并执行中断服务函数(interrupt serviceroutine,简称 ISR)。这句话说起来简单,但实际上 CPU 为了保证执行完中断服务函数之后能够顺利的回到之前的工作,需要做一系列的准备工作,如下图中第 3 项所述。

     1563072408310250.png

       在执行完当前一条指令后, 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 控制的语句。

           1563072924614961.png

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

     四,中断优先级和中断向量表 

        MSP430 的很多外设都能触发中断,那么如果多个中断同时被触发会怎么样呢?之前在介绍中断工作过程时提到过, MSP430 在进入中断时会自动关闭全局中断使能位(GIE) ,所以如果MSP430 已经在一个中断里,是不能再进入另一个中断的,这时另一个(或多个)中断排队等候,等到当前中断结束后再进入下一个中断。
       如果有多个中断同时在等候,需要注意它们的优先级并不是由时间顺序决定的,而是由MSP430 本身规定的中断优先级来决定,这个优先级在中断向量表里可以看到。所以在当前中断 结束之后, CPU 会自动按照中断优先级来依次执行。
       说了这么多,下面我们就来见一见中断向量表的“真身”,中断向量表在
MSP430G2553datasheet 的“Interrupt Vector Addresses”部分可以找到。 

       1563073303523915.png      

           表中第一列是中断事件来源的名称;第二列是对应的中断标志位;第三列表示中断的性质,其中 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 口的中断使能。
       五,去写一个中断函数的流程

         重点来啦啊!!!要写一个带有中断的程序,需要做下面几件事情:
      1) 配置外设中和中断有关的寄存器,例如 I/O 口中断是上升沿触发还是下降沿触发, 定时器中断的计数方式和定时值等等。

          打开或者局部中断:打开局部中断一般是想关特殊功能寄存器相关的位置1。

                以P1口外部中断为例;打开局部中断:P1IE |= BIT0;//打开P1.0外部中断, BIT0的值表示0x01,即把P1IE的第一位置1

                关闭局部中断的话也就是置0;  P1IE &= ~BIT0://表示为关闭P1.0外部中断。

      2) 依照中断服务函数的模板写中断服务函数,添加中断后要干什么的代码。
      3) 使能外设的中断,使能全局中断(GIE
       

      4) 一旦中断发生, CPU 停下主函数中的任务,并标记位置,进入中断服务函数,执行完中断服务函数之后回到主函数标记位置处继续运行。 

    1563075108961271.png       1563076837351390.png

        中断服务函数
   我们想要单片机在中断里执行什么任务,都是由中断服务函数决定的。中断服务函数的编写和一般的函数略有不同, 我们以 P1 端口的中断服务函数来做一个说明:

#pragma vector=PORT1_VECTOR//
__interrupt void Port_1(void)
{
P1OUT ^= 0x41; // 两个led灯 
P1IFG &= ~0x08; // P1.3 IFG已清除
}


    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

  1563075372212336.png

    六,做一个实验

          要求用中断函数用按键控制两个LED灯同时亮灭。

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

          第一步初始化:

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

P1OUT = 0x08; // P1.3 启用全局中断1.3设置,否则重置
P1REN |= 0x08; // P1.3 上拉
P1IE |= 0x08; // P1.3 中断已启用
P1IES |= 0x08; // P1.3 高-低边缘
P1IFG &= ~0x08; // P1.3 IFG已清除

_BIS_SR(GIE);// 启用全局中断

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

          第二步while()循环

    初始化完成后,需要写一个 while 循环, 由于现在按键检测是通过中断功能自动完成的,因此在这个循环中 CPU 其实没有做任何事情,只是简单地等待中断事件的发生。 当按键按下的一刹那(即 P1.3 的下降沿), CPU 将检测到中断事件,并自动进入对应的中断服务函数。 

int main(void)
{
......
while (1) {
__no_operation(); // Do nothing
}
}

          第三步 中断服务函数

       编写中断服务函数时,首先要找到对应的中断向量。本练习中我们使用的是 P1 端口的中断,其中断向量名为 PORT1_VECTOR。接下由上述的说明编写中断服务函数。
    中断服务函数里具体要做的事情包括:
    i) 翻转 P1.0和P1.6的电平;
    ii) 清除 P1.3 中断标志位。
    这样,当每一次按下
按键时,板子上的 LED 灯就会改变状态。 

#pragma vector=PORT1_VECTOR//中断向量名
__interrupt void Port_1(void)
{
P1OUT ^= 0x41; // 两个led灯 
P1IFG &= ~0x08; // P1.3 IFG已清除
}

      然后就构成了完整的程序了。

     程序代码如下:

#include "MSP430G2553.h"

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
//配置io口和中断的有关寄存器
P1DIR = 0x41; //两个led灯 

P1OUT = 0x08; // P1.3 启用全局中断1.3设置,否则重置
P1REN |= 0x08; // P1.3 上拉
P1IE |= 0x08; // P1.3 中断已启用
P1IES |= 0x08; // P1.3 高-低边缘
P1IFG &= ~0x08; // P1.3 IFG已清除

_BIS_SR(GIE);// 启用全局中断

while (1)
{
__no_operation(); // Do nothing
}
}
#pragma vector=PORT1_VECTOR//中断向量名
__interrupt void Port_1(void)
{
P1OUT ^= 0x41; // 两个led灯 
P1IFG &= ~0x08; // P1.3 IFG已清除
}

      这次学习中,最重要的一点就是头文件发生了改变!!!所以,大家在写中断函数过程中一定要记得修改头文件。




高工
2019-07-15 09:44:39    评分
2楼

很不错,很详细,继续努力


共2条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]