STM32F103实现双击、长按、短按后续

2024-06-08 11:12

本文主要是介绍STM32F103实现双击、长按、短按后续,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

经过上次(上一篇文章)的bug,这次进行了修改,基本原理就是使用基本定时器的计数功能,根据计算赋值合适的arr(预装载值)以及psc(预装载系数),使其实现100ms计时一次,在封装两个函数,一个返回当前的时间,另一个计算上次记录的时间与这次之间的比较,废话不多说直接上代码。

time_base.h代码中笔者封装了TIM6以及TIM7,你可以选择都是用这两个计时器,也可以选择使用一个计时器。具体操作只需修改BASE_TIM6 BASE_TIM7这两个宏定义的值即可,0 禁用,1 启用

time_base.h

/*********** @Author : 桃杬* @describe : 实现计时功能* @Data : 2024.06.08
***************/#ifndef _TIM_BASE_H
#define _TIM_BASE_H#include "stm32f10x.h"#define BASE_TIM6 1
#define BASE_TIM7 0void BaseTim_Init(void);#if BASE_TIM6
uint32_t GetTime6(void);
uint32_t GetTime6Difference(uint32_t new_time,uint32_t last_time);
#endif#if BASE_TIM7
uint32_t GetTime7(void);
uint32_t GetTime7Difference(uint32_t new_time,uint32_t last_time);
#endif#endif

需要注意的是笔者这里只用了TIM6定时器,有需要的大家自行打开。

time_base.c

#include "tim_base.h"/***** 
* 计数时间计数器
* CK_INT:内部时钟,psc:分频系数
* 计算公式 Time(100ms) = (arr+1)/[(CK_INT/(psc+1))/1000]
* 此处设置100ms一跳,即CK_INT标准库设置的为72MHZ,psc设置7199,arr设置999
* 根据公式带入数值 Time(ms) = 1000/72000000HZ/7200/1000 = 100ms
*****/
#if BASE_TIM6
uint32_t Time6_Count = 0;
#endif#if BASE_TIM7
uint32_t Time7_Count = 0;
#endifvoid BaseTim_Init(void)
{//定义TIM结构体TIM_TimeBaseInitTypeDef TIM_TimBaseInitStructure;//定义NVIC结构体NVIC_InitTypeDef NVIC_InitStructure;//使能时钟
#if BASE_TIM6RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
#endif#if BASE_TIM7RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);
#endif//配置TIMTIM_TimBaseInitStructure.TIM_Period = 7200-1;  //预分频系数 pscTIM_TimBaseInitStructure.TIM_Prescaler = 1000-1; //重装在值 arr#if BASE_TIM6TIM_TimeBaseInit(TIM6,&TIM_TimBaseInitStructure); //初始化计数器
#endif#if BASE_TIM7TIM_TimeBaseInit(TIM7,&TIM_TimBaseInitStructure); //初始化计数器
#endif#if BASE_TIM6TIM_ClearFlag(TIM6,TIM_FLAG_Update); //清除中断标志位TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //开启计数器中断
#endif#if BASE_TIM7TIM_ClearFlag(TIM7,TIM_FLAG_Update); //清除中断标志位TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //开启计数器中断
#endif//配置中断NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //设置中断组为0组
#if BASE_TIM6NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn; //中断源NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断源NVIC_Init(&NVIC_InitStructure);
#endif#if BASE_TIM7NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn; //中断源NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断源NVIC_Init(&NVIC_InitStructure);
#endif#if BASE_TIM6TIM_Cmd(TIM6,ENABLE);  //使能计数器
#endif#if BASE_TIM7TIM_Cmd(TIM7,ENABLE);  //使能计数器
#endif
}#if BASE_TIM6
/***
* @func : GetTime6(void)
* @describe : 返回当前时间 设置的是100ms一跳,即100ms为一个单位
* @param : void
* @ret : 返回当前时间 
* @note :可以将时间转换为s,只需在接收时对 Time6_Count/10 操作即可
***/
uint32_t GetTime6(void)
{return Time6_Count;
}/***
* @func : GetTime6Difference(uint32_t new_time,uint32_t last_time)
* @param : new_time : 当前时间, last_time : 上次时间
* @describe : 返回时间差值 设置的是100ms一跳,即100ms为一个单位
* @ret : 返回时间的差值
* @note :可以将时间差值转换为s,只需在接收时对 Time6_Count/10 操作即可
***/
uint32_t GetTime6Difference(uint32_t new_time,uint32_t last_time)
{return (new_time > last_time) ? (new_time - last_time) : (0xFFFFFFFF - last_time + new_time);
}#endif#if BASE_TIM7
/***
* @func : GetTime7(void)
* @describe : 返回当前时间 设置的是100ms一跳,即100ms为一个单位
* @param : void
* @ret : 返回当前时间 
* @note :可以将时间转换为s,只需在接收时对 Time6_Count/10 操作即可
***/
uint32_t GetTime7(void)
{return Time7_Count;
}/***
* @func : GetTime7Difference(uint32_t new_time,uint32_t last_time)
* @param : new_time : 当前时间, last_time : 上次时间
* @describe : 返回时间差值 设置的是100ms一跳,即100ms为一个单位
* @ret : 返回时间的差值
* @note :可以将时间差值转换为s,只需在接收时对 Time6_Count/10 操作即可
***/
uint32_t GetTime7Difference(uint32_t new_time,uint32_t last_time)
{return (new_time > last_time) ? (new_time - last_time) : (0xFFFFFFFF - last_time + new_time);
}#endif#if BASE_TIM6
void TIM6_IRQHandler()
{if(TIM_GetITStatus(TIM6,TIM_IT_Update) == SET) //检测到中断{Time6_Count++;}TIM_ClearFlag(TIM6,TIM_FLAG_Update); //清除定时器溢出中断
}
#endif#if BASE_TIM7
void TIM7_IRQHandler()
{if(ITM6->SR & TIM_SR_UIF) //检测到中断{Time7_Count++;}TIM7->SR = ~TIM_SR_UIF;
}
#endif

