STM32速成笔记—按键检测

news2024/10/7 8:28:52

如果需要本文程序工程,请评论区留邮箱或者私信。

文章目录

  • 一、按键检测原理
  • 二、硬件连接
  • 三、程序设计
    • 3.1 初始化GPIO
    • 3.2 按键扫描函数
  • 四、按键控制LED
    • 4.1 初始化LED和KEY的GPIO
    • 4.2 编写按键扫描函数
    • 4.2 编写LED控制函数
    • 4.3 编写按键服务函数
  • 五、拓展
    • 5.1 一个按键单独控制一个LED亮灭
    • 5.2 按键长短按

一、按键检测原理

按键检测原理比较简单,按键按下和不按下,其连接引脚的电平是不一样的,按键检测正是通过检测按键引脚的电平变化来实现的。比如按键未按下时引脚电平为高电平,按键按下后为低电平。我们在检测按键时只需要检测按键引脚是否变为低电平来确定按键是否按下。

二、硬件连接

按键的硬件连接决定了我们在配置按键IO时IO的状态。以我们使用的普中核心板为例,上面有三个按键需要硬件电路的小伙伴可以看看本人上传的资源,下载查看原理图。

普中核心板按键硬件电路图
其中K1一端接VCC,另一端接单片机。K2和K3一端接地,另一端接单片机。硬件电路不同,导致他们在进行按键检测时IO的配置不同。

针对K1这种按键电路,按键按下时,单片机的引脚接到VCC,因此在未按下的情况下该引脚的默认电平为低电平,也就是要把IO设置为输入下拉模式。同理,对于K2和K3这种连接方式,对应IO应该配置为输入上拉模式,使得按键未被按下时,引脚处于高电平状态。

三、程序设计

按键检测主要有以下步骤

  • 初始化GPIO
  • 检测按下按键
  • 消抖(防误触,一般通过延时实现)
  • 松手检测
  • 执行按键功能

3.1 初始化GPIO

根据原理图,谱中的STM32核心板提供了三个按键,我们使用K1和K2来实现点亮和关闭LED的操作。K1对应的IO为PA0,K2对应的IO为PE4。
按键对应GPIO
根据上一节了解的初始化GPIO程序,初始化按键GPIO。

/*
 *==============================================================================
 *函数名称:Drv_KeyGpio_Init 
 *函数功能:初始化KEY的GPIO
 *输入参数:无
 *返回值:无
 *备  注:根据硬件电路确定GPIO模式
 *==============================================================================
 */
void Drv_KeyGpio_Init (void)
{
	GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
	// 开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);

	// 配置结构体 WK UP
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   // 输入下拉
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 配置结构体 KEY0,KEY1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 输入上拉
	GPIO_Init(GPIOE, &GPIO_InitStructure);
}

3.2 按键扫描函数

按键扫描函数的功能是检测是否有按键按下,按下的按键是哪一个。检测方法上面已经叙述,通过检测按键引脚的电平。以WK UP按键为例。当WK UP被按下时,其对应的引脚PA0会变为高电平。此时检测PA0的输入电平,如果确实是低电平,则说明WK UP可能被按下。说可能是因为PA0为低电平不一定是WK UP按下造成,也可能是抖动,所以这里就需要消抖操作。这里的消抖操作比较简单粗暴,直接延时10ms看该引脚是否依旧是低电平。如果延时10ms后依旧是高电平,则认为确实是由按键按下导致的电平变化,而不是机械抖动。

确定检测到按键按下后,需要等待按键被松开在执行按键功能。为什么需要进行松手检测?举个例子,比如设置阈值时,按键按下阈值加1,如果不进行松手检测,那么按下一次按键会加很多次,因为在不停地执行按键功能。这里按键的松手检测也比较简单粗暴,用一个while死循环等待松手。比如WK UP被按下后,其引脚会一直保持高电平,也就是PAin(0)一直等于1,此时用一个while (PAin(1));来等待松手,做松手检测。

四、按键控制LED

