蓝桥杯嵌入式学习记录——按键的使用

目录

一、按键原理简介

二、cubeMX的配置

三、按键的短按代码

四、按键的长按代码


一、按键原理简介

        在STM32中,按键连接通常使用GPIO(通用输入/输出)端口来实现。当按键未被按下时,GPIO端口处于高电平状态(即1),当按键被按下时,GPIO端口会被拉低(即0)。因此,通过检测GPIO端口的电平状态变化,可以检测到按键是否被按下。

        为了防止按键抖动,通常需要使用软件消抖。消抖的方法通常是在检测到按键被按下时,等待一段时间,并再次检测GPIO端口的状态,只有当GPIO端口仍然处于低电平状态时,才认为按键被有效触发。同时,还可以通过使用外部上拉电阻或下拉电阻,以确保GPIO端口在未连接按键时处于稳定状态。上拉电阻将GPIO端口拉高,下拉电阻将GPIO端口拉低,这样可以避免未连接按键时的漂浮状态。

        总结来说,STM32按键的工作原理是通过检测GPIO端口的电平状态变化来判断按键是否被按下,并通过软件消抖和外部上拉/下拉电阻来确保按键的稳定性。

二、cubeMX的配置

        在cubeMX中,我们除了需要配置四个按键引脚的模式外,还需要配置定时器相关的参数等。我们用定时器来实现按键消抖,即通过定时器每过10ms检测一次按键引脚的电平。具体配置操作如下:

1、打开cubeMX软件,将开发板上的四个按键对应的引脚设置为输入模式,即将PA0、PB0、PB1、PB2设置为GPIO_Input

2、点击左边的GPIO,选中PA0、PB0、PB1、PB2四个GPIO口,并如图设置为上拉模式,即按下时GPIO口为低电平(0)

3、点击Timers,选择一个定时器,我选择的是通用定时器TIM3,如图设置时钟源为内部时钟。再设置定时器的预分频器值和计数器重载值,由于我们设置的定时器时钟频率为80MHz,通过定时器的计算公式,当我们想要定时10ms时,我们需要将预分频器值设置为80-1数器重载值设置为10000-1.公式如下:

定时时间 = (预分频器值\times计数器重载值)/ 定时器时钟频率

三、按键的短按代码

interrupt.h

代码后已经注释了代码的大致含义

// interrupt.h#ifndef _INTERRUPT_H
#define _INTERRUPT_H#include "main.h"      // 在main.h中宏定义uchar、uintstruct keys            // 定义一个结构体,设置三个状态变量
{uchar judge_sta;   // 设置标志位,反应定时器中断服务函数进行到哪一步uchar key_sta;     // 检测按键引脚的电平并保存到key_sta,当按键按下时key_sta为0uchar flag;        // 当按键真正按下后,让flag = 1
};void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);  // 中断服务函数#endif

interrupt.c

        这里主要就是编写定时器的中断服务函数,首先读取四个按键引脚的电平,通过定时器每过10ms检测一次电平,当第一次检测为低电平时,等待10ms后再检测一次,若仍为低电平,则视为按键真的按下,即令flag = 1.

// interrupt.c#include "interrupt.h"struct keys key[4] = {0, 0, 0, 0};void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3)       // 使用定时器TIM3{// 读取四个按键引脚的电平key[0].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);  key[1].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key[2].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key[3].key_sta = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);}for(int i=0; i<4; i++)  // 循环扫描四个按键的状态{switch (key[i].judge_sta){case 0:{if(key[i].key_sta == 0){key[i].judge_sta = 1;}}break;case 1:{if(key[i].key_sta == 0){key[i].flag = 1;key[i].judge_sta = 2;}else  key[i].judge_sta = 0;}break;case 2:{if(key[i].key_sta == 1){key[i].judge_sta = 0;}}break;}}}

main.c

        主函数中我只展示与按键相关的代码,首先需要声明一个全局变量extern struct keys key[]来方便后面使用key[i].flag进行判断

        然后需要打开定时器中断服务函数,使用HAL_TIM_Base_Start_IT(&htim3);

        最后在while循环中用key[i].flag判断按键是否真的按下,当按键真的按下时执行一系列指令

// main.c
#include "main.h"
#include "tim.h"
#include "gpio.h"#include "led.h"
#include "interrupt.h"extern struct keys key[];HAL_TIM_Base_Start_IT(&htim3);while (1){if(key[0].flag == 1){LED(0x01);key[0].flag = 0;}if(key[1].flag == 1){LED(0x00);key[1].flag = 0;}}

四、按键的长按代码

interrupt.h

