中断相关知识
中断的定义
中断,是在主程序运行过程中,出现了特定的中断触发条件,这个中断触发条件就是中断源,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。
可以看出来,中断就是在正常主程序执行的某个时刻,发生了中断触发条件,比如对于外部中断来说,可以是引脚发生了电平跳变;对于定时器来说,可以是定时的时间到了;对于串口通信来说,可以是接收到了数据。
当这些事件发生时,情况会比较紧急,比如外部中断来了,你如果不来处理,下一个跳变信号就跟着过来了
外部中断和中断系统;比如串口接收中断来了,你如果不来读取接收到的数据,那下一个数据再过来,就会把原来的数据覆盖掉。所以我们希望当中断条件满足时,CPU能够立即停止当前执行的程序,转而去处理这些中断事件的程序。比如外部中断来了,我想要计次,那就“变量++”,串口中断来了,我就把接收到的数据转存起来,当这些紧急事件处理完成之后,CPU还能回到原来的停下的地方继续运行,这就是中断的处理流程和用途。
使用中断系统能够极大的提高程序的效率,比如如果没有中断系统,为了防止外部中断被忽略或者串口数据被覆盖,那主程序就只能不断地查询是否有这些事件是否发生,不能再干其他事情了。比如没有定时器中断,那主程序只有靠Delay函数,才能实现定时的功能,但有了中断系统之后,主程序就可以放心执行其他事情,有中断的时候再去处理,这样效率就会大大提升。
中断优先级
当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。
也就是说,中断优先级就是中断的紧急程度,这个中断优先级是我们根据程序设计的需求,自己设置的。中断优先级是为了在多个中断同时申请时,判断一下,应该先处理哪个,如果事件非常紧急,那你就把优先级设置的高一些,如果不是那么紧急,那你就可以把优先级设置的低一些,这样就可以更好的安排这些中断事件,防止紧急的事件被别的中断耽误。在STM32中,中断的优先级还有多种分类,这个我们一会会写。
中断嵌套
中断嵌套是指一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完后依次进行返回。
这种把中断程序再次中断的现象,就叫做中断嵌套,中断嵌套也是为了照顾非常紧急的中断的,如果现在CPU已经在执行某个中断程序了,这时又发生一个非常紧急的中断,那这个中断就可以把当前中断程序进行二次中断,这样新的紧急中断就可以立即被执行了。那能否进行中断嵌套,也是由中断优先级来决定的。
中断执行流程

一般中断程序都是在一个子函数里的,这个函数不需要我们调用,当中断来临时,由硬件自动调用这个函数。
STM32中断
STM32F1系列有68个可屏蔽中断通道,这个中断通道就是中断源的意思,68个中断源,这个是F1系列最多的中断数量,对应于一个具体的型号,可能没有那么多中断。
STM32的中断包含EXTI外部中断、TIM定时器、ADC模数转换器、USART串口、SPI通信、I2C通信、RTC实时时钟等多个外设,STM32的中断是非常多的,几乎所有模块都可以申请中断。
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,还可以对优先级进行分组,进一步设置抢占优先级和响应优先级。
NVIC就是STM32中用来管理中断、分配优先级的。NVIC的中断优先级总共有16个等级,还可以设置抢占优先级和响应优先级,这个是非常灵活的。

NVIC基本结构

这个NVIC的名字叫做嵌套中断向量控制器,在STM32中,它是用来统一分配中断优先级和管理中断的。
NVIC是一个内核外设,是CPU的小助手,我们刚才知道,STM32的中断非常多,如果把这些中断全部接到CPU上,那CPU还得引出很多线进行适配,设计上就很麻烦,并且如果很多中断同时申请,或者中断很多产生了拥堵,CPU也很难处理,毕竟CPU主要用来运算的,中断分配的任务就放到别到地方吧,所以NVIC就出现了,NVIC有很多输入口,你有多少中断线路,都可以接过来,线上画了个斜杠,上面写个n,这个意思是一个外设可能会同时占用多个中断通道,所以这里有n条线。
NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后通过右边的输出口告诉CPU,你该处理哪个中断,对于中断先后顺序分配的任务,CPU不需要知道。
NVIC优先级分组