这里做一个小练习,用普中核心板上的按键KEY0和KEY1来控制LED1的亮灭。步骤如下

  • 初始化LED和KEY的GPIO
  • 编写LED控制函数
  • 编写按键检测函数(检测按键)
  • 编写按键服务函数(实现按键功能)

4.1 初始化LED和KEY的GPIO

/*
 *==============================================================================
 *函数名称:Drv_LedGpio_Init
 *函数功能:初始化LED的GPIO
 *输入参数:无
 *返回值:无
 *备  注:无
 *==============================================================================
 */
void Drv_LedGpio_Init (void)
{
	GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
	// 开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOE,ENABLE);

	// 配置结构体 LED0
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽式输出
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB,GPIO_Pin_5);   // 熄灭LED
	
	// 配置结构体 LED1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;   // 推挽式输出
	GPIO_Init(GPIOE, &GPIO_InitStructure);
	GPIO_SetBits(GPIOE,GPIO_Pin_5);   // 熄灭LED
}
/*
 *==============================================================================
 *函数名称:Drv_KeyGpio_Init 
 *函数功能:初始化KEY的GPIO
 *输入参数:无
 *返回值:无
 *备  注:根据硬件电路确定GPIO模式
 *==============================================================================
 */
void Drv_KeyGpio_Init (void)
{
	GPIO_InitTypeDef GPIO_InitStructure;   // 定义结构体
	// 开启时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOE,ENABLE);

	// 配置结构体 WK UP
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   // 输入下拉
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	// 配置结构体 KEY0,KEY1
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;   // 输入上拉
	GPIO_Init(GPIOE, &GPIO_InitStructure);
}

4.2 编写按键扫描函数

