STM32独立按键扫描,支持同时按下、长按、快速键值

news2024/10/5 14:21:09

背景

有个项目在实际应用中,采用8个独立按键,每个按键都赋予不同功能,实际使用过程中很多时候都是需要比较特殊的按键操作,例如:长按10s按键、长按5s按键,或者长按需要有快速按键值的反馈,这个情况就类似,我们需要快速增加一个设定值时,按住加号+按键不松手,这个按键值就一直增大。

原理图设计

如下图所示,采用STM32F103C8T6,进行按键设计,这里使用按键较多

KEY_TimeSet            ---->              时间设定功能,单击进去时间设定

KEY_Program/Back  ---->              返回按键,菜单选择

KEY_ON/OFF           ---->               启动按键

KEY_Temp+             ---->               设定温度增加按键,支持快速按键功能

KEY_Temp-              ---->               设定温度减少按键,支持快速按键功能

KEY_Threshold+     ---->                参数调整按键,支持快速按键功能

KEY_Threshold-      ---->               参数调整按键,支持快速按键功能

KEY_AutoTuning     ---->               单击功能

软件设计 

软件采用stm32cubemx,如下图所示,时钟选用外部时钟倍频到72Mhz

 对应原理图STM32对应管脚应配置为输入模式

这里就不再贴图了,比较简单,配置对应IO输入模式即可。

这里使用FREERTOS功能,可以通过如下配置,添加自己的任务或者信号量,队列等。

使用FREERTOS的目的,是将按键扫描的结果的有效值,放进队列中,供对应的按键处理任务进行使用。

配置按键扫描定时器,20ms扫描一次按键

实际代码如下

按键扫描中断函数,进行按键扫描,并将有效键值放入到队列


void TIM2_IRQHandler(void)  
{  
	uint8_t key;
	uint16_t msg;
	portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;

    TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	
	//按键扫描函数
	//printf("RK\r\n");//test tim2 20ms 中断
	key = ReadKey();
	if(key != NO_KEY)
	{		
		msg = MSG_KEY_DOWN | key;
		xQueueSendFromISR( segscanQueue, &msg, &xHigherPriorityTaskWoken );
	}
	portEND_SWITCHING_ISR( xHigherPriorityTaskWoken );

}

读取按键流程如下



