STM32学习笔记(6)中断优先级及EXTI外部中断(改)

简介

中断的概念:举例

为什么需要中断

中断可以看作是放在while(1)语句中一个不停判断,准备执行的语句,问题是中断只相当于一个突发事件,我们不可能占用这么多资源去时时刻刻判断它是否发生(就像小明不可能时时刻刻盯着小丽,等她叫小明去买口红)。并且如果放在while(1)里面去判断,一旦加入其他代码,会使得突发事件的处理没有时效性。(就像小明不能等喝酒这一事件完结后再去陪小丽买口红),因此,我们需要一种只有在发生某种事件时,才会执行,平时又不占资源的功能,这就是中断。

中断优先级是什么

当有多个中断时,系统会根据中断优先级决定先执行哪一个中断函数。就像小明同时收到两个中断请求,老婆小丽叫小明陪她去买口红,朋友小张叫小明一起开黑,此时小明会根据事情的轻重缓急(优先级),决定去做哪一件事情。

中断优先级分组(NVIC)

CM3 内核支持 256 个中断,其中包含了 16 个内核中断和 240 个外部中断,并且具有 256级的可编程中断设置。但STM32 只有 其中84 个中断,包括 16 个内核中断和 68 个可屏蔽中断,具有 16 级可编程的中断优先级。在 STM32F103 系列上面,又只有 60 个(在 107 系列有 68 个)。本文将对STM32F103 系列这 60 个可屏蔽中断进行介绍。

相关寄存器介绍

ISER[8]:( Interrupt Set-Enable Registers),中断使能寄存器组。由 8 个 32 位寄存器来控制,每个位控制一个中断。对于STM32F103,有用的就是两个(ISER[0]ISER[1]), ISER[0]的 bit0~bit31 分别对应中断 0~31。 ISER[1]的 bit0~27 对应中断 32~59;要使能某个中断,必须设置相应的 ISER 位为 1。具体每一位对应哪个中断,请参考 stm32f10x.h 里面的第 140 行处(针对编译器 MDK5 来说)。

ICER[8]:( Interrupt Clear-Enable Registers),中断除能寄存器组。与 ISER 的作用相反,用来清除某个中断的使能的。其对应位的功能,也和 ICER 一样。这里要专门设置一个 ICER 来清除中断位(即写1),而不是向 ISER 写 0 来清除。

CM3 中可以有 240 对使能位/除能位,每个中断拥有一对。这 240 个对子分布在 8 对 32 位寄存器中(最后一对没有用完)。欲使能一个中断,你需要写 1 到对应 SETENA 的位中;欲除能一个中断,你需要写 1 到对应的 CLRENA 位中;如果往它们中写 0,不会有任何效果。通过这种方式,使能/除能中断时只需把“当事位”写成1,其它的位可以全部为零。
防止有些位被写入 0 而破坏其对应的中断设置(写 0 没有效果),从而实现每个中断都可以自顾地设置,而互不侵犯

ISPR[8]:( Interrupt Set-Pending Registers),中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。 (不常用)

ICPR[8]:( Interrupt Clear-Pending Registers),中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断接挂。(不常用)

IABR[8]:( Interrupt Active Bit Registers),中断激活标志位寄存器组。对应位所代表的中断和 ISER 一样,如果为 1,则表示该位所对应的中断正在被执行。这是一个只读寄存器,通过它可以知道当前在执行的中断是哪一个。在中断执行完了由硬件自动清零。 (不常用)

IP[240]:( Interrupt Priority Registers),中断优先级控制寄存器组。 240 个 8bit 的寄存器组成,每个可屏蔽中断占用 8bit,总共表示 240 个可屏蔽中断。 而 STM32 只用到了其中的前 60 个。 IP[59]~IP[0]分别对应中断 59~0。 而每个可屏蔽中断占用的 8bit 并没有全部使用,而是 只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,子优先级在后。而这两个优先级各占几个位又要根据 SCB>AIRCR 中的中断分组设置来决定。

抢占优先级和子优先级的解释:
抢占优先级:高优先级可打断正在进行的低优先级。
子优先级:中断同时发生,高优先级先执行。
若两个中断的两种优先级相同,则先发生先执行。(值越低,优先级越高)

STM32 的中断分组: STM32 将中断分为 5 个组,组 0~4。该分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。具体的分配关系 :
在这里插入图片描述

例如选择2组,则每种优先级通过两位来设置,则各有2^2,即0~3共4种等级差别。

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_X); //X=0~4,该函数在系统中只能被调用一次来确定组别

优先级设定

设定相关的结构体:

typedef struct
{
uint8_t NVIC_IRQChannel;//定义初始化的是哪个中断 
uint8_t NVIC_IRQChannelPreemptionPriority;//定义该中断的抢占优先级别。 
uint8_t NVIC_IRQChannelSubPriority;//定义该中断的子优先级别。 
FunctionalState NVIC_IRQChannelCmd; //该中断是否使能。 
} NVIC_InitTypeDef; 

以初始化串口 1 的中断,同时设置抢占优先级为 1,子优先级位 2 为例:

