当前位置: 首页 > article >正文

【星云 Orbit • STM32F4】04.一触即发:GPIO 外部中断

【星云 Orbit- • STM32F4】04. 一触即发:外部中断控制

摘要

本文详细介绍了如何使用STM32F407微控制器的HAL库实现外部中断功能。通过配置GPIO引脚作为外部中断源,并在中断回调函数中处理按键事件,实现了按键控制LED状态翻转的功能。本文旨在为初学者提供一个详细、易懂的教程,包括硬件电路设计、软件架构、代码实现及应用示例。


1. 引言

外部中断是嵌入式系统中常见的功能,用于检测外部事件并触发相应的中断处理程序。STM32F407微控制器提供了丰富的GPIO资源和灵活的中断配置,通过HAL库可以方便地实现外部中断功能。本文以STM32F407开发板为例,详细介绍了如何配置外部中断,实现按键控制LED状态翻转的功能。


2. 硬件设计

2.1 硬件资源

本实验使用STM32F407开发板上的两个按键和两个LED,具体资源如下:

  • LED
    • LED0:连接到GPIOF的Pin 9
    • LED1:连接到GPIOF的Pin 10
  • 按键
    • WKUP:连接到GPIOA的Pin 0
    • KEY0:连接到GPIOE的Pin 4

2.2 硬件接线

本实验使用的两个 STM32F407 板载按键,分别为 KEY0 按键和 WKUP 按键,其与板载 MCU 的连接原理图如下所示:

在这里插入图片描述
从原理图中可以看出,WKUP 按键和 KEY0 按键的一端连接到了电源正极(VCC),而另一端分别与 MCU 的 PA0 引脚和 PE4 引脚相连接。对于该硬件设计,PA0 引脚和 PE4 引脚应当配置为下拉模式,这样一来,在按键被按下的时候,PA0 引脚或 PE4 引脚就会从原来的低电平状态变为高电平状态,在这期间就会有一个上升沿的跳变,因此可以使用该上升沿信号作为中断的触发源。


3. 软件设计

3.1 软件架构

本实验的软件架构主要包括以下几个部分:

  1. 系统初始化:初始化HAL库、时钟、延时、串口和LED。
  2. 外部中断初始化:配置GPIO引脚作为外部中断源,并设置中断优先级。
  3. 中断服务函数:处理外部中断事件,实现按键控制LED状态翻转的功能。

为了提高代码的可读性和可维护性,我们将代码分为多个模块,每个模块负责一个特定的功能。

3.2 GPIO 配置

使用 HAL 库配置 GPIO 引脚为外部中断模式:

  • 模式:上升沿触发中断。
  • 上拉/下拉:下拉模式。

3.3 中断配置

3.3.1 设置中断优先级

使用 HAL_NVIC_SetPriority 函数设置中断优先级:

HAL_NVIC_SetPriority(IRQn_Type IRQn, uint32_t PreemptPriority, uint32_t SubPriority);
  • IRQn:NVIC 中断请求,例如 EXTI0_IRQnUSART1_IRQn 等(在 stm32f407xx.h 文件中有定义)。
  • PreemptPriority:抢占优先级。
  • SubPriority:子优先级。
3.3.2 使能中断

使用 HAL_NVIC_EnableIRQ 函数使能中断:

HAL_NVIC_EnableIRQ(IRQn_Type IRQn);
  • IRQn:NVIC 中断请求,例如 EXTI0_IRQnUSART1_IRQn 等(在 stm32f407xx.h 文件中有定义)。

3.3 中断服务函数

编写中断服务函数,处理按键按下事件:

  • WKUP_INT_IRQHandler:处理 WKUP 按键中断。
  • KEY0_INT_IRQHandler:处理 KEY0 按键中断。

3.4 中断回调函数

使用 HAL_GPIO_EXTI_Callback 函数处理中断事件,翻转 LED 状态。


4. 代码实现

4.1 系统初始化模块

4.1.1 HAL库初始化
#include "stm32f4xx_hal.h"

// 初始化HAL库
void hal_init(void)
{
    HAL_Init(); // 初始化HAL库
}
4.1.2 时钟配置
#include "stm32f4xx_hal.h"

// 配置时钟
void sys_stm32_clock_init(uint32_t pllm, uint32_t plln, uint32_t pllp, uint32_t pllr)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    // 初始化RCC Osc
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLM = pllm;
    RCC_OscInitStruct.PLL.PLLN = plln;
    RCC_OscInitStruct.PLL.PLLP = pllp;
    RCC_OscInitStruct.PLL.PLLR = pllr;
    if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
    {
        // 初始化失败
        while (1)
        {
        }
    }

    // 初始化RCC Clk
    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_DIV4;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
    {
        // 初始化失败
        while (1)
        {
        }
    }
}
4.1.3 延时初始化
#include "stm32f4xx_hal.h"

