理论篇
通俗的说:比如我正在写博客,老板突然给我一个任务,我暂停的写博客,转而把老板布置的任务完成之后,再继续写博客,这个过程就可以理解成中断。
百度引用:‘中断是指CPU在处理某一件事A时,发生了另一件事B,请求CPU迅速去处理(这个过程是“中断发生”);此时,CPU暂时停止当前的A事件(这个过程是“中断响应”),转去处理B事件(这个过程是“中断服务”);待CPU将B事件处理完毕后,再回到事件A被中断打断的地方继续处理事件A(这个过程是“中断返回”)。这一过程,称之为中断。’
我认为要想理解中断的概念,作为一个工科生,还应该理解线程的概念
线程:程序运行流的最小单元,一个程序是有一个或多个线程组成。
你可以理解成线程就是单片机执行的一个任务,比如,流水灯、读取发送某一个传感器的数据。但是我们平时写的单片机程序一般就是单线程的,就是单片机一个
多线程编程:如果你还是个小白,可以简单的理解我们使用的简单的单片机就只能执行一个任务,但是后面通过系统的学习你还会知道,就算是单线程的话,单片机也可以分时“执行多个任务”,这里是借助了计算机实现多线程的编程方法(比如一边播放视频一边听音乐,它可以先执行听音乐的任务,当视频的下一帧到,它先把音乐的任务暂停,保存这个任务所有的变量,执行播放视频到任务,然后再把播放视频的任务暂停,执行播放音乐的任务,不断地循环下去,另外你还要知道单片机的执行速度是非常快滴,快到你根本分辨不出来中间有卡顿。所以就会认为任务同时做了两件事)
这里可能有很多人想,现在的很多电子设备都是几核几线程的(多核、线程、进程、多线程的理解这里就不多讲了),多个CPU实现了并行运行方式,属于真正的多线程,但是多个CPU的控制方案成本几乎成倍的增加,而且要是实现多个CPU之间的通讯花费的实践较长,并且干扰影响较大。
总结起来,就是你需要知道中断就是单片机处理任务A时,发生了触发中断的条件停下来执行另一个任务B,触发中断的条件就属于中断源,任务B就是中断任务(中断服务函数)。当中断任务执行完成后中断返回,在回到任务A中断的地方继续处理任务A。
为使系统能及时响应并处理发生的所有中断,系统根据引起中断事件的重要性和紧迫程度,硬件将中断源分为若干个级别,称作中断优先级。百度百科
这里再举一个简单的例子:仍然是我正在写博客,老板给我发了一个任务A,但是同时老婆也给我一个任务B,两个任务同时到达,我该先执行哪一个呢?当然先执行老婆的任务B了,然后再执行老板的任务A,再完成之后在继续写我的博客。在这里执行老婆的任务B就比执行老板任务A的中断优先级级别高。但是我要是正在执行老婆B的任务,这个时候老板的任务A过来了,这个时候我会继续执行老婆的任务B,也是因为老板的任务A没有老婆B的任务优先级高,这里
老婆任务 > 老板任务 > 写博客任务
所以多级中断的处理原则:当多级中断同时发生时,CPU按照由高到低的顺序响应。高级中断可以打断低级中断处理程序的运行,转而执行高级中断处理程序。当同级中断同时到时,则按位响应。
STM32 的中断向量具有两个属性,一个为抢占属性,另一个为响应属性,其属性编号越小,表明它的优先级别越高。
抢占属性:是指打断其他中断的属性,即因为具有这个属性会出现嵌套中断(在执行中断服务函数A 的过程中被中断B 打断,执行完中断服务函数B 再继续执行中断服务函数A),抢占属性由NVIC_IRQChannelPreemptionPriority 的参数配置。
响应属性:应用在抢占属性相同的情况下,当两个中断向量的抢占优先级相同时,如果两个中断同时到达, 则先处理响应优先级高的中断, 响应属性由NVIC_IRQChannelSubPriority 参数配置。
NVIC 的优先级组:在配置优先级的时候,还要注意一个很重要的问题,即中断种类的数量。NVIC 只可以配置16 种中断向量的优先级,也就是说,抢占优先级和响应优先级的数量由一个4 位的数字来决定,把这个4 位数字的位数分配成抢占优先级部分和响应优先级部分。
这些中断事件主要根据中断源不同划分。
定时器中断是你需要定时的时间A ms到了,会产生定时器中断,执行A ms后的中断内容。主要执行定时完成的任务。属于片内中断。
外部中断是中断源来自单片机的外部,比如61单片机的P3.2引脚检测到了跳变沿,接着执行外部中断任务。主要执行由外部因素影响的任务。属于片外中断。
串行口发送或接受中断是在串行口有接收到数据执行的中断任务。主要接收串行口的数据。属于片内中断。
实战篇
这里通过一个51单片机的外部中断程序来和STM32F1的中断程序加深对中断的理解,定时器中断、串口中断在以后的文章里会介绍到。这里要知道51单片机只有P3.2和P3.3引脚的低电平或下降沿信号中断。而STM32每个引脚都可以。。。/可怕不,差距。
注意51单片机一共支持5个中断源,其中2个外部中断源,3个内部中断源
(1)外部中断0,由INT0(P3.2引脚)输入。
(2)外部中断1,由INT1(P3.3引脚)输入。
(3)定时/计数器0溢出中断(T0)请求。
(4)定时/计数器0溢出中断(T1)请求。
(5)串行口发送/接收中断请求。
这里使用论坛51单片机中断学习知识点总结中的代码修改解释
原文:https://blog.csdn.net/whalefall/article/details/79900817
1、设置中断触发方式,即IT0=1或0,IT1=1或0 当IT0=0时,为电平触发方式。当IT0=1时,为边沿触发方式(下降沿有效)。
2、开对应的外部中断,即EX0=1或EX1=1; 外部中断0允许位或外部中断1允许位;;
3、开总中断,即EA=1;
4、等待外部设备产生中断请求,即通过P3.2,P.3.3口连接外部设备产生中断
5、中断响应,执行中断服务函数
eg. 外部中断0实验 *
实现现象:下载程序后按下K3按键可以对D1小灯状态取反。
#include "reg52.h" //此文件中定义了单片机的一些特殊功能寄存器
typedef unsigned int u16; //对数据类型进行声明定义
typedef unsigned char u8;
sbit k3=P3^2; //定义独立按键K3
sbit led=P2^0; //定义P20口是led
void delay(u16 i)
{
while(i--);
}
void Int0Init()
{
//设置INT0,外部中断0
IT0=1; //跳变沿出发方式(下降沿)
EX0=1; //打开INT0的中断允许。
EA=1; //打开总中断
}
void main()
{
Int0Init(); // 设置外部中断0
while(1); //这里系统就是不断死循环,如果有中断请求,执行中断程序,然后继续死循环。
}
void Int0() interrupt 0 //外部中断0的中断函数
{
delay(1000); //延时消抖
if(k3==0)
{
led=~led;
}
}
STM32的每个IO口大可以作为中断输入。但是需要设置IO 口与中断线的映射关系,这里内部的工作原理感兴趣的可以查阅芯片手册,
这里附上正点原子额开发指南的部分如下
1) 初始化 IO 口为输入。
这一步设置你要作为外部中断输入的 IO 口的状态,可以设置为上拉/下拉输入,也可以设置为浮空输入,但浮空的时候外部一定要带上拉,或者下拉电阻。否则可能导致中断不停的触发。在干扰较大的地方,就算使用了上拉/下拉,也建议使用外部上拉/下拉电阻,这样可以一定程度防止外部干扰带来的影响。
2)开启 IO 口复用时钟,设置 IO 口与中断线的映射关系。
STM32 的 IO 口与中断线的对应关系需要配置外部中断配置寄存器 EXTICR,这样我们要先开启复用时钟,然后配置 IO 口与中断线的对应关系。才能把外部中断与中断线连接起来。
3)开启与该 IO 口相对的线上中断/事件,设置触发条件。
这一步,我们要配置中断产生的条件, STM32 可以配置成上升沿触发,下降沿触发,或者任意电平变化触发,但是不能配置成高电平触发和低电平触发。这里根据自己的实际情况来配置,同时要开启中断线上的中断。这里需要注意的是:如果使用外部中断,并设置该中断的 EMR位的话,会引起软件仿真不能跳到中断,而硬件上是可以的。而不设置 EMR,软件仿真就可以进入中断服务函数,并且硬件上也是可以的。建议不要配置 EMR 位。
4)配置中断分组( NVIC),并使能中断。
这一步,我们就是配置中断的分组,以及使能,对 STM32 的中断来说,只有配置了 NVIC的设置,并开启才能被执行,否则是不会执行到中断服务函数里面去的。
5)编写中断服务函数。
这是中断设置的最后一步,中断服务函数,是必不可少的,如果在代码里面开启了中断,但是没编写中断服务函数,就可能引起硬件错误,从而导致程序崩溃!所以在开启了某个中断后,一定要记得为该中断编写服务函数。在中断服务函数里面编写你要执行的中断后的操作。
//外部中断 0 服务程序
void EXTI0_IRQHandler(void)
{
delay_ms(10); //消抖
if(WK_UP==1) //WK_UP 按键
{
LED0=!LED0;;
}
EXTI->PR=1<<0; //清除 LINE0 上的中断标志位
}
//外部中断初始化程序
//初始化 PA0 为中断输入.
void EXTIX_Init(void)
{
KEY_Init();
Ex_NVIC_Config(GPIO_A,0,RTIR); //上升沿触发
MY_NVIC_Init(2,3,EXTI0_IRQn,2); //抢占 2,子优先级 3,组 2
}
int main(void)
{
Stm32_Clock_Init(9); //系统时钟设置
delay_init(72); //延时初始化
LED_Init(); //初始化与 LED 连接的硬件接口
EXTIX_Init(); //初始化外部中断输入
LED0=0; //先点亮红灯
while(1)
{
printf("OK\r\n");
delay_ms(1000);
}
}
//实验结果就是按下KEY0控制DS0开关,CPU不断地执行主函数里的while(1)中程序,每隔一秒种打印一个OK,
//当有外部中断时,进入反转LED的中断服务程序。执行完之后继续执行主函数里的死循环。
最后,皮一下!/手动滑稽