/******************************************************************************
* 函数名称: KeyPro()
* 功能描述: 按键扫描
* 输入参数: 
* 输出参数: 
* 返 回 值: 无
* 其它说明: 
* 修改日期      版本号      修改人     修改内容
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
* 2013/02/22    V1.0.0.0       
******************************************************************************/
static unsigned char ReadKey(void)
{
	static unsigned char KeyState = 0, KeyOldValue = NO_KEY;
	static unsigned char KeyOldReturn = 0, KeyTimeCnt = 0;
	static unsigned int no_key_times = 0;
	unsigned char KeyReturn = NO_KEY, KeyCurValue = NO_KEY;
	KeyCurValue = (KEY8_VAL << 7) | (KEY7_VAL << 6) | (KEY6_VAL << 5) | (KEY5_VAL << 4) | (KEY4_VAL << 3) | (KEY3_VAL << 2) | (KEY2_VAL<<1) | KEY1_VAL; //读取6个按键值

	switch(KeyState)
	{
		case 0:
		{
            if(NO_KEY != KeyCurValue)        //检测到有键按下
            {
               KeyOldValue =  KeyCurValue;   //保存键值
               KeyState++;                   //转到消抖确认状态 
               KeyTimeCnt = 0;
			   no_key_times = 0;
            }
			else
			{
				no_key_times++;
				if(no_key_times >= 500)//500*20ms=10s
				{
					no_key_times = 0;
					KeyReturn = NO_KEY_10S_DOWN;
					KeyOldReturn = NO_KEY;
				}
			}
			break;
		}
        case 1:
		{
            if(KeyCurValue == KeyOldValue)   //和上次按键相同确认有键按下
            {
                /*switch(KeyCurValue)          //键盘编码 返回编码值
                {
					case SET_KEY_DOWN:
						KeyReturn = SET_KEY_DOWN;
					break;
					case UP_KEY_DOWN:
						KeyReturn = UP_KEY_DOWN;
					break;
					case DOWN_KEY_DOWN:
						KeyReturn = DOWN_KEY_DOWN;
					break;
					case ENTER_KEY_DOWN:
						KeyReturn = ENTER_KEY_DOWN;
					break;
					case ONOFF_KEY_DOWN:
						KeyReturn = ONOFF_KEY_DOWN;
					break;
					default:
						KeyReturn = NO_KEY;
					break;
                }*/
				KeyState++;                  //转入等待按键释放状态
				
				if(KeyCurValue == KEY7_DOWN)
				{
					KeyState = 4;//单独对时间按键进行处理
				}
				else
				{
					KeyReturn = KeyCurValue;
					KeyOldReturn = KeyReturn;
					//这里针对按键做优化处理,长按加速、和检测长按释放
					if(KeyOldReturn == KEY1_DOWN || KeyOldReturn == KEY2_DOWN)//增加快速按键值
					{
						KeyState++;	
					}
				}
            }
            else
                KeyState--;                  //两次键值不同 返回等待按键状态
            KeyTimeCnt = 0;
            KeyOldValue = KeyCurValue; 
			break;
		}
        case 2:  
		{
            if(NO_KEY == KeyCurValue)           // 按键已经释放
            {
                KeyState = 0;
                KeyOldReturn = NO_KEY; 				
            }
            else
            {

            }
			break;
		}
        case 3:
		{
            if(NO_KEY == KeyCurValue)           // 按键已经释放
            {
                KeyState = 0;
                KeyOldReturn = NO_KEY; 
            }
            else
            {
                KeyTimeCnt++;
                if(KeyTimeCnt >= 10)      
                {
                    KeyReturn = KeyOldReturn;  //增加快速键值
                    KeyTimeCnt = 0; 
                }
            }
			break;
		}
		//增加时间按键的长按、短按检测
		case 4:  
		{
			if(NO_KEY == KeyCurValue)           // 按键已经释放
			{
					KeyReturn = KEY7_DOWN;
					KeyOldReturn = KeyReturn;	
					KeyState = 0;				
			}
			else
			{
					KeyTimeCnt++;
					if(KeyTimeCnt >= 100)      
					{
							KeyReturn = KEY7_DOWN_LONG;  //检测到长按
							KeyOldReturn = KeyReturn;
						  KeyTimeCnt = 0; 
							KeyState = 5;
					}
			}
			break;
		}
		case 5:  
		{
			if(NO_KEY == KeyCurValue)           // 按键已经释放
			{
					KeyState = 0;
					KeyOldReturn = NO_KEY;	
			}
			else
			{

			}
			break;
		}
		default:
		{
			KeyState = 0;
			break;
		}
    }
    return KeyReturn;
}

按键的定义如下

这里根据原理,将按键信息写入到代码中,每次定时器中断到来都对如下管脚进行扫描,用来获取新的按键消息。

//-----------按键配置 配置-------------------------------------------------------------------

#define KEY1_VAL   			            PAin(8)//KEY_Temp+
#define KEY2_VAL  			            PBin(3)//KEY_Temp-
#define KEY3_VAL  			            PBin(15)//KEY_Threshold+
#define KEY4_VAL    			          PAin(15)//KEY_Threshold-
#define KEY5_VAL  			            PBin(14)//KEY_AutoTuning
#define KEY6_VAL   			            PAin(12)//KEY_TimeSet
#define KEY7_VAL   			            PBin(1)//
#define KEY8_VAL   			            PBin(0)


#define KEY1_DOWN                  0xFE
#define KEY1_DOWN_LONG             (KEY1_DOWN | KEY_DOWN_LONG)

#define KEY2_DOWN                  0xFD
#define KEY2_DOWN_LONG             (KEY2_DOWN | KEY_DOWN_LONG)

#define KEY3_DOWN                  0xFB
#define KEY3_DOWN_LONG             (KEY3_DOWN | KEY_DOWN_LONG)

#define KEY4_DOWN                  0xF7
#define KEY4_DOWN_LONG             (KEY4_DOWN | KEY_DOWN_LONG)

#define KEY5_DOWN                  0xEF
#define KEY5_DOWN_LONG             (KEY5_DOWN | KEY_DOWN_LONG)

#define KEY6_DOWN                  0xDF
#define KEY6_DOWN_LONG             (KEY6_DOWN | KEY_DOWN_LONG)