为了处理不同形式的优先级,STM32的NVIC可以对优先级进行分组,分为抢占优先级和响应优先级,那这两种优先级有什么区别呢?
举个病人叫号的例子:
对于紧急的病人,其实有两种优先的形式,一种是,上一个病人在看病,外面排队了很多病人,当上一个病人看病看完后,紧急的病人即使是后来的,也会最先进去看病,这种相当于插队的优先级,就叫响应优先级。响应优先级高的,可以插队提前看病。
另外,如果这个病人更加紧急,并且此时已经有人在看病了,那他还可以不等上一个人看完,直接冲到医生的屋里,让上一个病人先靠边站,先给他看病,等他看完了,然后上一个病人再继续,上一个病人结束了,叫号系统再看有没有人来,这种形式的优先就是我们之前写的中断嵌套。这种决定是不是可以中断嵌套的优先级,就叫抢占优先级。抢占优先级高的,可以进行中断嵌套。
这些就是抢占优先级和响应优先级的解释。
那我们刚才写了,每个中断有16个优先级,为了把这个优先级再区分为 抢占优先级和响应优先级,就需要对这16个优先级进行分组了。
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这里的4位二进制可以表示0~15的数,对应16个优先级,这个优先级的数是值越小,优先级越高,0就是最高优先级。这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队。

分组0就是0位的抢占等级,取值只能为0,4位的响应等级,取值可以是0~15。
分组1就是1位的抢占等级,取值是0~1,3位的响应等级,取值是0~7。
分组2就是2位的抢占等级,取值是0~3,2位的响应等级,取值是0~3。
分组3就是3位的抢占等级,取值是0~7,1位的响应等级,取值是0~1。
分组4就是4位的抢占等级,取值是0~15,0位的响应等级,取值只能是0。
这五种分组方式,是我们在程序中自己来选择的,选好分组方式之后,我们在配置优先级的时候,就要注意抢占优先级和响应优先级的取值范围了,不要超出这个表里规定的取值范围。
EXTI外部中断
了解完NVIC这个叫号系统和叫号规则,我们就来看一下第一个病人,EXTI外部中断。
EXTI(Extern Interrupt)外部中断 EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序,这就是EXTI的基本功能,简单来就是引脚电平变化,申请中断。
支持的触发方式:上升沿/下降沿/双边沿/软件触发
支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断,意思是,比如PA0和PB0不能同时用或者PA1\PB1\PC1这样的,端口GPIO_Pin一样的,只能选1个作为中断引脚。所以如果有多个中断引脚,要选择不同的Pin的引脚,比如PA6\PA7和PB15\PB0\PB1这样的都可以。
通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒共20个中断线路。
触发响应方式:中断响应/事件响应(中断响应是正常流程,引脚电平变化触发中断;事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作)
EXTI基本结构

首先最左边是GPIO口的外设,比如GPIOA、GPIOB、GPIOC等等,每个GPIO外设都有16个引脚,所以进来16根线,那我们刚才说了,EXTI模块只有16个GPIO的通道,但这里每个GPIO外设都有16个引脚,如果每个引脚占用一个通道,那EXTI的16个通道显然是不够用的,所以在这里会有一个AFIO中断引脚选择的电路模块,这个AFIO就是一个数据选择器,它可以在这前面3个GPIO外设的16个引脚里选择其中一个连接到后面的EXTI的通道里,所以前面说,相同的Pin不能同时触发中断,因为对于PA0、PB0、PC0这些,通过AFIO选择之后,只有一其中一个能接到EXTI的通道0上,同理,PA1、PB1、PC1这些,也只能有一个,连接到通道1上。这就是所有GPIO口都能触发中断,但相同的Pin不能同时触发中断的原因。
然后通过AFIO选择之后的16个通道,就接到了EXTI边沿检测及控制电路上,同时,下面这4个蹭网的外设(PVD\RTC\USB\ETH)也并列接进来,这些加起来就组成了EXTI的20个输入信号。
然后经过EXTI边沿检测电路之后,分为了两种输出,其中接到了NVIC的这些,是用来触发中断的。(这里注意下,本来20路输入,应该有20路中断的输出,但是可能ST公司觉得这20个输出太多了,比较占用NVIC的通道资源,所以就把其中外部中断的9~5和15~10,给分到了一个通道里,也就是说,外部中断的9~5会触发同一个中断函数,15~10也会触发同一个中断函数,在编程的时候,我们在这两个中断函数里,需要再根据标志位来区分到底是哪个中断进来的);另外一种输出,是有20条输出线路接到了其他外设,这就是用来触发其他外设操作的,也就是我们刚才说的事件响应。
AFIO复用IO口内部电路图

