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

有没有教做帽子的网站/营业推广名词解释

有没有教做帽子的网站,营业推广名词解释,卓拙科技做网站吗,专注WordPress网站建设开发今天来分享一个使用EXIT外部中断加TIM计时器实现按键长短按以及双击操作,不过笔者在双击上有点瑕疵,就是当你按下双击第一下停顿几秒按第二下依然会识别为双击操作,笔者猜测只要板子不停电即便到第二天按下第二下依旧会识别双击操作&#xff…

今天来分享一个使用EXIT外部中断加TIM计时器实现按键长短按以及双击操作,不过笔者在双击上有点瑕疵,就是当你按下双击第一下停顿几秒按第二下依然会识别为双击操作,笔者猜测只要板子不停电即便到第二天按下第二下依旧会识别双击操作,正常来说2秒内如果没有按下第二下会认为双击操作失败,重新开始,不过整体使用效果还可以,解决这个问题的方案笔者抽时间在好好想想。

下面为大家介绍以下大体思路,我们知道每个I/O端口都会对应一个EXIT中断,由此我们可以使用EXIT的双边沿触发来实现TIM定时器的开启关闭,当检测按下时开启TIM定时器,当检测松开按键时关闭TIM定时器,从而记录按键按下的时间,根据按键按下的时间长短不同可以判断按键的长短按,另外需要设定标志位来判断双击,下面开始上代码。

首先是TIM计时器的代码,笔者使用的是TIM2计时器,大家找一个板子上没用到的计时器即可。

time2.h

/*********** @Author : 桃杬* @describe : TIM2计时器,在此处主要用来实现计时操作* @Data : 2024.06.06
***************/#ifndef _TIME2_H
#define _TIME2_H#include "stm32f10x.h"extern uint16_t Time_Count; //外部声明,让Time_Count成为全局变量void TIME2_Init(uint16_t arr,uint16_t psc);#endif

下面是time.c的代码,具体解释笔者在里面都有备注,需要注意的是笔者设置每次发生溢出的时间为10ms,即Time_Count每10ms都会计数加1,实际时间 = Time_Count * 10,单位ms。

time2.c

#include "time2.h"/******** TIM溢出次数 Tout(溢出时间) = (arr+1)/(psc+1)/Tclk(时钟分割)* 当 arr = 10000, psc = 72 时,10ms溢出一次
*******/
uint16_t Time_Count;void TIME2_Init(uint16_t arr,uint16_t psc)
{//定义TIM结构体TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;//定义NVIC嵌套向量中断NVIC_InitTypeDef NVIC_InitStrucure;//使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);TIM_InternalClockConfig(TIM2); //配置TIM2内部时钟,可以不配置,默认是一直开着的//配置TIMTIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //不用时钟分割TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数方式TIM_TimeBaseStructure.TIM_Period = arr - 1; //自动装载值TIM_TimeBaseStructure.TIM_Prescaler = psc - 1; //预分频系数TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //周期计数器值,这个是高级定时器,基本通用定时器不用开启TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure);TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除TIM的挂起标志TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //使能指定TIM中断,寻去中断更新//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置优先级分组NVIC_InitStrucure.NVIC_IRQChannel = TIM2_IRQn; //中断源NVIC_InitStrucure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道NVIC_InitStrucure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级NVIC_InitStrucure.NVIC_IRQChannelSubPriority = 1; //子优先级(也是响应优先级)NVIC_Init(&NVIC_InitStrucure);
}void TIM2_IRQHandler(void)
{if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET){//进入中断表示定时器计数(CNT)溢出, 自增(10ms溢出一次, 可通过配置ARR, PSC和Tclk自定义溢出时间)Time_Count++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除定时器溢出中断}
}

接下来便是key.h代码,如果需要移植的话只需要修改每个按键定义以及中断部分,其他部分不需要修改,笔者这里使用了四个按键,如果大家是少于四个按键可以将按键启用的值改为0即可,即禁用不需要的按键,如将isEnableKey4 该为 0就会禁用按键4。如果大家的按键大于4个可以按照笔者写的格式增加按键,下面可以参考笔者写的代码。

key.h

