外部中断相关寄存器
108.3.3外部中断相关寄存器
/* ==================== 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 */
一、外部中断相关寄存器
PE3、PE4 的下降沿中断涉及四组硬件:
flowchart LR
RCC[RCC<br/>开启 GPIOE/AFIO 时钟] --> GPIO[GPIOE<br/>输入上拉]
GPIO --> AFIO[AFIO_EXTICR<br/>端口 E 映射到 EXTI3/4]
AFIO --> EXTI[EXTI<br/>下降沿、屏蔽、挂起]
EXTI --> NVIC[NVIC<br/>使能与优先级]
NVIC --> CPU[CPU 进入向量函数]
只看 EXTI 寄存器还不够;EXTI 不负责选择“哪一个端口的 3 号脚”,这由 AFIO_EXTICR 完成,也不负责 CPU 级优先级,这由 NVIC 完成。
1.1 从代码层面对外部中断进行剖析
课程配置:
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);
GPIO_MODE_IT_FALLING 在 HAL 头文件中编码为 0x10210000。这个数不是直接写入某一个硬件寄存器,而是供 HAL_GPIO_Init 拆解:
0x10210000
│ ││
│ │└─ GPIO 输入配置
│ └── 中断模式
└──── 下降沿触发
HAL 对每一个选中的引脚依次配置 GPIO 四位字段、AFIO 映射、RTSR/FTSR、EMR/IMR。
1.1.1 GPIO的初始化
PE3 和 PE4 配为“上拉/下拉输入”:
GPIOE_CRL:
PE3 四位字段 CRL[15:12] = CNF3 MODE3 = 10 00
PE4 四位字段 CRL[19:16] = CNF4 MODE4 = 10 00
GPIOE_ODR:
ODR3 = 1 → 选择内部上拉
ODR4 = 1 → 选择内部上拉
STM32F1 的内部上拉和下拉共用 CNF=10;方向不是 CRL 中另一个位,而是由 ODR 对应位选择。HAL 在配置上拉时向 BSRR 写目标位,使 ODR 位变成 1;配置下拉时通过 BRR 清 ODR 位。
1.1.2 STM32的复位和时钟控制
RCC_APB2ENR 相关位:
| 位 | 名称 | 本实验 |
|---|---|---|
| 6 | IOPEEN | 置 1,开启 GPIOE |
| 3 | IOPBEN | 置 1,开启 GPIOB |
| 2 | IOPAEN | 工程也置 1 |
| 0 | AFIOEN | HAL 配置 EXTI 时自动置 1 |
没有 AFIO 时钟,EXTICR 映射不能正确配置;没有 GPIOE 时钟,PE3/PE4 输入配置无效。
1.1.3 通用和复用功能I/O
AFIO_EXTICR1~4 每个寄存器管理四条 EXTI 线,每条线占 4 位:
| 寄存器 | 管理的线 |
|---|---|
| EXTICR1 | EXTI0~3 |
| EXTICR2 | EXTI4~7 |
| EXTICR3 | EXTI8~11 |
| EXTICR4 | EXTI12~15 |
端口编码:
| 编码 | 端口 |
|---|---|
| 0000 | PA |
| 0001 | PB |
| 0010 | PC |
| 0011 | PD |
| 0100 | PE |
| 0101 | PF |
| 0110 | PG |
所以本实验:
AFIO_EXTICR1[15:12] = EXTI3[3:0] = 0100 → PE3
AFIO_EXTICR2[ 3: 0] = EXTI4[3:0] = 0100 → PE4
字段计算公式:
寄存器下标 = line / 4
字段左移位 = (line % 4) × 4
HAL 源码中的 AFIO->EXTICR[position >> 2] 和 4 * (position & 3) 正是这两个运算。
1.1.4 外部中断/事件控制器
EXTI_TypeDef 只有六个 32 位寄存器:
typedef struct
{
__IO uint32_t IMR; /* 0x00 中断屏蔽 */
__IO uint32_t EMR; /* 0x04 事件屏蔽 */
__IO uint32_t RTSR; /* 0x08 上升沿选择 */
__IO uint32_t FTSR; /* 0x0C 下降沿选择 */
__IO uint32_t SWIER; /* 0x10 软件请求 */
__IO uint32_t PR; /* 0x14 挂起,写1清除 */
} EXTI_TypeDef;
位 0~18 分别对应 EXTI0~18;位 19 只在互联型器件上有效。普通 STM32F103 应把未实现位视为保留位。
| 寄存器位 x | 写 0 | 写 1 |
|---|---|---|
| IMR.MRx | 屏蔽中断请求 | 开放中断请求 |
| EMR.MRx | 屏蔽事件请求 | 开放事件请求 |
| RTSR.TRx | 不检测上升沿 | 检测上升沿 |
| FTSR.TRx | 不检测下降沿 | 检测下降沿 |
| SWIER.SWIEx | 无请求 | 产生软件请求并置挂起 |
| PR.PRx | 无清除作用 | 清除该挂起位 |
PR 的“写 1 清除”最容易出错。正确做法是:
EXTI->PR = GPIO_PIN_3; /* 清 PR3 */
不要用普通变量思维执行 EXTI->PR &= ~GPIO_PIN_3;这会先读到多个挂起位,再以不合适的方式写回,可能错误清除其他线路。
1.1.5 外部中断的初始化过程
以 PE3 为例,HAL_GPIO_Init 的过程可展开为:
1. GPIOE_CRL[15:12] = 0b1000 输入上拉/下拉
2. GPIOE_BSRR = 1 << 3 ODR3=1,选择上拉
3. RCC_APB2ENR.AFIOEN = 1 开 AFIO 时钟
4. AFIO_EXTICR1[15:12] = 0b0100 EXTI3 选择端口 E
5. EXTI_RTSR.TR3 = 0 关闭上升沿
6. EXTI_FTSR.TR3 = 1 打开下降沿
7. EXTI_EMR.MR3 = 0 不走事件通路
8. EXTI_IMR.MR3 = 1 打开中断通路
PE4 采用相同过程,目标位换成 4,映射字段位于 EXTICR2[3:0]。
1.1.5.1 GPIO速度定义
GPIO 速度宏只对输出模式有意义:
| HAL 宏 | STM32F1 MODE[1:0] | 最大输出速度 |
|---|---|---|
| GPIO_SPEED_FREQ_LOW | 10 | 2 MHz |
| GPIO_SPEED_FREQ_MEDIUM | 01 | 10 MHz |
| GPIO_SPEED_FREQ_HIGH | 11 | 50 MHz |
PE3/PE4 是输入模式,MODE 必须为 00,所以不使用 Speed 字段。把“输入中断速度”理解为 2/10/50 MHz 是不正确的;输入边沿能力由输入缓冲、EXTI 和数据手册电气特性决定。
1.1.6 外部中断的实现过程
按下 PE3 后:
sequenceDiagram
participant P as PE3
participant X as EXTI3
participant N as NVIC
participant H as EXTI3_IRQHandler
participant C as Callback
P->>X: 1→0
X->>X: FTSR3=1,因此 PR3置1
X->>N: IMR3=1,提交中断
N->>H: IRQ已使能且优先级允许
H->>X: HAL读取PR3
H->>X: 向PR3写1清除
H->>C: 参数GPIO_PIN_3
若边沿发生时 NVIC 通道关闭,EXTI 的 PR 位仍可能挂起;之后打开 NVIC,未清的请求可能立即得到服务。初始化结束前可按需要先清历史挂起位,再启用 NVIC。
1.1.7 PR寄存器
PR 是 Pending Register:
PRx=0:该线没有待处理请求
PRx=1:检测到所选边沿或软件请求
写 PRx=1:清除挂起
HAL 宏通常定义为:
#define __HAL_GPIO_EXTI_GET_IT(__PIN__) (EXTI->PR & (__PIN__))
#define __HAL_GPIO_EXTI_CLEAR_IT(__PIN__) (EXTI->PR = (__PIN__))
清挂起应在确认请求后及时进行。如果先执行很长的业务逻辑再清除,期间的新边沿可能和旧状态混在一起;机械按键还会因抖动反复置位。
二、结束
PE3/PE4 下降沿中断的最终寄存器检查表:
| 检查项 | PE3 | PE4 |
|---|---|---|
| GPIO 输入字段 | CRL[15:12]=1000 | CRL[19:16]=1000 |
| 上拉选择 | ODR3=1 | ODR4=1 |
| AFIO 映射 | EXTICR1[15:12]=0100 | EXTICR2[3:0]=0100 |
| 上升沿 | RTSR3=0 | RTSR4=0 |
| 下降沿 | FTSR3=1 | FTSR4=1 |
| 中断开放 | IMR3=1 | IMR4=1 |
| 事件关闭 | EMR3=0 | EMR4=0 |
| 挂起清除 | 向 PR3 写 1 | 向 PR4 写 1 |
三、外部中断触发函数
EXTI3_IRQHandler 和 EXTI4_IRQHandler 是真正写入向量表的入口。它们只调用 HAL 分发函数,不直接读取按键或控制负载,这使底层清挂起与用户业务分离。
若使用 PE8,则对应 EXTI8,但 NVIC 入口是共享的 EXTI9_5_IRQHandler:
void EXTI9_5_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
}
共享处理函数可能需要依次处理多个引脚,不能处理一个后直接忽略其他挂起位。
四、中断向量表
启动文件中的向量表保存处理函数地址。Cortex-M3 复位后通过向量表取得初始栈顶和 Reset_Handler;发生 EXTI3/EXTI4 中断时,NVIC 根据异常号索引对应入口。
向量表基址 VTOR
+ 0x00 初始 MSP
+ 0x04 Reset_Handler
...
+ 对应偏移 EXTI3_IRQHandler
+ 对应偏移 EXTI4_IRQHandler
函数名拼错、启动文件型号不匹配、目标文件未参与链接,都会导致弱默认处理函数被采用。此时外设寄存器可能已正确产生挂起,但程序进入默认死循环。因此寄存器、NVIC 状态和向量符号要一起检查。