NVIC_InitTypeDef NVIC_InitStructure;//初始化相应结构体
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1 中断对应名
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1 ;// 抢占优先级为 1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;// 子优先级位 2
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道(中断请求通道)使能
NVIC_Init(&NVIC_InitStructure); //根据上面指定的参数初始化 NVIC 寄存器 

EXIT外部中断相关

概述

STM32每个IO口都可以作为外部中断输入,其中断控制器支持19个外部中断/事件请求,分别为:
线 0~15:对应外部 IO 口的输入中断。
线 16:连接到 PVD 输出。
线 17:连接到 RTC 闹钟事件。
线 18:连接到 USB 唤醒事件。

对STM32的IO口,可分为GPIOx.0~GPIOx.15(x=A,B,C,D,E,F,G)分别对应中断线 0~15。这样每个中断线对应了最多 7 个 IO 口。如图:(同一时刻,一组GPIO只能有一个IO口映射到中断线)
在这里插入图片描述
中断线对应中断服务函数

EXTI0_IRQHandler
EXTI1_IRQHandler
EXTI2_IRQHandler
EXTI3_IRQHandler
EXTI4_IRQHandler
EXTI9_5_IRQHandler
EXTI15_10_IRQHandler 

中断控制器框图

在这里插入图片描述

相关寄存器简介

1.EXTI_IMR:中断屏蔽寄存器,控制是否屏蔽来自线上的中断请求;
2.EXTI_EMR:事件屏蔽寄存器,控制是否屏蔽来自线上的事件请求;

事件:是表示检测到某一动作(电平边沿)触发事件发生了。
中断:有某个事件发生并产生中断,并跳转到对应的中断处理程序中。
事件只是一个触发信号(脉冲),而中断则是一个固定的电平信号 。

3.EXTI_RTSR:上升沿触发选择寄存器,控制是否允许输入线x上的上升沿触发(中断和事件);
4. EXTI_FTSR:下降沿触发选择寄存器,控制是否允许输入线x上的下降沿触发(中断和事件);
5.EXTI_SWIER:软件中断事件寄存器,当该位为’0’时,写’1’将设置EXTI_PR中相应的挂起位。如果在EXTI_IMR和EXTI_EMR中允许
产生该中断,则此时将产生一个中断。
6.EXTI_PR:挂起寄存器,控制是否发生触发请求;
寄存器相关位配置参考相关资料(中文参考手册第九章)

EXIT_Init函数

相应结构体:

typedef struct
{
  uint32_t EXTI_Line;//指定配置的中断线        
  EXTIMode_TypeDef EXTI_Mode;//中断模式      
  EXTITrigger_TypeDef EXTI_Trigger; //触发方式
  FunctionalState EXTI_LineCmd;//使能/失能    
}EXTI_InitTypeDef;

使用步骤

( 1)初始化 IO 口为输入。
(2)开启 AFIO 时钟
(3) 设置 IO 口与中断线的映射关系。

void GPIO_EXTILineConfig(uint8_t GPIO_PortSource, uint8_t GPIO_PinSource);

(4)初始化线上中断,设置触发条件(上升沿触发,下降沿触发,边沿触发等)等。

void EXTI_Init(EXTI_InitTypeDef* EXTI_InitStruct);

(5)配置中断分组(NVIC),并使能中断。
(6)编写中断服务函数。

例:

void EXTIX_Init(void)//自定义相关初始化函数
{
 EXTI_InitTypeDef EXTI_InitStrue;//初始化相关结构体
 NVIC_InitTypeDef NVIC_InitStrue; 
 
 KEY_Init();//按键初始化,为中断服务函数准备
 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//开启AFIO时钟 
  
 GPIO_EXTILineConfig(GPIO_PortSourceGPIOE,GPIO_PinSource4);//设置 IO 口与中断线的映射关系
 
 EXTI_InitStrue.EXTI_Line=EXTI_Line4;
 EXTI_InitStrue.EXTI_Mode=EXTI_Mode_Interrupt;
 EXTI_InitStrue.EXTI_Trigger=EXTI_Trigger_Falling;//下降沿触发
 EXTI_InitStrue.EXTI_LineCmd=ENABLE;
  EXTI_Init(&EXTI_InitStrue);//初始化线上中断
 
 NVIC_InitStrue.NVIC_IRQChannel=EXTI4_IRQn;
 NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=2;
 NVIC_InitStrue.NVIC_IRQChannelSubPriority=2;
 NVIC_InitStrue.NVIC_IRQChannelCmd=ENABLE;
 NVIC_Init(&NVIC_InitStrue);//初始化优先级设置
}
 
void EXTI4_IRQHandler(void)//相关中断服务函数
{
 delay_ms(10);//防抖
 if(KEY0==0)//低电平被触发
 {
 LED0=!LED0;
 LED1=!LED1;
  }
 EXTI_ClearITPendingBit(EXTI_Line4);//清除标志位
}

中断与插入函数的区别

类似而不相同,中断相当于一个时刻判断条件的函数,而插入在程序的函数(插入在while(1)里),想要运行必须等到程序执行到那里,当程序存在delay时,插入的函数检测会变得困难,而中断不会,中断甚至在符合条件后停止正在运行的主函数,去运行中断函数。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值