/*********** @Author : 桃杬* @describe : 实现按键长短按 双击操作* @Data : 2024.06.06
***************/#ifndef _KEY_H
#define _KEY_H#include "stm32f10x.h"/**** 是否启用按键* 根据个人需求修改* 1 启用按键 0 失能按键* 默认都启用
****/
#define isEnableKey1 1
#define isEnableKey2 1
#define isEnableKey3 1
#define isEnableKey4 1#if isEnableKey1
//定义KEY1按键引脚以及中断
#define KEY1_GPIO_CLK RCC_APB2Periph_GPIOC //使能时钟端口
#define KEY1_GPIO_PORT GPIOC //KEY1端口
#define KEY1_GPIO_PIN GPIO_Pin_1 //KEY1引脚
#define KEY1_GPIO_EXTI_PORT_SOURCE GPIO_PortSourceGPIOC //外部中断源端口
#define KEY1_GPIO_EXTI_PIN_SOURCE GPIO_PinSource1 //外部中断源引脚
#define KEY1_EXTI_LINE EXTI_Line1 //EXTI事件线
#define KEY1_IRQN EXTI1_IRQn //中断源
#endif#if isEnableKey2
//定义KEY2按键引脚以及中断
#define KEY2_GPIO_CLK RCC_APB2Periph_GPIOC //使能时钟端口
#define KEY2_GPIO_PORT GPIOC //KEY2端口
#define KEY2_GPIO_PIN GPIO_Pin_5 //KEY2引脚
#define KEY2_GPIO_EXTI_PORT_SOURCE GPIO_PortSourceGPIOC //外部中断源端口
#define KEY2_GPIO_EXTI_PIN_SOURCE GPIO_PinSource5 //外部中断源引脚
#define KEY2_EXTI_LINE EXTI_Line5 //EXTI事件线
#define KEY2_IRQN EXTI9_5_IRQn //中断源
#endif#if isEnableKey3
//定义KEY3按键引脚以及中断
#define KEY3_GPIO_CLK RCC_APB2Periph_GPIOC //使能时钟端口
#define KEY3_GPIO_PORT GPIOC //KEY3端口
#define KEY3_GPIO_PIN GPIO_Pin_4 //KEY3引脚
#define KEY3_GPIO_EXTI_PORT_SOURCE GPIO_PortSourceGPIOC //外部中断源端口
#define KEY3_GPIO_EXTI_PIN_SOURCE GPIO_PinSource4 //外部中断源引脚
#define KEY3_EXTI_LINE EXTI_Line4 //EXTI事件线
#define KEY3_IRQN EXTI4_IRQn //中断源
#endif#if isEnableKey4
//定义KEY4按键引脚以及中断
#define KEY4_GPIO_CLK RCC_APB2Periph_GPIOA //使能时钟端口
#define KEY4_GPIO_PORT GPIOA //KEY4端口
#define KEY4_GPIO_PIN GPIO_Pin_0 //KEY4引脚
#define KEY4_GPIO_EXTI_PORT_SOURCE GPIO_PortSourceGPIOA //外部中断源端口
#define KEY4_GPIO_EXTI_PIN_SOURCE GPIO_PinSource0 //外部中断源引脚
#define KEY4_EXTI_LINE EXTI_Line0 //EXTI事件线
#define KEY4_IRQN EXTI0_IRQn //中断源
#endif//定义读取按键状态
#if isEnableKey1
#define KEY1 GPIO_ReadInputDataBit(KEY1_GPIO_PORT,KEY1_GPIO_PIN) //读取按键 LEFT
#endif#if isEnableKey2
#define KEY2 GPIO_ReadInputDataBit(KEY2_GPIO_PORT,KEY2_GPIO_PIN) //读取按键 RIGHT
#endif#if isEnableKey3
#define KEY3 GPIO_ReadInputDataBit(KEY3_GPIO_PORT,KEY3_GPIO_PIN) //读取按键 DOWN
#endif#if isEnableKey4
#define KEY4 GPIO_ReadInputDataBit(KEY4_GPIO_PORT,KEY4_GPIO_PIN) //读取按键 WAKEUP
#endif/******** 定义返回值状态 **********/
#if isEnableKey1
//KEY1返回值
#define KEY1_ShortPress_Value 0x0001  //短按
#define KEY1_LongPress_Value 0x0002   //长按
#define KEY1_DoubleClick_Value 0x0004 //双击
#endif#if isEnableKey2
//KEY2返回值
#define KEY2_ShortPress_Value 0x0010  //短按
#define KEY2_LongPress_Value 0x0020   //长按
#define KEY2_DoubleClick_Value 0x0040 //双击
#endif#if isEnableKey3
//KEY3返回值
#define KEY3_ShortPress_Value 0x0100  //短按
#define KEY3_LongPress_Value 0x0200   //长按
#define KEY3_DoubleClick_Value 0x0400 //双击
#endif#if isEnableKey4
//KEY4返回值
#define KEY4_ShortPress_Value 0x1000  //短按
#define KEY4_LongPress_Value 0x2000   //长按
#define KEY4_DoubleClick_Value 0x4000 //双击
#endif/**************** 函数部分 ********************/
void Key_Init(void); //初始化按键
void Key_Scan(void); //检测按键是否按下
uint16_t GetKeyNum(void); //返回按键值#endif

