4. 外部中断处理
任务目标
在MCU上配置一个外部中断引脚,连接按键。当按键按下时触发外部中断服务程序(ISR),在ISR中翻转LED的亮灭状态。本案例以STM32F103系列(标准外设库)为例,实现完整的按键中断控制LED。
硬件连接
- 按键:PA0(接3.3V,按下时产生上升沿)
- LED:PC13(低电平点亮,板载LED)
- 外部中断线:EXTI0,映射到PA0
关键实现
外部中断配置分为四步:GPIO时钟使能、GPIO引脚配置为输入模式、EXTI与NVIC配置、中断服务函数编写。注意PA0需要配置为下拉输入(按键接VCC时),EXTI0触发方式选择上升沿。中断服务函数中必须清除EXTI挂起位,否则中断会反复进入。
完整代码
#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_rcc.h"
#include "misc.h"
// LED状态标志,在中断中翻转
volatile uint8_t led_state = 0;
// 按键外部中断初始化
void EXTI_Key_Init(void) {
GPIO_InitTypeDef GPIO_InitStructure;
EXTI_InitTypeDef EXTI_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 1. 使能GPIOA和GPIOC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// 2. 配置PA0为下拉输入(按键接3.3V)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3. 配置PC13为推挽输出(LED)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 4. 将PA0连接到EXTI0
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);
// 5. 配置EXTI0:上升沿触发
EXTI_InitStructure.EXTI_Line = EXTI_Line0;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
// 6. 配置NVIC:使能EXTI0中断通道
NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
// 外部中断0服务函数
void EXTI0_IRQHandler(void) {
// 检查EXTI0挂起位
if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
// 翻转LED状态
led_state = !led_state;
if (led_state) {
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 灭
} else {
GPIO_ResetBits(GPIOC, GPIO_Pin_13); // 亮
}
// 清除挂起位,必须执行
EXTI_ClearITPendingBit(EXTI_Line0);
}
}
int main(void) {
// 初始化LED为灭状态
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_SetBits(GPIOC, GPIO_Pin_13); // 初始灭
// 初始化外部中断
EXTI_Key_Init();
while (1) {
// 主循环无任务,等待中断
}
}
编译与烧录
- 使用Keil MDK或IAR创建工程,芯片选择STM32F103C8T6(或对应型号)。
- 添加标准外设库文件:
stm32f10x_gpio.c、stm32f10x_exti.c、stm32f10x_rcc.c、misc.c。 - 在工程设置中定义
STM32F10X_MD,并包含标准外设库头文件路径。 - 编译后将生成的HEX文件通过ST-Link或串口烧录到开发板。
验证方法
烧录后观察以下现象:
- 上电时LED处于熄灭状态(PC13输出高电平)。
- 每按一次按键(PA0接高电平),LED状态翻转一次:亮→灭→亮→灭。
- 长按按键不会连续翻转(上升沿触发,只有按下瞬间触发一次)。
若按下按键LED无反应,检查PA0是否确实连接到按键输出、按键另一端是否接3.3V,以及EXTI0中断是否已在NVIC中使能。
常见问题
- 按键抖动导致多次翻转:本案例未做软件消抖,若需消除抖动可在ISR中加入10~20ms延时或使用定时器消抖。
- 中断不触发:确认GPIO时钟和AFIO时钟均已使能(EXTI需要AFIO时钟),且
GPIO_EXTILineConfig已正确调用。 - 编译报错:检查是否遗漏
misc.c(NVIC配置依赖该文件)。