当前位置: 首页 > news >正文

STM32框架之按键扫描新思路

STM32框架之按键扫描新思路

  • 引入
  • 代码展示
  • 思路分析

我们学习了定时器实现毫秒级/秒级任务框架,这期我们基于任务框架学习按键扫描新思路。

引入

在按键扫描的过程中,最重要的一步就是按键消抖,解决的方法最简单粗暴的就是先扫描一次按键状态,判断按键按下后,延时,再次判断按键状态。就像这样:

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扫描一次按键状态,如果连续八次都是按下,则认为按键已经按下,想要的执行认为即可。反之,如果八次都是松开,则认为按键松开了,也可以执行按键松开的相关任务。

具体细节,大家看代码吧,这个只是大体思路,代码还有很多精彩的地方。

相关文章:

STM32框架之按键扫描新思路

STM32框架之按键扫描新思路 引入代码展示思路分析 我们学习了定时器实现毫秒级/秒级任务框架&#xff0c;这期我们基于任务框架学习按键扫描新思路。 引入 在按键扫描的过程中&#xff0c;最重要的一步就是按键消抖&#xff0c;解决的方法最简单粗暴的就是先扫描一次按键状态&…...

完美解决k8s master节点无法ping node节点中的IP或Service NodePort的IP

1、问题一 使用搭建好了K8S集群&#xff0c;先是node节点加入k8s集群时&#xff0c;用的内网IP&#xff0c;导致master节点无法操作node节点中的pod&#xff08;这里的不能操作&#xff0c;指定是无法查看node节点中pod的日志、启动描述、无法进入pod内部&#xff0c;即 kubec…...

弗洛伊德算法(C++)

目录 介绍&#xff1a; 代码&#xff1a; 结果&#xff1a; 介绍&#xff1a; 弗洛伊德算法&#xff08;Floyd algorithm&#xff09;也称为Floyd-Warshall算法&#xff0c;是一种用于求解所有节点对之间的最短路径的动态规划算法。它使用了一个二维数组来存储所有节点…...

相对定位、绝对定位、固定定位、绝对定位堆叠顺序

相对定位&#xff1a;相对自己本身进行偏移 CSS语法&#xff1a; position: relative;/*相对自己进行定位*/ top: 10px;/*距离上边*/ left: 10px;/*距离左边*/ 演示图&#xff1a; 绝对定位&#xff1a;默认以浏览器进行定位。如果想依照父盒子定位&#xff0c;需要在父盒子…...

px4+vio实现无人机室内定位

文章主要讲述px4 如何利用vins_fusion里程计数据实现在室内定位功能。 文章基于以下软、硬件展开。 硬件软件机载电脑&#xff1a; Intel NUC系统&#xff1a;Ubuntu 20.04相机&#xff1a; Intel Realsense D435iros&#xff1a;noetic飞控&#xff1a;Pixhawk 2.4.8固件&am…...

享元模式 rust和java的实现

文章目录 享元模式介绍实现javarust实现代码 rust仓库rust仓库 享元模式 享元模式&#xff08;Flyweight Pattern&#xff09;主要用于减少创建对象的数量&#xff0c;以减少内存占用和提高性能。这种类型的设计模式属于结构型模式&#xff0c;它提供了减少对象数量从而改善应…...

XmlElement注解在Java的数组属性上,以产生多个相同的XML元素

例如&#xff0c;下面这段XML数据&#xff0c;有多个data元素&#xff0c;并且它们级别相同: <?xml version"1.0" encoding"UTF-8"?><request><reqtype>05</reqtype><secret>test</secret><body><userid&…...

SQLServer 数字加千分位 用FORMAT函数强转不管多大位数

问题 CONVERT ( money, CONVERT ( money, CAST ( round( FTP_AMOUNT, 2 ) AS NUMERIC ( 20, 2 ) ) ) 1 ) AS FTP_AMOUNT用的money函数 结果空间不足&#xff0c;无法将 money 值转换为 varchar。 可以强转 select FORMAT(CAST ( round( ‘-8926143870680.62000000’, 2 ) AS N…...

说说mvc和mvvm的区别和联系

mvvm 与mvc mvvm 与mvcmvc和mvvm的区别和联系 举例说明mvvm与mvc MVC是一种用于构建应用程序的架构模式&#xff0c;它也将应用程序的逻辑和界面分离。它由三个主要组件组成&#xff1a; 模型&#xff08;Model&#xff09;&#xff1a;表示应用程序的数据和业务逻辑。视图&a…...

linux rsyslog综合实战2