注意的是delay.h中是笔者写的简单的延迟函数,自行使用大家的延迟函数即可,只在消抖处用到了延时函数,其余部分没用用到。其次,中断处理双击中又嵌套了两个if else,时间上有点大,有点屎山代码的味道了😂,目前能想到的是开一个定时器,每当符合双击第一次时开启计数器,当检测到双击第二次时停止计数器,在2s内算是符合双击操作,否则丢弃,当然这只是笔者目前的想法🤣,后续笔者会完善以下。

key.c

#include "key.h"
#include "delay.h"
#include "time2.h"/*** 最终返回状态值 长按 短按 ***/
uint16_t Key_Value = 0x0000;/*** 记录上次最终返回状态值 ***/
uint16_t Old_Key_Value = 0x0000;//初次进入中断标志位
uint8_t Key_IT_Flag = 0;//初级进入双击标志位
uint8_t DoubleClickFlag = 0;//记录上次按键的值
uint16_t Old_KeyNum = 0x0000;/***按键按下宏定义***/
#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 : 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 双击
*****/
uint16_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){if(Key1_Press && Key1State) //松开按键{Key1State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if(Time_Count > 0 && Time_Count <= 20) //短按时间判断{if(!DoubleClickFlag) //第一次双击按下{DoubleClickFlag = 1; //改变标志位Old_KeyNum |= KEY1_DoubleClick_Value; //记录按下值Old_Key_Value = Old_KeyNum; //记录上次返回的状态值}else {Old_KeyNum &= 0x0000; //清除记录Old_KeyNum |= KEY1_DoubleClick_Value; //更新上一次的值if(Old_Key_Value != Old_KeyNum)  //上次最终返回的状态值与记录第一次双击按下值不一致Old_Key_Value = Old_KeyNum;  //更新最后返回状态值 else{DoubleClickFlag = 0; //重置双击按下标志位Old_Key_Value = 0x0000; //重置最后返回状态Old_KeyNum = 0x0000; //重置按下值Key_Value |= KEY1_DoubleClick_Value; //双击返回状态值}}}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); //测试时使用} else if(!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){if(Key2_Press && Key2State) //松开按键{Key2State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if(Time_Count > 0 && Time_Count <= 20) //短按时间判断{if(!DoubleClickFlag) //第一次双击按下{DoubleClickFlag = 1; //改变标志位Old_KeyNum |= KEY2_DoubleClick_Value; //记录按下值Old_Key_Value = Old_KeyNum; //记录上次返回的状态值}else {Old_KeyNum &= 0x0000; //清除记录Old_KeyNum |= KEY2_DoubleClick_Value; //更新上一次的值if(Old_Key_Value != Old_KeyNum)  //上次最终返回的状态值与记录第一次双击按下值不一致Old_Key_Value = Old_KeyNum;  //更新最后返回状态值 else{DoubleClickFlag = 0; //重置双击按下标志位Old_Key_Value = 0x0000; //重置最后返回状态Old_KeyNum = 0x0000; //重置按下值Key_Value |= KEY2_DoubleClick_Value; //双击返回状态值}}}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); //测试时使用} else if(!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){if(Key3_Press && Key3State) //松开按键{Key3State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if(Time_Count > 0 && Time_Count <= 20) //短按时间判断{if(!DoubleClickFlag) //第一次双击按下{DoubleClickFlag = 1; //改变标志位Old_KeyNum |= KEY3_DoubleClick_Value; //记录按下值Old_Key_Value = Old_KeyNum; //记录上次返回的状态值}else {Old_KeyNum &= 0x0000; //清除记录Old_KeyNum |= KEY3_DoubleClick_Value; //更新上一次的值if(Old_Key_Value != Old_KeyNum)  //上次最终返回的状态值与记录第一次双击按下值不一致Old_Key_Value = Old_KeyNum;  //更新最后返回状态值 else{DoubleClickFlag = 0; //重置双击按下标志位Old_Key_Value = 0x0000; //重置最后返回状态Old_KeyNum = 0x0000; //重置按下值Key_Value |= KEY3_DoubleClick_Value; //双击返回状态值}}}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); //测试时使用} else if(!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){if(Key4_Press && Key4State) //松开按键{Key4State = 0; //松开标志Key_IT_Flag = 0; //退出中断标志位TIM_Cmd(TIM2,DISABLE); //关闭时钟if(Time_Count > 0 && Time_Count <= 20) //短按时间判断{if(!DoubleClickFlag) //第一次双击按下{DoubleClickFlag = 1; //改变标志位Old_KeyNum |= KEY4_DoubleClick_Value; //记录按下值Old_Key_Value = Old_KeyNum; //记录上次返回的状态值}else {Old_KeyNum &= 0x0000; //清除记录Old_KeyNum |= KEY4_DoubleClick_Value; //更新上一次的值if(Old_Key_Value != Old_KeyNum)  //上次最终返回的状态值与记录第一次双击按下值不一致Old_Key_Value = Old_KeyNum;  //更新最后返回状态值 else{DoubleClickFlag = 0; //重置双击按下标志位Old_Key_Value = 0x0000; //重置最后返回状态Old_KeyNum = 0x0000; //重置按下值Key_Value |= KEY4_DoubleClick_Value; //双击返回状态值}}}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); //测试时使用} else if(!Key_IT_Flag && !Key4State) //按下按键{Key4State = 1; //按下标志位Key_IT_Flag = 1; //已进入中断标志位Time_Count = 0; //每次按下从0开始计时TIM_Cmd(TIM2,ENABLE); //开启时钟}EXTI_ClearITPendingBit(KEY4_EXTI_LINE); //清除中断标志位}
}#endif