在STM32中,AFIO主要完成两个任务:
1.中断引脚选择。
2.复用功能引脚重映射。
复用功能引脚重映射就是我们最开始提到的引脚定义表,当我们想把引脚的默认复用功能的引脚换到重定义的这个位置来,就是用AFIO来完成的,这也是AFIO的一大主要功能。
EXTI内部框图


弯弯的符好是或门,可以多个输入但只能有一个输出,执行的是或的逻辑,在输入端只要有一个是高电平1,输出就是高电平1。

带直边的符好是与门,它同样也可以有多个输入,但只能有一个输出,执行的是与的逻辑,在输入端只要有一个是低电平0,输出就是0。
![]()
非门只有一个输入,一个输出,输入1就输出0,输入0就输出1。

数据选择器的符好是一个梯形,有多个输入,一个输出,在侧面有选择控制端,根据控制端的数据,从输入选择一个接到输出。
20根输入线首先进入边沿检测电路,在上面的上升沿寄存器和下降沿寄存器可以选择上升沿触发、下降沿触发和双边沿触发。

接着触发信号就进入到了或门的输入端了,在或门输入端这里,硬件出发和软件中断寄存器的值接到这或门上,也就是任意一个为1,或门就可以输出1,所以我们说EXTI支持的触发方式是上升沿、下降沿、双边沿和软件触发。
触发信号通过这个或门之后,就兵分两路,上一路是触发中断的,下一路是触发事件的。

触发中断会首先会置一个挂起寄存器,这相当于一个中断标志位了,我们可以读取这个寄存器判断是哪个通道触发的中断,如果中断挂起寄存器置1,触发信号就会继续想左走和中断屏蔽寄存器共同进入一个与门,然后到NVIC中断控制器。这里的与门实际上的作用就是开关,相当于中断屏蔽器寄存器给1,那另一个输入就是直接输出,也就是允许中断;中断屏蔽寄存器给0,那另一个输入无论是什么输出都是0,相当于屏蔽了这个中断。

触发事件,首先也是一个事件屏蔽寄存器进行开关控制,最后通过一个脉冲发生器到其他外设。这个脉冲发生器就是给一个电平脉冲,同来触发其他外设的动作。

这上面是外设接口和APB总线,我们可以通过总线来访问这些寄存器。
这些就是EXTI的内部结构了。
旋转编码器介绍
旋转编码器:用来测量位置、速度或旋转方向的装置,当其旋转轴旋转时,其输出端可以输出与旋转速度和方向对应的方波信号,读取方波信号的频率和相位信息即可得知旋转轴的速度和方向/
类型:机械触点式/霍尔传感器式/光栅式/

在我们套件内,使用的是第二种旋转编码器。
编码盘是一系列想光栅一样的东西,只不过这些不是光栅,而是金属触点,在旋转时,一次接通和断开两边的触点。并且,还有个关键的部分是,这个金属盘的位置是经过设计的,它能让两侧触点的通断产生一个90度的相位差,最终配合一下外部电路,这个编码器的两个输出就会输出这样的波形:

这样,正转和反转就可以区分开了,这种相位相差90度的波形叫做正交波形,带正交波形信号输出的编码器,是可以用来测方向的。
旋转编码器硬件电路

左边是旋转编码器的内部电路图,上面按键的两根线,这个模块是并没有使用,所以悬空,下面的这里就是编码器内部的两个触点,旋转轴旋转时,这两个触点以相位差90度的方式交替导通,因为这只是个开关信号,所以要配合外围电路才能输出高低电平。
右边是我们在使用这个模块的时候,上面的VCC、GND接电源,下面的A相输出和B相输出接到STM32的两个引脚上,比如PB0和PB1,注意引脚的GPIO_Pin编号不要一样就行。