【星云 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外部中断的基本配置和使用方法,为后续的嵌入式系统开发打下坚实的基础。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/981174.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

探索Elasticsearch:索引的CRUD

在企业环境中,Elasticsearch的索引CRUD(创建Create、读取Read、更新Update、删除Delete)操作是非常基础且频繁使用的功能。这些操作对于管理和维护数据至关重要,尤其是在处理大规模数据集和需要实时搜索与分析的应用场景中。 目录…

React antd的datePicker自定义,封装成组件

一、antd的datePicker自定义 需求:用户需要为日期选择器的每个日期单元格添加一个Tooltip,当鼠标悬停时显示日期、可兑换流量余额和本公会可兑流量。这些数据需要从接口获取。我需要结合之前的代码,确保Tooltip正确显示,并且数据…

NVIDIA GPU 架构详解:Pascal、Volta、Turing、Ampere、Ada、Hopper、Blackwell

目录 1. Pascal(帕斯卡)架构(2016)关键技术性能特性代表产品应用场景 2. Volta(伏特)架构(2017)关键技术性能特性代表产品应用场景 3.Turing(图灵)架构&#…

Linux 命令行的基本命令(生信)

常见的操作系统包括 Windows、Mac OS X 和 Unix 。Linux 是类 Unix 操作系 统, 可安装在各种各样的电脑硬件设备, 从手机、平板电脑、路由器到超级计算 机。Linux 是一个领先的操作系统,世界上最快的十台超级计算机运行的都是 Linux 操作系统…

ECharts--中国地图(无敌详细)

前段时间需要做一个中国地图的页面,要求是展示各地产品的销量,我就在网上搜了很多ECharts的资料,学习了一下怎么使用。 本着互相学习,共同进步的原则,特此分享一下自己的学习经验以及使用技巧。如果有用的话可以给老弟…

QwenVL 2.5-本地安装编译布署全教程

开篇 DeepSeek开源后我国又开源了一个震撼大模型,QwenVL2.5,这是一个多模态的模形,它可以认图、识图、更能作图,还能读懂video。 Qwen2.5-VL 的主要特点如下所示: 感知更丰富的世界:Qwen2.5-VL 不仅擅长识别常见物体,如花、鸟、鱼和昆虫,还能够分析图像中的文本、图表…

【含文档+PPT+源码】基于SpringBoot电脑DIY装机教程网站的设计与实现

项目介绍 本课程演示的是一款 基于SpringBoot电脑DIY装机教程网站的设计与实现,主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含:项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署运行本…

React高级内容探索

flushSync确保了DOM立即更新 flushSync让你强制React同步刷新提供回调中的任何更新,这确保了DOM立即更新 flushSync是DOM更新之后的,像vue中的nextTick: import { useState,useRef} from "react" import { flushSync} from &quo…

基于 MetaGPT 自部署一个类似 MGX 的多智能体协作框架

MGX(由 MetaGPT 团队开发的 mgx.dev)是一个收费的多智能体编程平台,提供从需求分析到代码生成、测试和修复的全流程自动化功能。虽然 MGX 本身需要付费,但您可以通过免费服务和开源项目搭建一个类似的功能。以下是一个分步骤的实现…

主时钟与虚拟时钟约束

1、主时钟约束 1.1、主时钟约束语法&#xff1a; create_clock -name< clock_name > -period <period> -waveform{ <rise_time> <fall_time> } [get_ports< port_name >] 说明&#xff1a; name 之后的<clock_name> 是clk 的name&a…

CyberRT(apollo) 定时器模块简述及bug分析

timer 模块 timer的定义&#xff0c;cyberrt中timer模块用于设置定时器任务&#xff0c;字面意思&#xff0c;设置设置定时周期及出发频次&#xff08;周期 or oneshot)&#xff0c;到达指定时间时间触发callback time wheel 时钟节拍轮&#xff0c;常见的定时器设计&#x…

网络安全月度报告

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 3.1.1网络安全现状及安全挑战 网络的出现给人们的工作和生活带来了极大的便利&#xff0c;但同时也带来了极大的安全风险。在信息传输和交换时&#xff0c;需要对…

nio多线程版本

多线程多路复用 多线程NIO&#xff0c;&#xff0c;就是多个线程&#xff0c;每个线程上都有一个Selector&#xff0c;&#xff0c;&#xff0c;比如说一个系统中一个线程用来接收请求&#xff0c;&#xff0c;剩余的线程用来读写数据&#xff0c;&#xff0c;每个线程独立干自…

一周学会Flask3 Python Web开发-Flask3之表单处理WTForms安装与定义WTForms表单类

锋哥原创的Flask3 Python Web开发 Flask3视频教程&#xff1a; 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 我们平时开发项目&#xff0c;都会用到表单&#xff0c;编写表单&#xff0c;提交表单&#xff0c;验证表单&#xff0c;如果…

基于NI USRP 硬件的下一代O-RAN研究测试台​

目录 基于NI SDR硬件的下一代O-RAN研究测试台​挑战&#xff1a;解决方案&#xff1a; 基于NI SDR硬件的下一代O-RAN研究测试台​ “OAIC提供了一个开放平台&#xff08;包括软件架构、库和工具集&#xff09;&#xff0c;用于对基于AI的无线接入网(RAN)控制器进行原型开发和测…

【开源-鸿蒙土拨鼠大理石系统】鸿蒙 HarmonyOS Next App+微信小程序+云平台

✨本人自己开发的开源项目&#xff1a;土拨鼠充电系统 ✨踩坑不易&#xff0c;还希望各位大佬支持一下&#xff0c;在GitHub给我点个 Start ⭐⭐&#x1f44d;&#x1f44d; ✍GitHub开源项目地址&#x1f449;&#xff1a;https://github.com/cheinlu/HarmonyOS-groundhog-mar…

笔记本电脑本地部署ollama大模型(显存不足调用CUDA Unified Memory方法)

软硬件&#xff1a;win11,NVIDIA GeForce RTX 3050 显存4g 一.ollama模型最低要求 1. Llama 3.1 (8B) 模型 GPU: 至少需要 1 张具有 16 GB 显存的 GPU&#xff08;例如 NVIDIA Tesla V100 或 A100&#xff09;。CPU: 高性能的多核处理器&#xff08;例如 Intel Xeon 或 AMD …

【Rancher】简化Kubernetes容器管理与部署的开源平台

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是Rancher 2、Rancher诞生里程 …

vscode通过ssh远程连接(linux系统)不能跳转问题

1.问题描述 unbantu中的vscode能够通过函数跳转到函数定义&#xff0c;而windows通过ssh连接unbantu的vscode却无法跳转 2.原因&#xff1a; 主要原因是这里缺少插件&#xff0c;这里是unbantu给主机的服务器&#xff0c;与ubantu本地vscode插件相互独立&#xff0c;能否跳转…

思维链 Chain-of-Thought Prompting

论文: Chain-of-Thought Prompting Elicits Reasoning in Large Language Models (Wei et al., 2022) 核心贡献: 首次提出通过显式的中间推理步骤&#xff08;即思维链&#xff09;提升大语言模型的复杂推理能力。该方法通过示例展示多步推理过程&#xff0c;引导模型生成逻辑…