最后main函数代码进行测试,大家注意的是需要对printf函数进行重写。

main.c

#include "stm32f10x.h"
#include "sys.h"
#include "usart.h"
#include "key.h"
#include "time2.h"int main()
{uint16_t keyNum = 0;USART1_Init(115200); //初始化USARTTIME2_Init(10000,72); //初始TIM2Key_Init(); //按键初始化while(1){keyNum = GetKeyNum();switch(keyNum){case 11:printf("按键1短按\n");printf("短按返回值 keyNum = %2d\n",keyNum);puts("");break;case 12:printf("按键1长按\n");printf("长按返回值 keyNum = %2d\n",keyNum);puts("");break;case 14:printf("按键1双击\n");printf("双击返回值 keyNum = %2d\n",keyNum);puts("");break;case 21:printf("按键2短按\n");printf("短按返回值 keyNum = %2d\n",keyNum);puts("");break;case 22:printf("按键2长按\n");printf("长按返回值 keyNum = %2d\n",keyNum);puts("");break;case 24:printf("按键2双击\n");printf("双击返回值 keyNum = %2d\n",keyNum);puts("");break;case 31:printf("按键3短按\n");printf("短按返回值 keyNum = %2d\n",keyNum);puts("");break;case 32:printf("按键3长按\n");printf("长按返回值 keyNum = %2d\n",keyNum);puts("");break;case 34:printf("按键3双击\n");printf("双击返回值 keyNum = %2d\n",keyNum);puts("");break;case 41:printf("按键4短按\n");printf("短按返回值 keyNum = %2d\n",keyNum);puts("");break;case 42:printf("按键4长按\n");printf("长按返回值 keyNum = %2d\n",keyNum);puts("");break;case 44:printf("按键4双击\n");printf("双击返回值 keyNum = %2d\n",keyNum);puts("");break;default:break;}}
}

