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

一、外部中断/事件控制器(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_3uint8_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_ONLED2_ONLED3_ON}//如果松开了,就熄灭else{LED1_OFFLED2_OFFLED3_OFF}}/* USER CODE END 3 */
}

四、运行效果

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【5】openGL使用宏和函数进行错误检测

当我们编写openGL程序&#xff0c;没有报编译链接错误&#xff0c;但是运行结果是黑屏&#xff0c;这不是我们想要的。 openGL提供了glGetError 来检查错误&#xff0c;我们可以通过在运行时进行打断点查看glGetError返回值&#xff0c;得到的是一个十进制数&#xff0c;将其转…

Nacos服务注册和服务配置

Nacos 是什么 Nacos (Dynamic Naming and Configuration Service)&#xff0c;其命名由三部分组成&#xff1a; Na (naming/nameServer)&#xff0c;即服务注册中心。 co (configuration)&#xff0c;即配置中心。 s (service)&#xff0c;即服务&#xff0c;表示 Nacos 实现的…

华为 连接OSPF和RIP网络---OSPF和RIP网络相互引入

路由引入简介 不同路由协议之间不能直接共享各自的路由信息&#xff0c;需要依靠配置路由的引入来实现。 获得路由信息一般有3种途径&#xff1a;直连网段、静态配置和路由协议。可以将通过这3种途径获得的路由信息引入到路由协议中&#xff0c;例如&#xff0c;把直连网段引入…

【文心一言】学习笔记

学习资料 《听说文心一言App霸榜了&#xff0c;那必须来一波全方位实测了》 情感陪伴&#xff1a;文心一言 App 可以充当用户的情感树洞&#xff0c;提供知心姐姐、【暖男】等角色扮演&#xff0c;为用户提供情绪疏导、情感分析、约会建议等服务。 1. 模型属性 【提示词工具…

无涯教程-Android - CheckBox函数

CheckBox是可以由用户切换的on/off开关。为用户提供一组互不排斥的可选选项时,应使用复选框。 CheckBox 复选框属性 以下是与CheckBox控件相关的重要属性。您可以查看Android官方文档以获取属性的完整列表以及可以在运行时更改这些属性的相关方法。 继承自 android.widget.T…

nuxt3+ts+vue3的ssr项目总结

目录 一、什么是SSR、SEO、SPA&#xff0c;它们之间的关系又是怎样的。 二、VUE做SSR的几种方法 1、插件prerender-spa-plugin 2、VUE开启SSR渲染模式 3、使用NUXT框架 三、NUXT3VUE3TS &#xff08;一&#xff09;基本配置 1、文件夹介绍 assets components pages…

Docker安装MySQL教程

虽然 docker 安装 mysql 不是一个很好的方案&#xff0c;但是为了个人使用方便&#xff0c;使用 docker 安装 mysql 还是没什么问题的。 本文为了方便&#xff0c;我们直接通过yum方式安装。所以&#xff0c;我们在安装之前需要电脑可以联网&#xff0c;不然我们这种方式是安装…

Python的由来和基础语法(一)

目录 一、Python 背景知识 1.1Python 是咋来的? 1.2Python 都能干啥? 1.3Python 的优缺点 二、基础语法 2.1常量和表达式 2.2变量和类型 变量的语法 (1) 定义变量 (2) 使用变量 变量的类型 (1) 整数 (2) 浮点数(小数) (3) 字符串 (4) 布尔 (5) 其他 动态类型…

《TCP/IP网络编程》阅读笔记--基于Windows实现Hello Word服务器端和客户端

目录 1--Hello Word服务器端 2--客户端 3--编译运行 3-1--编译服务器端 3-2--编译客户端 3-3--运行 1--Hello Word服务器端 // gcc hello_server_win.c -o hello_server_win -lwsock32 // hello_server_win 9190 #include <stdio.h> #include <stdlib.h> #i…