与短按相比,长按的头文件只是在结构体中多定义两个变量:key_time和long_flag

key_time用来判断按键按下的时间长短

long_flag用来判断按键是否长按

// interrupt.h#ifndef _INTERRUPT_H
#define _INTERRUPT_H#include "main.h"struct keys
{uchar judge_sta;uchar key_sta;uchar flag;uint key_time;uchar long_flag;       
};void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);#endif

interrupt.c

        与短按类似,只是当第一次判断按键按下后,key[i].time开始自加,直到按键松开后判断key[i].time的时间,大于70ms视为长按,小于70ms视为短按

// interrupt.c#include "interrupt.h"struct keys key[4] = {0, 0, 0, 0};void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if(htim->Instance == TIM3){key[0].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0);key[1].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1);key[2].key_sta = HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2);key[3].key_sta = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);}for(int i=0; i<4; i++){switch (key[i].judge_sta){case 0:{if(key[i].key_sta == 0){key[i].judge_sta = 1;key[i].key_time = 0;}}break;case 1:{if(key[i].key_sta == 0){key[i].judge_sta = 2;}else{key[i].judge_sta = 0;}}break;case 2:{if(key[i].key_sta == 1){key[i].judge_sta = 0;if(key[i].key_time < 70)key[i].flag = 1;}else key[i].key_time++;if(key[i].key_time > 70) key[i].long_flag = 1;}break;}}}

main.c

主函数除了while循环的判断之外,都与短按类似

  while (1){if(key[0].flag == 1){LED(0x00);key[0].flag = 0;}if(key[1].long_flag == 1){LED(0x01);key[1].long_flag = 0;}}

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

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

相关文章

Linux第48步_编译正点原子的出厂Linux内核源码

编译正点原子的出厂 Linux 内核源码&#xff0c;为后面移植linux做准备。研究对象如下&#xff1a; 1)、linux内核镜像文件“uImage” 路径为“arch/arm/boot”&#xff1b; 2)、设备树文件“stm32mp157d-atk.dtb” 路径为“arch/arm/boot/dts” 3)、默认配置文件“stm32m…

3D高斯溅射:面向三维场景的实时渲染技术

1. 前言 高斯溅射技术【1】一经推出&#xff0c;立刻引起学术界和工业界的广泛关注。相比传统的隐式神经散射场渲染技术&#xff0c;高斯溅射依托椭球空间&#xff0c;显性地表示多目图像的三维空间关系&#xff0c;其计算效率和综合性能均有较大的提升&#xff0c;且更容易理…

【AI视野·今日NLP 自然语言处理论文速览 第七十八期】Wed, 17 Jan 2024

AI视野今日CS.NLP 自然语言处理论文速览 Wed, 17 Jan 2024 (showing first 100 of 163 entries) Totally 100 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Computation and Language Papers Deductive Closure Training of Language Models for Coherence, Accur…

VueCLI核心知识3:全局事件总线、消息订阅与发布

这两种方式都可以实现任意两个组件之间的通信 1 全局事件总线 1.安装全局事件总线 import Vue from vue import App from ./App.vueVue.config.productionTip false/* 1.第一种写法 */ // const Demo Vue.extend({}) // const d new Demo()// Vue.prototype.x d // 把Dem…

XML学习

XML学习 1 XML介绍2 XML语法3 XML特殊字符4 XML文档结构5 XML命名空间 1 XML介绍 XML是可扩展的标记性语言&#xff0c;常用来传输和存储数据。可见于Web中的web.xml和Maven中的pom.xml version是版本&#xff0c;encoding是编码 <?xml version"1.0" encodin…

IMX6ULL移植U-Boot 2022.04

目录 目录 1.编译环境以及uboot版本 2.默认编译测试 3.uboot中新增自己的开发板 3.编译测试 4.烧录测试 5.patch文件 1.编译环境以及uboot版本 宿主机Debian12u-boot版本lf_v2022.04 ; git 连接GitHub - nxp-imx/uboot-imx: i.MX U-Boot交叉编译工具gcc-arm-10.3-2021.0…

【PyQt】08 - 编辑Tab顺序

文章目录 前言一、Tab顺序二、编辑Tab顺序总结 前言 介绍了什么是Tab顺序&#xff0c;以及如何修改Tab顺序。 一、Tab顺序 当你的界面设计好之后&#xff0c;在输入栏按住Tab按键&#xff0c;他会按照你摆放的顺序一次转跳 二、编辑Tab顺序 方法一 然后鼠标左击就可以改变…

PHP毕业设计图片分享网站76t17