最后的最后献上测试截图,感谢大家的观看👀。

相关文章:

STM32f103实现按键长按 短按 双击

今天来分享一个使用EXIT外部中断加TIM计时器实现按键长短按以及双击操作&#xff0c;不过笔者在双击上有点瑕疵&#xff0c;就是当你按下双击第一下停顿几秒按第二下依然会识别为双击操作&#xff0c;笔者猜测只要板子不停电即便到第二天按下第二下依旧会识别双击操作&#xff…...

【WP】猿人学13_入门级cookie

https://match.yuanrenxue.cn/match/13 抓包分析 抓包分析发现加密参数是cookie中有一个yuanrenxue_cookie 当cookie过期的时候&#xff0c;就会重新给match/13发包&#xff0c;这个包返回一段js代码&#xff0c;应该是生成cookie的 <script>document.cookie(y)(u)(a…...

分享一款提取抖音小店商家电话的软件使用教程

抖音作为一款国内非常流行的短视频分享平台&#xff0c;吸引了大量用户和商家。许多商家在抖音上开设了小店&#xff0c;但是抖音并没有提供直接获取商家电话的功能。本文将分享一款提取抖音小店商家电话的软件&#xff0c;并附带使用教程和代码。 教程 步骤一&#xff1a;安…...

反转链表的三种方法--面试必考(图例超详细解析,小白一看就会!!!)

目录 一、前言 二、题目描述 三、解题方法 ⭐ 头插法 --- 创建新的链表 ⭐ 迭代法 --- 三指针 ⭐ 递归法 四、总结与提炼 五、共勉 一、前言 反转链表这道题&#xff0c;可以说是--链表专题--&#xff0c;最经典的一道题&#xff0c;也是在面试中频率最高的一道题目&…...

Springboot注意点

1.Usermapper里加param注解 2.RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody的区别&#xff1a; RequestParam 和 RequestBody 是Spring框架中用于处理HTTP请求的两个不同的注 get请求一般用url传参数&#xff0c;所以参数名和参数的值就在ur…...

数组和指针的联系(C语言)

数组和指针是两种不同的数据类型&#xff0c;数组是一种构造类型&#xff0c;用于存储一组相同类型的变量&#xff1b;而指针是一种特殊类型&#xff0c;专门用来存放数据的地址。数组名除了sizeof(数组名)和&数组名表示整个数组外&#xff0c;其他情况下都表示的是首元素的…...

安全区域边界

文章目录 安全区域边界边界防护跨边界流量通过受控接口通信非法内联非法外联限制无线网络 访问控制启用基于白名单的访问控制策略优化访问控制表根据五元组控制根据会话状态控制根据应用协议和内容控制 入侵防范外部发起的攻击内部发起的攻击对新型攻击防范及时检测攻击行为 恶…...

力扣每日一题 6/6

2938.区分黑球与白球[中等] 题目&#xff1a; 桌子上有 n 个球&#xff0c;每个球的颜色不是黑色&#xff0c;就是白色。 给你一个长度为 n 、下标从 0 开始的二进制字符串 s&#xff0c;其中 1 和 0 分别代表黑色和白色的球。 在每一步中&#xff0c;你可以选择两个相邻的…...

游戏心理学Day05

第三章 游戏即学习 《超级马里奥》是游戏史上的经典之作&#xff0c;我们都记得第一次踩到敌人&#xff0c;第一次顶碎砖块时的快乐&#xff0c;也记得为了通过某个关卡而付出的努力和艰辛。当我们掌握了规律和技巧之后&#xff0c;这些难题就不再是难题&#xff0c;因为我们习…...

【C、C++编译工具】CLion工具介绍与安装

