TIM定时器控制按键(按键长短按)
最近在做的公司给我练手的小项目,是在MCU上做一个管理系统,其中需要用按键的长短按来控制开关机。最初第一个方案是使用延时,可是很快就发现弊端,这样的长按时间很不稳定,经常出差错,所以很快就被我否定了,项目再简单也是项目毕竟不是学校里面的课设。所以我决定用TIM定时器中断来判断开关状态以此来完成长按短按功能。
一、标志位法
通过一些资料的搜集,发现使用标志位法,首先完成TIM定时器的初始化,我这里使用的是CUBE。
首先打开TIM定时器并且初始化好,以及TIM定时器更新中断:
配置好分频系数以及计数器周期。
打开定时器更新中断。
这样关于定时器的初始化就完成了,其他的外设大家可以根据需要配置。
最终得到初始化好的代码:
#include "tim.h"TIM_HandleTypeDef htim1;/* TIM1 init function */
void MX_TIM1_Init(void)
{TIM_ClockConfigTypeDef sClockSourceConfig = {0};TIM_MasterConfigTypeDef sMasterConfig = {0};htim1.Instance = TIM1;htim1.Init.Prescaler = 800-1;htim1.Init.CounterMode = TIM_COUNTERMODE_UP;htim1.Init.Period = 100-1;htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;htim1.Init.RepetitionCounter = 0;htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;if (HAL_TIM_Base_Init(&htim1) != HAL_OK){Error_Handler();}sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK){Error_Handler();}sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK){Error_Handler();}/* USER CODE BEGIN TIM1_Init 2 */HAL_TIM_Base_Start_IT(&htim1); //使能定时器 1和定时器 1 更新中断/* USER CODE END TIM1_Init 2 */}void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM1){__HAL_RCC_TIM1_CLK_ENABLE();HAL_NVIC_SetPriority(TIM1_UP_IRQn, 0, 0);HAL_NVIC_EnableIRQ(TIM1_UP_IRQn);}
}void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{if(tim_baseHandle->Instance==TIM1){__HAL_RCC_TIM1_CLK_DISABLE();HAL_NVIC_DisableIRQ(TIM1_UP_IRQn);}
}
其中,第三十五行代码是用来使能定时器以及定时器更新中断的,这个需要手动添加,不然定时器和中断不会工作:
HAL_TIM_Base_Start_IT(&htim1);
定时器配置好了,那么我们来算算多久会激活一次定时器更新中断,上述代码可见我的分频系数是800,周期是100。
定时器的时钟为 8Mhz,分频系数为 800,所以分频后的计数频8Mhz/800=10KHz,然后计数到 100,所以时长为 100/10000=0.01s,也就是 10ms。所以说每1ms都会触发一次定时器更新中断。所以我们在中断回调函数里面判断开关状态:
1、首先设置按键标志位(为了方便看清楚,提供一个思路,不然一组标志位就够了):
int iButtonCount; //ButtonCount表示按键计数变量
int iButtonFlag; //ButtonFlag表示重按键标志,1代表重新按键_0为没有重新按键
int g_iButtonState; //g是globle代表全局变量,会在其他地方引用;ButtonState表示按键标志_1代表按下_0代表松开int longcount;
int longflag;
int longstate;
2、编写中断回调函数(两组标志位完全没卵用,只是分开给人看着更清晰一些):
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//按键中断回调
{if(htim==(&htim1)){if( HAL_GPIO_ReadPin(GPIOE,GPIO_PIN_3) == GPIO_PIN_RESET )//如果弿关丿引脚棿测到为使{ longcount++;iButtonCount++; //按键按下,计数iButtonCount加一if(iButtonCount>=30) //1ms中断服务函数里运行一次,iButtonCount大于等于10,即按键已稳定按30ms{if(iButtonFlag==0) //判断有没有重按键{g_iButtonState=1; //设置按键标志iButtonCount=0;iButtonFlag=1; //设置重按键标志}else //如果重按键,则重新计数iButtonCount=0; }else //如果没有稳定按下10ms,则代表没有按下按键g_iButtonState=0;if(longcount >= 300)//3s长按{if(longflag == 0){longState = 1;longcount = 0;longflag = 1;}elselongcount = 0;//如果重按键,则重新计数}elselongState = 0;//如果没有稳定按下3s,则代表没有长按}else //如果一直没有检测到测到低电平,即一直无按键按下{iButtonCount=0; //清零iButtonCountg_iButtonState=0; //清除按键标志iButtonFlag=0; //清除重按键标志longState = 0;longcount = 0;longflag = 0;}}
}
在主函数加入相关判断(通过led变化分辨):
if(g_iButtonState == 1){HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5, GPIO_PIN_RESET); g_iButtonState = 0; }if(longState == 1){HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5, GPIO_PIN_SET);longState = 0;}
如此功能确实是实现了,但是问题也来了,这样长按的时候短按也触发了一次,尽管是两个标志位(其实一个两个都没插别,因为终究是一个计数器)。所以这样很明显不行,不够严谨。所以这个时候就要用到状态机,将长按和短按状态划分开来。
二、状态机
切换成状态机也就说明前面的回调可以说白写了,当然前面的工作也没有完全白做,起码定时器还是需要的。状态机我没写过不清楚原理,所以是抄了一位大佬的作业,可以去看看原文(名字是「Net_Walke)。原文链接:https://blog.csdn.net/qq_34142812/article/details/119721386
首先,初始化状态枚举,第一个枚举是按键状态枚举,三个成员分别代表,检测状态,按下状态、释放状态;第二个枚举则是代表按键值,分别为空、短按、长按。
#define Key HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)typedef enum
{KEY_CHECK = 0,KEY_COMFIRM = 1,KEY_RELEASE = 2
}KEY_STATE;typedef enum
{NULL_KEY = 0,SHORT_KEY =1,LONG_KEY
}KEY_TYPE;KEY_STATE KeyState = KEY_CHECK; // 初始化按键状态为检测状态
u8 g_KeyFlag = 0; // 按键有效标志,0: 按键值无效; 1:按键值有效KEY_TYPE g_KeyActionFlag; //用于区别长按和短按
按键可能没什么难的,但是还是会惊叹别人清晰的思路,对KEYstate进行判断:
void Key_Scan(void)
{static uint32_t TimeCnt = 0;static uint8_t lock = 0;switch (KeyState){//按键未按下状态,此时判断Key的值case KEY_CHECK: if(Key) {KeyState = KEY_COMFIRM; //如果按键Key值为1,说明按键开始按下,进入下一个状态}TimeCnt = 0; //计数复位lock = 0;break;case KEY_COMFIRM:if(Key) //查看当前Key是否还是1,再次确认是否按下 {if(!lock){lock = 1;} TimeCnt++; /*按键时长判断*/if(TimeCnt >= 300) // 长按 3 s{g_KeyActionFlag = LONG_KEY;TimeCnt = 0; lock = 0; //重新进入检查模式KeyState = KEY_RELEASE; // 需要进入按键释放} } else {if(lock==1) // 不是第一次进入,释放按键才执行{g_KeyActionFlag = SHORT_KEY; // 短按KeyState = KEY_RELEASE; // 要进入按键释放状态 }else // 当前Key值为1,确认为抖动,则返回上一个状态{KeyState = KEY_CHECK; // 返回上一个状态}} break;case KEY_RELEASE:if(!Key) //当前Key值为1,说明按键已经释放,返回检测状态{ KeyState = KEY_CHECK; }break;default: break;}
}
//修改回调函数为:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//定时器中断回调
{if(htim == (&htim1)){ Key_Scan();}
}
最后在主函数,对回调函数返回的值进行判断:
switch(g_KeyActionFlag){case SHORT_KEY:printf("short time\r\n");HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5, GPIO_PIN_RESET);g_KeyActionFlag = 0; //回到空break;case LONG_KEY://printf("long time,standy now!!\r\n");HAL_GPIO_WritePin(GPIOE,GPIO_PIN_5, GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOB,GPIO_PIN_5, GPIO_PIN_SET);g_KeyActionFlag = 0; //回到空// Sys_Enter_Standby();break;default: break;}
这样对长按和短按有了完美划分,就不存在按键冲突问题,长按不松手是没办法进入短按的。状态之间一环扣一环,不会随随便便切换成其他状态。
TIM定时器控制按键(按键长短按)相关推荐
- 三档按键定时器c语言程序,单片机C语言程序设计:定时器控制4个LED滚动闪烁
/* 名称:定时器控制 4 个 LED 滚动闪烁 说明:4 只 LED 在定时器控制下滚动闪烁. */ #include #define uchar unsigned char #define ui ...
- STM32硬件SPI控制TM1638 按键数码管LED显示模块
STM32硬件SPI控制TM1638按键数码管LED显示模块 从淘宝买来的,TM1638专门是控制LED的,LED组合起来就可以变成数码管,还有按键,这个我就没管了,不想管了,发这个帖子只是为了记 ...
- STM32F4 定时器TIM(1)定时器控制输出【使用库函数】
高级时钟控制定时器TIM1&TIM8简介: STM32F4的高级控制定时器包含一个自动重装载计数器,计数器的输入是一个被预分频的系统时钟. 这个定时器有多种用途,包括车辆输入信号长度(输入捕获 ...
- 《ESP8266学习笔记》之 采用定时器内的按键扫描方法,摒弃传统的延时按键消抖
简介:传统的按键扫描程序,大部分都是采用 delay_ms(5); 这样的语句来进行按键消抖,但当你把它放在你高速运行的程序中时,这5ms可能会拖慢你的成语运行,导致体验感受下降,因此,我便找到了新的 ...
- 【51定时器】独立按键-短按与长按
之前的代码用Delay(xms)延时,是阻塞性延时,程序会卡住20ms,还有while写检测按键松手一般会卡个500ms,这样程序会慢很多. 用定时器写按键就不会出现上面的问题 //定时器0初始化模板 ...
- STM32 TIM定时器的使用(1)——定时
1.定时器简介 STM32中,定时器的应用非常广泛,涉及计时.信号检测.电机控制等等,并且定时器章节的介绍在STM32F1的手册里面也占据了大量的篇幅,足以看出定时器的重要性. 我将会做5个实验来学习 ...
- 每节课都是一个项目 手把手用STM32打造联网气象站-4-STM32基础三件套-TIM定时器和SYSTICK初始化
STM32基础系列包含了三件套,掌握了这三件套,类似于掌握了程咬金三板斧,就可以开始干项目,创造价值了.毕竟,真正的编程是一项实战性很强的技术,掌握编程主要靠实战,而不是靠知识灌输. STM32的编程 ...
- STM32学习笔记(四)丨TIM定时器及其应用(定时中断、内外时钟源选择)
本篇文章包含的内容 一.TIM 定时器 1.1 TIM 定时器简介 1.2 TIM 定时器类型及其工作原理简介 1.2.1 基本定时器工作原理及其结构 1.2.2 通用定时器工作原理及其结构 1.2. ...
- STM32学习笔记(六)丨TIM定时器及其应用(输入捕获丨测量PWM波形的频率和占空比)
本篇文章包含的内容 一.输入捕获 1.1 输入捕获简介 1.2 输入捕获通道的工作原理 1.3 输入捕获的主从触发模式 1.4 输入捕获和PWMI结构 二.频率的测量方法 2.1 测频法 2.2 测周 ...
最新文章
- 技术总监的反思录:我是如何失去团队掌控的?
- Hibernate, 想说爱你不容易
- nginx学习笔记(7)Nginx如何处理一个请求---转载
- H264分辨率解码概述
- ElementUI中el-form实现表单重置以及将方法抽出为全局方法
- 【Python】编程笔记11
- iphone UITableView及UIWebView的使用
- 为什么Redux需要reducer成为“纯函数”
- 【TensorFlow】TensorFlow从浅入深系列之十三 -- 教你深入理解模型持久化(模型保存、模型加载)
- matlab状态空间法算反馈阵,matlab中已知系统的状态方程怎样绘制系统阶跃响应曲线...
- Avalon and Indigo CTP- March 2005提供公开下载!
- Win10电脑如何查看本机mac地址
- java vk减号_Vue入门经常使用指令
- 三、Win10 64位PyCharm下打包.py程序为可执行exe文件且兼容32位和64位
- java操作.ini文件
- windows环境 java jdbc 连接impala (kerberos认证)
- c语言程序设计西华大学,知到C语言程序设计(西华大学)章节答案
- ubuntu18.04 安装软件中心(software-center)
- R markdown的笔记02
- 国内IDC数据中心星级评判标准怎么划分
热门文章
- 最短路径(Dijkstra算法和Floyd算法)
- 【HDR Imaging Digital Overlap】时域多帧HDR技术
- Linux(CentOS7)在VMware上的安装以及认识操作系统
- 图像处理: Canny边缘检测
- java queryinterface_Inside COM读书笔记------QueryInterface接口
- webconfig machineKey
- HTML5是什么与什么合作推出的语言,H5和Html5是一回事吗?-- -H5和Html5问答
- python微信群定时发早安_Python每天定时发早安晚安语录
- 单片机汇编语言程序学习
- 玩转k8s(二)—— Kubernetes架构