// 延时函数
void delay_ms(uint32_t ms)
{
    HAL_Delay(ms); // 使用HAL库提供的延时函数
}
4.1.4 串口初始化
#include "stm32f4xx_hal.h"

// 初始化串口
void usart_init(uint32_t baudrate)
{
    USART_HandleTypeDef huart1;

    huart1.Instance = USART1;
    huart1.Init.BaudRate = baudrate;
    huart1.Init.WordLength = UART_WORDLENGTH_8B;
    huart1.Init.StopBits = UART_STOPBITS_1;
    huart1.Init.Parity = UART_PARITY_NONE;
    huart1.Init.Mode = UART_MODE_TX_RX;
    huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart1.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart1) != HAL_OK)
    {
        // 初始化失败
        while (1)
        {
        }
    }
}
4.1.5 LED初始化
#include "stm32f4xx_hal.h"

// 定义LED和按键的GPIO引脚
#define LED0_GPIO_PORT GPIOF
#define LED0_GPIO_PIN GPIO_PIN_9
#define LED1_GPIO_PORT GPIOF
#define LED1_GPIO_PIN GPIO_PIN_10

// 初始化LED
void led_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    // 使能GPIO端口时钟
    __HAL_RCC_GPIOF_CLK_ENABLE();

    // 配置LED0引脚
    gpio_init_struct.Pin = LED0_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_PP;
    gpio_init_struct.Pull = GPIO_NOPULL;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(LED0_GPIO_PORT, &gpio_init_struct);

    // 配置LED1引脚
    gpio_init_struct.Pin = LED1_GPIO_PIN;
    HAL_GPIO_Init(LED1_GPIO_PORT, &gpio_init_struct);

    // 关闭LED
    HAL_GPIO_WritePin(LED0_GPIO_PORT, LED0_GPIO_PIN, GPIO_PIN_RESET);
    HAL_GPIO_WritePin(LED1_GPIO_PORT, LED1_GPIO_PIN, GPIO_PIN_RESET);
}

4.2 外部中断初始化模块

4.2.1 GPIO引脚配置
#include "stm32f4xx_hal.h"

// 定义按键的GPIO引脚
#define WKUP_INT_GPIO_PORT GPIOA
#define WKUP_INT_GPIO_PIN GPIO_PIN_0
#define WKUP_INT_GPIO_CLK_ENABLE() do { HAL_RCC_GPIOA_CLK_ENABLE(); } while (0)
#define WKUP_INT_IRQn EXTI0_IRQn
#define WKUP_INT_IRQHandler EXTI0_IRQHandler

#define KEY0_INT_GPIO_PORT GPIOE
#define KEY0_INT_GPIO_PIN GPIO_PIN_4
#define KEY0_INT_GPIO_CLK_ENABLE() do { HAL_RCC_GPIOE_CLK_ENABLE(); } while (0)
#define KEY0_INT_IRQn EXTI4_IRQn
#define KEY0_INT_IRQHandler EXTI4_IRQHandler

// 初始化外部中断
void exti_init(void)
{
    GPIO_InitTypeDef gpio_init_struct = {0};

    // 使能GPIO端口时钟
    WKUP_INT_GPIO_CLK_ENABLE();
    KEY0_INT_GPIO_CLK_ENABLE();

    // 配置WKUP控制引脚
    gpio_init_struct.Pin = WKUP_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_RISING;
    gpio_init_struct.Pull = GPIO_PULLDOWN;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(WKUP_INT_GPIO_PORT, &gpio_init_struct);

    // 配置KEY0控制引脚
    gpio_init_struct.Pin = KEY0_INT_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_IT_RISING;
    gpio_init_struct.Pull = GPIO_PULLDOWN;
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_LOW;
    HAL_GPIO_Init(KEY0_INT_GPIO_PORT, &gpio_init_struct);

    // 配置中断优先级并使能中断
    HAL_NVIC_SetPriority(WKUP_INT_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(WKUP_INT_IRQn);
    HAL_NVIC_SetPriority(KEY0_INT_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(KEY0_INT_IRQn);
}

4.3 中断服务函数模块

4.3.1 外部中断服务函数
#include "stm32f4xx_hal.h"

// 外部中断服务函数
void WKUP_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(WKUP_INT_GPIO_PIN); // 处理WKUP按键的外部中断
}

void KEY0_INT_IRQHandler(void)
{
    HAL_GPIO_EXTI_IRQHandler(KEY0_INT_GPIO_PIN); // 处理KEY0按键的外部中断
}
4.3.2 HAL库外部中断回调函数
#include "stm32f4xx_hal.h"

// HAL库外部中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    // 机械按键消抖(仅演示,切勿在实际工程的中断服务函数中进行阻塞延时)
    delay_ms(20);

    switch (GPIO_Pin)
    {
    case WKUP_INT_GPIO_PIN:
        HAL_GPIO_TogglePin(LED0_GPIO_PORT, LED0_GPIO_PIN); // 翻转LED0状态
        break;
    case KEY0_INT_GPIO_PIN:
        HAL_GPIO_TogglePin(LED1_GPIO_PORT, LED1_GPIO_PIN); // 翻转LED1状态
        break;
    }
}