#define KEY7_DOWN                  0xBF
#define KEY7_DOWN_LONG             0x61

#define KEY8_DOWN                  0x7F
#define KEY8_DOWN_LONG             (KEY8_DOWN | KEY_DOWN_LONG)

#define KEY_1_4_DOWN               0xF6

#define NO_KEY_10S_DOWN            0x60
#define NO_KEY                     0XFF    

对按键消息的处理

如下代码所示,在任务中如果需要按键响应,就直接获取按键消息,如果拿到想要的按键消息,则进行按键动作处理。如下代码中如果监测到1、4按键同时按下,则进行恢复出厂设置的动作处理。

		if( xQueueReceive( pq, &Msg, 1000 ) == pdPASS )
		{
			if(MSG_KEY_DOWN == (MSG_NAME(Msg)))
			{
					key = MSG_DATA(Msg);
					if(key == KEY8_DOWN)//Esc
					{
						menu1();						
					}
					else if(key == KEY_1_4_DOWN)//回复出厂设置按键,移位按键和加按键同时按下
					{
						    //清屏
						Dispay_Clear();
						factorySetting();
					}
			}
		}

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

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

相关文章

wordcloud背景图多图形演示

文章目录 前言一、问题二、多图层1.部分重叠1.1背景图1.2词云图 2.完全重叠2.1背景图2.2词云图 3.不重叠3.1背景图3.2词云图 三、不同形状1.背景图2.词云图 四、代码总结 前言 大家好&#xff0c;我是空空star&#xff0c;本篇给大家分享一下wordcloud背景图多图形演示。 一、问…

网络安全真的那么好吗?

近几年&#xff0c;随着网络安全被列为国家安全战略的一部分&#xff0c;这个曾经细分的领域发展提速了不少&#xff0c;除了一些传统安全厂商以外&#xff0c;一些互联网大厂也都纷纷加码了在这一块的投入&#xff0c;随之而来的吸引了越来越多的新鲜血液不断涌入。 网络安全…

多源环境(QGIS\PostgreSQL\ARCGIS\MAXENT\R语言)支持下的SolVES 模型生态系统服务功能社会价值评估

生态系统服务是人类从自然界中获得的直接或间接惠益&#xff0c;可分为供给服务、文化服务、调节服务和支持服务4类&#xff0c;对提升人类福祉具有重大意义&#xff0c;且被视为连接社会与生态系统的桥梁。自从启动千年生态系统评估项目&#xff08;Millennium Ecosystem Asse…

Python自动化测试实现的思路及方法

Python自动化测试常用于Web应用、移动应用、桌面应用等的测试 Python自动化实现思路通常分为以下几步&#xff1a; 1. 确定自动化测试的范围和目标&#xff1a; 首先需要明确需要进行自动化测试的范围和目标&#xff0c;包括测试场景、测试用例、测试数据等。 2. 选择自动化测…

Kubernetes第2天

第四章 实战入门 本章节将介绍如何在kubernetes集群中部署一个nginx服务&#xff0c;并且能够对其进行访问。 Namespace ​ Namespace是kubernetes系统中的一种非常重要资源&#xff0c;它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。 ​ 默认情况下&…

机器学习——决策树原理及CART算法

问&#xff1a;CART决策树可以有多个分支结构。 答&#xff1a;错误。CART决策树每个内部节点只能有两个分支结构&#xff0c;这些分支分别对应于二进制判定的是或否。因此&#xff0c;CART决策树的每个节点将数据集分成两个较小的子集&#xff0c;其中一个子集满足某种特定条…

【系分知识】需求评审

目录 背景介绍标题目的与意义方法与流程成果展示 背景介绍 软件开发是一项复杂的工作&#xff0c;需要涉及到众多的人员和团队。在这个过程中&#xff0c;需求评审是保证项目质量和进度的关键环节。它是在项目启动之前&#xff0c;对所有的需求进行全面的评估和审查&#xff0c…

C#异步编程之数据并行及任务并行

基于Parallel.ForEach的数据并行使用 1.数据非并行 var items new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; DateTime t1 DateTime.Now; foreach (var item in items) {Console.WriteLine("数据非并行输出:{0}", item); } 2.数据并行,只要使用Parallel.ForEach P…

Unity UI -- (2) 给场景加一个标题

