3. 外部中断触发

任务目标

本案例基于STM32F103系列MCU,通过外部中断实现按键控制LED翻转。核心目标是:配置GPIO外部中断线,使按键按下时触发中断服务函数(ISR),在ISR中翻转LED状态,并演示中断优先级与嵌套的基本配置方法。

硬件连接与前置条件

  • MCU:STM32F103C8T6(Blue Pill板)
  • 按键PA0接GND(按下为低电平),外部中断线EXTI0
  • LED接PC13(板上LED,低电平点亮)
  • 开发环境:STM32CubeIDE 或 Keil MDK + HAL库

完整代码实现

/* main.c - 外部中断触发LED翻转 */
#include "main.h"

/* 中断优先级分组:抢占优先级2位,子优先级2位 */
void HAL_MspInit(void)
{
    HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);
}

/* PA0外部中断初始化 */
static void EXTI_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    /* 使能GPIOA时钟 */
    __HAL_RCC_GPIOA_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    /* PA0配置为外部中断输入,上拉(按键接GND) */
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;  /* 下降沿触发 */
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* PC13配置为推挽输出 */
    GPIO_InitStruct.Pin = GPIO_PIN_13;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* 设置EXTI0中断优先级:抢占优先级1,子优先级0 */
    HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 0);
    HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}

/* 外部中断0服务函数(在stm32f1xx_it.c中调用) */
void EXTI0_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0);
}

/* 中断回调:按键按下时翻转LED */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == GPIO_PIN_0)
    {
        /* 简单消抖:延时20ms后再次检测电平 */
        HAL_Delay(20);
        if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET)
        {
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        }
    }
}

int main(void)
{
    HAL_Init();
    EXTI_Init();

    /* 初始状态:LED灭(PC13高电平) */
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);

    while (1)
    {
        /* 主循环空闲,等待中断触发 */
    }
}

中断优先级与嵌套说明

本案例使用NVIC_PRIORITYGROUP_2分组,即4位优先级中高2位为抢占优先级、低2位为子优先级。EXTI0配置为抢占优先级1、子优先级0。若需演示嵌套中断,可再配置一个更高抢占优先级的中断源(如定时器中断,抢占优先级0),当EXTI0中断服务执行期间该中断到来时,会暂停当前ISR优先处理高优先级中断。本例仅配置单个外部中断,但优先级分组和数值设置已为嵌套扩展预留接口。

编译与烧录

  1. 在STM32CubeIDE中新建STM32F103C8T6工程,将上述代码替换main.c内容。
  2. 确认系统时钟已通过HAL_RCC_ClockConfig()配置(默认使用HSI 8MHz或外部晶振)。
  3. 编译生成HEX文件,通过ST-Link烧录至开发板。

验证方法与判定标准

  • 功能验证:上电后LED处于熄灭状态。每次按下PA0按键并释放,LED状态翻转一次(亮→灭或灭→亮)。
  • 消抖验证:快速连续按动按键,LED状态应稳定翻转,不会出现一次按键多次翻转的现象。
  • 优先级验证(可选):若额外配置一个定时器中断(抢占优先级0),在定时器ISR中翻转另一个LED,可观察到定时器中断能打断EXTI0的ISR执行,证明抢占优先级生效。

常见问题

  • 按键无响应:检查PA0是否正确上拉,按键另一端是否接GND;确认EXTI0_IRQHandler已在启动文件中声明。
  • LED状态乱跳:消抖延时不足或按键接触不良,可增大延时至30~50ms。
  • 编译报错:确认HAL库版本与芯片型号匹配,main.h中已包含必要头文件。