4.4 主程序模块

4.4.1 主程序
#include "stm32f4xx_hal.h"
#include "system_init.h"
#include "exti_init.h"
#include "led_init.h"
#include "usart_init.h"
#include "delay.h"

int main(void)
{
    hal_init(); // 初始化HAL库
    sys_stm32_clock_init(336, 8, 2, 7); // 配置时钟,168MHz
    delay_init(168); // 初始化延时
    usart_init(115200); // 初始化串口
    led_init(); // 初始化LED
    exti_init(); // 初始化外部中断

    while (1)
    {
        // 主循环中不执行任何操作
    }
}

5. 下载验证

在完成编译和烧录操作后,可以看到板子上的LED0和LED1默认是处于熄灭的状态。若此时按下WKUP按键,则能够看到LED0的亮灭状态发生了一次翻转。同样的,若此时按下KEY0按键,则能够看到LED1的亮灭状态发生了一次翻转,与预期的实验现象效果相符。


6. 总结

本文详细介绍了如何使用STM32F407微控制器的HAL库实现外部中断功能,通过配置GPIO引脚作为外部中断源,并在中断回调函数中处理按键事件,实现了按键控制LED状态翻转的功能。本文涵盖了硬件电路设计、软件架构、代码实现及应用示例,旨在为初学者提供一个详细、易懂的教程。通过本文的学习,读者可以掌握STM32F407外部中断的基本配置和使用方法,为后续的嵌入式系统开发打下坚实的基础。

在完成编译和烧录操作后,可以看到板子上的LED0和LED1默认是处于熄灭的状态。若此时按下WKUP按键,则能够看到LED0的亮灭状态发生了一次翻转。同样的,若此时按下KEY0按键,则能够看到LED1的亮灭状态发生了一次翻转,与预期的实验现象效果相符。


6. 总结

本文详细介绍了如何使用STM32F407微控制器的HAL库实现外部中断功能,通过配置GPIO引脚作为外部中断源,并在中断回调函数中处理按键事件,实现了按键控制LED状态翻转的功能。本文涵盖了硬件电路设计、软件架构、代码实现及应用示例,旨在为初学者提供一个详细、易懂的教程。通过本文的学习,读者可以掌握STM32F407外部中断的基本配置和使用方法,为后续的嵌入式系统开发打下坚实的基础。


http://www.kler.cn/a/570297.html

相关文章:

  • 网络问题排查基本命令
  • Android Studio 新版本Gradle通过JitPack发布Maven仓库示例
  • SpringBoot读取properties中文乱码解决方案
  • 【通俗讲解电子电路】——从零开始理解生活中的电路(三)
  • element-push el-date-picker日期时间选择器,禁用可选中的时间 精确到分钟
  • 迷你世界脚本游戏接口:Game
  • 【C语言】联合体妙用之:数据类型转换
  • 聊聊学习之数据-信息-知识-洞见-智慧-影响力
  • Django 项目模块化开发指南:实现 Vue 风格的组件化
  • Lasso回归、Ridge岭回归
  • 常见webshell工具的流量特征
  • 贪心算法+题目
  • 打造个人知识库(Page Assist版)- 私人专属AI-本地化部署deepseek
  • 电源测试系统有哪些可以利用AI工具的科技??
  • 2025 年:SAP 咨询的关键转折点
  • Qt镜像地址
  • 【虚拟机 IP 配置深度剖析】
  • Javaee:IO和文件操作
  • 【机器人手眼标定算法简介】
  • 【算法】acwing算法基础875. 快速幂