STM32F407HAL库-4.中断管理及按键中断

中断是指当CPU执行程序时,由于发生了某种随机的事件(外部或内部),引起CPU暂停正在运行的程序,转去执行一段特殊的服务程序(中断服务子程序或中断处理程序),以处理该事件,该事件处理完后又返回被中断的程序继续执行,这一过程就称为中断,我们把引起中断的原因,或者能够发出中断请求信号的来源统称为中断源。

CM4内核支持256个中断,其中包含了16内核中断和240个外部中断,并且具有256级的可编程中断设置,但STM32F4并没有使用CM4内核的全部东西,而是只用了它的一部分。STM32F407系统异常有10个,外部中断有82个,除了个别异常的优先级被定死外,其它异常的优先级都是可编程的。有关具体的系统异常和外部中断可在标准库文件stm32f4xx.h这个头文件查询到,在IRQn_Type这个结构体里面包含了F4系列全部的异常声明。

STM32F407中断向量表如下所示:

中断向量表分别介绍了Cortex-M0所有中断名称、说明以及中断的地址,以上只列出中断向量表一部分,详细请参考STM32F407参考手册P372。

NVIC,全称:Nested vectored interrupt controller,即嵌套向量中断控制器,控制着整个芯片中断相关的功能,它跟内核紧密耦合,是内核里面的一个外设。NVIC寄存器结构如下:(详细寄存器说明请参考《Cortex-M4 Devices Generic User Guide》P219)

NVIC_ISER[0-7]:ISER全称是:Interrupt Set-Enable Registers,即中断使能寄存器组。上面说了 CM4 内核支持 256 个中断,这里用 8 个 32 位寄存器来控制,每个位控制一个中断。但是STM32F4 的可屏蔽中断最多只有82个,所以对我们来说,有用的就是三个(ISER[0~2]),总共可以表示 96 个中断。而 STM32F4 只用了其中的前 82 个。ISER[0]的 bit0~31 分别对应中断0~31;ISER[1]的 bit0~32 对应中断 32~63;ISER[2]的 bit0~17 对应中断 64~81;这样总共 82 个中断就分别对应上了。你要使能某个中断,必须设置相应的 ISER 位为 1,使该中断被使能(这里仅仅是使能,还要配合中断分组、屏蔽、IO 口映射等设置才算是一个完整的中断设置)。

ICER[0-7]:全称是:Interrupt Clear-Enable Registers,是一个中断除能寄存器组。该寄存器组与 ISER 的作用恰好相反,是用来清除某个中断的使能的。这里要专门设置ICER相应为1来清除中断位,而不是向 ISER 写 0 来清除,是因为 NVIC 的这些寄存器都是写 1 有效的,写 0 是无效的。

ISPR[0-7]:全称是:Interrupt Set-Pending Registers,是一个中断挂起控制寄存器组。每个位对应的中断和 ISER 是一样的。通过置 1,可以将正在进行的中断挂起,而执行同级或更高级别的中断。写 0 是无效的。

ICPR[0-7]:全称是:Interrupt Clear-Pending Registers,是一个中断解挂控制寄存器组。其作用与 ISPR 相反,对应位也和 ISER 是一样的。通过设置 1,可以将挂起的中断解挂。写 0 无效。

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

IP[240]:全称是:Interrupt Priority Registers,是一个中断优先级控制的寄存器组。STM32F4 的中断分组与这个寄存器组密切相关。IP 寄存器组由 240 个 8bit的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。而 STM32F4只用到了其中的 82 个。IP[81]~IP[0]分别对应中断 81~0。而每个可屏蔽中断占用的 8bit 并没有全部使用,而是只用了高 4 位。这 4 位,又分为抢占优先级和响应优先级。抢占优先级在前,响应优先级在后。如下所示:

用于表达优先级的这 4bit,又被分组成抢占优先级和子优先级。如果有多个中断同时响应,抢占优先级高的就会抢占优先级低的优先得到执行,如果抢占优先级相同,就比较子优先级。如果抢占优先级和子优先级都相同的话,就比较他们的硬件中断编号,编号越小,优先级越高。

这两个优先级各占几个位根据 SCB->AIRCR 中bit10-8的中断分组设置来决定。SCB->AIRCR寄存器结构如下所示:(详细寄存器说明请参考《Cortex-M4 Devices Generic User Guide》P233)

STM32F407分为了5组。具体的分频关系如下:主优先级=抢占优先级