一、问题 最近突发奇想想学学最开始接触的语言C&#xff0c;之前大学的时候用的更多的工具还是VC&#xff0c;工作后慢慢接触了CLion&#xff0c;跟pycharm其实差不多&#xff0c;都是集成开发环境&#xff08;IDE&#xff09; 解释&#xff1a;什么是 IDE&#xff1f; 根据计…...

LabVIEW中进行步进电机的位置控制

在LabVIEW中进行步进电机的位置控制&#xff0c;通常涉及以下几个关键步骤&#xff1a;设置硬件、配置通信、编写控制算法和实施反馈控制。以下是一个详细的介绍。 硬件设置 步进电机&#xff1a;选择合适的步进电机&#xff0c;根据负载和应用需求选择适当的步数和转矩。 驱…...

目标检测-AnyLabeling标注格式转换成YOLO格式

Anylabel可以极大的增加数据的标注效率&#xff0c;但是其标注格式如何能转换成YOLO标注格式&#xff0c;具体内容如下所示。 关于AnyLabeling的其它详细介绍如下链接所示 https://blog.csdn.net/u011775793/article/details/134918861 Github链接 https://github.com/vietanhd…...

MongoDB管理内存使用

优化MongoDB内存使用&#xff0c;可以通过一下几点来降低系统内存占用&#xff0c;本次主要配置WiredTiger Cache来实现 WiredTiger Cache&#xff1a; MongoDB 使用 WiredTiger 存储引擎&#xff0c;其缓存使用最近最少使用 (LRU) 算法管理。频繁访问的数据会保留在内存中&am…...

【Elasticsearch】IK分词器的下载及使用

安装IK分词器 网址&#xff1a;https://github.com/infinilabs/analysis-ik 3.1.在线安装ik插件&#xff08;较慢,不推荐&#xff09; # 进入容器内部 es为容器名称 docker exec -it es /bin/bash# 在线下载并安装 7.17.21为镜像版本要与之前保持一致 ./bin/elasticsearch-pl…...

Hyper-SD: diffusion实时出图,一步搞定,字节出品

Hyper-SD: diffusion实时出图&#xff0c;一步搞定&#xff0c;字节出品 先看效果 Real-Time Generation Demo of Hyper-SD. Abstract 近来&#xff0c;一系列面向扩散模型&#xff08;Diffusion Models&#xff0c;DM&#xff09;的迭代紧凑式传播推断算法陆续出现&#xf…...

:长亭雷池社区版动态防护体验测评

序 长亭雷池在最近发布了动态防护功能&#xff0c;据说可以动态加密保护网页前端代码和阻止爬虫行为、阻止漏洞扫描行为等。今天就来体验测试一下 WAF 是什么 WAF 是 Web Application Firewall 的缩写&#xff0c;也被称为 Web 应用防火墙。区别于传统防火墙&#xff0c;WAF …...

数据结构复习

基本概念和术语&#xff1a; 数据&#xff1a;是描述客观事物的符号&#xff0c;是计算机中可以操作的对象&#xff0c;是能被计算机识别&#xff0c;并输入给计算机处理的符号集合。 数据元素&#xff1a;是组成数据的&#xff0c;具有一定意义的基本单位&#xff0c;在计算机…...

小世界网络生成及其分析

研究背景: 小世界网络是一种介于规则网络和随机网络之间的网络模型,具有短平均路径和高聚集性的特点。这种网络模型被广泛应用于社交网络、互联网、生物网络等领域的研究中。研究小世界网络的生成和分析可以帮助我们理解和揭示复杂网络的结构和特性,以及网络中信息传播、动力…...

Flutter基础 -- Flutter布局练习(小项目)

目录 1. Splash 布局&#xff08;第一页&#xff09; 1.1 目标 1.2 当前效果图 1.3 创建 Splash 界面 1.4 设置 MaterialApp 1.5 设置 Splash 背景色 1.6 布局 Splash 界面 1.7 总结 2. Splash 圆角图片 2.1 目标 2.2 当前效果图 2.3 蓝湖下载图片 2.4 图片导入项…...

详解布隆过滤器,实现分布式布隆过滤器