图片分享网站主要是为了提高工作人员的工作效率和更方便快捷的满足用户&#xff0c;更好存储所有数据信息及快速方便的检索功能&#xff0c;对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定考虑用户的可操作性&#xff0c;遵循开发的系统优化的原则&#xff0c;…

Vegeta压测工具学习与使用

Vegeta压测工具学习与使用 目标&#xff1a; 能够在命令行下使用Vegeta对指定API进行测试了解如何导出结果&#xff0c;以及能获得什么样的结果(P99,P99.9,QPS)探索能否导出其他结果&#xff0c;是否能够执行复杂命令或简易脚本等 时间比较紧迫&#xff0c;预计两到三个小时内完…

Sentinel 流控-链路模式

链路模式 A B C 三个服务 A 调用 C B 调用 C C 设置流控 ->链路模式 -> 入口资源是 A A、B 服务 package com.learning.springcloud.order.controller;import com.learning.springcloud.order.service.BaseService; import org.springframework.beans.factory.annotatio…

【JAVA】计算机软件工程人工智能研究生复试资料整理

1、JAVA 2、计算机网络 3、计算机体系结构 4、数据库 5、计算机租场原理 6、软件工程 7、大数据 8、英文 自我介绍 1. Java 1. == 和 equals的区别 比较基本数据类型是比较的值,引用数据类型是比较两个是不是同一个对象,也就是引用是否指向同 一个对象,地址是否相同,equ…

鸿蒙开发系列教程(十八)--页面内动画(1)

页面内的动画 显示动画 语法&#xff1a;animateTo(value: AnimateParam, event: () > void): void 第一个参数指定动画参数 第二个参数为动画的闭包函数。 如&#xff1a;animateTo({ duration: 1000, curve: Curve.EaseInOut }, () > {动画代码}&#xff09; dura…

手撕链表OJ

&#x1d649;&#x1d65e;&#x1d658;&#x1d65a;!!&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦&#x1f44f;&#x1f3fb;‧✧̣̥̇‧✦ &#x1f44f;&#x1f3fb;‧✧̣̥̇:Solitary-walk ⸝⋆ ━━━┓ - 个性标签 - &#xff1a;来于“云”的“羽球人”。…

python调用golang中函数方法

一、原因说明&#xff1a;由于simhash方法有多种实现方式&#xff0c;现python中simhash方法与golang中的不一样&#xff0c;需要两者代码生成结果保持一致&#xff0c;故采用python中的代码调用golang编译的so文件来实现。 环境配置&#xff1a;①Windows10系统要有gcc环境&a…

Python 读取pdf文件

Python 实现读取pdf文件简单示例。 安装命令 需要安装操作pdf的三方类库&#xff0c;命令如下&#xff1a; pip install pdfminer3K 安装过程如下&#xff1a; 引入类库 需要引入很多的类库。 示例如下&#xff1a; import sys import importlib importlib.reload(sys)fr…

车载诊断协议DoIP系列 —— OSI模型DoIP参考

车载诊断协议DoIP系列 —— OSI模型DoIP参考 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师(Wechat:gongkenan2013)。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 本就是小人物,输了就是输了,不要在意别人怎么看自己。江湖一碗茶,喝完再…

MQTT的学习与应用

文章目录 一、什么是MQTT二、MQTT协议特点三、MQTT应用领域四、安装Mosquitto五、如何学习 MQTT 一、什么是MQTT MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;是一种轻量级的消息传输协议&#xff0c;设计用于在低带宽、不稳定的网络环境中进行高效的通信…

深入了解pip和conda:高效Python环境管理的必备指南

pip相关命令: 更新包之前最好更新一下pip&#xff0c;因为更新其他包底层是依赖 pip pip show pippython -m pip install --upgrade pippython更新包&#xff1a; - ​ pip install --upgrade 包 pip install pandas- ​ pip install --upgrade 包名称版本号查看那些包需要更…

【C++第二阶段】赋值运算符重载

你好你好&#xff01; 以下内容仅为当前认识&#xff0c;可能有不足之处&#xff0c;欢迎讨论&#xff01; 文章目录 赋值运算符重载 赋值运算符重载 实验①&#xff0c;还没有对析构运算符重载时 #include<iostream> #include<string> using namespace std;clas…

操作系统(14)----文件系统的结构

目录 一.文件系统的层次结构 1.用户接口&#xff1a; 2.文件目录系统&#xff1a; 3.存取控制模块&#xff1a; 4.逻辑文件系统与文件信息缓冲区&#xff1a; 5.物理文件系统&#xff1a; 二.文件系统的全局结构 1.文件系统在外存中的结构 &#xff08;1&#xff09;物…