目录
一、中断的概念
中断就是在做一件事情时,出现了一个优先级更高的突发事件,从而我们要停止手头的工作,需要先把那件更紧急的工作做完,再回过头来做最开始的事情:

比如,这是一段闪灯程序和一段串口接收程序:

我们想实现在闪灯时接收数据的功能,那么直接把这两段代码合并到一个while循环,实现一次亮、灭灯后,接收一次程序行不行呢?

是不可以的。因为STM32的串口有两级缓冲,而每级缓冲只能保存一个字节,理想情况下,Rx引脚每接收一个字节,那个字节就直接通过一二级缓冲,就把它读出来,但若数据读取的没有那么及时,比如接收1、2、3、4、5五个字节时,1还卡在二级缓冲,后面的数据不断冲进来,它们就只能先存在于一级缓冲,于是2、3、4、5这些不断在一级缓冲将上一个字节给覆盖,最终缓冲区内就只剩下了1和5两个数字:


(不及时读取二级缓冲的数据,将二级缓冲的地方腾出来,一级缓冲就会不断地去覆盖数据,直到只剩下最后一字节数据,只剩下1和5)
串口什么时候收到的数据,我们事先是不知道的,它是完全随机的,可能发生在程序执行的任何阶段,我们先假设串口接收数据是发生在闪灯的过程中:

由代码可知,闪灯程序的执行时间是400ms,而串口接收5个字节只需要:

由此可知,当串口的波特率是115200时,串口接收5个字节只需要0.5ms(即5个字节全部进入缓冲的时间) ,但闪灯程序的执行至少需要400ms。若在闪灯程序执行过程中接收到5个字节,单片机此时需要执行完闪灯程序后才能执行读取字节的命令。但是闪灯程序长达400ms,缓冲中的字节不能及时读取,就会被后来接收到的字节覆盖,导致数据丢失。
但我们可以使用中断的方法,它的逻辑是"不等你程序做完,我直接喊你处理数据",如果在闪灯的过程中,哪怕闪灯刚到一半,串口只要接收到1个字节,立刻触发"中断信号",此时闪灯程序会立即停止,立刻执行"中断响应函数",去取串口数据,并将缓冲区里的字节放到一边,中断程序执行完成后,再去取回之前取出的数据,继续之前的步骤 :

每通过串口收到一个数据,就触发一次中断,调用一次中断响应函数,然后我们在中断响应函数里面,及时的把数据读出来并处理,这样就再也不用担心数据丢失的问题了,中断在单片机的编程过程中使用的非常广泛,在单片机内部,几乎每一个模块都能产生中断。
二、中断优先级
1.中断优先级分组
对于我们使用的单片机来说,它用四bit位的二进制数来表示中断优先级,紧急性从按序号的增长由高到低,共分为16个中断优先级:

这16个中断优先级又可以手动操作分为两个部分:

你可以手动将它们分为这些分组方式:

2.中断排队
中断响应函数里面要是有新的中断,就要排队:

所以它们的执行顺序应该是:

3.中断嵌套
在中断响应函数执行时,如果来了个新中断,它比旧中断更加紧急来不及排队,这时就要打断旧中断:

而是否发生嵌套,只和抢占优先级有关:

根据实际状况,你可以手动设置抢占优先级的多少:


4.例子
假设我们采用两位抢占优先级和两位子优先级的分组方式,其中,
中断1的响应优先级是11,11的二进制是1011B,它代表抢占优先级是2,子优先级是3;
中断2的响应优先级是8,8的二进制是1000,它代表抢占优先级是1,子优先级是0,
在1执行的过程中,来了2,1与2的抢占优先级相同,所以不会发生中断嵌套,它需要排队,所以等1执行完,2再执行。当后面又来了3、4、5、6,它们的执行顺序应该是这样的:

三、NVIC模块
1.引子
在设置完CubeMX三件套后(建工程、选芯片、选debug),点击NVIC模块且有很多参数:

在I2C、SPI、USART模块都可以看到有NVIC Settings:

2.理解:

管理中断
NVIC具体怎么管理中断的呢?

(模块指GPIO、RCC、I2C、SPI、USART、DMA、TIM、ADC)
大部分模块可以产生多个中断,所以中断数量是n*m级别的,CubeMX中点开所有中断也可以看到有大量的选项:

这么多中断需要一个管理员才能更高效的管理,而NVIC就是中断的管理员,下图的复选框和图中的开关一一对应,断开开关就是即使中断发生了,我们也不去响应它,由此来控制中断是否发生:


分配中断优先级
而这四个方快就是中断优先级,蓝色虚线是可以移动的,用来中断优先级分组:

上面我们学习到的能够手动优先级分组,对应到CubeMX中的操作,就是下面这些选项:


仲裁电路
决定着发生嵌套还是排队,以及排队的先后顺序,如果一个中断响应函数胜出了,就需要在Flash中查表并调用它的中断响应函数来执行。
Flash
单片机内的flesh相当于硬盘,中断响应函数相当于一串代码,也存储在flesh中:

中断向量表
中断响应函数的字典,单片机需要查询中断向量表才能找到中断响应函数。
四、串口中断接收实验
使用中断的方式,实现串口数据的接收
通过串口控制LED的闪灯速度
1.搭建电路


2.代码实现
通过控制闪灯间隔,实现LED的快、中、慢闪:

选择debug、设置PC13引脚板载LED:


编写代码:



因为稍后要调试代码,所以先关掉代码优化

编译、调试、全速运行(run),发现单片机慢闪(全速运行相当于不断按下载)。
打开Watch1,从而实现变量的快速修改:


右下角出现监视窗口Watch1,Name中填上要监视的变量,并取消勾选Hexad...,Value将会以十进制呈现:


Value改成300,闪烁会加快。
点到这个界面:

而这个中断的详细解释就是:

打勾,使能这个中断,再次生成代码,我们来进行一下中断的使用。
3.使用中断
学习一下使用中断的代码:

(IT是interrupt的意思)
用中断方式接收数据,while执行常规程序,每接收一次数据,进行一次中断,并把数据保存在数据缓冲区内,接收到最后一个数据后调用回调函数,对数据进行处理:

通过中断能决定每次接收几个字节的数据,那么如何接收不定长度的数据?
不断接收一个字节的数据,然后直到数据接收完成:


keil上代码实现如下:
先打开uart.c文件,到2607行:

_weak 是"可覆盖"的意思:


我们先声明变量,再将_weak后面的语句复制下来,构建函数:

(注意c语言格式)

之后进行编译、下载、复位:

通过发送字符可控制灯闪的快慢。