本次我们通过rsyslog服务将A节点服务器上的两个(E.g:多个日志也可以)日志(Path:/var/log/245-1.log、245-2.log)实时同步到B节点服务器目录下(Path:/opt/rsyslog/245) 1.rsyslog架构 2.环境信息 环境信息 HostnameIpAddressOS versionModuleNotersyslog1192.168.10.245CentOS…...

AcWing 4. 多重背包问题 I 学习笔记

有 N&#xfffd; 种物品和一个容量是 V&#xfffd; 的背包。 第 i&#xfffd; 种物品最多有 si&#xfffd;&#xfffd; 件&#xff0c;每件体积是 vi&#xfffd;&#xfffd;&#xff0c;价值是 wi&#xfffd;&#xfffd;。 求解将哪些物品装入背包&#xff0c;可使物…...

解决selenium使用chrome下载文件(如pdf)时,反而打开浏览器的预览界面

文章目录 解决方法完整的配置 解决方法 在初始化浏览器的时候&#xff0c;添加以下配置即可&#xff1a; option webdriver.ChromeOptions()prefs {"profile.managed_default_content_settings.images": 2, # 禁止加载图片# permissions.default.stylesheet: 2, …...

2024年山东省职业院校技能大赛中职组“网络安全”赛项竞赛试题-C

2024年山东省职业院校技能大赛中职组 “网络安全”赛项竞赛试题-C 一、竞赛时间 总计&#xff1a;360分钟 二、竞赛阶段 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 A、B模块 A-1 登录安全加固 180分钟 200分 A-2 本地安全策略设置 A-3 流量完整性保护 A-4 …...

基于Python实现用于实时监控和分析 MySQL 服务器的性能指标和相关信息工具源码

MySQL命令行监控工具 - mysqlstat 介绍 mysqlstat 是一个命令行工具&#xff0c;用于实时监控和分析 MySQL 服务器的性能指标和相关信息。 它可以帮助 DBA&#xff08;数据库管理员&#xff09;和开发人员定位和解决数据库性能问题。 以下是 mysqlstat 工具的主要功能&#…...

Android 10-13鼠标右键返回功能适配

Android 10-13鼠标右键返回功能适配 文章目录 Android 10-13鼠标右键返回功能适配一、前言二、鼠标右键适配修改1、Android 10 以及更低版本2、Android11 或者更高版本三、总结1、鼠标右键返回功能修改主要代码2、标右键返回修改代码系统源码搜索3、其他 一、前言 Android 原生…...

51单片机/STM32F103/STM32F407学习1_点亮LED灯

目录&#xff1a; 基础知识单片机从0实现单片机GPIO介绍 参考连接&#xff1a; 野火霸天虎教程 https://doc.embedfire.com/products/link/zh/latest/mcu/stm32/ebf_stm32f407_batianhu_v1_v2/download/stm32f407_batianhu_v1_v2.html x.1 基础知识 x.1.1 指针中的取地址&a…...

(Transfer Learning)迁移学习在IMDB上训练情感分析模型

1. 背景 有些场景下&#xff0c;开始的时候数据量很小&#xff0c;如果我们用一个几千条数据训练一个全新的深度机器学习的文本分类模型&#xff0c;效果不会很好。这个时候你有两种选择&#xff0c;1.用传统的机器学习训练&#xff0c;2.利用迁移学习在一个预训练的模型上训练…...

蓝桥杯每日一题2023.11.20

题目描述 “蓝桥杯”练习系统 (lanqiao.cn) 题目分析 方法一&#xff1a;暴力枚举&#xff0c;如果说数字不在正确的位置上也就意味着这个数必须要改变&#xff0c;进行改变记录即可 #include<bits/stdc.h> using namespace std; const int N 2e5 10; int n, a[N], …...

【迅搜02】究竟什么是搜索引擎?正式介绍XunSearch

究竟什么是搜索引擎&#xff1f;正式介绍XunSearch 啥&#xff1f;还要单独讲一下啥是搜索引擎&#xff1f;不就是百度、Google嘛&#xff0c;这玩意天天用&#xff0c;还轮的到你来说&#xff1f; 额&#xff0c;好吧&#xff0c;虽然大家天天都在用&#xff0c;但是我发现&am…...

【Sql】sql server还原数据库的时候,提示:因为数据库正在使用,所以无法获得对数据库的独占访问权。

【问题描述】 sql server 还数据库的时候&#xff0c;提示失败。 点击左下角进度位置&#xff0c;可以得到详细信息&#xff1a; 因为数据库正在使用&#xff0c;所以无法获得对数据库的独占访问权。 【解决方法】 针对数据库先后执行下述语句&#xff0c;获得独占访问权后&a…...

