第5章 CHAPTER 5 中断控制器NVIC与EXTI 5.1什么是中断 在嵌入式系统中,中断是一种机制,用于在系统执行正常程序流程的同时,及时响应特定事件或条件的发生。当某个特定的事件发生时(如外部硬件触发、定时器到达指定时间),中断会打断当前正在执行的程序流程,转而执行一个预先定义好的中断服务程序(Interrupt Service Routine,ISR),来处理该事件。而中断嵌套是指中断系统正在执行一个中断服务时,有另一个优先级更高的中断提出中断请求,这时会暂时终止当前正在执行的级别较低的中断服务程序,去执行级别更高的中断服务程序,待处理完毕,再返回被中断了的中断服务程序继续执行过程。中断的处理过程如图5.1所示。 图5.1中断的处理过程示意图 中断的出现主要是为了提高系统的响应速度和效率。相比于轮询(Polling)方式,中断可以让系统在等待事件发生时不会浪费处理器资源,而是通过硬件或软件的方式,让事件一旦发生就立即得到响应。这样可以让系统在处理多个任务时更加灵活高效。 中断处理过程一般包括以下几个步骤。 (1) 中断请求触发: 某个事件或条件发生,触发了相应的中断请求。 (2) 中断控制器响应: 中断控制器检测到中断请求,根据优先级确定下一个要执行的中断服务程序。 (3) 保存上下文: 在转到中断服务程序之前,系统需要保存当前程序的执行状态(如寄存器值、程序计数器等),以便在中断服务程序执行完毕后能够恢复到中断之前的状态。 (4) 执行中断服务程序: 执行与中断相关的中断服务程序,处理中断事件。 (5) 恢复现场: 中断服务程序执行完毕后,系统需要恢复之前保存的程序执行状态,以便回到中断发生之前的程序执行流程。 (6) 继续执行: 恢复程序执行流程,继续执行中断发生时被打断的程序。 通过合理地使用中断机制,可以实现系统的多任务处理、实时响应外部事件等功能,提高系统的性能和可靠性。 在STM32中还需要区分异常这个概念。中断和异常都是用于处理系统中的特殊事件,但它们的触发原因和处理方式有所不同。中断是由CPU外部事件触发的,如外部设备的状态改变、定时器溢出、外部I/O引脚的状态变化等,系统可以设计中断服务程序来处理这些事件,并且可以被优先级化和屏蔽; 而异常是由CPU内部的条件触发的,如除零、非法指令、访问未定义地址等,通常表示系统出现了错误或者特殊情况。 视频讲解 5.2嵌套向量中断控制器NVIC 5.2.1NVIC简介 STM32系列单片机支持众多的系统异常和外部中断。如STM32F10xxx系列有多达10个系统内部异常和60个外部中断,STM32F405xx系列有多达10个系统内部异常和82个外部中断,STM32U5系列有多达12个系统内部异常和141个外部中断。中断向量表存放在内存中的特定位置,其中存储着各种中断服务程序的入口地址。当一个中断事件发生时,处理器会根据中断向量表中对应中断号的地址跳转到相应的中断服务程序,以执行相应的中断处理操作。每个中断号对应着一个特定的中断事件,比如外部中断、定时器中断等,也对应着一个中断服务程序入口地址。通过中断向量表,嵌入式系统可以实现有效的中断处理,提高系统的响应速度和可靠性。表5.1为STM32U5系列的部分中断向量表。 表5.1STM32U5系列的部分中断向量表 自然优先级 优先级类型 名称 说明 地址 — — — 保留 0x0000 0000 -4 固定 Reset 复位 0x0000 0004 -2 固定 NMI 不可屏蔽中断。RCC 时钟安全系统(CSS) 连接到NMI向量 0x0000 0008 -3或-1 固定 Secure HardFault 安全硬件错误 0x0000 000C -1 固定 Nonsecure HardFault 非安全硬件故障,所有类别的故障 0x0000 000C 0 可设置 MemManage 内存管理 0x0000 0010 1 可设置 BusFault 预取指失败,存储器访问失败 0x0000 0014 2 可设置 UsageFault 未定义的指令或非法状态 0x0000 0018 3 可设置 SecureFault 安全故障 0x0000 001C — — — 保留 0x0000 0020~0x0000 002B 4 可设置 SVC 通过 SWI 指令调用的系统服务 0x0000 002C 5 可设置 Debug Monitor 调试监控器 0x0000 0030 — — — 保留 0x0000 0034 6 可设置 PendSV 可挂起的系统服务 0x0000 0038 7 可设置 SysTick 系统嘀嗒定时器 0x0000 003C 8 可设置 WWDG 窗口看门狗中断 0x0000 0040 9 可设置 PVD_PVM 可编程电压检测(PVD) /外设电压检测 0x0000 0044 10 可设置 RTC RTC全局非安全中断 0x0000 0048 … 146 可设置 DCACHE2 DCACHE 2全局中断 0x0000 0268 147 可设置 GFXTIM GFXTIM全局中断 0x0000 026C 148 可设置 JPEG JPEG同步中断 0x0000 0270 为了方便地管理这些中断和内部异常,STM32使用了内嵌向量中断控制器(Nested Vectored Interrupt Controller,NVIC)。以下是STM32 NVIC的一些重要特点和功能。 (1) 中断向量表: NVIC使用中断向量表来确定中断服务程序的位置。在STM32中,中断向量表通常位于内部Flash的起始地址处,包含每个中断的地址。 (2) 中断优先级: NVIC允许每个中断有抢占优先级和子优先级。较低抢占优先级的中断可以被较高抢占优先级的中断打断,这样可以确保重要的中断能够及时得到处理。当多个中断或任务具有相同的抢占优先级时,子优先级决定了哪一个会首先得到执行。优先级的序号越小,优先级越高。 (3) 中断控制: NVIC可以用于使能或禁用特定中断。 (4) 异常处理: 除普通的外部中断请求外,NVIC还负责处理内部异常,如硬件错误、系统异常等。 (5) 中断挂起: NVIC允许中断被暂时挂起,直到某些条件满足后重新使能。 通过合理配置NVIC,STM32可以有效地管理各种中断,并确保系统的可靠性和响应性。 5.2.2NVIC的优先级 为了设置每个中断的优先级,便于高优先级的中断优先得到响应,STM32具有灵活的优先级管理方式。 首先,每个中断可被设置为不同的抢占优先级和子优先级等级。抢占优先级高的中断可以打断抢占优先级低的中断从而优先执行。当抢占优先级相同时,子优先级较高的中断会优先执行,但不会打断子优先级较低的中断。若两个中断的抢占优先级和子优先级相同,则按照自然优先级排序。需要注意的是,优先级的序号越小,优先级越高。 在STM32中,还有优先级分组的概念,一般可选择5个组中的一个组。设置为组0时,无法设置中断的抢占优先级的等级,可以用4个位设置子优先级的等级。设置为组1时,可以用1个位设置中断的抢占优先级的等级,可以用3个位设置子优先级的等级。具体采用哪个组的方案需要通过设置应用中断和复位控制寄存器(Application Interrupt and Reset Control Register,AIRCR)的第[10∶8]位指定,如表5.2所示。 表5.2NVIC的优先级分组 优先级分组 AIRCR[10∶8] 优先级划分 0 111 0个位用于抢占优先级,4个位用于子优先级 1 110 1个位用于抢占优先级,3个位用于子优先级 2 101 2个位用于抢占优先级,2个位用于子优先级 3 100 3个位用于抢占优先级,1个位用于子优先级 4 011 4个位用于抢占优先级,0个位用于子优先级 举例来说,如果在系统初始化时选择使用组2的划分方式,那么对于某个中断,我们可以将其抢占优先级设置为0~3中的一个数,将其子优先级设置为0~3中的一个数。如果选择使用组4的划分方式,那么对于某个中断,可以将其抢占优先级设置为0~16中的一个数,而不可设置其子优先级。序号越小,优先级越高。 5.2.3NVIC的STM32CubeMX配置 在NVIC设置页面中,Priority Group即为设置优先级分组的位置,其中有5个分组,如图5.2所示。如果选择4 bits for preemption priority,那么抢占优先级可以设置为0~15中的一个,而子优先级不可以设置,因为抢占优先级和子优先级所使用的位数一共是4位。 图5.2STM32CubeMX中的NVIC优先级分组设置 在Code generation页面,如果想调用中断处理函数,则需要选中Generate IRQ handler和Call HAL handler复选框,这样在生成的代码中才会生成中断处理函数和HAL库中的对应外设的中断处理函数,如图5.3所示。 图5.3NVIC的Code generation页面 这样,在所生成工程中的stm32u5xx_it.c文件中,会有如图5.4所示的两个中断处理函数。 图5.4STM32CubeMX生成的中断处理函数 视频讲解 5.3EXTI 5.3.1EXTI简介 EXTI的全称是External Interrupt/Event Controller,即外部中断/事件控制器,它允许外部事件(如GPIO引脚状态变化、以太网唤醒、USB唤醒等)触发中断或事件,并在需要时唤醒系统。EXTI与其他外设中断的不同是,EXTI专指来源于芯片外部的信号所触发的中断,而其他外设中断来自芯片内部。 这里还需要区分一下中断和事件的概念。在STM32的手册中会经常看到中断(Interrupt)和事件(Event)。中断是事件的一种,可以称为中断事件。中断发生时会立即中断当前的程序执行流程,转而执行与该中断相关联的中断服务程序(Interrupt Service Routine,ISR),处理完中断服务程序后返回源程序继续执行。非中断事件不会立即中断当前CPU程序的执行,而是会生成一个事件信号,可以通过软件轮询、触发DMA传输、使能其他外设等方式来处理。 5.3.2EXTI的内部架构 一个EXTI线上可以部署多个外部中断/事件来源。STM32F1、STM32F4和STM32U5系列芯片都有多个EXTI线,前16个EXTI线的功能大致相同,都负责GPIO引脚的外部中断,其他的EXTI线不尽相同,不同型号STM32的EXTI线的总数也不太一样,如表5.3所示。 表5.3不同型号的STM32的EXTI线的功能说明 EXTI线 STM32F103 STM32F405 STM32U575 0~15 GPIO GPIO GPIO 16 PVD输出 PVD输出 PVD输出 17 RTC闹钟事件 RTC闹钟事件 COMP1输出 18 USB唤醒事件 USB OTG FS唤醒事件 COMP2输出 19 以太网唤醒事件 以太网唤醒事件 VDDUSB电压监测 20 — USB OTG HS(在 FS 中配置)唤醒事件 VDDIO2电压监测 21 — RTC入侵和时间戳事件 VDDA电压监测1 22 — RTC唤醒事件 VDDA电压监测2 STM32F103、STM32F405的EXTI内部架构如图5.5所示。 图5.5STM32F103、STM32F405的EXTI内部架构图 STM32U575的EXTI内部架构如图5.6所示。 尽管两个结构框图细节不太一样,但是它们包含相似的结构和功能。图5.6的可配置事件输入对应图5.5的输入线,AHB接口对应图5.5的APB总线,hclk对应图5.5的PCLK2,它们都受EXTI内部的寄存器和逻辑单元控制,从而向CPU或芯片内部其他单元输出中断或事件信息。 图5.5的输入线连接着边沿检测电路,这对应图5.6的异步边沿探测电路。也就是说,需要通过外部信号电平跳变时产生的上升沿或下降沿来判断中断是否产生。 5.3.3GPIO的外部中断 由GPIO引脚触发的外部中断是经常被用到的,下面着重介绍GPIO外部中断的原理。如5.3.2节所述,EXTI0~EXTI15连接的是GPIO。由于GPIO引脚众多,远远超过16个,因此每根EXTI线需要连接多个GPIO引脚。因为一组GPIO(如GPIOA、GPIOB)有16个引脚,所以可以利用前16根EXTI线对这些引脚进行分组。分组方式如图5.7所示。 通过该结构图可以知道,不管同一组的哪个引脚触发了中断,该组的EXTI线上都会产生信号。但是,每个组只能设置一个引脚触发中断。举例来说,如果把PA0设置为外部中断模式,该组的其他引脚都不能被设置为外部中断。因此,实际上只能使用16个GPIO外部中断。每组使用的引脚编号由EXTICRx(x=1~4)寄存器设置。每个寄存器分为4个区域,每个区域的8位用于配置一个组内采用的中断引脚是哪个。 在STM32F405中,EXTI5~EXTI9共用一个中断地址,EXTI10~EXTI15共用一个中断地址。这意味着EXTI5~EXTI9会共用一个中断服务程序EXTI9_5_IRQHandler(),如图5.8所示。 在STM32U575中,EXTI0~EXTI15中每根中断线都有独立的中断地址,可以使用独立的中断服务程序。 图5.6STM32U575的EXTI内部架构 图5.7GPIO与EXTI线的分组方式 图5.8STM32F405的EXTI中断地址分配 5.3.4EXTI的STM32CubeMX配置 在STM32CubeMX中随便找一个GPIO引脚设置为EXTI模式,即可在GPIO界面中看到相应的配置,如图5.9所示。 图5.9STM32CubeMX中的GPIO的EXTI配置页面 Pin Privilege access可以设置特权访问属性。Privilegedonly access(仅特权访问)是一种访问权限,它意味着只有处于特权模式(Privileged Mode)的代码或者任务才能够配置外部中断线。这种权限设置可以确保只有系统的关键部分才能够对外部中断进行控制和操作,从而增强系统的安全性和稳定性。该配置是STM32U5中特有的,在STM32F405、STM32F103中没有。 GPIO mode可以设置中断的触发条件,如上升沿触发、下降沿触发、上升沿或下降沿触发; 也可以设置为外部中断模式或外部事件模式。在外部事件模式下,引脚上的电平或边沿的变化并不会直接触发中断,而是通过外部事件触发器(External Event Trigger)来控制。这通常用于同步外部事件和微控制器内部的操作,可以通过配置的方式触发相应的外部事件,如定时器的开始和结束、某些数据的传输等。 GPIO Pullup/Pulldown用于配置引脚的上拉电阻或下拉电阻。 设置完GPIO的触发模式后,还需要在NVIC页面使能该中断,如图5.10所示。 图5.10STM32CubeMX的GPIO的NVIC使能页面 5.3.5EXTI的寄存器 下面介绍几个常用的寄存器。 1. EXTI上升沿触发选择寄存器(EXTI_RTSR)和EXTI下降沿触发选择寄存器(EXTI_FTSR) 如图5.11和图5.12所示的两个寄存器用于配置某个中断线是否可被上升沿或下降沿触发。在CortexM33架构的单片机中,这两个寄存器各有26个有效位,对应26根EXTI线(特定型号的芯片不会全部用到),而在CortexM3架构的单片机中,只有20个。 图5.11EXTI上升沿触发选择寄存器EXTI_RTSR 图5.12EXTI下降沿触发选择寄存器EXTI_FTSR 寄存器中的每个位对应一根EXTI线。当该位置1时,即可设置为上升沿或下降沿触发。如果两个寄存器对应位都被设置成了1,则上升沿和下降沿都触发。 2. EXTI软件中断事件寄存器(EXTI_SWIER) EXTI_SWIER寄存器用于软件触发事件,每个位对应每根EXTI线。当向对应位写1时,则可以软件触发该EXTI线上的事件,如图5.13所示。 图5.13EXTI软件中断事件寄存器EXTI_SWIER 3. EXTI外部中断选择寄存器(EXTI_EXTICRm) EXTI_EXTICRm寄存器用于配置EXTI0~EXTI15这16根EXTI线连接到哪个引脚上(m=1~4),每个寄存器可配置4根EXTI线,每根EXTI线使用8个位进行配置,如图5.14所示。 图5.14EXTI外部中断选择寄存器EXTI_EXTICRm PA~PJ对应0x00~0x09的写入值。例如,如果想配置EXTI0由PC0引脚触发,那么EXTICR1的位[7∶0]应该被写入0x02。如果想配置EXTI5由PE5引脚触发,那么EXTICR2的位[15∶8]应该写入0x04。 5.3.6EXTI的HAL库配置流程 配置EXTI的中断并编写中断服务程序共涉及以下几个流程。 (1) 设置中断触发条件(在STM32CubeMX中配置)。如配置GPIO的某个引脚采用上升沿或下降沿触发。 (2) 设置中断优先级(在STM32CubeMX中配置)。涉及使用HAL_NVIC_SetPriority()设置中断优先级,如HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0)。 (3) 使能中断(在STM32CubeMX中配置)。涉及使用HAL_NVIC_EnableIRQ()使能中断,如HAL_NVIC_EnableIRQ(EXTI0_IRQn)。 (4) 在中断服务程序中判断中断外设来源。不同的中断会调用不同的中断服务程序,如EXTI0_IRQHandler()、TIM2_IRQHandler()等。这些函数的名称通常以PPP_IRQHandler()命名(PPP为外设简称)。在这些中断服务程序中又会调用外设通用中断处理函数HAL_PPP_IRQHandler()(PPP为外设简称)来向通用函数中引入外设句柄或名称。在HAL库中,同一种类型的外设会调用同一个通用中断处理函数。 void EXTI0_IRQHandler(void) { /* USER CODE BEGIN EXTI0_IRQn 0 */ /* USER CODE END EXTI0_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); /* USER CODE BEGIN EXTI0_IRQn 1 */ /* USER CODE END EXTI0_IRQn 1 */ } (5) 在外设通用中断处理函数中会依据引入的外设句柄或名称来访问对应的中断标志位,判断到底哪个外设发生了中断,并清除该标志位,然后访问外设通用中断回调函数HAL_PPP_Callback()(PPP为外设简称)。 /** * @briefThis function handles EXTI interrupt request. * @paramGPIO_Pin Specifies the pins connected EXTI line * @retval None */ void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET) { __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin); HAL_GPIO_EXTI_Callback(GPIO_Pin); } } (6) 执行中断回调函数。外设通用中断回调函数HAL_PPP_Callback()是弱函数,可以在其他文件中重定义(一般写在main.c文件中),从而根据用户需求编写程序。 /** * @briefEXTI line detection callbacks. * @paramGPIO_Pin Specifies the pins connected EXTI line * @retval None */ weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { /* Prevent unused argument(s) compilation warning */ UNUSED(GPIO_Pin); /* NOTE: This function Should not be modified, when the callback is needed, the HAL_GPIO_EXTI_Callback could be implemented in the user file */ } 在上面的步骤中,执行中断时主要涉及3类函数: 外设中断服务程序PPP_IRQHandler()、HAL库外设通用中断处理函数HAL_PPP_IRQHandler()和外设通用中断回调函数HAL_PPP_Callback()。图5.15再次表明了它们之间的关系。 图5.15中断处理函数之间的关系 图5.15表明,不同中断来源所对应的中断服务程序(ISR)入口地址(启动文件中定义)不同,但是同一类外设会调用相同的HAL库外设通用中断处理函数HAL_PPP_IRQHandler(),进而调用相同的外设通用中断回调函数HAL_PPP_Callback()。这主要是因为HAL库的设计考虑到了可移植性和模块化。 视频讲解 5.4实验: 用外部中断进行按键上升沿/下降沿检测 5.4.1应用场景及目的 第4章进行的按键实验是依靠轮询的方式判断状态的,这会大量占用CPU的资源,也会导致程序在执行其他任务时检测不到突然的电平变化。在实际应用中,更多地会采用外部中断的方式来判断按键是否按下。当按键按下时,连接按键的引脚会触发一个电平变化。电平由高到低变化会产生下降沿,由低到高变化会产生上升沿。可以通过利用边沿的变化来触发中断,进而执行相应的动作。 本实验使用底板上的USER按键和五向按键来控制核心板上D1处的LED灯的亮灭。当按下USER按键时,点亮LED灯,当按下五向按键时,关闭LED灯。 本实验对应的例程为041_STM32U575_GPIO_EXTI_Rise_Fall。 5.4.2原理图 先来看USER按键的连接原理图,如图5.16所示。可以看到当按下USER按键后,USER引脚会变为低电平,也就是产生下降沿。因此需要将与USER引脚相连的GPIO引脚PA12设置为下降沿触发中断。 图5.16USER按键原理图 在底板原理图中包含了五向按键的电路,如图5.17所示。U7B处的运算放大器被连接为电压跟随器,U7A处的运算放大器被连接为电压比较器。当按键抬起时,U7B的5号引脚为3.3V电压,7号引脚也输出3.3V电压。7号引脚与U7A处的3号引脚相连。U7A处的2号引脚通过R30与R31的分压会得到2.5V电压。由于U7A处的3号引脚电压大于2号引脚,因此会在1号引脚处输出5V电压。此时Q3处的场效应管导通,IO INT4引脚为低电平。当五向按键按下时,IO INT4引脚为高电平。因此按下五向按键会产生一个上升沿。 图5.17五向按键原理图 通过底板原理图的J2(见图5.18)可知,IO INT4连接到了PA0上。PA0需要被设置为上升沿触发才能在按下五向按键时触发中断。 图5.18底板原理图的J2处插座 5.4.3程序流程 本实验程序流程如图5.19所示。 图5.19按键中断检测实验程序流程 通过流程图可知,PA0需要配置为上升沿触发中断,PA12需要配置为下降沿触发中断,然后在使能对应的EXTI中断后,进入空的主循环。后续任务完全由按键按下产生的中断所对应的中断服务程序执行。 5.4.4程序配置 打开STM32CubeMX,根据芯片型号新建一个工程。在打开的页面中,分别配置PA0和PA12为EXTI0和EXTI12,设置为上拉输入模式,分别配置为上升沿触发和下降沿触发,如图5.20所示。 图5.20GPIO配置页面 然后在NVIC标签页使能中断,如图5.21所示。 图5.21使能GPIO的中断 如图5.22所示的NVIC配置页面自动选中了两个中断的Generate IRQ handler和Call HAL handler。这对应stm32**xx_it.c文件中的EXTI0_IRQHandler()、EXTI12_IRQHandler()和HAL_GPIO_EXTI_IRQHandler()。 图5.22NVIC配置页面 由于本实验要控制核心板上D1处的LED灯,该灯连接到了PC13引脚,因此PC13引脚需要被配置为推挽输出模式,默认为低电平,如图5.23所示。 图5.23PC13引脚的配置 配置英文工程目录Toolchain/MDK,设置为复制必要的文件,分别生成.c/.h文件后,生成并打开工程。 main.c文件中调用的MX_GPIO_Init()函数中配置了对应的引脚模式,并使能了中断。 在stm32u5xx_it.c文件中,分别生成了EXTI0和EXTI12的中断处理函数。在对应的函数中又调用了HAL_GPIO_EXTI_IRQHandler()函数并输入了对应的引脚编号。 /** * @brief This function handles EXTI Line0 interrupt. */ void EXTI0_IRQHandler(void) { /* USER CODE BEGIN EXTI0_IRQn 0 */ /* USER CODE END EXTI0_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); /* USER CODE BEGIN EXTI0_IRQn 1 */ /* USER CODE END EXTI0_IRQn 1 */ } /** * @brief This function handles EXTI Line12 interrupt. */ void EXTI12_IRQHandler(void) { /* USER CODE BEGIN EXTI12_IRQn 0 */ /* USER CODE END EXTI12_IRQn 0 */ HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); /* USER CODE BEGIN EXTI12_IRQn 1 */ /* USER CODE END EXTI12_IRQn 1 */ } 在HAL_GPIO_EXTI_IRQHandler()函数中,通过输入引脚编号来判断是否为该引脚产生的中断标志。若是对应引脚,则清除该中断标志,然后调用HAL_GPIO_EXTI_Falling_Callback()或HAL_GPIO_EXTI_Rising_Callback()函数进入回调函数。 void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) { /* EXTI line interrupt detected */ if (__HAL_GPIO_EXTI_GET_RISING_IT(GPIO_Pin) != 0U) { __HAL_GPIO_EXTI_CLEAR_RISING_IT(GPIO_Pin); HAL_GPIO_EXTI_Rising_Callback(GPIO_Pin); } if (__HAL_GPIO_EXTI_GET_FALLING_IT(GPIO_Pin) != 0U) { __HAL_GPIO_EXTI_CLEAR_FALLING_IT(GPIO_Pin); HAL_GPIO_EXTI_Falling_Callback(GPIO_Pin); } } 在main.c文件的USER CODE BEGIN 4后面重新编写这两个函数: void HAL_GPIO_EXTI_Falling_Callback(uint16_t GPIO_Pin)//下降沿触发的回调函数 { if(GPIO_Pin== GPIO_PIN_12) { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_12)==GPIO_PIN_RESET)//读取按键是否按下 { HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);//点亮LED灯 } } } void HAL_GPIO_EXTI_Rising_Callback(uint16_t GPIO_Pin)//上升沿触发的回调函数 { if(GPIO_Pin== GPIO_PIN_0) { if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)==GPIO_PIN_SET)//读取按键是否按下 { HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);//熄灭LED灯 } } } 在下降沿触发的回调函数中,先判断触发下降沿中断的引脚是不是GPIO_PIN_12。这里不用判断是PA12还是PB12,因为PA12、PB12、PC12这些引脚都在EXTI12线上,只有其中一个能够被设置成EXTI模式。如果是GPIO_PIN_12产生的,则设置PC13为高电平,点亮LED灯。 另外,在NVIC页面中HAL_Delay()函数所依赖的System tick timer默认的抢占优先级是最低的,如图5.24所示。因此,如果此时在EXTI中断回调函数中调用HAL_Delay()进行延时用于消抖的话,会导致程序无法正常执行。因为在执行高抢占优先级的中断时,无法执行低抢占优先级的中断,HAL_Delay()无法获得计时值。 图5.24NVIC中的Systick中断优先级配置 5.4.5实验现象 编译工程,选择好调试下载器后,将程序下载到开发板上。复位后按下USER按键,即可看到核心板上D1处的LED灯点亮; 按下五向键,即可看到该灯熄灭。 5.5习题 简答题 1. 什么是中断?它在程序执行过程中有什么作用? 2. NVIC的抢占优先级和子优先级有什么区别? 3. PA12在EXTI的几号线上?能同时使能PA1和PC1为外部中断模式吗?尝试从芯片手册中找到依据。 4. 什么是中断向量表?发生对应中断时,程序是如何跳转到对应的中断服务程序中的? 5. 如果用户设置的两个中断的抢占优先级和子优先级相同,那么如何判断哪个优先级高?找到官方手册对应的说明位置。 思考题 设计一个方案,利用外部中断实现按下按键时打开某个外设,松开按键时关闭某个外设,主循环中不写任何程序。