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

news2024/11/25 6:47:37

目录

一、按键原理简介

二、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、uint

struct keys            // 定义一个结构体,设置三个状态变量
{
	uchar judge_sta;   // 设置标志位,反应定时器中断服务函数进行到哪一步
	uchar key_sta;     // 检测按键引脚的电平并保存到key_sta,当按键按下时key_sta为0
	uchar 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.coloradmin.cn/o/1449527.html

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

相关文章

【Go语言】第一个Go程序

第一个 Go 程序 1 安装 Go Go语言官网&#xff1a;Download and install - The Go Programming Language&#xff0c;提供了安装包以及引导流程。 以 Windows 为例&#xff0c;进入windows安装包下载地址&#xff1a;All releases - The Go Programming Language&#xff0c…

【Midjourney】解密Midjourney付费订阅:畅享全新体验!(详细流程与各版本一览)

一、Midjourney 付费订阅流程 1、在首页点击Purchase plan 2、进入到midjourney年月选择页面 3、这里续费一个最便宜的版本 , 按年付费 8 , 按月 10 4、输入银行卡信息 , 用的WildCard虚拟信用卡 &#xff0c;打开 5、填写完银行卡信息就订阅成功 二、Midjourney 各版本介绍…

洛谷_P1923 【深基9.例4】求第 k 小的数_python写法

哪位大佬可以出一下这个的题解&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;话说蓝桥杯可以用numpy库吗&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f; 这道题有一个很简单的思路就是排序完成之后再访问。 but有很大的问题&…

SAP PP学习笔记- 豆知识01 - 怎么查询既存品目

SAP系统当中已经有哪些品目要怎么查询呢&#xff1f; 1&#xff0c;MM60 品目一览 这里可以输入Plant&#xff0c;然后可以查询该工厂的所有品目。 2&#xff0c;SE16 > MARA MARA 品目一般データ&#xff0c;存放的是品目基本信息。 要查询该品目属于哪个Plant&#xff…

如何在30天内使用python制作一个卡牌游戏

如何在30天内使用python制作一个卡牌游戏 第1-5天&#xff1a;规划和设计第6-10天&#xff1a;搭建游戏框架第11-20天&#xff1a;核心游戏机制开发第21-25天&#xff1a;游戏界面和用户体验第26-30天&#xff1a;测试和发布附加建议游戏类型游戏规则设计界面设计技术选型第6-…

下一代Windows系统曝光:基于GPT-4V,Agent跨应用调度,代号UFO

下一代Windows操作系统提前曝光了&#xff1f;&#xff1f; 微软首个为Windows而设的智能体&#xff08;Agent&#xff09; 亮相&#xff1a; 基于GPT-4V&#xff0c;一句话就可以在多个应用中无缝切换&#xff0c;完成复杂任务。整个过程无需人为干预&#xff0c;其执行成功…

MySQL 基础知识(四)之表操作

目录 1 约束 2 查看已有表 3 创建表 4 查看表结构 5 修改表 6 删除表 1 约束 主键约束 primary key&#xff1a;唯一&#xff0c;标识表中的一行数据&#xff0c;此列的值不可重复&#xff0c;且不能为 NULL&#xff0c;此外&#xff0c;可以多个列组成主键唯一约束 uniq…

新机Word/PowerPoint新建空白文档后闪退问题

首先可以尝试一下常规的修复&#xff1a; 设置-应用-安装的应用-搜索office-点击Micros Office Home and Student...右侧三个点-选择修改-点击是-快速修复-修复 再不行就按上面的选择联机修复&#xff0c;这个会卸载现有Office然后自动帮你重新下载 我做了以上两个都没有解决问…

java基础实现的图书管理系统

文章目录 项目介绍项目功能代码讲解如何实现不同用户之间的操作权限不同 项目介绍 该项目是用的是javase的一些知识包括了类和对象封装&#xff0c;继承多态等面向对象的三大特性。主要是为了让我们能够更好的使用之前学到的知识。 接下来给大家讲解一下这个项目的一个特点。首…

FL Studio 21.2.3.4004 All Plugins Edition Win/Mac音乐软件

FL Studio 21.2.3.4004 All Plugins Edition 是一款功能强大的音乐制作软件&#xff0c;提供了丰富的音频处理工具和插件&#xff0c;适用于专业音乐制作人和爱好者。该软件具有直观的用户界面&#xff0c;支持多轨道录音、混音和编辑&#xff0c;以及各种音频效果和虚拟乐器。…

blender在几何节点中的这些变换中的旋转,其实可以是两种旋转顺序

看似xyz的旋转角度&#xff0c;但如果按照欧拉角来谈它的旋转&#xff0c;就大有学问了。 我们知道&#xff0c;在blender中有局部旋转和全局旋转。但其实这两者在某种情况下可以等价。 那就是&#xff0c;如果参照全局坐标系&#xff0c;按xyz的顺序进行欧拉旋转&#xff0c;…

【Linux】并发解决(上)-中断屏蔽,原子操作

&#x1f525;博客主页&#xff1a;PannLZ &#x1f38b;系列专栏&#xff1a;《Linux系统之路》 &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 文章目录 并发解决1.中断屏蔽2.原子操作2.1整形原子操作2.2位原子操作原子变量使用例子 并发解决…

【Linux学习】线程池

目录 23.线程池 23.1 什么是线程池 23.2 为什么需要线程池 23.3 线程池的应用场景 23.4 实现一个简单的线程池 23.4.1 RAII风格信号锁 23.4.2 线程的封装 23.4.3 日志打印 22.4.4 定义队列中存放Task类任务 23.4.5 线程池的实现(懒汉模式) 为什么线程池中需要有互斥锁和条件变…

无监督学习:探索数据的潜在结构与规律

目录 前言1. 概念2. 聚类2.1 定义2.2 应用场景 3. 异常检测3.1 定义3.2 应用场景 结语 前言 在当今信息时代&#xff0c;数据扮演着至关重要的角色&#xff0c;其海量、多样的形式为我们提供了前所未有的机会和挑战。在这个大数据的背景下&#xff0c;无监督学习的概念日益引起…

《剑指offer》--字符串左旋【超详细建议收藏】

字符串左旋的三种方法 1. 一个一个字符挪2. 库函数---strcpy和strncat3. 三段逆置法 1. 一个一个字符挪 代码实现如下&#xff1a; #include <stdio.h> #include <string.h>void Left_Reverse(char* str,int k) {int len strlen(str);//6int time 0;time k % …

开源≠不赚钱,开源软件盈利的7大模式。

开源不是目的&#xff0c;目的是圈用户&#xff0c;留住用户&#xff0c;盈利自然不成问题。 开源系统可以通过多种方式赚钱&#xff0c;以下是其中几种常见的方式&#xff1a; 提供付费支持&#xff1a; 开源系统可以提供付费的技术支持服务&#xff0c;包括安装、配置、维…

代码随想录 Leetcode406. 根据身高重建队列

题目&#xff1a; 代码(首刷看解析 2024年2月15日&#xff09;&#xff1a; class Solution { static bool cmp(const vector<int>& A, const vector<int>& B) {if(A[0] B[0]) return A[1] < B[1];return A[0] > B[0]; } public:vector<vector&…

2001-2022年368个地级市平均气温数据

2001-2022年368个地级市平均气温数据 1、时间:2001-2022年 2、范围&#xff1a;368个地级市 3、来源&#xff1a;基于NOAA下属NCEI提供的原始数据编制而成的。 4、指标&#xff1a;年份、省份、省份代码、城市、城市代码、平均气温 5、指标解释&#xff1a;平均气温指某一…

Java奇缘:林浩然与杨凌芸的数学冒险记

Java奇缘&#xff1a;林浩然与杨凌芸的数学冒险记 Java Adventure: The Mathematical Odyssey of Lin Haoran and Yang Lingyun 在Java编程世界的某一个角落&#xff0c;住着两位才华横溢的程序员——林浩然和杨凌芸。林浩然&#xff0c;人称“算法大侠”&#xff0c;对Java Ma…

Linux第56步_根文件系统第3步_将busybox构建的根文件系统烧录到EMMC

1、第1次将“rootfs”打包 1)、打开第1个终端&#xff0c;准备在“mnt”目录下创建挂载目录“rootfs”&#xff1b; 输入“ls回车” 输入“cd /mnt回车” 输入“ls回车”&#xff0c;查看“mnt”目录下的文件和文件夹 输入“sudo mkdir rootfs回车”&#xff0c;在“mnt”…