添加Text并将其放到正中位置 我们来增加项目的第一个UI元素&#xff1a;标题。 1. 在Hierarchy中&#xff0c;点击右键&#xff0c;选择UI > Text - TextMeshPro。如果弹出了一个TextMeshPro Importer窗口&#xff0c;选择Import TMP Essentials。TextMeshPro&#xff08;TM…

优雅地处理参数传递:Spring Boot中的技巧

目录 一&#xff1a;四种传参方式 1.1&#xff1a;在 URL 中传递参数 1.2&#xff1a;PathVariable 传递参数&#xff08;Restful 风格&#xff09; 1.3&#xff1a;在请求体中传递参数 1.4&#xff1a;在请求头中传递参数 二&#xff1a;文件上传接口测试 2.1 : test.jav…

5_推荐系统算法详解

推荐系统算法详解 主要内容常用推荐算法分类&#xff08;重点&#xff09;基于人口统计学的推荐算法用户画像 基于内容的推荐算法相似度计算 基于内容推荐系统的高层次结构特征工程数值型特征处理 类别型特征处理时间型特征处理统计型特征处理推荐系统常见反馈数据基于 UGC 的推…

【c语言】组件化打包—动态链接库dll

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c语言系列专栏&#xff1a;c语言之路重点知识整合 &#x…

Qt创建多线程的两种方法

Qt创建多线程的两种方法 [1] Qt创建多线程的两种方法1.摘要2.Qt多线程方法1 继承QThread2.1写一个继承于QThread的线程2.2 QThread的几个函数quit、exit、terminate函数2.3 正确的终止一个线程2.4 如何正确启动一个线程2.5 继承QThread的一些总结 Qt使用多线程的一些心得——2.…

什么是边缘计算盒子?边缘计算盒子可以做什么?一文带你了解边缘计算云服务器 ECS

上文&#xff0c;我们已经为大家介绍了什么是边缘计算、边缘计算的诞生、以及边缘计算与CDN之间的关系&#xff0c;感兴趣的小伙伴欢迎阅读以往文章&#xff1a; 边缘计算节点是啥&#xff1f;边缘计算与CDN有什么关系&#xff1f;一文带你了解边缘计算节点BEC&#xff08;1&am…

在线PS软件有哪些不错的推荐

许多新的UI设计合作伙伴非常关心在线ps工具的选择。现在市场上有各种各样的ps网页替代工具&#xff0c;数量众多&#xff0c;令人眼花缭乱。本文简要介绍了10个在线PS工具&#xff0c;我相信一定有一个适合你&#xff01; 1.即时设计 即时设计是一款在线 UI 设计工具&#xf…

渗透测试--5.1.Crunch创建密码字典

目录 1.crunch简介 2.常用命令 3.使用实例 &#xff08;1&#xff09;.生成字母组合 &#xff08;2&#xff09;. 生成缺位的手机号码 &#xff08;3&#xff09;.生成pass01-pass99所有组合 &#xff08;4&#xff09;.生成六位密码&#xff0c;其中前四位为pass&#xf…

docker-compose部署开发环境

docker-compose部署开发环境 部署nginx欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义…

Java内部类与匿名类

内部类 定义&#xff1a; 一个类的内部又完整的嵌套了另一个类结构&#xff0c;被嵌套的类就被我们称为内部类&#xff0c;嵌套内部类的类被我们称为外部类 //外部类 class Outer {//内部类class Inner{} }package Innerclass;//外部其他类 public class c1 { }class Outer {/…

数据结构--线段树--洛谷模板线段树1-2

写在前面&#xff1a; 学习之前需要知道以下内容&#xff1a; 1. 递归 2. 二叉树 文章目录 线段树介绍用途建树修改单点修改区间修改 查询 代码实现。建树更新lazy传递查询优化一下 练习洛谷 P3372 【模板】线段树 1题目描述题解 【模板】线段树 2题目描述题解 线段树 介绍 …

生产流程图怎么制作?思路提供

生产流程图是一种图表&#xff0c;用来展示生产流程中的各个环节及其顺序。这种图表可以帮助企业管理者更好地了解生产过程中的各个环节&#xff0c;从而更好地进行管理和优化。生产流程图通常包括各个生产环节的名称、所需时间、参与人员、设备和工具等信息。 在制作生产流程图…