外部中断(EXTI) - 按键控制LED

news2024/11/27 6:26:16

一、外部中断/事件控制器(EXTI)结构图

1、结构图分析

外部中断主要由外部中断/事件控制器(External interrupt/event controller, EXTI)控制,它管理了外部中断或者事件的使能与否、触发方式等功能。

 ( 外部中断/事件控制器(EXTI)结构图 )

下图为中文翻译版

图中有两条从右向左走向的线路,红色线路用于产生中断,黄色线路产生事件

如上图,信号输入后首先会经过边沿检测电路,这个检测电路会通过查看 上边沿触发 和下边沿触发 寄存器的值,去判断信号到底在哪个边沿采集。(所谓上边沿和下边沿,即电平从低->高 or 从高->低 变化时的那条竖直的边)

经过边沿检测电路之后,就来到上图标号3的位置。这里是个或门连接着输入信号和软件中断事件寄存器。也就是说,除了信号触发中断,我们还可以控制相关的寄存器从而产生中断事件。

过了步骤3或门这一步之后,产生中断和产生事件的流程就有所区别了。

我们先来看看中断的产生过程:
首先中断请求信号会先进入 请求挂起寄存器 。这个寄存器的存在意义在于,如果中断发生时,正在处理同级或高优先级中断,则中断不能立即得到响应,此时中断被悬起。悬挂意味着等待而不是舍去,当优先级高的或者同等级先发生的中断完成后,被挂起的中断才会执行。

落实到stm32这里,可以看到在标号4的位置,是一个与门,所以,中断请教什么时候推送到NVIC控制器执行,就是中断屏蔽寄存器应该控制的事了。

同理,对于事件发生来说,事件是否生成,就是事件屏蔽寄存器的事了。不同点在于,事件的产生没有优先级的概念,所有无需挂起寄存器这么个东西。

2、外部中断线路映像

EXTI控制器支持多达20个软件的中断/事件请求。

我们单片机的外部中断通常是由GPIO的电平跳变引起的中断。
在STM32中,每一个GPIO都可以作为外部中断的触发源,外部中断通用I/O映像一共有16条线,对应着GPIO的0-15引脚,每一条外部中断都可以与任意一组的对应引脚相连,但不能重复使用

还有另外四个EXTI线的连接方式如下:
EXTI线16连接到PVD输出
EXTI线17连接到RTC闹钟事件
EXTI线18连接到USB唤醒事件
EXTI线19连接到以太网唤醒事件(只适用于互联型产品)

3、中断模式

外部中断支持GPIO的三种电平模式:
上升沿中断:当GPIO的电平从低->高时,引发外部中断。
下降沿中断:当GPIO的电平从高->低时,引发外部中断。
上升沿和下降沿中断(双边沿中断):当GPIO的电平从低->高和从高->低,都能引发外部中断。

二、外部中断控制LED灯

1、原理图

如下图,我们把三个LED灯分别连接在PA0、PA1、PA2上,按键 KEY1 连接在 PA3 上。

2、中断消抖

考虑到由于按键的机械结构具有弹性,按下时开关不会立刻接通,断开时也不会立刻断开,这就导致按键的输入信号在按下和断开时都会存在抖动。
如果不先将抖动问题进行处理,则读取的按键信号可能会出现错误。

因此为了消除这一问题,我们可以通过软件消抖或者硬件消抖两种方式来实现。

2.1 硬件消抖

硬件消抖一般有两种实现方式:“RS触发器”、“电容滤波器”

2.11 RS触发器

在RS触发器中,R是RESET的意思,代表复位,S是SET的意思,代表置位。

利用RS触发器来吸收按键的抖动。当按键未按下时,输出为0,一旦有键按下,触发器立即翻转,输出为1,触电的抖动便不会再对输出产生影响,起到抗抖作用,按键释放时也一样。RS触发电路消抖电路图如下。

注:详细分析可学习数电相关知识

2.12 电容滤波器

将电容并联在按键的两端,利用电容两端的电压不能突变的特性以及放电的延时特性。将产生抖动的电平通过电容吸收掉。从而达到消抖的作用,电容消抖电路图如原理图所示。

具体分析:

按键按下(高电平->低电平):按键按下,电容与按键形成回路,电容开始放电,当电容放电结束后,抖动就基本结束了。在放电期间,PA3处一直向外输出高电平。

主要是因为按键的电阻很小,电容和按键的并联电阻也被拉小,大部分回路电压会加到电阻上,电容电压变小,又由于电容两端电压不能突变,根据 I=dq/dt,带入电容公式q=uc,得出I=C*du/dt,当du/dt<0时,电容电荷减少,即电容放电,放电维持电平至按键抖动结束。

按键松开(低电平->高电平):按键松开,电容左极板接电源,右极板接地,开始充电,当电容充电结束后,抖动也就基本结束了。在电容充电期间,PA3处一直向外输出为低电平。

按键松开时,回路电压基本加在电容端,电容本来两边都是低电平,现在一边接高电平,一边接低电平,就开始充电,充电维持电平至按键抖动结束。

2.2 软件消抖

2.21 延时函数按键消抖

优点:简单方便
缺点:程序在空跑浪费CPU资源、不够精准

值得注意的是:由于我们这里按键是用中断的方式实现,所以不能在中断服务函数里面使用延时函数,因为中断服务函数最基本的要求就是快进快出。

2.22 定时器按键消抖

优点:节约CPU资源
缺点:消耗一个定时器

原理:按键采用中断驱动方式,当按键按下以后触发按键中断,在按键中断中开启一个定时器,定时周期为()ms,当定时时间到了以后就会触发定时器中断,最后在定时器中断处理函数中读取按键的值,如果按键值还是按下状态那就表示这是一次有效的按键。

定时器详情可参考STM32定时器TIM控制_沉默的道路的博客-CSDN博客

3、消抖选择

或许有人觉得用硬件电容滤波器消抖容易点,就焊接一个电容,但其实也是根据实际情况而定。

1. 电容也是钱啊,如果做产品,需求大量时也是一笔消费,主打一个省钱。
2. 中断闲着也是闲着,合理规划好代码也不乱,逻辑也不会变复杂。再复杂的逻辑,拆分好,规划好,都可以条理清晰,一目了然。
3. 再打比方如果在产品中,加电容在按键这玩意上,只要出现一次不可靠,人家就会认为你做的整个东西不可靠。
4. 不能100%依靠硬件消抖,这样究竟要选多大的电容合理,小了没有作用,大了反应慢,充电过程中可不可能存在临界状态,就是1,0的边沿。

当然不管硬件消抖也好软件消抖也罢,只要是人按的都不是绝对可靠,你消抖比如50ms,我乱按总可能抖出那么一两个。还是根据实际情况而定吧。

综上,如果能硬件+软件防抖就比较理想了! 

三、程序代码

1、按键中断在CubeMX中配置

将PA3号引脚设置为按键的输入引脚,将其设置为外部中断模式。
将GPIO模式设置为双边沿触发的外部中断,电阻设置为上拉电阻,最后设置用户标签为KEY1。

在NVIC中,勾选开启外部中断。

2、模块化封装led

2.1 宏定义与预处理-led
#ifndef __LED_DRIVER_H__
#define __LED_DRIVER_H__


#include "main.h"
void LED_Init(void);


/*********************
 * 开启时钟宏定义(便于代码的移植修改)
**********************/
#define LED_CLK_ENABLE()  __HAL_RCC_GPIOA_CLK_ENABLE();//使用前面的宏(LED_CLK_ENABLE),代替后面的函数
//#define LED2_CLK_ENABLE()  __HAL_RCC_GPIOA_CLK_ENABLE();    //如果是不同的引脚
//#define LED3_CLK_ENABLE()  __HAL_RCC_GPIOA_CLK_ENABLE();


/*********************
 * 引脚宏定义
**********************/
#define LED1_Pin		GPIO_PIN_0 //使用前面的宏(LED1_Pin),代替后面的函数
#define LED2_Pin		GPIO_PIN_1
#define LED3_Pin		GPIO_PIN_2


