STM32f103实现按键长按 短按 双击

注:刚刚优化了这个bug,写在了下篇文章中(下篇文章)🐱‍🐉。

今天来分享一个使用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内部时钟,可以不配置,默认是一直开着的
    
    //配置TIM
    TIM_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中断,寻去中断更新
    
    //配置NVIC
    NVIC_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 isEnableKey1
        RCC_APB2PeriphClockCmd(KEY1_GPIO_CLK,ENABLE); //key1所在的时钟
    #endif
    #if isEnableKey2
        RCC_APB2PeriphClockCmd(KEY2_GPIO_CLK,ENABLE); //key2所在的时钟
    #endif
    #if isEnableKey3
        RCC_APB2PeriphClockCmd(KEY3_GPIO_CLK,ENABLE); //key3所在的时钟
    #endif
    #if isEnableKey4
        RCC_APB2PeriphClockCmd(KEY4_GPIO_CLK,ENABLE); //key4所在的时钟
    #endif
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
    
    //配置KEY
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    
    #if isEnableKey1
        GPIO_InitStructure.GPIO_Pin = KEY1_GPIO_PIN;
        GPIO_Init(KEY1_GPIO_PORT,&GPIO_InitStructure);
    #endif
    
    #if isEnableKey2
        GPIO_InitStructure.GPIO_Pin = KEY2_GPIO_PIN;
        GPIO_Init(KEY2_GPIO_PORT,&GPIO_InitStructure);
    #endif
    
    #if isEnableKey3
        GPIO_InitStructure.GPIO_Pin = KEY3_GPIO_PIN;
        GPIO_Init(KEY3_GPIO_PORT,&GPIO_InitStructure);
    #endif
    
    #if isEnableKey4
        GPIO_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 isEnableKey1
    if(KEY1 == RESET) //检测到按键1
    {
        delay_ms(20); //消抖
        if(KEY1 == RESET) //再次检测按键1
        {
            Key1_Press = 1; //记录此时按键状态
        }
    }else
        Key1_Press = 0;
    #endif
    
    #if isEnableKey2
    if(KEY2 == RESET) //检测到按键2
    {
        delay_ms(20); //消抖
        if(KEY2 == RESET) //再次检测按键2
            Key2_Press = 1; //记录此时按键状态
    }else
        Key2_Press = 0;
    #endif
    
    #if isEnableKey3
    if(KEY3 == RESET) //检测到按键3
    {
        delay_ms(20); //消抖
        if(KEY3 == RESET) //再次检测按键3
            Key3_Press = 1; //记录此时按键状态
    }else
        Key3_Press = 0;
    #endif
    
    #if isEnableKey4
    if(KEY4 == SET) //检测到按键4  注意的是按键四是高电平检测到按键按下
    {
        delay_ms(20); //消抖
        if(KEY4 == SET) //再次检测按键4
            Key4_Press = 1; //记录此时按键状态
    }else
        Key4_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 isEnableKey1
            case 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 isEnableKey2
            case 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 isEnableKey3
            case 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 isEnableKey4
            case 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;
            #endif
            
            default:
                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); //初始化USART
    TIME2_Init(10000,72); //初始TIM2
    Key_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;
        }
    }
}

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

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值