中断扫盲

什么是中断

CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂时中断正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程称为中断。

在计算机科学中,中断(Interrupt)是指处理器接收到来自硬件或软件的信号,提示发生了某个事件,应该被注意,这种情况就称为中断。

通常,在接收到来自外围硬件(相对于中央处理器和内存)的异步信号,或来自软件的同步信号之后,处理器将会进行相应的硬件/软件处理。发出这样的信号称为进行中断请求(interrupt request,IRQ)。

硬件中断导致处理器通过一个运行信息切换(context switch)来保存执行状态(以程序计数器和程序状态字等寄存器信息为主);软件中断则通常作为CPU指令集中的一个指令,以可编程的方式直接指示这种运行信息切换,并将处理导向一段中断处理代码。

为什么要有中断?

如果计算机系统没有中断,则处理器与外部设备通信时,它必须在向该设备发出指令后进行忙等待(Busy waiting),反复轮询该设备是否完成了动作并返回结果。这就造成了大量处理器周期被浪费。

引入中断以后,当处理器发出设备请求后就可以立即返回以处理其他任务,而当设备(如:定时器)完成动作后,发送中断信号给处理器,后者就可以再回过头获取处理结果。这样,在设备进行处理的周期内,处理器可以执行其他一些有意义的工作,而只付出一些很小的切换所引发的时间代价。

下面以STM32为例介绍中断机制。

EXTI

外部中断/事件控制器包含多达 23 个用于产生事件/中断请求的边沿检测器。

每根输入线都可单独进行配置,以选择类型(中断或事件)和相应的触发事件(上升沿触发、下降沿触发或边沿触发)。

每根输入线还可单独屏蔽。

注:挂起寄存器用于保持中断请求的状态线。

外部中断/事件才需要经过EXTI,芯片外设的中断不用经过EXTI,直接到达NVIC。

image-20200815143912180
  • 下降沿:数字电路中,数字电平从高电平(数字“1”)变为低电平(数字“0”)的那一瞬间叫作下降沿。
  • 上升沿:数字电路中,数字电平从低电平(数字“0”)变为高电平(数字“1”)的那一瞬间叫作上升沿。

外部中断/事件线映射

image-20200815145501467

注意:一条中断线在同一时间只能被一个IO口映射。

比如:PA0占用了EXTI0,其他PB0~PI0是不能使用的。


NVIC

嵌套向量中断控制器 (NVIC) ,有两个重要功能:通道的设置,优先级设置。

NVIC特性
嵌套向量中断控制器 NVIC 包含以下特性:

  • STM32F405xx/07xx 和 STM32F415xx/17xx 具有 82 个可屏蔽中断通道,STM32F42xxx和STM32F43xxx 具有多达 86 个可屏蔽中断通道(不包括 Cortex™-M4F 的 16 根中断线)

  • 16 个可编程优先级(使用了 4 位中断优先级)

  • 低延迟异常和中断处理

  • 电源管理控制

  • 系统控制寄存器的实现

嵌套向量中断控制器 (NVIC) 和处理器内核接口紧密配合,可以实现低延迟的中断处理和晚到中断的高效处理。

中断向量表(中断通道)

cortex-M处理器的异常中,编号1-15的为系统异常,16及以上的则为中断输入。

部分系统异常具有可编程的优先级,部分系统异常具有固定优先级。

image-20200815145124002 image-20200914125509271

PENDSV和SYSTICK的中断优先级可以编程,一般要把PENDSV的优先级设置成最低。

SYSTICK的优先级:

  • 一般无需设置(高于外部中断的优先级),毕竟这是系统的时钟源(ucos心脏)。
  • 可根据项目需要(有些外部中断,项目上要求务必实时),将SYSTICK优先级设置与合适的位置。很多项目对于实时没有很高的要求,干脆将PENDSV和SYSTICK的优先级都设置成0xFF。此时,因为PENDSV在中断向量表中排在SYSTICK前面,所以如果PENDSV,SYSTICK同时产生中断,PENDSV优先中断。

中断优先级分组

首先,对STM32中断进行分组,组0~4。同时,对每个中断设置一个抢占优先级和一个响应优先级值。分组配置是在寄存器SCB->AIRCR中配置。

  • 第0组:所有4位用于指定响应优先级
  • 第1组:最高1位用于指定抢占式优先级,最低3位用于指定响应优先级
  • 第2组:最高2位用于指定抢占式优先级,最低2位用于指定响应优先级
  • 第3组:最高3位用于指定抢占式优先级,最低1位用于指定响应优先级
  • 第4组:所有4位用于指定抢占式优先级

