单片机学习
目录
前言
本文主要讲述STM32的中断系统和外部中断,中断系统是管理和执行中断的逻辑结构;外部中断是众多能产生中断的外设之一。
一、中断系统
- 中断:在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。
- 中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。
- 中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。
1.1 什么是中断
中断定义:在主程序运行过程中,出现了特定的中断触发条件,这里的中断触发条件就是中断源,使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。
当CPU正在执行某个程序时,由计算机内部或外部原因引起的紧急事件向CPU发出请求处理的信号,CPU在允许的情况下响应请求信号,暂时停止正在执行的程序,保护好断点处的现场,转向执行一个用于处理该紧急事件的程序,处理完成后又返回被终止的程序断点处,继续执行原程序,这一过程就是上图中中断响应和处理的过程。
中断就是在正常主程序执行的某个时刻发生了中断触发条件:
- 对于外部中断来说,可以是引脚发生了电平跳变;
- 对于定时器来说,可以是定时时间到了;
- 对于串口通信来说,可以是接收到了数据。
当这些事件发生时,情况就会产生比较紧急的问题:
- 当外部中断来了,如果不来处理,下一个跳变信号就跟着过来了;
- 当串口接收中断来了,如果不来读取接收到的数据,那下一个数据再过来,就会把原来的数据覆盖掉;
所以就希望当中断条件满足时,CPU能够立即停下当前执行的程序。转而去处理这些中断事件的程序:
- 当外部中断来了,我想要计次,那就变量++;
- 当串口中断来了,我就把接收到的数据转存起来。
当这些紧急事件处理完成后,CPU还能回到原来停下的地方继续运行。这就是中断的处理流程和用途,使用中断系统,能够极大地提高程序的效率。
- 如果没有中断系统,为了防止外部中断被忽略或者串口数据被覆盖,那主程序就只能不断地查询是否有这些事件发生,不能再干其它事情了。
- 如果没有定时器中断,那主程序只有靠Delay函数,才能实现定时的功能,但有了中断系统后,主程序就可以放心执行其它事情,有中断的时候再去处理。这样效率就会大大提升。
1.2 中断优先级
中断优先级:当有多个中断源同时申请中断时,CPU会根据中断源的轻重缓急进行裁决,优先响应更加紧急的中断源。
1.中断优先级就是中断的紧急程度。
2.中断优先级是根据程序设计的需求而设置的。
3.中断优先级是为了在多个中断同时申请时,判断一下,应该先处理哪个。
- 如果事件非常紧急,就把优先级设置高一些;
- 如果事件不紧急,就把优先级设置低一些。
这样可以更好的安排这些中断事件,防止紧急的事件被别的中断耽误。
1.3 中断嵌套
中断嵌套:当一个中断程序正在运行时,又有新的更高优先级的中断源申请中断,CPU再次暂停当前中断程序,转而去处理新的中断程序,处理完成后依次进行返回。
中断嵌套就是把中断程序再次中断的现象,中断嵌套也是为了处理非常紧急的中断。
如果在CPU已经在执行某个中断程序时,又发生了一个非常紧急的中断,那这个非常紧急的中断就可以把当前的中断程序进行二次中断。这样新的紧急中断就可以立即被执行了。等紧急的中断结束,再继续执行原来的中断。原来的中断结束再继续主程序。
能否进行中断嵌套,是由中断优先级来决定的。
1.4 C语言中的中断程序
带有中断程序的代码示例:
int main(void)//主函数
{
while(1)
{
//主程序
//……
//主程序
}
}
void EXTI0_IRQHandler(void)
{
//中断程序
//……
//中断程序
}
在主函数中,while(1) 死循环里就是主程序。正常情况下,程序就是在主程序里不断循环执行。当中断条件满足时,主程序就会暂停,自动跳转到中断程序里运行。中断程序执行完之后,再返回主程序继续执行。
一般中断程序都是在一个子函数里的,这个子函数不需要我们调用。当中断来临时,由硬件自动调用这个函数。
二、STM32的中断通道和中断向量
- 68个可屏蔽中断通道,包含EXTI、TIM、ADC、USART、SPI、I2C、RTC等多个外设
- 使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级
2.1 中断通道
中断通道就是中断源的意思。68个可屏蔽中断通道是F1系列芯片最多的中断数量,具体型号的芯片要对应它型号的数据手册为准。
STM32的可屏蔽中断通道包含EXTI外部中断、TIM定时器、ADC模数转换器、USART串口、SPI通信、I2C通信、RTC实时时钟等多个外设。由此可看出STM32的中断是非常多的,几乎所有的模块都能申请中断。
2.2 嵌套向量中断控制器NVIC
2.2.1 什么是NVIC
NVIC就是STM32中用来管理中断、分配优先级的。
使用NVIC统一管理中断,每个中断通道都拥有16个可编程的优先等级,可对优先级进行分组,进一步设置抢占优先级和响应优先级。
STM32F10xxx产品(小容量、中容量和大容量)的向量表
STM32内核中断:
表中第一个复位中断,当产生复位事件时,程序就会自动执行复位中断函数,也就是复位后程序开始执行的位置。
NMI不可屏蔽中断、硬件失效、储存管理、总线错误、错误应用等等这些都是内核里面的,一般这些中断都比较高深,看上去也难以理解,但是这些内核的中断一般使用不到,了解即可。
STM32外设中断:
表中第一个,窗口看门狗,这个是用来监测程序运行状态的中断,如果程序卡死了,没有及时喂狗,窗口看门狗就会申请中断,让程序跳到窗口看门狗的中断程序里。在中断程序里就可以进行一些错误检查,看看出现了什么问题。
表中第二个,PVD电源电压监测,如果你供电电压不足,PVD电路就会申请中断,在中断里就可以知道,现在供电不足,是不是电池没电了,要赶紧保存一下重要数据。
表中以下这些,也都是类似的功能。外设电路检测到有什么异常或者事件,需要提示一下CPU的时候,就可以申请中断,让程序调到对应的中断函数里运行一次。用来处理这个异常或事件。
表中EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5、EXTI15_10是外部中断对应的中断资源。
表的最右边一列是中断对应的地址,这是因为程序中断函数的地址是由编译器来分配的,是不固定的。但是我们的中断跳转,由于硬件的限制,只能跳转到固定的地址执行程序。所以为了能让硬件跳转到一个不固定的中断函数里,这里就需要在内存中定义一个地址的列表。列表地址是固定的,中断发生后,就跳到这个固定的位置。然后在这个固定位置,由编译器再加上一条跳转到中断函数的代码,这样中断跳转就可以跳转到任意位置了。这个中断地址的列表就叫中断向量表,相当于中断跳转的一个跳板。不过用C语言编程的话,不需要管这个中断向量表的,编译器都已经把工作做好了,所以使用起来还是很省心的。
2.2.2 NVIC基本结构
NVIC的名字叫做嵌套中断向量控制器。在STM32中,它是用来统一分配中断优先级和管理中断的。NVIC是一个内核外设,是CPU的小助手。
从上文STM32外设中断表格中可以看到,STM32的中断非常多,如果把这些中断全都接到CPU上,那CPU还得引出很多线进行适配,设计上就很麻烦。并且,如果很多中断同时申请,或者中断很多产生了拥堵,CPU也会很难处理,毕竟CPU主要是用来运算的,中断分配的任务适合放在别的地方。所以就有了NVIC来做这项工作。
NVIC有很多输入口,有多少中断线路都可以接过来,比如图中可以接到EXTI、TIM、ADC、USART等。
图中线上画了个斜杠,上面写了n,意思是一个外设可能会同时占用多个中断通道,所以这里有n条线。NVIC只有一个输出口,NVIC根据每个中断的优先级分配中断的先后顺序,之后通过右边这一个输出口告诉CPU,该处理哪个中断。对于中断先后分配的任务,CPU不需要知道。
举个生活中的例子,CPU就好比是一个医生。如果医院只有医生的话,当看病的人很多时,医生就需要安排一下先看谁,后看谁。如果有紧急的病人,那还得让紧急的病人最先来,安排先后次序的任务很繁琐,会影响医生看病的效率。所以医院就安排了一个叫号系统,来病人了统一取号,并且根据病人的等级,分配一个优先级。然后叫号系统看一下现在正在排队的病人,优先叫号紧急的病人。最终叫号系统给医生输出的就是一个一个排好队的病人,医生就可以专心看病了。这个叫号系统在STM32里就是NVIC。
2.2.3抢占优先级和响应优先级
还是以上文医生看病,病人叫号的例子来讲述抢占优先级和响应优先级。
对于紧急的病人,有两种形式的优先:
- 一种是,上一个病人在看病,外面排队了很多病人,当上一个病人看完后,紧急的病人即使是后来的,也会最先进去看病,这种相当于插队的优先级,就叫响应优先级。响应优先级高的,就可以插队提前看病。
- 另一种是,如果这个病人更加紧急,并且此时已经有人在看病了,那紧急的病人可以不等上一个人看完,直接冲到医生的就诊室里,让上一个病人先靠边站,先给紧急的病人看病,等他看完了,上一个看了一半病的病人再继续,上一个病人结束了,叫号系统再看看有没有人来。这种形式的优先级就是中断嵌套,决定是不是可以中断嵌套的优先级,就叫抢占优先级。抢占优先级高的,可以进行中断嵌套。
1.抢占优先级:pre-emption priority (先占优先级)
抢占是指打断其他中断的属性,即低抢占优先级的中断A可以被高抢占优先级的中断B打断,执行完中断服务函数B后,再返回继续执行中断服务函数A,由此会出现中断嵌套。
2.响应优先级:subpriority(从占优先级)
响应属性应用在抢占属性相同的情况下,也就是当两个中断源的抢占优先级相同时,分以下几种情况处理:
- 如果两个中断同时到达,则中断控制器会优先处理响应优先级高的中断。
- 当一个中断到来后,如果正在处理另一个中断,则这个后到的中断就要等到前一个中断处理完之后才能被处理(高优先级的中断不可以打断低响应优先级的中断)。
- 如果他们的抢占优先级和响应优先级都相等,则根据它们在中断表中的排位顺序决定先处理哪一个。
2.2.4 NVIC的优先级分组
为了处理不同形式的优先级,STM32的NVIC可以对优先级进行分组,分为抢占优先级和响应优先级。
每个中断有16个优先级,为了把优先级再区分为抢占优先级和响应优先级,就需要对这16个优先级进行分组。
- NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这4位可以进行切分,分为高n位的抢占优先级和低4-n位的响应优先级
- 抢占优先级高的可以中断嵌套,响应优先级高的可以优先排队,抢占优先级和响应优先级均相同的按中断号排队,中断号就是上文向量表中优先级那一列。数值小的优先响应。
NVIC的中断优先级由优先级寄存器的4位(0~15)决定,这里的4位二进制可以表示0~15的数,对应16个优先级。优先级的数值越小,优先级越高,0就是最高优先级。
STM32的中断不存在先来后到的排队方式,在任何时候,都是优先级高的先响应。
因为优先级总共是4位,所以就有(0,4)、(1,3)、(2,2)、(3,1)、(4,0)这五种分组方式。
- 分组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。
这个分组方式可选择,通过调用STM32固件库的函数NVIC_PriorityGroupConfig(); 来选择优先分组方式。选好分组方式之后,在配置优先级的时候要注意抢占优先级和响应优先级的取值范围。不要超出表里规定的取值范围。
三、STM32 外部中断EXTI
3.1EXIT简介
- EXTI(Extern Interrupt)外部中断。
- EXTI可以监测指定GPIO口的电平信号,当其指定的GPIO口产生电平变化时,EXTI将立即向NVIC发出中断申请,经过NVIC裁决后即可中断CPU主程序,使CPU执行EXTI对应的中断程序。
- 支持的触发方式:上升沿、下降沿、双边沿、软件触发。
- 支持的GPIO口:所有GPIO口,但相同的Pin不能同时触发中断。
- 通道数:16个GPIO_Pin,外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒。
- 触发响应方式:中断响应、事件响应。
1.EXTI是Extern Interrupt的缩写,意思是外部中断。
2.EXTI的基本功能简单来说就是引脚电平变化来申请中断。
3.触发方式,EXTI支持引脚电平的变化类型:
- 上升沿,就是电平从低电平变到高电平的瞬间触发中断;
- 下降沿,就是电平从高电平变到低电平的瞬间触发中断;
- 双边沿,就是上升沿和下降沿都可以触发中断;
- 软件触发,就是引脚啥事没有,程序里执行一句代码,就能触发中断。
4.外部中断支持的GPIO口,也就是任意的GPIO口都可以当作外部中断的引脚。但相同的Pin不能同时触发中断,意思就是,比如PA0、PB0、PC0这样的端口GPIO_Pin一样的不能同时用,只能选1个作为中断引脚。若有多个中断引脚,要选择不同Pin的引脚,如PA6、PA7、PB9、PC0这样的。
5.外部中断占用的通道,其中有16个GPIO_Pin,这就对应GPIO_Pin_0到GPIO_Pin_15。外加PVD输出、RTC闹钟、USB唤醒、以太网唤醒。这些加起来总共有20个中断线路。这里的16个GPIO_Pin是外部中断的主要功能,后面跟着的这四个东西其实是来“蹭网”的,外部中断有个功能,就是从低功耗模式的停止模式下唤醒STM32。
- 对于PVD电源电压检测,当电源从电压过低恢复时,就需要PVD借助以下外部中断退出停止模式。
- 对于RTC闹钟来说,有时候为了省电,RTC定一个闹钟之后,STM32会进入停止模式,等到闹钟响的时候再唤醒,这也需要借助外部中断。
对于USB唤醒、以太网唤醒,也都是类似的作用。
6.外部中断的触发响应方式可以是中断响应和事件响应。
- 中断响应就是申请中断,让CPU执行中断函数。
- 事件响应是STM32对外部中断增加的一种额外的功能,当外部中断检测到引脚电平变化时,正常的流程时选择触发中断,但在STM32中也可以选择触发一个事件。如果选择触发事件,那外部中断的信号就不会通向CPU了,而是通向其他外设,用来触发其他外设的操作,如触发ADC转换、触发DMA等。
触发响应方式总结:中断响应是正常流程,引脚电平变化触发中断。事件响应不会触发中断,而是触发别的外设操作,属于外设之间的联合工作。
3.2EXIT基本结构
上图为外部中断的整体结构图。最左边是GPIO口的外设,比如GPIOA、GPIOB、GPIOC等等。每个GPIO外设有16个引脚,所以每个GPIO外设都进来16根线。
上文说到EXTI模块只有16个GPIO的通道,但每个GPIO外设都有16个引脚,如果每个引脚占用一个通道,那EXIT的16个通道显然就不够用了。所以在GPIO外设和EXIT中间会有一个AFIO中断引脚选择的电路模块。
AFIO就是一个数据选择器,它根据GPIO外设端口的引脚序号不同,把不同GPIO端口的同一个序号的引脚组成一组(如PA0、PB0、PC0、PD0、PE0、PF0、PG0为一组),每组对应一个通道连接到EXIT。所以上文说,相同的Pin不能同时触发中断,因为对于PA0、PB0、PC0这些相同的Pin,通过AFIO选择后,只有其中一个能接到EXIT的通道EXIT0上。(对照下文3.3 AFIO复用IO口小节中的图能更清晰的理解)这也就是所有的GPIO口都能触发中断,但相同的Pin不能同时触发中断的原因。
通过AFIO选择之后的16个通道,就接到了EXIT边沿检测及控制电路上。同时下方的PVD输出、RTC闹钟、USB唤醒、ETH以太网唤醒的外设也是并列接进来的。这些加起来就组成了EXIT的20个输入信号。
经过EXIT电路之后,分为了两种输出,其中上方的EXTI0、EXTI1、EXTI2、EXTI3、EXTI4、EXTI9_5、EXTI15_10、PVD、RTC、USB、ETH这些接到了NVIC,是用来触发中断的。下方有20条输出线路连接到了其他外设,用来触发其他外设操作的,就是事件响应。
这里注意,本来20路输入,应该有20路中断输出,但是为了节约NVIC的通道资源,把其中外部中断的9~5分配到一个通道EXTI9_5,中断的15~10也分配到一个通道EXTI15_10。也就是外部中断的9~5会触发同一个中断函数,外部中断的15~10也会触发同一个中断函数。在编程的时候,对这两个中断函数需要再根据标志位来区分是哪个中断进来的。
3.3AFIO复用IO口
- AFIO主要用于引脚复用功能的选择和重定义,也就是数据选择器的作用。
- 在STM32中,AFIO主要完成两个任务:复用功能引脚重映射、中断引脚选择。
上图为AFIO选择中断引脚的结构图。
3.4EXIT框图
EXTI 右边(图中右下角)就是20根输入线,输入线首先进入边沿检测电路,在边沿检测电路上面的上升沿寄存器和下降沿寄存器可以选择是上升沿触发、下降沿触发或双边沿触发。
回到边沿检测电路这一块,触发信号就进入到或门输入端了,同时软件中断事件寄存器的值也接在同一个或门上,或门输入任意一个为1,或门就输出1,所以上文EXIT简介中有关EXIT支持的触发方式包含上升沿、下降沿、双边沿、软件触发这四种。
触发信号经过或门之后就兵分两路,向上的一路是触发中断的,平直向左方的一路是触发事件的。
触发中断首先会置一个请求挂起寄存器,这相当于是一个中断标志位,我们可以读取这个寄存器,判断是哪个通道触发的中断。如果请求挂起寄存器置1,信号就会继续向左走,和中断屏蔽寄存器共同进入一个与门,与门输出至NVIC中断控制器。
这里的断屏蔽寄存器和与门实际上就是开关的作用:
- 与门输入1与任意数x,输出任意数x;
- 与门输入0与任意数x,输出0。
就相当于:
- 中断屏蔽寄存器给1,另一边输入可直接输出,也就是允许中断;
- 中断屏蔽寄存器给0,另一边无论输出什么,输出都是0,也就是屏蔽了这个中断
触发事件首先是和一个事件屏蔽寄存器共同进入与门,事件屏蔽寄存器和与门进行开关控制,最后通过一个脉冲发生器到其他外设。脉冲发生器就是给一个电平脉冲,用来触发其它外设的动作。
总结
以上就是今天要讲的内容,本文仅仅简单介绍了STM32的中断基础知识。