/*********************
 * 函数宏定义
**********************/
#define LED1_ON 		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_SET);//定义一个宏,通过使用前面的宏(LED1_ON),代替后面的函数,点亮对应灯
#define LED2_ON 		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_SET);
#define LED3_ON 		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_SET);
#define LED1_OFF 		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_0,GPIO_PIN_RESET);//定义一个宏,通过使用前面的宏(LED1_OFF),代替后面的函数,关闭对应灯
#define LED2_OFF		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_1,GPIO_PIN_RESET);
#define LED3_OFF		HAL_GPIO_WritePin(GPIOA,GPIO_PIN_2,GPIO_PIN_RESET);


#endif
2.2 主函数-led
#include "led_driver.h"


void LED_Init(void)
{
	/*定义GPIO的结构体变量*/
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	/*使能LED的GPIO对应的时钟,开启时钟(GPIOA)*/
	LED_CLK_ENABLE() //用.h文件中定义的宏代替_ _HAL_RCC_GPIOA_CLK_ENABLE();
	
	/*设置参数*/
	GPIO_InitStruct.Pin = LED1_Pin|LED2_Pin|LED3_Pin;//选择LED1 LED2 LED3的引脚
	GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP ;//设置为推挽输出模式
	GPIO_InitStruct.Pull = GPIO_NOPULL;   //没有上下拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;//引脚输出速度设置为慢
	
	/*初始化引脚配置,让设置的参数生效*/
	HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);//&GPIO_InitStruct定义的结构体

	
}

3、模块化封装Exti

3.1 宏定义与预处理-EXti
#ifndef __EXTI_DRIVER_H__
#define __EXTI_DRIVER_H__

#include "main.h" //#include "stm32f1xx_hal.h"
void EXTI_Init(void);
	
#define KEY1_GPIO_CLK_EN()    __HAL_RCC_GPIOA_CLK_ENABLE();
 
#define KEY1_Pin		 GPIO_PIN_3


uint8_t Getkey1val(void); //返回key1按键状态 


#endif

3.2 主函数-Exti
#include "Exti_driver.h"

uint8_t key_val = 0; //定义按键返回值变量

void EXTI_Init(void)
{
	/*定义GPIO的结构体变量*/
	GPIO_InitTypeDef GPIO_InitStruct = {0};
	
	/*使能LED的GPIO对应的时钟,开启时钟(GPIOA)*/
	KEY1_GPIO_CLK_EN() //用.h文件中定义的宏代替__HAL_RCC_GPIOA_CLK_ENABLE();
	
	/*设置参数*/
	GPIO_InitStruct.Pin = KEY1_Pin;//选择按键引脚
	GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING	;//设置为双边沿触发外部中断
	GPIO_InitStruct.Pull = GPIO_PULLUP;   //默认上拉
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;//引脚输出速度设置为高速
	
	/*初始化引脚配置,让设置的参数生效*/
	HAL_GPIO_Init(GPIOA,&GPIO_InitStruct);//&GPIO_InitStruct定义的结构体
	

	HAL_NVIC_SetPriority(EXTI3_IRQn, 0, 0);// 设置外部中断优先级
	HAL_NVIC_EnableIRQ(EXTI3_IRQn);// 使能外部中断
	
}

	//外部中断3中断处理函数
void EXTI3_IRQHandler(void)
{
   HAL_GPIO_EXTI_IRQHandler(KEY1_Pin);
}

//外部中断回调函数。中断触发后,进入此函数,处理该函数中的代码之后,才会退出中断,此函数称为回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)// 回调函数
{
	if(GPIO_Pin == KEY1_Pin) //确定是否是KEY1键按下
	{
		key_val = HAL_GPIO_ReadPin(GPIOA, KEY1_Pin);
	}
	
}

//返回key1按键值
uint8_t Getkey1val(void)
{
	return key_val;
}



3.21 中断处理/回调函数

在HAL库的底层驱动说明中,有这么两段说明

大概就是,每当产生外部中断时,程序首先会进入外部中断服务函数。在stm32f1xx_it.c中,可以找到函数EXTI0_IRQHandler,它通过调用函数HAL_GPIO_EXTI_IRQHandler()对中断类型进行判断,并对涉及中断的寄存器进行处理,在处理完成后,它将调用中断回调函数。HAL_GPIO_EXTI_Callback(),在中断回调函数中编写在此次中断中需要执行的功能。