vue+element-ui el-table组件二次封装实现虚拟滚动,解决数据量大渲染DOM过多而卡顿问题

一、此功能已集成到TTable组件中 二、最终效果 三、需求 某些页面不做分页时&#xff0c;当数据过多&#xff0c;会导致页面卡顿&#xff0c;甚至卡死 四、虚拟滚动 一、固定一个可视区域的大小并且其大小是不变的&#xff0c;那么要做到性能最大化就需要尽量少地渲染 DOM 元素…

kotlin 转 Java

今天突然想研究下有些kotlin文件转为Java到底长什么样&#xff0c;好方便优化kotlin代码&#xff0c;搞了半天发现一个非常简单的Android Studio或者Intellij idea官方插件Kotlin&#xff0c;Kotlin是插件的名字&#xff0c;真是醉了&#xff1b; 这里以AS为例&#xff0c;使用…

OTFS-ISAC通信最新进展

测试场景 Tx DD域帧结构导频区域 Rx DD域帧导频区域 原始星座图 信道估计及数据检测 经过MP算法后的星座图 误码率曲线

ELK安装、部署、调试 (二) ES的安装部署

ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口操作ES&#xff0c;也可以利用Java API。Elasticsearch是用Java开发的&#xff0c;并作为Apache许可条款下的开放源码发布&#xff0c;是当前流行的企业…

windows-nessus安装

1、下载 路径&#xff1a;Download Tenable Nessus | Tenable 2、获取active code 路径&#xff1a;Tenable Nessus Essentials Vulnerability Scanner | Tenable 3、安装 challenge code:上图马赛克位置 active code:获取active code第二张图片的马赛克位置 4、激活 5、安装…

CTFhub-文件上传-.htaccess

首先上传 .htaccess 的文件 .htaccess SetHandler application/x-httpd-php 这段内容的作用是使所有的文件都会被解析为php文件 然后上传1.jpg 的文件 内容为一句话木马 1.jpg <?php echo "PHP Loaded"; eval($_POST[a]); ?> 用蚁剑连接 http://ch…

SpringCloudAlibaba OpenFeign整合及详解

SpringCloudAlibaba OpenFeign 在前面&#xff0c;我们使用Nacos服务注册发现后&#xff0c;服务远程调用可以使用RestTemplateRibbon或者OpenFeign调用。实际开发中很少使用RestTemplate这种方式进行调用服务&#xff0c;每次调用需要填写地址&#xff0c;还要配置各种的参数&…

软件测试/测试开发丨Pytest和Allure报告 学习笔记

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/26755 Pytest 命名规则 类型规则文件test_开头 或者 _test 结尾类Test 开头方法/函数test_开头注意&#xff1a;测试类中不可以添加__init__构造函数 注…

使用 BERT 进行文本分类 (03/3)

一、说明 在使用BERT&#xff08;2&#xff09;进行文本分类时&#xff0c;我们讨论了什么是PyTorch以及如何预处理我们的数据&#xff0c;以便可以使用BERT模型对其进行分析。在这篇文章中&#xff0c;我将向您展示如何训练分类器并对其进行评估。 二、准备数据的又一个步骤 …

IDEA中Run/Debug Configurations添加VM options和Program arguments

1. 现象描述 我在我的IDEA当中打开配置模板后&#xff0c;发现没有VM options和Program arguments&#xff0c;也就是虚拟机选项和程序实参这两项&#xff0c;导致我不能配置系统属性参数和命令行参数&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff0…

【vue2第十一章】v-model的原理详解 与 如何使用v-model对父子组件的value绑定 和修饰符.sync

v-model的原理详解 v-model的本质就是一个语法糖&#xff0c;实际上就是 :value"msg" 与 input"msg $event.target.value" 的简写。 :value"msg" 从数据单向绑定到input框&#xff0c;当data数据中的msg内容一旦改变&#xff0c;而input框数据…