3分钟掌握AsrTools:零配置语音转文字工具终极指南

3分钟掌握AsrTools&#xff1a;零配置语音转文字工具终极指南 【免费下载链接】AsrTools ✨ AsrTools: Smart Voice-to-Text Tool | Efficient Batch Processing | User-Friendly Interface | No GPU Required | Supports SRT/TXT Output | Turn your audio into accurate text…...

Linux下构建本地优先的语音编码助手:voice2agent项目实战

1. 项目概述&#xff1a;解放双手的智能语音编码伴侣作为一名长期与代码和终端打交道的开发者&#xff0c;我深知在构思复杂逻辑或向AI编程助手&#xff08;如Claude Code、Codex&#xff09;描述需求时&#xff0c;反复敲击键盘的疲惫感。市面上的语音输入方案要么过于笨重&am…...

科技巨头平台战争:生态战略、技术驱动与从业者实战指南

1. 平台战争的核心逻辑&#xff1a;从“卖产品”到“圈用户” 在科技行业摸爬滚打了十几年&#xff0c;我亲眼见证了竞争焦点的几次重大转移。早期大家拼的是硬件参数和软件功能&#xff0c;后来开始讲“用户体验”&#xff0c;而最近这十年&#xff0c;整个战场的核心词汇变成…...

Windows平台OpenClaw 2.6.6安装配置完整教程与避坑全攻略

OpenClaw 2.6.6 Windows 一键部署教程&#xff5c;10分钟搭建本地AI智能助手 OpenClaw&#xff08;小龙虾&#xff09;是一款可在 Windows 平台本地运行的 AI 智能操作工具&#xff0c;能够通过自然语言指令完成文件管理、办公自动化、浏览器操控、数据处理等各类电脑任务。 …...

智能矩阵大灯核心技术解析:从图形MCU到百万像素LED驱动的工程实践

1. 项目概述&#xff1a;从“照亮”到“沟通”的智能车灯革命如果你和我一样&#xff0c;在汽车电子行业摸爬滚打了十几年&#xff0c;就会深刻感受到&#xff0c;汽车安全的演进史&#xff0c;本质上是一部感知与交互技术的进化史。从最初的被动安全&#xff08;安全带、气囊&…...

嵌入式电机控制与机器人系统设计:核心矛盾、架构选型与工程实践

1. 项目概述&#xff1a;一次嵌入式设计思想的深度巡游如果你在工业自动化、电机控制或者机器人领域摸爬滚打过&#xff0c;那你一定对“设计选择”这个词深有感触。它远不止是在原理图上选一个更快的MCU&#xff0c;或者画PCB时多用几层板那么简单。它关乎整个系统的稳定性、成…...

用OpenCV和Python手把手实现Meanshift目标跟踪(附完整代码与避坑指南)

用OpenCV和Python手把手实现Meanshift目标跟踪&#xff08;附完整代码与避坑指南&#xff09; 在计算机视觉领域&#xff0c;目标跟踪是一个基础而重要的任务。想象一下这样的场景&#xff1a;你正在开发一个智能监控系统&#xff0c;需要持续追踪画面中的特定行人&#xff1b;…...

从深度图到3D点云:用奥比中光摄像头和OpenNI玩转Python三维视觉(实战项目)

从深度图到3D点云&#xff1a;用奥比中光摄像头和OpenNI玩转Python三维视觉 当RGBD摄像头捕捉到的深度数据在屏幕上跳动时&#xff0c;那些数字背后隐藏着一个完整的三维世界。想象一下&#xff0c;你不仅能"看到"物体的平面图像&#xff0c;还能精确感知每个像素点…...

AI编码工具实战指南:从选型到集成,提升开发效率

1. 项目概述&#xff1a;一份AI编码工具的实战指南如果你和我一样&#xff0c;每天大部分时间都在和代码编辑器打交道&#xff0c;那你肯定也感受到了这两年AI工具对开发流程的冲击。从最初只是好奇地试用GitHub Copilot&#xff0c;到现在几乎离不开Cursor、Windsurf这类“AI原…...

3分钟实现B站视频转文字:bili2text技术架构与实现原理深度解析

3分钟实现B站视频转文字&#xff1a;bili2text技术架构与实现原理深度解析 【免费下载链接】bili2text Bilibili视频转文字&#xff0c;一步到位&#xff0c;输入链接即可使用 项目地址: https://gitcode.com/gh_mirrors/bi/bili2text B站视频转文字工具bili2text是一个…...