设置优先级分组可调用库函数 NVIC_PriorityGroupConfig() 实现,有关 NVIC中断相关的函数都在文件 stm32f4xx_hal_cortex.c和 stm32f4xx_hal_cortex.h 中。

EXTI,全称:External interrupt/event controller,即外部中断/事件控制器,管理了控制器的23个中断/事件线。每个中断/事件线都对应有一个边沿检测器,可以实现输入信号的上升沿检测和下降沿的检测。EXTI可以实现对每个中断/事件线进行单独配置,可以单独配置为中断或者事件,以及触发事件的属性。

EXTI的功能框图包含了 EXTI最核心内容,掌握了功能框图,对 EXTI就有一个整体的把握,在编程时就思路就非常清晰。

功能框图如下所示:

图中信号线上打一个斜杠并标注“23”字样,这个表示在控制器内部类似的信号线路有23个,这与EXTI总共有23个中断/时间线是吻合的。

EXTI可分为两大部分功能,一个是产生中断,另一个是产生事件,这两个功能从硬件上就有所不同。首先我们来看图中红色虚线指示的电路流程。它是一个产生中断的线路,最终信号流入到 NVIC 控制器内。

编号 1 是输入线,EXTI控制器有 23 个中断/事件输入线,这些输入线可以通过寄存器设置为任意一个 GPIO,也可以是一些外设的事件,输入线一般是存在电平变化的信号。

编号 2 是一个边沿检测电路,它会根据上升沿触发选择寄存器(EXTI_RTSR)和下降沿触发选择寄存器(EXTI_FTSR)对应位的设置来控制信号触发。边沿检测电路以输入线作为信号输入端,如果检测到有边沿跳变就输出有效信号 1给编号 3 电路,否则输出无效信号0。而 EXTI_RTSR和 EXTI_FTSR 两个寄存器可以控制需要检测哪些类型的电平跳变过程,可以是只有上升沿触发、只有下降沿触发或者上升沿和下降沿都触发。

编号 3 电路实际就是一个或门电路,它一个输入来自编号 2电路,另外一输入来自软件中断事件寄存器(EXTI_SWIER)。EXTI_SWIER 允许我们通过程序控制就可以启动中断/事件线。我们知道或门的作用就是“有”就为 1,所以这两个输入随便一个有效信号 1就可以输出 1 给编号 4和编号 6电路。

编号 4 电路是一个与门电路,它一个输入编号 3电路,另外一个输入来自中断屏蔽寄存器(EXTI_IMR)。与门电路要求输入都为 1才输出 1,如果EXTI_IMR 设置为 1时,最终编号 4 电路输出的信号才由编号 3电路的输出信号决定,这样我们可以简单的控制 EXTI_IMR 来实现是否产生中断的目的。编号 4电路输出的信号会被保存到挂起寄存器(EXTI_PR)内,如果确定编号 4电路输出为 1就会把 EXTI_PR 对应位置 1。

编号 5 是将 EXTI_PR 寄存器内容输出到 NVIC 内,从而实现系统中断事件控制。

接下来我们来看看绿色虚线指示的电路流程。它是一个产生事件的线路,最终输出一个脉冲信号。

产生事件线路是在编号 3电路之后与中断线路有所不同,之前电路都是共用的。编号6 电路是一个与门,它一个输入来自编号 3电路,另外一个输入来自事件屏蔽寄存器(EXTI_EMR)。如果 EXTI_EMR设置为 1时,最终编号 6 电路输出的信号才由编号 3 电路的输出信号决定,这样我们可以简单的控制 EXTI_EMR来实现是否产生事件的目的。

编号 7 是一个脉冲发生器电路,当它的输入端,即编号 6 电路的输出端,是一个有效信号 1 时就会产生一个脉冲;如果输入端是无效信号就不会输出脉冲。

编号 8 是一个脉冲信号,就是产生事件的线路最终的产物,这个脉冲信号可以给其他外设电路使用,比如定时器 TIM、模拟数字转换器 ADC 等等。

中断和事件的区别:产生中断线路目的是把输入信号输入到 NVIC,进一步会运行中断服务函数,实现功能,这样是软件级的。而产生事件线路目的就是传输一个脉冲信号给其他外设使用,并且是电路级别的信号传输,属于硬件级的。

EXTI有 23 个中断/事件线,每个 GPIO 都可以被设置为输入线,占用 EXTI0至EXTI15,还有另外七根用于特定的外设事件。如下图所示:

EXTI 线 0~15:对应外部 IO 口的输入中断。

EXTI 线 16:连接到 PVD 输出。

EXTI 线 17:连接到 RTC 闹钟事件。

EXTI 线 18:连接到 USB OTG FS 唤醒事件。

EXTI 线 19:连接到以太网唤醒事件。

EXTI 线 20:连接到 USB OTG HS(在 FS 中配置)唤醒事件。

EXTI 线 21:连接到 RTC 入侵和时间戳事件。

EXTI 线 22:连接到 RTC 唤醒事件。

外部中断配置寄存器包括:中断屏蔽寄存器(EXTI_IMR)、事件屏蔽寄存器(EXTI_EMR)、上升沿触发选择寄存器(EXTI_RTSR)、下降沿触发选择寄存器(EXTI_FTSR)、软件中断事件寄存器(EXTI_SWIER)、等待寄存器(EXTI_PR)。

我们在使用每一个中断的时候,都需要编写中断服务函数,在启动文件 startup_stm32f40xx.s 中我们预先为每个中断都写了一个中断服务函数,只是这些中断函数都是为空,为的只是初始化中断向量表。实际的中断服务函数都需要我们重新编写。

关于中断服务函数的函数名必须跟启动文件里面预先设置的一样,如果写错,系统就在中断向量表中找不到中断服务函数的入口,直接跳转到启动文件里面预先写好的空函数,并且在里面无限循环,实现不了中断。

按键中断配置步骤:

  1. 初始化按键对应的GPIO引脚
  2. 配置按键引脚外部中断的优先级及使能中断。
  3. 配置外部中断的中断服务函数,在中断服务函数中调用HAL_GPIO_EXTI_IRQHandler,该函数负责处理中断请求,在HAL_GPIO_EXTI_IRQHandler函数调用了HAL_GPIO_EXTI_Callback回调函数,进行中断应用配置。
  4. 定义HAL_GPIO_EXTI_Callback回调函数。

下面是按键中断程序:

#include "bsp_key.h"
#include "bsp_led.h"
#include "bsp_buzzer.h"

// 按键初始化
void KEY_Init(void)
{
    // Key0  Key1  Key2
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOE_CLK_ENABLE(); // 开始GPIOE时钟
	
    GPIO_Initure.Pin = GPIO_PIN_2 | GPIO_PIN_3 | GPIO_PIN_4; // PE2,PE3,PE4引脚
    GPIO_Initure.Mode = GPIO_MODE_IT_RISING_FALLING; // 双边沿触发模式
    GPIO_Initure.Pull = GPIO_PULLUP; // 上拉
    GPIO_Initure.Speed = GPIO_SPEED_HIGH; // 高速
    HAL_GPIO_Init(GPIOE, &GPIO_Initure); // GPIOE初始化
	
    // 配置外部中断及优先级
    HAL_NVIC_SetPriority(EXTI2_IRQn, 2, 1); // 抢占优先级为2,子优先级为1
    HAL_NVIC_SetPriority(EXTI3_IRQn, 2, 2); // 抢占优先级为2,子优先级为2	
    HAL_NVIC_SetPriority(EXTI4_IRQn, 2, 3); // 抢占优先级为2,子优先级为3	
	
    // 使能中断线
    HAL_NVIC_EnableIRQ(EXTI2_IRQn); // 使能中断线2
    HAL_NVIC_EnableIRQ(EXTI3_IRQn); // 使能中断线3
    HAL_NVIC_EnableIRQ(EXTI4_IRQn);	// 使能中断线4
}

// 中断线2的中断服务函数
void EXTI2_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_2);
}

// 中断线3的中断服务函数
void EXTI3_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3);
}

// 中断线4的中断服务函数
void EXTI4_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4);
}

// 中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    switch(GPIO_Pin)
    {
	case GPIO_PIN_2:
	    if(KEY0){ LED1_OFF;}
	    else    { LED1_ON; }
	    break;
	case GPIO_PIN_3:
	    if(KEY1){ LED2_OFF;}
	    else    { LED2_ON; }			
	    break;
	case GPIO_PIN_4:
	    if(KEY2){ BUZZER_OFF;}
	    else    { BUZZER_ON; }
	    break;
    }
}

参考文章:正点原子    STM32F4开发指南-库函数版本_V1.1

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值