什么是布隆过滤器&#xff1f; 原理 布隆过滤器是一种基于位数组&#xff08;bit array&#xff09;和多个哈希函数的数据结构。其核心原理是&#xff1a; 初始化一个长度为m的位数组&#xff0c;所有位初始化为0。使用k个不同的哈希函数将元素映射到位数组中的k个位置。当插…...

程序员职业素养:AI新时代下的机遇与挑战

目录 一、引言二、程序员职业素养的五大要点1. 技术能力2. 沟通能力3. 团队合作4. 责任心5. 敬业精神 三、实际案例解析四、程序员职业素养在实际工作中的应用五、AI新时代的程序员的职业发展建议六、总结七、结语 一、引言 在当今这个科技飞速发展的时代&#xff0c;程序员这…...

智能管理,无忧报修——高校校园报事报修系统小程序全解析

随着数字化、智能化的发展&#xff0c;高校生活也迎来了前所未有的变革。你是否还在为宿舍的水龙头漏水、图书馆的灯光闪烁而烦恼&#xff1f;你是否还在为报修流程繁琐、等待时间长而焦虑&#xff1f;今天&#xff0c;这一切都将成为过去式&#xff01;因为一款震撼高校圈的新…...

nc解决自定义参照字段前台保存后只显示主键的问题

nc解决自定义参照字段前台保存后只显示主键的问题 自定义参照类VoucherRefModel.java package nc.ui.jych.ref;import nc.ui.bd.ref.AbstractRefModel;/*** desc 凭证号参照* author hanh**/ public class VoucherRefModel extends AbstractRefModel {Overridepublic String[…...

鸿蒙全栈开发-一文读懂鸿蒙同模块不同模块下的UIAbility跳转详解

前言 根据第三方机构Counterpoint数据&#xff0c;截至2023年三季度末&#xff0c;HarmonyOS在中国智能手机操作系统的市场份额已经提升至13%。短短四年的时间&#xff0c;HarmonyOS就成长为仅次于安卓、苹果iOS的全球第三大操作系统。 因此&#xff0c;对于鸿蒙生态建设而言&a…...

【Python】使用 SQLObject orm 库快速将接口数据存入数据库

使用 SQLObject orm 库快速将接口数据存入数据库 文章目录 使用 SQLObject orm 库快速将接口数据存入数据库背景orm python 版本都有哪些&#xff1f; SQLObject 简单的使用 背景 因为测试需要&#xff0c;要将百万条数据接口查询数据存入数据库中&#xff0c;为了减少 mysql …...

@EnableResourceServer资源服务注解源码分析

文章目录 学习参考EnableResourceServer概要ResourceServerConfiguration属性定义configure(HttpSecurity)ResourceServerSecurityConfigurerinit(HttpSecurit)configure(HttpSecurity) 学习参考 Spring Security框架配置运行流程完整分析 - 【必看】 Security OAuth2 授权 &…...

SpringBoot实现图片文件上传和回显的两种方式

目录 一 功能需求 二 上传本地 2.1 实现文件上传的controller层 2.2 图片访问资源映射 二 上传OSS 一 功能需求 实现图片的上传和回显功能其实在业务中是非常常见的&#xff0c;比如需要上传头像&#xff0c;或者交易平台需要上传物品的图片等等&#xff0c;都需要上传和回…...

进程和计划任务以及步骤

进程 进程和程序有关&#xff0c;把该文件放到内存里&#xff0c;进程是动态的&#xff0c;不同时刻的状态不一样 内存&#xff1a;放置正在运行的程序和所需数据的位置 程序启动 ——》将相关文件和数据放到内存里 ——》进程&#xff08;processes&#xff09; 进程相关命令 …...

使用Python实现深度学习模型:序列到序列模型(Seq2Seq)

序列到序列&#xff08;Seq2Seq&#xff09;模型是一种深度学习模型&#xff0c;广泛应用于机器翻译、文本生成和对话系统等自然语言处理任务。它的核心思想是将一个序列&#xff08;如一句话&#xff09;映射到另一个序列。本文将详细介绍 Seq2Seq 模型的原理&#xff0c;并使…...

力扣283. 移动零

给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 示例 1: 输入: nums [0,1,0,3,12] 输出:[1,3,12,0,0] 示例 2: 输入: nums [0] …...