STM框架之按键扫描新思路
STM框架之按键扫描新思路
- 引入
- 代码展示
- 思路分析
我们学习了定时器实现毫秒级/秒级任务框架,这期我们基于任务框架学习按键扫描新思路。
引入
在按键扫描的过程中,最重要的一步就是按键消抖,解决的方法最简单粗暴的就是先扫描一次按键状态,判断按键按下后,延时,再次判断按键状态。就像这样:
if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //判断按键0是否按下{HAL_Delay(20); //延时消抖 if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //再次判断按键0是否按下 {while(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) //等待按键0松开{}Key_Num = 1; //赋值按键值}}
这是最笨的方法,当然还可以
多次调用按键扫描函数,当按键按下的时候开始计数/计时。当数字/时间到达一定值时,判断按键按下,当有一次扫描到按键为按下时,计数/计时清零,代码如下:
int Key_Scan(void)
{int Key_Num = 0; //定义按键值int temp = 0; //定义临时变量static int Key0_Count=0,Key1_Count=0,Key2_Count=0,WKUP_Count=0 ; //定义不同按键的按键值if(1-HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)) temp = 1; //当某一个按键按下是,临时变量值为对应的按键值加一if(1-HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)) temp = 2; //加一是因为temp为0时无法进入switch中,所以案件之都加一if(1-HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)) temp = 3; //if(HAL_GPIO_ReadPin(WKUP_GPIO_Port,WKUP_Pin)) temp = 4;switch(temp){case 1: Key0_Count++; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ; //通过temp变量的值,将对应按键计数值加一break; //同时将其他按键计数值清零,case 2: Key1_Count++; Key0_Count=0;Key2_Count=0;WKUP_Count=0 ; break;case 3: Key2_Count++; Key0_Count=0;Key1_Count=0;WKUP_Count=0 ;break;case 4: WKUP_Count++; Key0_Count=0;Key1_Count=0;Key2_Count=0 ;break;default: Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;break;}if(Key0_Count >= 500) //当temp到达一定值时,则可判断按键按下{Key_Num = 1; //按键按下,将所有的按键计数值清零Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;}if(Key1_Count >= 500){Key_Num = 2;Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;}if(Key2_Count >= 500){Key_Num = 3;Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;}if(WKUP_Count >= 500){Key_Num = 4;Key0_Count=0; Key1_Count=0;Key2_Count=0;WKUP_Count=0 ;}return Key_Num; //返回按键值,当没有按键按下,或者存在抖动时,返回值为0
这种方法相对于第一种,逻辑上更严谨,但是不好控制计数值。并且多次进入函数,占用大量CPU资源,所以我们引入第三种方法,规定时间间隔,进行扫描,通过数组判断结果。代码如下:
代码展示
- main.c
/* USER CODE BEGIN PFP */
void Proc2msTask(void) //2ms任务
{static uint8_t i=0;if(Get_2ms_Flag() == 1) //获取2ms标志位{Clear_2ms_Flag(); //清除2ms标志位//2mstask codei++;if(i==4) //1s扫描一处按键状态{i=0;Key_One_Scan(Key_Name_Key0,Key0_Up_Task,Key0_Down_Task); //扫描Key0状态Key_One_Scan(Key_Name_Key1,Key1_Up_Task,Key1_Down_Task); //扫描Key1状态Key_One_Scan(Key_Name_Key2,Key2_Up_Task,Key2_Down_Task); //扫描Key2状态Key_One_Scan(Key_Name_WKUP,WWKUP_Up_Task,WKUP_Down_Task); //扫描WKUP状态}}
}void Proc1sTask(void) //1s任务
{if(Get_1s_Flag() == 1) //获取1s标志位{Clear_1s_Flag(); //清除1s标志位//1stask code
// HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);}
}
/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 *//* USER CODE END 0 *//*** @brief The application entry point.* @retval int*/
int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();MX_TIM10_Init();/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim10);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){Proc2msTask(); //调用2ms任务Proc1sTask(); //调用1s任务/* USER CODE END WHILE *//* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}
- Key.c
/* USER CODE BEGIN 2 */
void Key0_Down_Task(void)
{HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key0_Up_Task(void)
{HAL_GPIO_WritePin(LED0_GPIO_Port,LED0_Pin,GPIO_PIN_SET); //松开关闭
}
void Key1_Down_Task(void)
{HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key1_Up_Task(void)
{HAL_GPIO_WritePin(LED1_GPIO_Port,LED1_Pin,GPIO_PIN_SET); //松开关闭
}
void Key2_Down_Task(void)
{HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_RESET); //按下亮灯
}
void Key2_Up_Task(void)
{HAL_GPIO_WritePin(LED2_GPIO_Port,LED2_Pin,GPIO_PIN_SET); //松开关闭
}
void WKUP_Down_Task(void)
{HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_RESET); //按下亮灯
}
void WWKUP_Up_Task(void)
{HAL_GPIO_WritePin(LED3_GPIO_Port,LED3_Pin,GPIO_PIN_SET); //松开关闭
}void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void))
{static uint8_t Key_Val[Key_Name_Max]; //按键值的存放位置static uint8_t Key_Flag[Key_Name_Max]; //KEY0~2为0时表示按下,为1表示松开,WKUP反之Key_Val[KeyName] = Key_Val[KeyName] <<1; //每次扫描完,将上一次扫描的结果左移保存switch(KeyName){case Key_Name_Key0: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)); //读取Key0按键值break;case Key_Name_Key1: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)); //读取Key1按键值break;case Key_Name_Key2: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin)); //读取Key2按键值break;case Key_Name_WKUP: Key_Val[KeyName] = Key_Val[KeyName] | (HAL_GPIO_ReadPin(WKUP_GPIO_Port, WKUP_Pin)); //读取WKUP按键值break; default:break;}if(KeyName == Key_Name_WKUP) //WKUP的电路图与其他按键不同,所以需要特殊处理{//WKUP特殊情况//当按键标志为1(松开)是,判断是否按下,WKUP按下时为0xffif(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 1){(*OnKeyOneDown)();Key_Flag[KeyName] = 0;}//当按键标志位为0(按下),判断按键是否松开,WKUP松开时为0x00if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 0){(*OnKeyOneUp)();Key_Flag[KeyName] = 1;} }else //Key0~2按键逻辑判断{//Key0~2常规判断//当按键标志为1(松开)是,判断是否按下if(Key_Val[KeyName] == 0x00 && Key_Flag[KeyName] == 1){(*OnKeyOneDown)();Key_Flag[KeyName] = 0;}//当按键标志位为0(按下),判断按键是否松开if(Key_Val[KeyName] == 0xff && Key_Flag[KeyName] == 0){(*OnKeyOneUp)();Key_Flag[KeyName] = 1;} }}
/* USER CODE END 2 */
- Key.h
/* Includes ------------------------------------------------------------------*/
#include "main.h"/* USER CODE BEGIN Includes *//* USER CODE END Includes *//* USER CODE BEGIN Private defines */
typedef enum{Key_Name_Key0 = 0,Key_Name_Key1,Key_Name_Key2,Key_Name_WKUP,Key_Name_Max}EnumKeyOneName;/* USER CODE END Private defines */void MX_GPIO_Init(void);/* USER CODE BEGIN Prototypes */
void Key0_Down_Task(void);void Key0_Up_Task(void);void Key1_Down_Task(void);void Key1_Up_Task(void);void Key2_Down_Task(void);void Key2_Up_Task(void);void WKUP_Down_Task(void);void WWKUP_Up_Task(void);void Key_One_Scan(uint8_t KeyName ,void(*OnKeyOneUp)(void), void(*OnKeyOneDown)(void));
/* USER CODE END Prototypes */#ifdef __cplusplus
}
#endif
#endif /*__ GPIO_H__ */
思路分析
按键按下的过程中,难免会有抖动,但是抖动的时间通常在10~20ms之间,而按键按下的过程往往会持续100ms以上,所以我们可以每10ms扫描一次按键状态,如果连续八次都是按下,则认为按键已经按下,想要的执行认为即可。反之,如果八次都是松开,则认为按键松开了,也可以执行按键松开的相关任务。
具体细节,大家看代码吧,这个只是大体思路,代码还有很多精彩的地方。
相关文章:
STM框架之按键扫描新思路
STM框架之按键扫描新思路 引入代码展示思路分析 我们学习了定时器实现毫秒级/秒级任务框架,这期我们基于任务框架学习按键扫描新思路。 引入 在按键扫描的过程中,最重要的一步就是按键消抖,解决的方法最简单粗暴的就是先扫描一次按键状态&am…...
Linux服务器挂载另一台服务器的文件夹(mount)
我们实际应用中,会常遇到多个Linux服务器之间需要频繁共享文件,或者是一台服务器需要使用另一台服务器的闲置磁盘空间。最方便的方法就是挂载另一台linux文件夹(文件服务器),通俗理解为:当前服务器远程连接…...
剑指offer --- 用两个栈实现队列的先进先出特性
目录 前言 一、读懂题目 二、思路分析 三、代码呈现 总结 前言 当我们需要实现队列的先进先出特性时,可以使用栈来模拟队列的行为。本文将介绍如何使用两个栈来实现队列,并给出具体的思路和代码实现。 一、读懂题目 题目:用两个栈实现一…...
流媒体协议
◆ RTP(Real-time Transport Protocol),实时传输协议。 ◆ RTCP(Real-time Transport Control Protocol),实时传输控制协议。 ◆ RTSP(Real Time Streaming Protocol),实时流协议。 ◆ RTMP(Real Time Messaging Protocol),实时…...
ClickHouse的分片和副本
1.副本 副本的目的主要是保障数据的高可用性,即使一台ClickHouse节点宕机,那么也可以从其他服务器获得相同的数据。 Data Replication | ClickHouse Docs 1.1 副本写入流程 1.2 配置步骤 (1)启动zookeeper集群 (2&…...
C语言编程陷阱(五)
陷阱21:不要使用逗号运算符代替分号 C语言中,我们可以使用分号来结束一个语句,比如a = b;,这样可以让编译器知道语句的边界,以及执行的顺序。但是,如果我们想要在一个语句中执行多个表达式,就可以使用逗号运算符,比如a = (b = c, c + 1);,这样可以让编译器按照从左到右…...
chardet检测文件编码,使用生成器逐行读取文件
detect_encoding 函数使用 chardet 来检测文件的编码。然后,在 process_large_file 函数中,根据检测到的编码方式打开文件。这样,你就能够更准确地处理不同编码的文件。 import chardetdef detect_encoding(file_path):with open(file_path,…...
html所有标签和DOCTYPE的总结
一、DOCTYPE 1. 意义 DOCTYPE是一种标准通用标记语言的文档类型声明,告诉标准通用标记语言解析器它应该使用什么样的文档类型定义来解析文档。 2. 应用 现在,我们需要告诉标准通用标记语言解析器,我们接下去要用html来编写代码了。 <…...
2023年11月15号期中测验判断题(Java)
1-1 局部变量可以与成员变量重名。 正确答案:T 解释: 局部变量可以和成员变量重名,通常,为了区分局部变量和成员变量,会使用this关键字(C称this指针,python是self关键字)来特别声…...
基于 selenium 实现网站图片采集
写在前面 有小伙伴选题,简单整理理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对…...
vue3相关内容
ref声明/赋值 操作基本类型数据 string number // 引入方法 import {ref} from vue // 声明变量 const name ref(A) // 修改值 name.value Breactive声明/赋值 操作引用类型数据 array object proxy不能直接赋值,会破坏响应式对象 // 引入方法 import {reacti…...
AWTK实现汽车仪表Cluster/DashBoard嵌入式GUI开发(七):FreeRTOS移植
前言: 一般的GUI工程都需要一个操作系统,可能是linux,重量级的,也可能是FreeRTOS,轻量级的。 一句话理解那就是工程就是FreeRTOS task任务的集合。 一个main函数可以看到大框架: 很显然,除了第一个是硬件配置的初始化,中间最重要的部分就是要创建任务,把AWTK的应用…...
《洛谷深入浅出进阶篇》P1995 程序自动分析——并查集,离散化
上链接:P1955 [NOI2015] 程序自动分析 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)https://www.luogu.com.cn/problem/P1955 上题干: 首先给你一个整数t,代表t次操作。 每一次操作包含以下内容: 1.给你一个整数n,让…...
基于单片机的自动循迹小车(论文+源码)
1.系统设计 此次基于单片机的自动循迹小车的设计系统,结合循迹模块来共同完成本次设计,实现小车的循迹功能,其其整体框架如图2.1所示。其中,采用STC89C52单片机来作为核心控制器,负责将各个传感器等模块链接起来&…...
linux系统中安装python到指定目录
Linux系统中安装python 下载Python源码包 根据服务器系统和需要的Python版本,在Python官网下载对应的Python源码包。 安装依赖(需要权限) yum install gcc gcc-c patch libffi-devel python-devel zlib-devel bzip2-devel openssl-devel…...
分布式事务 - seata安装
分布式事务 - seata 一、本地事务与分布式事务 1.1、本地事务 本地事务,也就是传统的单机事务。在传统数据库事务中,必须要满足四个原则(ACID)。 1.2、分布式事务 分布式事务,就是指不是在单个服务或单个数据库架构…...
CentOS to 浪潮信息 KeyarchOS 迁移体验与优化建议
浪潮信息KeyarchOS简介 KeyarchOS即云峦操作系统(简称KOS), 是浪潮信息研发的一款面向政企、金融等企业级用户的 Linux 服务器操作系统。它基于Linux内核、龙蜥等开源技术,支持x86、ARM 等主流架构处理器,其稳定性、安全性、兼容性和性能等核心能力均已…...
Go解析soap数据和修改其中数据
一、解析soap数据 package main import ("fmt" "encoding/xml" ) type Envelope struct { XMLName xml.Name Header Header } type Header struct { XMLName xml.Name xml:"Header" Security Security xml:"Security" } type Secu…...
LeetCode98. Validate Binary Search Tree
文章目录 一、题目二、题解 一、题目 Given the root of a binary tree, determine if it is a valid binary search tree (BST). A valid BST is defined as follows: The left subtree of a node contains only nodes with keys less than the node’s key. The right sub…...
【LeetCode】206. 反转链表
206. 反转链表 难度:简单 题目 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2: 输入:head [1,2] 输…...
从SuperGlue到LoFTR:无检测器特征匹配是如何“卷”出来的?技术演进深度解读
从SuperGlue到LoFTR:无检测器特征匹配的技术革命与范式迁移 在计算机视觉领域,特征匹配一直是三维重建、SLAM、图像配准等任务的核心基础。传统方法如SIFT、ORB等基于手工设计的特征检测与描述算法,在过去二十年里主导了这一领域。然而&#…...
当你能证明你的代码能带来流量时,你就永远不会被视为“垃圾”。
在商业世界里,代码本身没有价值,代码产生的结果才有价值。 如果你写的代码逻辑完美、架构优雅、注释清晰,但用户不用、业务不增长,那它在老板眼里就是“成本”,甚至是“垃圾”。如果你写的代码哪怕有些粗糙、用了“笨办…...
OpenAI推安全漏洞赏金计划,应对AI潜在风险
OpenAI启动公共安全漏洞赏金计划,剑指AI潜在风险品玩3月26日消息,OpenAI正式推出公共安全漏洞赏金计划,此计划意在识别并修复其产品中潜在的AI滥用与安全风险。该计划是对现有安全漏洞赏金项目的补充,专门接纳那些虽不构成传统技术…...
CentOS7 下 Go 多版本管理与无缝升级指南
1. 为什么需要Go多版本管理? 在CentOS7系统上开发Go项目时,经常会遇到这样的尴尬:新项目需要用最新版Go的特性,而老项目必须跑在特定旧版本上才能兼容。我去年就踩过这个坑——用Go 1.21写完的微服务,部署到生产环境发…...
3大核心步骤打造专属翻译引擎:Zotero PDF Translate高级扩展指南
3大核心步骤打造专属翻译引擎:Zotero PDF Translate高级扩展指南 【免费下载链接】zotero-pdf-translate 支持将PDF、EPub、网页内容、元数据、注释和笔记翻译为目标语言,并且兼容20多种翻译服务。 项目地址: https://gitcode.com/gh_mirrors/zo/zoter…...
从教程到实战:在快马平台部署企业级openclaw数据采集与监控系统
今天想和大家分享一个实战经验:如何把openclaw这个数据采集工具从教程变成真正的企业级应用。最近我在InsCode(快马)平台上完整走通了从开发到部署的全流程,整个过程比想象中顺畅很多。 任务调度器的实现 首先需要解决的是任务调度问题。传统教程里可能…...
无人机控制中的模糊控制:一维与二维模糊控制及其实现要点
无人机 控制方面 模糊控制 有一维模糊和二维模糊两种,文字说明资料已遗失,数学模型可以根据仿真图推导,直接运维simulink会报错,是因为没有导入模糊规则,在运行simulink之前需要在命令窗口输入workreadfis work.fis ,这…...
LTI系统设计避坑指南:因果性与稳定性在实际工程中的5个关键检查点
LTI系统设计避坑指南:因果性与稳定性在实际工程中的5个关键检查点 在数字信号处理领域,线性时不变(LTI)系统的设计是工程师日常工作的核心。然而,理论推导与工程实践之间往往存在一道鸿沟——许多在数学上完美的系统模…...
OpenClaw异常处理手册:百川2-13B任务失败排查全攻略
OpenClaw异常处理手册:百川2-13B任务失败排查全攻略 1. 为什么需要这份手册 上周我尝试用OpenClaw百川2-13B模型自动处理日报生成任务时,连续三天凌晨任务失败。每次起床看到控制台的红色错误提示,都要花半小时翻日志找原因。最崩溃的是&am…...
Local AI MusicGen教育应用:帮助学生理解音乐情绪表达方式
Local AI MusicGen教育应用:帮助学生理解音乐情绪表达方式 1. 引言:当AI成为音乐老师 想象一下,你是一位音乐老师,正在给学生讲解“悲伤”这种情绪在音乐中是如何表达的。传统的教学方式可能是播放一段肖邦的夜曲,或…...