```c
/*
 *==============================================================================
 *函数名称:Med_KeyScan
 *函数功能:检测按下按键
 *输入参数:无
 *返回值:按键键值 0:按键WK UP,1:KEY0,2:KEY1
 *备  注:无
 *==============================================================================
 */
u8 Med_KeyScan (void)
{
	// 按键WK UP
	if (KEY_UP == 1)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY_UP == 1)
		{
			while (KEY_UP);   // 松手检测
			return 1;
		}
	}
	
	// 按键KEY0
	else if (KEY0 == 0)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY0 == 0)
		{
			while (!KEY0);   // 松手检测
			return 2;
		}
	}
	
	// 按键KEY1
	else if (KEY1 == 0)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY1 == 0)
		{
			while (!KEY1);   // 松手检测
			return 3;
		}
	}
	
	// 没有按键按下
	return 0xff;   // 用0xff表示没有按键按下
}

4.2 编写LED控制函数

/*
 *==============================================================================
 *函数名称:Med_Led_StateCtrl
 *函数功能:控制LED亮灭
 *输入参数:
           LEDx:可选择的LED(0~1)
					 State:LED亮灭状态(LED_ON,LED_OFF)
 *返回值:无
 *备  注:无
 *==============================================================================
 */
void Med_Led_StateCtrl (LED_TypeDef LEDx,u8 State)
{
	switch (LEDx)
	{
		case 0:
			PBout(5) = State;
		break;
		
		case 1:
			PEout(5) = State;
		break;
		
		default:
			break;
	}
}

下面是.h文件中的一些结构体和宏定义。

// 可选择的LED
typedef enum
{
	LED1 = 0,
	LED2
}LED_TypeDef;

// 亮灭电平需要根据硬件电路确定
#define LED_ON   0
#define LED_OFF  1

4.3 编写按键服务函数

这里没有再单独编写按键服务函数,直接在main函数中编写。KETY0按下点亮LED1,KEY1按下,熄灭LED1。

u8 gKeyValue = 0;   // 记录按键键值变量
u8 gKeyValue = 0;   // 记录按键键值变量

int main(void)
{
	Med_Mcu_Iint();   // 系统初始化
	
	while(1)
  {
		gKeyValue = Med_KeyScan();   // 获取按键键值
		
		// 按键KEY0按下
		if (gKeyValue == 2)
		{
			Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
		}
		
		// 按键KEY1按下
		if (gKeyValue == 3)
		{
			Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
		}
	}
}

至此,实现了利用KEY0和KEY1控制LED的亮灭状态。

五、拓展

5.1 一个按键单独控制一个LED亮灭

单片机的IO资源是比较珍贵的,在实际用用时很少会用两个IO资源来控制一个外设的开关,这里介绍一下方法并给出例程。比如使用普中核心板上的WK UP按键来控制LED2的亮灭状态。基本思路是在上面学会按键检测的基础上,增加一个按键按下计次变量。按键按下一次,该变量加1。如果检测到变量为1,那么点亮LED,如果检测到变量为2,那么熄灭LED,同时将计数变量清零。程序设计如下

u8 gKeyValue = 0;   // 记录按键键值变量
u8 gKeyWkUpCunt = 0;   // WK UP按下次数计数变量

int main(void)
{
	Med_Mcu_Iint();   // 系统初始化
	
	while(1)
  {
		gKeyValue = Med_KeyScan();   // 获取按键键值
		
		// 按键WK UP按下
		if (gKeyValue == 1)
		{
			gKeyWkUpCunt = gKeyWkUpCunt + 1;   // 按键按下次数计数变量加1
			
			// 第一次被按下
			if (gKeyWkUpCunt <= 1)
			{
				Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
			}
			// 不是第一次被按下
			else if (gKeyWkUpCunt > 1)
			{
				gKeyWkUpCunt = 0;   // 清空计数变量
				Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED2
			}
		}
	}
}

5.2 按键长短按

除了上面介绍的一些常规操作外,有时还会用到一个按键分长按和短按。这里给出一种简单粗暴的实现思路。需要检测按键长短按时,修改一下松手检测的逻辑。延时10ms后如果按键IO依旧保持按下状态,那么确定不是机械抖动,此时在之前的松手检测while中进行粗略地计时。定义一个计数变量,每隔10ms加1。直到按键被松开。根据计数变量的值来判断按键被按下的时间,从而来区别长短按。

这里给出一个例程,KEY1短按功能为熄灭LED1,长按功能为LED1和LED2交替闪烁两轮后熄灭。按下持续时间在1s内,认为是短按,按下超过2s认为是长按。短按返回3,长按返回4。程序设计如下

u8 gKeyValue = 0;   // 记录按键键值变量
u16 gKey1TimeCunt = 0;   // 按键KEY1的计时变量
/*
 *==============================================================================
 *函数名称:Med_KeyScan
 *函数功能:检测按下按键
 *输入参数:无
 *返回值:按键键值 0:按键WK UP,1:KEY0,2:KEY1
 *备  注:无
 *==============================================================================
 */
u8 Med_KeyScan (void)
{
	// 按键WK UP
	if (KEY_UP == 1)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY_UP == 1)
		{
			while (KEY_UP);   // 松手检测
			return 1;
		}
	}
	
	// 按键KEY0
	else if (KEY0 == 0)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY0 == 0)
		{
			while (!KEY0);   // 松手检测
			return 2;
		}
	}
	
	// 按键KEY1
	// 按下1s内认为是短按,返回3
	// 按下超过2s认为是长按,返回4
	else if (KEY1 == 0)
	{
		delay_ms (10);   // 延时10ms消抖
		if (KEY1 == 0)
		{
			// 等待松手
			while (!KEY1)
			{
				delay_ms (10);
				gKey1TimeCunt = gKey1TimeCunt + 1;   // 计时变量加1
			}
		}
		
		// 判断长短按
		if (gKey1TimeCunt <= 99)   // 小于等于1s
		{
			gKey1TimeCunt = 0;   // 清零计时变量
			return 3;   // 短按
		}
		else if (gKey1TimeCunt >= 199)   // 大于1s,等于2s
		{
			gKey1TimeCunt = 0;   // 清零计时变量
			return 4;   // 长按
		}
	}
	
	// 没有按键按下
	return 0xff;   // 用0xff表示没有按键按下
}

main函数中添加下述程序

// 长按KEY1
		if (gKeyValue == 4)
		{
			Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
			delay_ms (500);
			Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
			Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
			delay_ms (500);
			Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED2
			Med_Led_StateCtrl(LED1,LED_ON);   // 点亮LED1
			delay_ms (500);
			Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
			Med_Led_StateCtrl(LED2,LED_ON);   // 点亮LED2
			delay_ms (500);
			Med_Led_StateCtrl(LED1,LED_OFF);   // 熄灭LED1
			Med_Led_StateCtrl(LED2,LED_OFF);   // 熄灭LED1
		}

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

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

相关文章

如何使用二维码实现配电箱巡检

施工工地的外部环境条件恶劣,加之工地上机动车辆的运行和机械设备的应用&#xff0c;均易导致电气故障的发生。现场配电箱缺乏专业技术人员的管理,易造成触电伤害、火灾等事故。现场纸质巡检存在以下问题&#xff1a; 1、信息查询不便:配电箱信息、负责人&#xff0c;历史巡检维…

Flowable服务组件-扩展组件

Flowable服务组件-扩展组件 扩展组件 文章目录 Flowable服务组件-扩展组件前言Flowable给我们提供了非常丰富的组件&#xff0c;但是在实际场景中&#xff0c;我们有需要企业个性化的组件&#xff0c;如何扩展自己的组件至关重要 一、扩展微服务回调组件二、程序步骤1.定义我们…

618父亲节,感恩的祝福送给父亲!

父亲节&#xff08;Fathers Day&#xff09;&#xff0c;是感恩父亲的节日。Fathers day, is a day of thanksgiving for fathers. 第一个提出父亲节理念的人是1906年的多德夫人。她想用一个特殊的日子来纪念她的父亲&#xff0c;她的妈妈多年前就去世了。起初&#xff0c;多德…

1.4 场效应管

1.什么是场效应管&#xff1f; 场效应管&#xff08;Field-Effect Transistor&#xff0c;简称FET&#xff09;是一种基于电场效应调控电流的三端器件。它是一种用于电子电路中的重要元件&#xff0c;常用于放大信号、开关电路和模拟电路等应用。 场效应管主要由一个导电的沟…

git 的详细介绍使用

点击下载&#xff1a;Git下载地址 下载完成后在本地文件夹空白位置右键能看到即为安装成功 git简介&#xff1a;git是一个版本控制系统&#xff0c;见下方图详解 快速查看git的全局配置项 git config --list --global 查看指定的全局配置项 git config user.name git conf…

【Git删除大文件失败,提示WARNING: Ref ‘refs/heads/master‘ is unchanged的解决思路】

1. 问题描述 最近使用Gitee一直在独立开发某个项目&#xff0c;某次因为某个测试文件比较大&#xff0c;超过了100MB&#xff0c;gitee无法接受&#xff0c;上传失败。但是当时我没有发现上传失败&#xff0c;就没有处理&#xff0c;等到今天发现的时候&#xff0c;已经提交过…

Low-Light Image Enhancement with Wavelet-based Diffusion Models

Abstract 扩散模型在图像恢复任务中取得了很好的效果&#xff0c;但存在时间长、计算资源消耗大、恢复不稳定等问题。为了解决这些问题&#xff0c;我们提出了一种鲁棒和高效的基于扩散的微光图像增强方法&#xff0c;称为DiffLL。具体来说&#xff0c;我们提出了一个基于小波…

Vulnhub靶机:ME AND MY GIRLFRIEND_ 1

目录 介绍信息收集主机发现主机信息探测 网站探测SSH爆破 & 提权 介绍 系列&#xff1a;Me and My Girlfriend&#xff08;此系列共1台&#xff09; 发布日期&#xff1a;2019 年 12 月 13 日 难度&#xff1a;初级 运行环境&#xff1a;VMware 目标&#xff1a;取得 root…

Linux——MySQL数据库部署及自建数据库mysql-yum仓库

&#x1f618;作者简介&#xff1a;正在努力的99年打工人。 &#x1f44a;宣言&#xff1a;人生就是B&#xff08;birth&#xff09;和D&#xff08;death&#xff09;之间的C&#xff08;choise&#xff09;&#xff0c;做好每一个选择。 &#x1f64f;创作不易&#xff0c;动…

如何有效提升英文口语水平?这 15 个方法值得学习!

要提升英文口语水平&#xff0c;除了多说多练&#xff0c;还有很多方法可以尝试。下面&#xff0c;小编为大家整理了15个有效的方法&#xff0c;让你说英语更自信流利&#xff01; Record yourself speaking English and listen back to your pronunciation and intonation, i…

Docker:启动,停止

1.启动一个容器&#xff1a; docker run 可选参数 镜像名 [COMMAND] [ARG...] docker run -it ubuntu /bin/bash &#xff0c;启动一个使用ubuntu的docker&#xff0c;并使用/bin/bash做为dcoker中执行的命令。 其中818d5a1c32ac为容器ID 在宿主机上&#xff0c;可以通过docke…

Citespace软件基础应用

CiteSpace软件是Citation Space的简称&#xff0c;可以译为“引文空间”&#xff0c;由美国德雷赛尔大学计算机与情报学教授陈超美博士基于Java语言开发、基于引文分析理论的信息可视化软件。 它是一款分析科学文献中蕴含的潜在知识&#xff0c;并在科学计量学、数据和信息可视…

【Git原理与使用】-- 初步认识

目录 Git版本控制器的引入 版本控制器 Git安装&#xff08;已安装可以跳过&#xff09; Linux-centos Linux-ubuntu Git基本操作 创建Git本地仓库 配置 Git 认识工作区、暂存区、版本库 工作区、版本库 stage暂存区 工作区内容使用Git管理 Git版本控制器的引入 #&…

4端到端协议-4.3【实验】【计算机网络】

4端到端协议-4.3【实验】【计算机网络】 前言推荐4端到端协议4.2 TCP协议流捕获与TCP协议分析4.3 TCP连接管理实验目的实验内容及实验环境实验原理TCP连接管理的三个阶段TCP的连接建立TCP连接建立时的各状态TCP的数据传输TCP 的连接释放为什么A 必须等待 2MSL 的时间&#xff1…

京东到家机器学习平台建设

目录 前言 机器学习平台总体架构 模型训练平台 特征模型管理平台 在线模型预测服务 算法应用实践 总结和展望 1. 前言 京东到家作为行业领先的即时零售平台&#xff0c;一直把为消费者提供快捷便利高效高质量的即时零售服务作为自己的责任。到家算法团队作为支持京东到家各个…

Linux服务第一章:web基础与http协议

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、DNS与域名1.什么是域名&#xff1f;2.域名注册2.1阿里云域名申请流程 3.DNS解析 二、网页的概念1.网页&#xff08;HTTP/HTTPS&#xff09;的基本概念2.HTML概述…

【python】flask+pymysql 实现Web端操作数据库!

Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug &#xff0c;模板引擎则使用 Jinja2 。Flask使用 BSD 授权。 Flask也被称为 “microframework” &#xff0c;因为它使用简单的核心&#xff0c;用 extension 增加其他功能。Flask没有默认使用…

Modbus协议基于modscan 的设备数据收发过程模拟

Modbus协议基于modscan 的设备数据收发过程模拟 一、基本介绍二、工具使用说明2.1 Modsim32的使用 - 模拟从设备 - 生成设备数据2.1.1 新建虚拟设备 - modsim文件2.1.2 打开虚拟设备 - modsim文件2.1.3 连接设置2.1.3.1 modbus /tcp连接2.1.3.2 COM连接 2.1.4 配置 - 设置数据自…

python 加速(1)

文章目录 简单步骤像Python一样做torch 的一切安装Cmake安装 Torch &#xff08;GPU&#xff09;CMakeLists.txt试用小样设置 CLion 环境 Cuda配置VS C 环境建上手的文件step1: interpolation.cppstep2: interpolation_kernel.custep3: include/ utils.hstep4: setup.pystep5: …

FPGA_学习_08_有限状态机

状态机是FPGA编程必学内容之一&#xff0c;因为状态机在项目用的特别多。 那为什么状态机这么重要呢&#xff1f; 在写这篇blog之前&#xff0c;搜到CSDN一位大佬的博客&#xff0c;有一句话令我醍醐灌顶&#xff1a; “FPGA不同于CPU的一点特点就是CPU是顺序执行的&#xff0c…