4、主函数

/* USER CODE BEGIN Includes */
#include "Exti_driver.h"
#include "led_driver.h"
/* USER CODE END Includes */
//把两个头文件包含进去
  /* USER CODE BEGIN 2 */
	LED_Init();
	EXTI_Init();
  /* USER CODE END 2 */
//初始化
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		//如果按下了,就亮
		if(Getkey1val() == 0)
		{
			LED1_ON
			LED2_ON
			LED3_ON
		}
		//如果松开了,就熄灭
		else
		{
			LED1_OFF
			LED2_OFF
			LED3_OFF
		}
  }
  /* USER CODE END 3 */
}

四、运行效果

保存、编译、下载、烧录、上电。
可以看到一开始LED为点亮状态,当按键按下时,灯亮;当按键松开时,灯灭。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

记录错误:Access denied for user ‘root‘@‘localhost‘ (using password:No) 解决方案

他说我没输入密码&#xff0c;但是我输入了啊&#xff1f;&#xff1f;于是&#xff0c;我试了试这儿&#xff0c;password 一改就好了。。。 他原来是是我打的很快&#xff0c;快速生成的。。。。

ESP32之LEDC(PWM信号的输出)

一、PWM信号简介 PWM&#xff1a;脉冲宽度调制&#xff0c;简称脉宽调制频率(f)&#xff1a;一秒钟PWM有多少个周期(单位Hz)周期(T)&#xff1a;一个周期的时间占空比(duty)&#xff1a;在一个脉冲周期内&#xff0c;高电平的时间与整个周期时间的比例脉宽时间&#xff1a;一个…

React 中的 ref 如何操作 dom节点,使输入框获取焦点

聚焦文字输入框 .focus() 获取焦点 当用户点击按钮时&#xff0c;handleClick 函数会被调用&#xff0c;从而将焦点聚焦到文本输入框上。 // 焦文字输入框 import { useRef } from "react";const FocusForm () > {const inputRef useRef<any>(null);func…

C到C++的升级

C和C的关系 C继承了所有C语言的特性&#xff1b;C在C的基础上提供了更多的语法和特性&#xff0c;C语言去除了一些C语言的不好的特性。C的设计目标是运行效率与开发效率的统一。 变化一&#xff1a;所有变量都可以在使用时定义 C中更强调语言的实用性&#xff0c;所有的变量…

内网隧道代理技术(二十二)之 CS针对特定端口上线不出网机器

CS工具自带上线不出网机器 如图A区域存在一台中转机器,这台机器可以出网,这种是最常见的情况。我们在渗透测试的过程中经常是拿下一台边缘机器,其有多块网卡,边缘机器可以访问内网机器,内网机器都不出网。这种情况下拿这个边缘机器做中转,就可以使用CS工具自带上线不出网…

代码随想录Day_53打卡

①、最长公共子序列 给定两个字符串 text1 和 text2&#xff0c;返回这两个字符串的最长 公共子序列 的长度。如果不存在 公共子序列 &#xff0c;返回 0 。 一个字符串的 子序列 是指这样一个新的字符串&#xff1a;它是由原字符串在不改变字符的相对顺序的情况下删除某些字符…

Flutter状态管理 — 探索Flutter中的状态

前言 随着响应式编程的理念&Flutter被大众所了解以来&#xff0c;状态管理一直是一个引人深思的话题。如果想要学习好Flutter这样的响应式的编程框架就一定是离不开状态管理的。我遇到过很多没有了解过响应式编程框架的&#xff0c;或者从事后端开发&#xff0c;自己想用F…

jmeter While控制器

一种常见的循环控制语句&#xff0c;用于重复执行一段代码块&#xff0c;直到指定的条件不再满足。 参数&#xff1a; 空LASTJMeter变量、函数、属性或任意其他可用表达式 &#xff08;jmeter提供的方法&#xff09;。判断变量值count_num小于等于20&#xff0c;推荐简单的几…

Python入门教程 | Python3 元组(tuple)

创建元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。 元组使用小括号 ( )&#xff0c;列表使用方括号 [ ]。 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可。 >>> tup1 (Google, Tarzan, 1997, …

