外部中断理论代码
107.3.2外部中断理论代码
/* ==================== Core\Inc\main.h ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.h
* @brief : Header for main.c file.
* This file contains the common defines of the application.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __MAIN_H
#define __MAIN_H
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void Error_Handler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
/* Private defines -----------------------------------------------------------*/
#define EXIT_KEY1_Pin GPIO_PIN_3
#define EXIT_KEY1_GPIO_Port GPIOE
#define EXIT_KEY1_EXTI_IRQn EXTI3_IRQn
#define EXTI_KEY2_Pin GPIO_PIN_4
#define EXTI_KEY2_GPIO_Port GPIOE
#define EXTI_KEY2_EXTI_IRQn EXTI4_IRQn
#define LED1_Pin GPIO_PIN_5
#define LED1_GPIO_Port GPIOB
#define BEEP_Pin GPIO_PIN_8
#define BEEP_GPIO_Port GPIOB
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
#ifdef __cplusplus
}
#endif
#endif /* __MAIN_H */
/* ==================== Core\Inc\gpio.h ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file gpio.h
* @brief This file contains all the function prototypes for
* the gpio.c file
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __GPIO_H__
#define __GPIO_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
void MX_GPIO_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
/* ==================== Core\Inc\stm32f1xx_it.h ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.h
* @brief This file contains the headers of the interrupt handlers.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F1xx_IT_H
#define __STM32F1xx_IT_H
#ifdef __cplusplus
extern "C" {
#endif
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Exported types ------------------------------------------------------------*/
/* USER CODE BEGIN ET */
/* USER CODE END ET */
/* Exported constants --------------------------------------------------------*/
/* USER CODE BEGIN EC */
/* USER CODE END EC */
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
/* Exported functions prototypes ---------------------------------------------*/
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void EXTI3_IRQHandler(void);
void EXTI4_IRQHandler(void);
/* USER CODE BEGIN EFP */
/* USER CODE END EFP */
#ifdef __cplusplus
}
#endif
#endif /* __STM32F1xx_IT_H */
/* ==================== Core\Src\main.c ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
HAL_Delay(10);
if(HAL_GPIO_ReadPin(EXIT_KEY1_GPIO_Port, EXIT_KEY1_Pin)==0)
{
HAL_GPIO_TogglePin(BEEP_GPIO_Port, BEEP_Pin);
}
if(HAL_GPIO_ReadPin(EXTI_KEY2_GPIO_Port, EXTI_KEY2_Pin)==0)
{
HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
}
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/* ==================== Core\Src\gpio.c ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file gpio.c
* @brief This file provides code for the configuration
* of all used GPIO pins.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "gpio.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/*----------------------------------------------------------------------------*/
/* Configure GPIO */
/*----------------------------------------------------------------------------*/
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOE_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET);
/*Configure GPIO pins : PEPin PEPin */
GPIO_InitStruct.Pin = EXIT_KEY1_Pin|EXTI_KEY2_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = LED1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PtPin */
GPIO_InitStruct.Pin = BEEP_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLDOWN;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(BEEP_GPIO_Port, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
HAL_NVIC_SetPriority(EXTI4_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
}
/* USER CODE BEGIN 2 */
/* USER CODE END 2 */
/* ==================== Core\Src\stm32f1xx_it.c ==================== */
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 */
/* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/**
* @brief This function handles EXTI line3 interrupt.
*/
void EXTI3_IRQHandler(void)
{
/* USER CODE BEGIN EXTI3_IRQn 0 */
/* USER CODE END EXTI3_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(EXIT_KEY1_Pin);
/* USER CODE BEGIN EXTI3_IRQn 1 */
/* USER CODE END EXTI3_IRQn 1 */
}
/**
* @brief This function handles EXTI line4 interrupt.
*/
void EXTI4_IRQHandler(void)
{
/* USER CODE BEGIN EXTI4_IRQn 0 */
/* USER CODE END EXTI4_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(EXTI_KEY2_Pin);
/* USER CODE BEGIN EXTI4_IRQn 1 */
/* USER CODE END EXTI4_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
一、外部中断理论及代码编写
从按键到用户代码,完整调用链不是“按键直接执行回调”,而是:
sequenceDiagram
participant K as PE3/PE4 按键
participant E as EXTI
participant N as NVIC
participant I as IRQHandler
participant H as HAL
participant C as 用户 Callback
K->>E: 下降沿
E->>E: PR3/PR4=1
E->>N: 提出 IRQ
N->>I: 根据向量表进入处理函数
I->>H: HAL_GPIO_EXTI_IRQHandler(pin)
H->>E: 检查并清除 PR
H->>C: HAL_GPIO_EXTI_Callback(pin)
理解每一层的职责,可以快速判断故障位于 GPIO/EXTI、NVIC、向量表、HAL 还是用户逻辑。
1.1 外部中断触发函数配置
CubeMX 生成的两个入口:
void EXTI3_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(EXIT_KEY1_Pin);
}
void EXTI4_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(EXTI_KEY2_Pin);
}
函数名必须与启动文件中向量表的符号完全一致。EXTI3_IRQHandler 对应 EXTI3_IRQn,EXTI4_IRQHandler 对应 EXTI4_IRQn。函数参数是引脚掩码:
GPIO_PIN_3 = 0x0008 = 1 << 3
GPIO_PIN_4 = 0x0010 = 1 << 4
HAL 处理函数的核心:
if (__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0U)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
GET_IT 读取 EXTI_PR 对应位;CLEAR_IT 通过向 PR 写 1 清挂起。这里不能按普通寄存器的“写 0 清零”直觉操作。
1.2 回调函数介绍
HAL 驱动提供弱定义:
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
UNUSED(GPIO_Pin);
}
用户在 main.c 中定义同名普通函数后,链接器使用用户实现覆盖弱定义。无需改动 HAL 驱动文件,升级或重新生成工程时也更安全。
本工程回调先延时 10 ms,再分别读取 PE3 与 PE4:
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
HAL_Delay(10);
if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_8);
if (HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4) == GPIO_PIN_RESET)
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
这能展示现象,但存在两个重要问题:
- 没有使用 GPIO_Pin 参数。按 PE3 进入回调时,若 PE4 恰好也处于低电平,两路输出都会翻转;
- 在 ISR 中调用 HAL_Delay 可能卡住。HAL_Delay 等待 SysTick 增加全局 tick;若 SysTick 的抢占优先级不高于当前 EXTI,它不能抢占 EXTI,tick 不更新,延时就无法结束。
更稳妥的短 ISR 写法:
volatile uint16_t exti_pending_pin;
volatile uint32_t exti_pending_tick;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == EXIT_KEY1_Pin || GPIO_Pin == EXTI_KEY2_Pin)
{
exti_pending_pin = GPIO_Pin;
exti_pending_tick = HAL_GetTick();
}
}
主循环在事件发生 10~20 ms 后再次读取按键,确认仍为低电平,再执行翻转。若两键可能同时触发,单个变量会覆盖事件,应改用位标志:
volatile uint16_t exti_pending_mask;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
exti_pending_mask |= GPIO_Pin;
}
1.3 中断函数编写
代码应分清三个位置:
| 层级 | 文件 | 应放内容 |
|---|---|---|
| IRQHandler | stm32f1xx_it.c | 识别 IRQ,调用 HAL 分发函数 |
| HAL 分发 | stm32f1xx_hal_gpio.c | 检查/清挂起位,调用弱回调 |
| 用户回调 | main.c 或用户模块 | 记录事件,执行极短的设备相关处理 |
中断函数编写原则:
- 尽快清除或控制中断源;
- 不使用长延时和阻塞式串口收发;
- 不在 ISR 中做浮点格式化、大循环或动态内存分配;
- 与主循环共享的数据要考虑 volatile 与原子性;
- 只记录事件,耗时任务放到主循环或 RTOS 任务;
- 若使用共享 IRQ,逐一判断所有可能的挂起位。
中断返回时,Cortex-M3 会恢复进入异常时自动保存的 R0~R3、R12、LR、PC、xPSR。编译器生成的 ISR 前后代码还会按需要保存其他寄存器。
1.4 中断优先级配置
工程配置为:
HAL_NVIC_SetPriority(EXTI3_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
HAL_NVIC_SetPriority(EXTI4_IRQn, 7, 0);
HAL_NVIC_EnableIRQ(EXTI4_IRQn);
STM32F103 的 Cortex-M3 实现 4 个优先级位,逻辑优先级值越小,优先级越高。在默认 NVIC_PRIORITYGROUP_4 下,4 位全部用于抢占优先级,没有有效的子优先级位:
EXTI3:抢占优先级 5
EXTI4:抢占优先级 7
5 < 7,所以 EXTI3 可以抢占正在执行的 EXTI4。
EXTI4 不能抢占 EXTI3,只能保持挂起,等 EXTI3 返回后处理。
HAL_NVIC_EnableIRQ 只是在 NVIC 中打开该通道。还必须同时开启 GPIO/AFIO 时钟、配置 EXTI 映射和触发边沿、解除 EXTI_IMR 屏蔽。
1.5 抢占优先级和子优先级
优先级分组决定 4 个实现位如何在“抢占优先级”和“子优先级”之间分配:
| 分组 | 抢占优先级位 | 子优先级位 | 抢占级数量 | 子级数量 |
|---|---|---|---|---|
| NVIC_PRIORITYGROUP_0 | 0 | 4 | 1 | 16 |
| NVIC_PRIORITYGROUP_1 | 1 | 3 | 2 | 8 |
| NVIC_PRIORITYGROUP_2 | 2 | 2 | 4 | 4 |
| NVIC_PRIORITYGROUP_3 | 3 | 1 | 8 | 2 |
| NVIC_PRIORITYGROUP_4 | 4 | 0 | 16 | 1 |
两者作用不同:
- 抢占优先级决定一个中断能否打断另一个正在执行的中断;
- 子优先级只在多个相同抢占级的中断同时处于挂起状态时决定先服务谁;
- 子优先级更高并不能抢占当前 ISR;
- 数字越小表示逻辑优先级越高。
示例:在 Group 2 下,A=(抢占 1, 子 3),B=(抢占 2, 子 0)。A 能抢占 B,因为抢占级 1 更高;B 的子优先级数字更小也不能改变这一点。若 C=(抢占 1, 子 0) 与 A 同时挂起,C 先于 A,但 C 与 A 互相不能抢占。
优先级分组应在系统初始化阶段设置一次,不要在不同模块反复更改,否则早先配置的数值会被重新解释。还要特别检查 SysTick:若 ISR 内仍调用依赖 HAL tick 的 API,SysTick 必须能运行;更好的设计仍是让 ISR 不等待。