单片机的中断概念
【keysking的STM32教程】 第7集 深入讲解STM32中断_哔哩哔哩_bilibili
从一个小需求,引入中断。
需求:红色小灯循环亮2s,灭2s。按下按键Key1后,绿色小灯翻转亮灭状态。
发现:此小需求中,轮询代码检测Key1按下,按键不太有效。原因如下:
那么如何实现这个小需求的代码方案呢? 中断
中断-外部中断-检测GPIO口电平变化的中断
两个红LED循环亮灭还是在死循环里面实现。对按键GPIO的输入,设置为外部中断输入模式。
在stm32开发IDE中,设置GPIO的中断触发条件:电平上升沿还是下降沿(这个依赖按键电路图,按下时电平怎么变化的);
然后设置中断向量优先级
然后,在中断服务/处理函数中写下中断要执行的动作
反复按键过程中发现,有时候控制并不灵敏生效。原因还是硬件防抖不完美,我们试着在中断中用软件防抖补充。
此时发现红灯持续亮起,代码跑飞。原因是:HAL_Delay()函数依赖一个System_tick_timer的中断 。这个中断的优先级比当前中断的中断优先级低。
中断虽然可以层层嵌套(次紧急中断内中断,去处理更紧急的事情),但只能优先级只能更高才行。
对于当前项目,一个简单的方法就是,在中断向量表NVIC中,把“HAL_Delay()函数依赖一个System_tick_timer”中断的优先级改高。
外部中断/事件控制器
GPIO引脚设置为外部中断输入模式时,是哪些结构在起作用?
电平到达数据数据寄存器或者片上外设后,还会到达下面这样一个结构,外部输入中断模式就能起作用了。
下面是一个外部中断控制器结构,这样的结构对于最小系统STM32单片机来说,一共有19个,每个GPIO口对应下面一条输入线。不过,19个外部中断控制器共用一套寄存器。
结构一 事件相关结构
与中断无关。
中断信号会进入处理器,调用代码处理;
事件信号是直接进入相应外设。
边沿检测电路(检测中断信号来了没)
检测输入信号有咩有发生高低电平的转换。有的话,再根据对应上升沿/下降沿选择寄存器的值,选择是否接着向后输出一个高电平。
边沿触发选择寄存器
一个32位寄存器,一位为1,意味着对应GPIO配置了边沿检测选择寄存器。
请求挂起寄存器(记录下对应位的中断信号)
接受到高电平信号时,会将对应位置1.
中断屏蔽寄存器(请求挂起寄存器的已记录信号=向中断屏蔽寄存器申请中断正式生效)
对应引脚中断屏蔽寄存器的配置,在我们配置GPIO引脚为外部输入中断时,代码IDE就在代码中帮我们自动完成了
最后,审批通过的中断进入 NVIC,该排队排队,等待最终生效。
NVIC-嵌套向量中断控制器
根据中断信号找到对应中断处理函数
也就是 EXTI12中断信号到达 NVIC时,NVIC会找到中断向量 EXTI5_10,然后
注意:NVIC会持续检测某个中断线是否处于激活状态。如果其对应中断服务函数已经执行完成,发现中断线仍旧处于激活状态,就会再次执行中断服务函数。
解决这个问题,我们需要在中断函数执行完之后,将对应GPIO的请求挂起寄存器对应位置为0。
安排中断信号的的处理顺序
中断向量 不仅对应着 处理函数,还有着优先级信息。
情况一:当两个中断同时到达时,需要比较其优先级。
依次按照 抢占优先级、响应优先级、在中断向量表中的顺序 梯次比较,得出先后顺序。
情况二:A中断正在执行,B中断到达
中断优先级信息
抢占和响应优先级合起来共用一个 4bit位的优先级信息存储位置。
默认4位全用来设置抢占。0-15
一个GPIO外部输入中断,从配置到中断信号生效的全过程总结:
外设与中央处理器交互一般有两种手段:轮询和中断。
- 轮询与中断
外部设备与中央处理器交互一般有两种手段:轮询和中断。
(1)轮询(Polling)
很多I/O设备都有一个状态寄存器,用于描述设备当前的工作状态,每当设备状态发生改变时,设备将修改相应状态寄存器位。通过不断查询设备的状态寄存器,CPU就可以了解设备的状态,从而进行必要的I/O操作。为了节约CPU资源,查询工作往往不是连续的,而是定时进行。
轮询方式具有简单、易实现、易控制等优势,在很多小型系统中有大量应用。对那些实时敏感性不高、具有大量CPU资源的系统来说,轮询方式有很广泛的应用。最典型的用途就是在那些任务比较单一的单片机上,嵌入式系统中也有应用。
轮询的一种典型的实现可能是这样的:while(TRUE){/…/ select(,,timeout); /…/};当然这里的select()也可以使用poll()替换。
轮询方式主要存在以下不足:
<1>增加系统开销。无论是任务轮询还是定时器轮询都需要消耗对应的系统资源。
<2>无法及时感知设备状态变化。在轮询间隔内的设备状态变化只有在下次轮询时才能被发现,这将无法满足对实时性敏感的应用场合。
<3>浪费CPU资源。无论设备是否发生状态改变,轮询总在进行。在实际情况中,大多数设备的状态改变通常不会那么频繁,轮询空转将白白浪费CPU时间片。
(2)中断(Interrupt)
中断,顾名思义,就是打断正在进行中的工作。中断不需要处理器轮询设备的状态,设备在自己发生状态改变时将主动发送一个信号给处理器(PIC),后者在接收到这一通知信号时,会挂起当前正在执行的任务转而去处理响应外设的中断请求。中断通知机制通过硬件信号异步唤起处理器的注意,解决了外部设备与处理器之间速度不匹配导致的资源浪费问题。
现代设备绝大多数采用中断的方式与处理器进行沟通,因此设备驱动程序必须能够支持设备的中断特性。处理器在中断到达时会根据不同的中断号找到对应设备(IRR),并对中断请求进行响应处理。中断处理例程ISR(Interrupt Service Routine)由设备驱动程序提供,并在设备驱动模块初始化时注册到系统中断向量表中。从设备发出中断信号,到处理器最终调用ISR进行处理,期间会经过很多步骤,这个过程构成了中断处理框架。中断处理框架包括了进入ISR之前的很多进入路径(entry path),例如MIPS下要经历这样几个步骤:设置或屏蔽相关寄存器;进入异常入口点取指;现场保护;异常分类(MIPS下中断也是一种异常)处理;查找中断向量表路由ISR。不同的操作系统对中断处理框架的设计不尽相同,但是要达到的目的是一样的,那就是最终调用用户注册的设备ISR。
(3)中断与轮询的折衷
虽然轮询方式存在空转损耗导致名声不佳,但并非一无是处。中断模型也并非十全十美,其高优先级的VIP待遇和快速响应要求在极端条件下将造成“活锁”效应。有时候需要发挥粗暴中断和温和轮询各自的优势,根据实际应用情景,在两种模式之间切换。手机导航杆卡死情形的处理是个很好的案例。
在过去的一些手机和PDA设备上安装有导航杆,它支持3种动作(顺时针旋转、逆时针旋转和按键),可方便菜单导航。导航杆的三种动作都会向处理器发出中断。系统中通用的目的I/O(GPIO)端口和导航杆连接。中断处理函数的工作就是查看GPIO数据寄存器解析出导航杆运动。假定导航杆由于存在运动部件(如旋轮偶尔被卡住)引起的固有的硬件问题,从而在GPIO端口产生不同于方波的波形。被卡住的旋轮会不停地产生假的中断,并可能使系统冻结。为了解决这个问题,可以捕获波形分析,在卡住的情况下动态地从中断模式切换到轮询模式。如果旋轮恢复正常,再动态地从轮询模式切换到中断模式,软件也恢复正常模式。
在本文的最后,将介绍Linux网络设备驱动模型中的NAPI机制 ,它采用“中断+轮询”的处理方式代替纯中断处理方式,是中断和轮询的完美合体。 - 中断向量表
中断向量表其实是处理器内部的概念,因为处理器除了会被外部设备中断外,其内部也可能产生异常等事件,例如在MIPS中,中断只是异常的一种。当这些事件发生时,CPU必须暂停手头上的工作,转而去处理中断或异常,因此处理器需要知道到哪里去获得这些中断或异常的处理函数的目标地址。中断向量表就是用来解决这个问题,其中每一项都是一个中断或异常处理函数的入口地址,具体来说4个字节的函数指针将指向一段汇编微码(intConnectCode)执行跳转。
外部设备的中断常常对应向量表中的某一项,这是通用框架的外部中断处理函数入口,因此在进入通用的中断处理函数之后,系统必须知道正在处理的中断是哪一个设备产生的,而这正是由软件中断号irq定的决。中断向量表的内容是由操作系统在初始化阶段来填写,对于外部中断,操作系统负责实现一个通用的外部中断处理函数,然后把这个函数的入口地址放到中断向量表中的对应位置。用户注册设备驱动ISR,实际上就是挂接到中断向量表中,覆盖某一项的默认处理实现特化。