组相联cache如何快速实现cache line eviction并使用PMU events验证

如何快速实现cache line eviction 一&#xff0c;什么是cache hit、miss、linefill、evict &#xff1f;1.1 如果要程序员分别制造出cache hit、miss、linefill、evict这四种场景&#xff0c;该怎么做&#xff1f; 二&#xff0c;实现cache line eviction的方法1.1 直接填充法3…

STM32WB55开发(1)----监测STM32WB连接状态

STM32WB55开发----1.监测STM32WB连接状态 概述硬件准备视频教学样品申请选择芯片型号配置时钟源配置时钟树RTC时钟配置查看开启STM32_WPAN条件配置HSEM配置IPCC配置RTC启动RF开启蓝牙LED配置设置工程信息工程文件设置参考文档SVCCTL_App_Notification结果演示 概述 STM32WB系列…

useRef 定义的 ref 在控制台可以打印但是页面不生效?

useRef 是一个 React Hook&#xff0c;它能让你引用一个不需要渲染的值。 点击计时器 点击按钮后在控制台可以打印但是页面不生效。 useRef 返回的值在函数组件中不会自动触发重新渲染&#xff0c;所以控制台可以显示变化而按钮上无法显示 ref.current的变化。 import { use…

ConcurrentHashMap集合

什么是ConcurrentHashMap&#xff1f; ConcurrentHashMap 和HashMap一样&#xff0c;是一个存放键值对的容器。使用Hash算法来获取值的地址&#xff0c;因此时间复杂度是O(1)。查询非常快。ConcurrentHashMap 同时也是线程安全版的HashMap&#xff0c;可以实现线程安全的集合的…

文献阅读:Chain-of-Thought Prompting Elicits Reasoning in Large Language Models

文献阅读&#xff1a;Chain-of-Thought Prompting Elicits Reasoning in Large Language Models 1. 文章简介2. 具体方法3. 实验结果 1. 数学推理 1. 实验设计2. 实验结果3. 消解实验4. 鲁棒性考察 2. 常识推理 1. 实验设计2. 实验结果 3. 符号推理 1. 实验设计2. 实验结果 4.…

融合MMEdu和Transformers技术的视障出行智能辅助系统(上海浦育AI未来夏令营结题论文)

融合MMEdu和Transformers技术的视障出行智能辅助系统 摘要 面对社会生活中众多视障者对出行的需求&#xff0c;视障出行智能辅助系统融合MMEdu和Transformers技术为视障者提供实时路况分析。本系统利用图像分类、目标检测和深度估计等软件技术&#xff0c;对摄像头实时获取的每…

MATLAB中编译器中的变量联系到Simulink

MATLAB中编译器中的变量联系到Simulink 现在编译器中创建变量&#xff0c;进行编译&#xff0c;使其生成在工作区。 然后在Simulink中国使用变量即可。

操作视频的开始与暂停

调用 ref.current.play() 方法来播放视频&#xff1b; 如果视频需要暂停&#xff0c;我们调用 ref.current.pause() 方法来暂停视频。 通过 useRef 创建的 ref 操作视频的开始与暂停 当用户点击按钮时&#xff0c;根据当前视频的状态&#xff0c;我们会开始或暂停视频&…

如何利用开源工具搭建AI大模型底座

开源社区是技术发展的一个重要部分&#xff0c;对于AI大模型来说&#xff0c;也是如此。 我们在这篇文章中来尝试通过开源工具来构建AI大模型的底座&#xff0c;涉及到的技术包括&#xff1a; LangchainOpenAIFlowiseLocalAILlama 使用Langchain构建第一个对话应用 如果你使…

时序预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.MATLAB实现TCN-LSTM时间卷积长短期记忆神经网络时间序列预测…

17.Oauth2-微服务认证

1.Oauth2 OAuth 2.0授权框架支持第三方支持访问有限的HTTP服务&#xff0c;通过在资源所有者和HTTP服务之间进行一个批准交互来代表资源者去访问这些资源&#xff0c;或者通过允许第三方应用程序以自己的名义获取访问权限。 为了方便理解&#xff0c;可以想象OAuth2.0就是在用…