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

目录

一、按键原理简介

二、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.mzph.cn/news/683001.shtml

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

相关文章

springboot188基于spring boot的校园商铺管理系统

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…

高德地图上绘制热力图的方法

百度地图和高德地图的JavaScript API都提供了热力图的绘制方法&#xff0c;都是将热力图作为新的图层&#xff0c;叠加到地图上。但是百度地图的经纬度体系与我们的经纬度存在偏差&#xff0c;高德的与我们相符&#xff0c;应当使用高德地图JavaScript API。 因为是JavaScript…

COM初体验——新建文档并写入内容。

我想在程序里和Word交互。老师跟我说不要学COM&#xff0c;因为它已经过时了。但是我不想再把代码移植到C#上面&#xff0c;然后用VSTO——已经用了std::unordered_set&#xff01;因为我使用了Copilot&#xff0c;结合我的思考&#xff0c;写了下面的代码&#xff1a; #impor…

【Langchain Agent研究】SalesGPT项目介绍(四)

【Langchain Agent研究】SalesGPT项目介绍&#xff08;三&#xff09;-CSDN博客 github地址&#xff1a;GitHub - jerry1900/SalesGPT: Context-aware AI Sales Agent to automate sales outreach. 上节课&#xff0c;我们主要介绍了SalesGPT的类属性和它最重要的类方法f…

【教学类-47-01】20240206UIBOT+IDM下载儿童古诗+修改文件名

背景需求&#xff1a; 去年12月&#xff0c;我去了其他幼儿园参观&#xff0c;这是一个传统文化德育教育特色的学校&#xff0c;在“古典集市”展示活动中&#xff0c;小班中班大班孩子共同现场念诵《元日》《静夜思》包含了演唱版本和儿歌念诵版本。 我马上也要当班主任了&a…

微信小程序开发学习笔记《17》uni-app框架-tabBar

微信小程序开发学习笔记《17》uni-app框架-tabBar 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。建议仔细阅读uni-app对应官方文档 一、创建tabBar分支 运行如下的命令&#xff0c;基于master分支在本地创建tabBar子分支&#x…

Netty Review - 服务端channel注册流程源码解析

文章目录 PreNetty主从Reactor线程模型服务端channel注册流程源码解读入口 serverBootstrap.bind(port)执行队列中的任务 &#xff1a; AbstractUnsafe#register0注册 doRegister() 源码流程图 Pre Netty Review - ServerBootstrap源码解析 Netty Review - NioServerSocketCh…

TCP高频知识点

本篇文章主要讲述一下在面试过程中TCP的高频知识点 1.TCP三次握手流程图: 客户端发送一个SYN&#xff08;同步&#xff09;报文段给服务器&#xff0c;选择一个初始序列号&#xff0c;并设置SYN标志位为1。服务器接收到客户端的SYN报文段后&#xff0c;回复一个ACK&#xff08…

OJ刷题:杨氏矩阵【建议收藏】

看见这个题目&#xff0c;很多人的第一反应是遍历整个数组查找数字&#xff0c;但是这种方法不仅效率低&#xff0c;而且远远不能满足题目要求。下面介绍一种高效的查找方法&#xff1a; 代码实现&#xff1a; #include <stdio.h>int Yang_Find_Num(int arr[][3], int …

steam游戏搬砖项目靠谱吗?有没有风险?

作为一款fps射击游戏&#xff0c;csgo在近几年可谓是火出圈&#xff0c;作为一款全球竞技游戏&#xff0c;深受玩家喜爱追捧&#xff0c;玩家追求的就是公平公正&#xff0c;各凭本事&#xff0c;像其他游戏可能还会有皮肤等装备属性加成&#xff0c;在csgo里面是不存在的。 纯…

K8sGPT 的使用

K8sGPT 介绍 k8sgpt 是一个扫描 Kubernetes 集群、诊断和分类问题的工具。它将 SRE 经验编入其分析器中&#xff0c;并帮助提取最相关的信息&#xff0c;通过人工智能来丰富它。它还可以与 OpenAI、Azure、Cohere、Amazon Bedrock 和本地模型结合使用。 K8sGPT Github 地址 …

Vue插槽

Vue插槽 一、插槽-默认插槽1.作用2.需求3.问题4.插槽的基本语法5.代码示例6.总结 二、插槽-后备内容&#xff08;默认值&#xff09;1.问题2.插槽的后备内容3.语法4.效果5.代码示例 三、插槽-具名插槽1.需求2.具名插槽语法3.v-slot的简写4.代码示例5.总结 四、作用域插槽1.插槽…

安卓价值1-如何在电脑上运行ADB

ADB&#xff08;Android Debug Bridge&#xff09;是Android平台的调试工具&#xff0c;它是一个命令行工具&#xff0c;用于与连接到计算机的Android设备进行通信和控制。ADB提供了一系列命令&#xff0c;允许开发人员执行各种操作&#xff0c;包括但不限于&#xff1a; 1. 安…

在python中JSON数据格式的使用

什么是JSON&#xff1f; JSON是一种数据格式&#xff0c;由美国程序设计师DouglasCrockford创建的&#xff0c;JSON全名是JavaScript Object Notation,由JSON英文全文字义我们可以推敲JSON的缘由&#xff0c;最初是为JavaScript开发的。这种数据格式由于简单好用被大量应用在We…

DS Wannabe之5-AM Project: DS 30day int prep day15

Q1. What is Autoencoder? 自编码器是什么&#xff1f; 自编码器是一种特殊类型的神经网络&#xff0c;它通过无监督学习尝试复现其输入数据。它通常包含两部分&#xff1a;编码器和解码器。编码器压缩输入数据成为一个低维度的中间表示&#xff0c;解码器则从这个中间表示重…

数据库被人破解,删除数据,勒索

事情是这样的&#xff0c;我买了一台服务器自己部署项目玩儿玩儿&#xff0c;我的数据库运行在3306端口&#xff0c;密码没改&#xff0c;就是默认的123456&#xff0c;诡异的事情发生了&#xff0c;用了一段时间之后&#xff0c;数据库突然连接不上了&#xff0c;我一通操作猛…

Python爬虫——解析库安装(1)

目录 1.lxml安装2.Beautiful Soup安装3.pyquery 的安装 我创建了一个社区&#xff0c;欢迎大家一起学习交流。社区名称&#xff1a;Spider学习交流 注&#xff1a;该系列教程已经默认用户安装了Pycharm和Anaconda&#xff0c;未安装的可以参考我之前的博客有将如何安装。同时默…

【开源】SpringBoot框架开发企业项目合同信息系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 合同审批模块2.3 合同签订模块2.4 合同预警模块2.5 数据可视化模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 合同审批表3.2.2 合同签订表3.2.3 合同预警表 四、系统展示五、核心代码5.1 查询合同…

Linux多线程[二]

引入知识 进程在线程内部执行是OS的系统调度单位。 内核中针对地址空间&#xff0c;有一种特殊的结构&#xff0c;VM_area_struct。这个用来控制虚拟内存中每个malloc等申请的空间&#xff0c;来区别每个malloc的是对应的堆区哪一段。OS可以做到资源的精细度划分。 对于磁盘…

嵌入式软件设计入门:从零开始学习嵌入式软件设计

&#xff08;本文为简单介绍&#xff0c;个人观点仅供参考&#xff09; 首先,让我们了解一下嵌入式软件的定义。嵌入式软件是指运行在嵌入式系统中的特定用途软件,它通常被用来控制硬件设备、处理实时数据和实现特定功能。与桌面应用程序相比,嵌入式软件需要具备更高的实时性、…