抢占优先级 & 响应优先级区别:

  • 高优先级的抢占优先级是可以打断正在进行的低抢占优先级中断的。
  • 抢占优先级相同的中断,高响应优先级不可以打断低响应优先级的中断。
  • 抢占优先级相同的中断,当两个中断同时发生的情况下,哪个响应优先级高,哪个先执行。
  • 如果两个中断的抢占优先级和响应优先级都是一样的话,则看哪个中断先发生就先执行。抢占优先级相同且响应优先级相同的中断,假如同时发生,会按照硬件内部固定的优先级执行。见中断向量表。

中断优先级:无论是抢占优先级还是响应优先级,优先级数值越小,优先级别越高。

image-20200815152456795

中断实现案例

按键库开发要添加库文件:stm32f4xx_exti.cstm32f4xx_syscfg.c

  1. 理解按键输入原理图

  2. NVIC分组(一个工程当中只能配置一次分组,否则优先级的设置可能会乱,因为不同的优先级组的抢占优先级和响应优先级取值范围是不一样的)

    1
    void NVIC_PriorityGroupConfig(uint32_t NVIC_PriorityGroup);
  3. 使能SYSCFG时钟,使能GPIOA组时钟

1
2
3
4
5
6
7
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//这个函数非常重要,在使用外部中断的时候一定要先使能SYSCFG时钟
//函数说明
void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState)
函数功能:使能AHB1外设时钟
uint32_t RCC_AHB1Periph:外设
FunctionalState NewState:使能状态 ENABLE or DISABLE
  1. 初始化IO口为输入
1
2
3
4
5
6
7
8
9
10
11
12
13
void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
函数功能:GPIO初始化
GPIO_TypeDef* GPIOx:GPIO组(A B C D E F ...)
GPIO_InitTypeDef* GPIO_InitStruct:GPIO结构

typedef struct
{
uint32_t GPIO_Pin; //引脚
GPIOMode_TypeDef GPIO_Mode; //模式
GPIOSpeed_TypeDef GPIO_Speed; //速度
GPIOOType_TypeDef GPIO_OType; //输出类型
GPIOPuPd_TypeDef GPIO_PuPd; //上下拉
}GPIO_InitTypeDef;
  1. 设置IO口与中断线的映射关系

    1
    void SYSCFG_EXTILineConfig();
  2. 初始化线上中断,设置触发条件等

1
2
3
4
5
6
7
8
9
10
11
void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);
函数说明:外部中断控制器初始化
EXTI_InitTypeDef* EXTI_InitStruct:外部中断控制器结构体

typedef struct
{
uint32_t EXTI_Line; //中继线
EXTIMode_TypeDef EXTI_Mode; //中断模式
EXTITrigger_TypeDef EXTI_Trigger; //触发条件
FunctionalState EXTI_LineCmd; //中断线使能
}EXTI_InitTypeDef;
  1. 配置中断分组(NVIC),并使能中断
1
2
3
4
5
6
7
8
9
10
11
void NVIC_Init(NVIC_InitTypeDef* NVIC_InitStruct);
函数说明:NVIC控制控制器初始化
NVIC_InitTypeDef* NVIC_InitStruct:NVIC初始化结构体

typedef struct
{
uint8_t NVIC_IRQChannel; //NVIC通道,在stm32f4xx.h可查看通道
uint8_t NVIC_IRQChannelPreemptionPriority; //抢占优先级
uint8_t NVIC_IRQChannelSubPriority; //响应优先级
FunctionalState NVIC_IRQChannelCmd; //通道使能
} NVIC_InitTypeDef;
  1. 编写中断服务函数。(中断服务函数的名字在startup_stm32f40_41xxx.s文件中)

    EXTIx_IRQHandler();

  2. 按键消抖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//判断中断标志是否为1
if(EXTI_GetITStatus(EXTI_Line0) == SET)
{
if(Bit_RESET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
{
//消抖
delayms(15);
if(Bit_RESET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0))
{
//等待按键松开。如果按键一直按下不松开的话,程序会卡在这里,所以一般不需要加这一句
//如果设置按键中断的触发模式为下降沿,当有按键按下的时候,有下降沿产生,延时15ms后,如果还是读取到低电平,则会往下执行,改变灯的状态。在去掉这一句的情况下,程序不会卡在这里。而且,即使松开按键的时候存在下降沿,也不会二次进入这里来改变灯的状态,因为二次进入中断处理函数的时候,经过15ms的延时,GPIO_ReadInputDataBit读取到的将会是高电平,所以,不会进入这里来改变灯的状态
//while(Bit_RESET == GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0));

//变更灯状态
GPIO_ToggleBits(GPIOF, GPIO_Pin_9);
}
}
//清空标志位
EXTI_ClearITPendingBit(EXTI_Line0);
}
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2021-2025 wrd
  • 访问人数: | 浏览次数:

      请我喝杯咖啡吧~

      支付宝
      微信