key.c

#include "key.h"
#include "delay.h"
#include "time2.h"
#include "s_fputc.h"
#include "tim_base.h"/*** 最终返回状态值 长按 短按 ***/
uint16_t Key_Value = 0x0000;/*** 记录上次最终返回状态值 ***/
uint16_t Old_Key_Value = 0x0000;//初次进入中断标志位
uint8_t Key_IT_Flag = 0;//初级进入双击标志位
uint8_t DoubleClickFlag = 0;//记录上次按键的时间
uint32_t old_time;/***按键按下宏定义***/
#if isEnableKey1
static uint8_t Key1_Press = 0;
#endif#if isEnableKey2
static uint8_t Key2_Press = 0;
#endif#if isEnableKey3
static uint8_t Key3_Press = 0;
#endif#if isEnableKey4
static uint8_t Key4_Press = 0;
#endif/*****
* @func : Key_Init(void)
* @describe : 初始化按键 外部中断 以及嵌套向量中断控制器
* @param : void
* @return : void
* @note : 移植时除了控制优先级和子优先级在此处需改外其他不需要修改这里
*****/
void Key_Init(void)
{//定义GPIO结构体GPIO_InitTypeDef GPIO_InitStructure;//定义外部中断EXTI机构体EXTI_InitTypeDef EXTI_InitStructure;//定义嵌套向量中断控制器NVIC结构体NVIC_InitTypeDef NVIC_InitStructure;//使能时钟#if isEnableKey1RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE); //key1所在的时钟#endif#if isEnableKey2RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK,ENABLE); //key2所在的时钟#endif#if isEnableKey3RCC_APB2PeriphClockCmd(KEY3_GPIO_CLK,ENABLE); //key3所在的时钟#endif#if isEnableKey4RCC_APB2PeriphClockCmd(KEY4_GPIO_CLK,ENABLE); //key4所在的时钟#endifRCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//配置KEYGPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;#if isEnableKey1GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure);#endif#if isEnableKey2GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure);#endif#if isEnableKey3GPIO_InitStructure.GPIO_Pin = KEY3_GPIO_PIN;GPIO_Init(KEY3_GPIO_PORT,&GPIO_InitStructure);#endif#if isEnableKey4GPIO_InitStructure.GPIO_Pin = KEY4_GPIO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY4_GPIO_PORT,&GPIO_InitStructure);#endif#if isEnableKey1//配置KEY1外部中断及嵌套向量中断控制器GPIO_EXTILineConfig(KEY1_GPIO_EXTI_PORT_SOURCE,KEY1_GPIO_EXTI_PIN_SOURCE); //选择EXTI的信号源EXTI_InitStructure.EXTI_Line = KEY1_EXTI_LINE; //选择EXTI事件线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //双边沿中断EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组NVIC_InitStructure.NVIC_IRQChannel = KEY1_IRQN; //中断源NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级NVIC_Init(&NVIC_InitStructure);#endif#if isEnableKey2//配置KEY2外部中断及嵌套向量中断控制器GPIO_EXTILineConfig(KEY2_GPIO_EXTI_PORT_SOURCE,KEY2_GPIO_EXTI_PIN_SOURCE); //选择EXTI的信号源EXTI_InitStructure.EXTI_Line = KEY2_EXTI_LINE; //选择EXTI事件线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //双边沿中断EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组NVIC_InitStructure.NVIC_IRQChannel = KEY2_IRQN; //中断源NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级NVIC_Init(&NVIC_InitStructure);#endif#if isEnableKey3//配置KEY3外部中断及嵌套向量中断控制器GPIO_EXTILineConfig(KEY3_GPIO_EXTI_PORT_SOURCE,KEY3_GPIO_EXTI_PIN_SOURCE); //选择EXTI的信号源EXTI_InitStructure.EXTI_Line = KEY3_EXTI_LINE; //选择EXTI事件线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //双边沿中断EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组NVIC_InitStructure.NVIC_IRQChannel = KEY3_IRQN; //中断源NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级NVIC_Init(&NVIC_InitStructure);#endif#if isEnableKey4//配置KEY4外部中断及嵌套向量中断控制器GPIO_EXTILineConfig(KEY4_GPIO_EXTI_PORT_SOURCE,KEY4_GPIO_EXTI_PIN_SOURCE); //选择EXTI的信号源EXTI_InitStructure.EXTI_Line = KEY4_EXTI_LINE; //选择EXTI事件线EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; //EXTI中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling; //双边沿中断EXTI_InitStructure.EXTI_LineCmd = ENABLE; //使能中断EXTI_Init(&EXTI_InitStructure);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组NVIC_InitStructure.NVIC_IRQChannel =KEY4_IRQN; //中断源NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级NVIC_Init(&NVIC_InitStructure);#endif
}/*****
* @func : Key_Scan(void)
* @describe : 检测按键状态
* @param : void
* @return : void
* @note : 1 按下按键 0 松开按键
*****/
void Key_Scan(void)
{#if isEnableKey1if(KEY1 == RESET) //检测到按键1{delay_ms(20); //消抖if(KEY1 == RESET) //再次检测按键1{Key1_Press = 1; //记录此时按键状态}}elseKey1_Press = 0;#endif#if isEnableKey2if(KEY2 == RESET) //检测到按键2{delay_ms(20); //消抖if(KEY2 == RESET) //再次检测按键2Key2_Press = 1; //记录此时按键状态}elseKey2_Press = 0;#endif#if isEnableKey3if(KEY3 == RESET) //检测到按键3{delay_ms(20); //消抖if(KEY3 == RESET) //再次检测按键3Key3_Press = 1; //记录此时按键状态}elseKey3_Press = 0;#endif#if isEnableKey4if(KEY4 == SET) //检测到按键4  注意的是按键四是高电平检测到按键按下{delay_ms(20); //消抖if(KEY4 == SET) //再次检测按键4Key4_Press = 1; //记录此时按键状态}elseKey4_Press = 0;#endif
}/*****
* @func : GetKeyContinuousPressNum(void)
* @describe : 支持按键连续按下
* @param : void
* @return : 按键值
*       @para : 1 按键1; 2 按键2; 3 按键3; 4 按键4
* @note : 无
*****/
uint8_t GetKeyContinuousPressNum(void)
{Key_Scan();#if isEnableKey1if(Key1_Press)return 1;#endif#if isEnableKey2if(Key2_Press)return 2;#endif#if isEnableKey3if(Key3_Press)return 3;#endif#if isEnableKey4if(Key4_Press)return 4;#endifreturn 0;
}/*****
* @func : GetKeyNum(void)
* @describe : 返回按键值
* @param : void
* @return : 按键值
*       @ret : 11 按键1短按; 12 按键1长按; 13 按键1双击
*       @ret : 21 按键2短按; 22 按键2长按; 23 按键2双击
*       @ret : 31 按键3短按; 32 按键3长按; 33 按键3双击
*       @ret : 41 按键4短按; 42 按键4长按; 43 按键4双击
* @note : 返回值如 xx 形式
*         其十位代表按键几 个位则代表长短双击, 1 短按 2 长按 3 双击
*****/
uint8_t GetKeyNum(void)
{//最终返回值static uint8_t keyNum = 0;Key_Scan();if(!Key_IT_Flag){switch(Key_Value){#if isEnableKey1case 0x0001:keyNum = 11;  //按键1短按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0002:keyNum = 12;  //按键1长按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0004:keyNum = 14;  //按键1双击Old_Key_Value = 0x0000;Key_Value = 0x0000;break;#endif#if isEnableKey2case 0x0010:keyNum = 21;  //按键2短按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0020:keyNum = 22;  //按键2长按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0040:keyNum = 24;  //按键2双击Old_Key_Value = 0x0000;Key_Value = 0x0000;break;#endif#if isEnableKey3case 0x0100:keyNum = 31;  //按键3短按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0200:keyNum = 32;  //按键3长按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x0400:keyNum = 34;  //按键3双击Old_Key_Value = 0x0000;Key_Value = 0x0000;break;#endif#if isEnableKey4case 0x1000:keyNum = 41;  //按键4短按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x2000:keyNum = 42;  //按键4长按Old_Key_Value = Key_Value;Key_Value = 0x0000;break;case 0x4000:keyNum = 44;  //按键4双击Old_Key_Value = 0x0000;Key_Value = 0x0000;break;#endifdefault:keyNum = 0;Key_Value = 0x0000;break;}}return keyNum;
}#if isEnableKey1
/*****
* @func : EXTI1_IRQHandler()
* @describe : 中断函数
* @param : void
* @return : void
* @note : 中断需要的处理的操作在此处执行
*****/
void EXTI1_IRQHandler()
{static uint8_t Key1State = 0; //静态按键1状态 0 松开 1按下if((EXTI_GetITStatus(KEY1_EXTI_LINE) != RESET) && (Key1_Press && Key1State))  //发生中断并且检测到松开按键{Key1State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if((Time_Count > 0 && Time_Count <= 20) && !DoubleClickFlag) //短按时间判断{old_time = GetTime6(); //记录此次获取的时间DoubleClickFlag = 1; //改变标志位Old_Key_Value = KEY1_DoubleClick_Value; //记录上次返回的状态值
//            printf("old_time = %d\n",old_time); //测试时打开}else if((Time_Count > 0 && Time_Count <= 20) && DoubleClickFlag){if((GetTime6Difference(GetTime6(),old_time) <= 5) && (Old_Key_Value == KEY1_DoubleClick_Value)) //两次按键之间差值不超过500ms,视为双击{DoubleClickFlag = 0; //重置初次进入标志位Key_Value = KEY1_DoubleClick_Value; //返回双击状态值
//                printf("time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开}else {
//                printf("updata time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开old_time = GetTime6(); //更新时间Old_Key_Value = KEY1_DoubleClick_Value; //记录上次返回的状态值
//                printf("update old_time = %d\n",old_time); //测试时打开}}else if(Time_Count > 20 && Time_Count <= 40)  //短按时间判断Key_Value = KEY1_ShortPress_Value;  //短按时间返回状态值else if(Time_Count > 40)  //长按时间判断Key_Value = KEY1_LongPress_Value;  //长按时间返回状态值//        printf("Time_Count : %d\n",Time_Count); //测试时使用EXTI_ClearITPendingBit(KEY1_EXTI_LINE); //清除中断标志位} else if((EXTI_GetITStatus(KEY1_EXTI_LINE) != RESET) &&(!Key_IT_Flag && !Key1State)) //发生中断并且按下按键{Key1State = 1; //按下标志位Key_IT_Flag = 1; //已进入中断标志位Time_Count = 0; //每次按下从0开始计时TIM_Cmd(TIM2,ENABLE); //开启时钟EXTI_ClearITPendingBit(KEY1_EXTI_LINE); //清除中断标志位}
}#endif#if isEnableKey2
/*****
* @func : EXTI9_5_IRQHandler()
* @describe : 中断函数
* @param : void
* @return : void
* @note : 中断需要的处理的操作在此处执行
*****/
void EXTI9_5_IRQHandler()
{static uint8_t Key2State = 0; //静态按键1状态 0 松开 1按下if((EXTI_GetITStatus(KEY2_EXTI_LINE) != RESET) && (Key2_Press && Key2State))  //发生中断并且检测到松开按键{Key2State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if((Time_Count > 0 && Time_Count <= 20) && !DoubleClickFlag) //短按时间判断{old_time = GetTime6(); //记录此次获取的时间DoubleClickFlag = 1; //改变标志位Old_Key_Value = KEY2_DoubleClick_Value; //记录上次返回的状态值
//            printf("old_time = %d\n",old_time); //测试时打开}else if((Time_Count > 0 && Time_Count <= 20) && DoubleClickFlag){if((GetTime6Difference(GetTime6(),old_time) <= 5) && (Old_Key_Value == KEY2_DoubleClick_Value)) //两次按键之间差值不超过500ms,视为双击{DoubleClickFlag = 0; //重置初次进入标志位Key_Value = KEY2_DoubleClick_Value; //返回双击状态值
//                printf("time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开}else {
//                printf("updata time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开old_time = GetTime6(); //更新时间Old_Key_Value = KEY2_DoubleClick_Value; //记录上次返回的状态值
//                printf("update old_time = %d\n",old_time); //测试时打开}}else if(Time_Count > 20 && Time_Count <= 40)  //短按时间判断Key_Value = KEY2_ShortPress_Value;  //短按时间返回状态值else if(Time_Count > 40)  //长按时间判断Key_Value = KEY2_LongPress_Value;  //长按时间返回状态值//        printf("Time_Count : %d\n",Time_Count); //测试时使用EXTI_ClearITPendingBit(KEY2_EXTI_LINE); //清除中断标志位} else if((EXTI_GetITStatus(KEY2_EXTI_LINE) != RESET) &&(!Key_IT_Flag && !Key2State)) //发生中断并且按下按键{Key2State = 1; //按下标志位Key_IT_Flag = 1; //已进入中断标志位Time_Count = 0; //每次按下从0开始计时TIM_Cmd(TIM2,ENABLE); //开启时钟EXTI_ClearITPendingBit(KEY2_EXTI_LINE); //清除中断标志位}
}#endif#if isEnableKey3
/*****
* @func : EXTI4_IRQHandler()
* @describe : 中断函数
* @param : void
* @return : void
* @note : 中断需要的处理的操作在此处执行
*****/
void EXTI4_IRQHandler()
{static uint8_t Key3State = 0; //静态按键1状态 0 松开 1按下if((EXTI_GetITStatus(KEY3_EXTI_LINE) != RESET) && (Key3_Press && Key3State))  //发生中断并且检测到松开按键{Key3State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if((Time_Count > 0 && Time_Count <= 20) && !DoubleClickFlag) //短按时间判断{old_time = GetTime6(); //记录此次获取的时间DoubleClickFlag = 1; //改变标志位Old_Key_Value = KEY3_DoubleClick_Value; //记录上次返回的状态值
//            printf("old_time = %d\n",old_time); //测试时打开}else if((Time_Count > 0 && Time_Count <= 20) && DoubleClickFlag){if((GetTime6Difference(GetTime6(),old_time) <= 5) && (Old_Key_Value == KEY3_DoubleClick_Value)) //两次按键之间差值不超过500ms,视为双击{DoubleClickFlag = 0; //重置初次进入标志位Key_Value = KEY3_DoubleClick_Value; //返回双击状态值
//                printf("time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开}else {
//                printf("updata time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开old_time = GetTime6(); //更新时间Old_Key_Value = KEY3_DoubleClick_Value; //记录上次返回的状态值
//                printf("update old_time = %d\n",old_time); //测试时打开}}else if(Time_Count > 20 && Time_Count <= 40)  //短按时间判断Key_Value = KEY3_ShortPress_Value;  //短按时间返回状态值else if(Time_Count > 40)  //长按时间判断Key_Value = KEY3_LongPress_Value;  //长按时间返回状态值//        printf("Time_Count : %d\n",Time_Count); //测试时使用
//        EXTI_ClearITPendingBit(KEY3_EXTI_LINE); //清除中断标志位} else if((EXTI_GetITStatus(KEY3_EXTI_LINE) != RESET) &&(!Key_IT_Flag && !Key3State)) //发生中断并且按下按键{Key3State = 1; //按下标志位Key_IT_Flag = 1; //已进入中断标志位Time_Count = 0; //每次按下从0开始计时TIM_Cmd(TIM2,ENABLE); //开启时钟EXTI_ClearITPendingBit(KEY3_EXTI_LINE); //清除中断标志位}
}#endif#if isEnableKey4
/*****
* @func : EXTI0_IRQHandler()
* @describe : 中断函数
* @param : void
* @return : void
* @note : 中断需要的处理的操作在此处执行
*****/
void EXTI0_IRQHandler()
{static uint8_t Key4State = 0; //静态按键1状态 0 松开 1按下if((EXTI_GetITStatus(KEY4_EXTI_LINE) != RESET) && (Key4_Press && Key4State))  //发生中断并且检测到松开按键{Key4State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if((Time_Count > 0 && Time_Count <= 20) && !DoubleClickFlag) //短按时间判断{old_time = GetTime6(); //记录此次获取的时间DoubleClickFlag = 1; //改变标志位Old_Key_Value = KEY4_DoubleClick_Value; //记录上次返回的状态值
//            printf("old_time = %d\n",old_time); //测试时打开}else if((Time_Count > 0 && Time_Count <= 20) && DoubleClickFlag){if((GetTime6Difference(GetTime6(),old_time) <= 5) && (Old_Key_Value == KEY4_DoubleClick_Value)) //两次按键之间差值不超过500ms,视为双击{DoubleClickFlag = 0; //重置初次进入标志位Key_Value = KEY4_DoubleClick_Value; //返回双击状态值
//                printf("time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开}else {
//                printf("updata time_difference = %d\n",GetTime6Difference(GetTime6(),old_time)); //测试时打开old_time = GetTime6(); //更新时间Old_Key_Value = KEY4_DoubleClick_Value; //记录上次返回的状态值
//                printf("update old_time = %d\n",old_time); //测试时打开}}else if(Time_Count > 20 && Time_Count <= 40)  //短按时间判断Key_Value = KEY4_ShortPress_Value;  //短按时间返回状态值else if(Time_Count > 40)  //长按时间判断Key_Value = KEY4_LongPress_Value;  //长按时间返回状态值//        printf("Time_Count : %d\n",Time_Count); //测试时使用EXTI_ClearITPendingBit(KEY4_EXTI_LINE); //清除中断标志位} else if((EXTI_GetITStatus(KEY4_EXTI_LINE) != RESET) &&(!Key_IT_Flag && !Key4State)) //发生中断并且按下按键{Key4State = 1; //按下标志位Key_IT_Flag = 1; //已进入中断标志位Time_Count = 0; //每次按下从0开始计时TIM_Cmd(TIM2,ENABLE); //开启时钟EXTI_ClearITPendingBit(KEY4_EXTI_LINE); //清除中断标志位}
}#endif

其余的代码跟上一篇文章一样,在这里笔者就不发了。

其实仅仅使用基本定时器或者Systick滴答定时器就能实现长按、短按、以及双击操作,并不需要开启TIM2通用定时器,只是当时笔者没想那么多,使用两个定时器确实造成资源浪费😂,大家只需要再设置一个记录时间的变量,每次进入中断要记录此次进入的时间,在调用时间查函数判断按键持续了多久,很简单的就能实现同样的功能,大家有兴趣的话可以再这个代码的基础上实现逻辑层的应用即可,也就是中断函数里面的部分,不需要大家大改动。

最后,笔者在此感谢大家耐心的看完👀。

这篇关于STM32F103实现双击、长按、短按后续的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1042017

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机