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

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

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

time_base.h

cpp 复制代码
/**********
 * @Author : 桃杬
 * @describe : 实现计时功能
 * @Data : 2024.06.08
***************/

#ifndef _TIM_BASE_H
#define _TIM_BASE_H

#include "stm32f10x.h"

#define BASE_TIM6 1
#define BASE_TIM7 0

void 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

cpp 复制代码
#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;
#endif

void BaseTim_Init(void)
{
    //定义TIM结构体
    TIM_TimeBaseInitTypeDef TIM_TimBaseInitStructure;
    
    //定义NVIC结构体
    NVIC_InitTypeDef NVIC_InitStructure;
    
    //使能时钟
#if BASE_TIM6
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6,ENABLE);
#endif
    
#if BASE_TIM7
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7,ENABLE);
#endif
    
    //配置TIM
    TIM_TimBaseInitStructure.TIM_Period = 7200-1;  //预分频系数 psc
    TIM_TimBaseInitStructure.TIM_Prescaler = 1000-1; //重装在值 arr
    
#if BASE_TIM6
    TIM_TimeBaseInit(TIM6,&TIM_TimBaseInitStructure); //初始化计数器
#endif

#if BASE_TIM7
    TIM_TimeBaseInit(TIM7,&TIM_TimBaseInitStructure); //初始化计数器
#endif

#if BASE_TIM6
    TIM_ClearFlag(TIM6,TIM_FLAG_Update); //清除中断标志位
    TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE); //开启计数器中断
#endif

#if BASE_TIM7
    TIM_ClearFlag(TIM7,TIM_FLAG_Update); //清除中断标志位
    TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE); //开启计数器中断
#endif

    //配置中断
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0); //设置中断组为0组
#if BASE_TIM6
    NVIC_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_TIM7
    NVIC_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_TIM6
    TIM_Cmd(TIM6,ENABLE);  //使能计数器
#endif

#if BASE_TIM7
    TIM_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

cpp 复制代码
#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 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 : GetKeyContinuousPressNum(void)
* @describe : 支持按键连续按下
* @param : void
* @return : 按键值
*       @para : 1 按键1; 2 按键2; 3 按键3; 4 按键4
* @note : 无
*****/
uint8_t GetKeyContinuousPressNum(void)
{
    Key_Scan();
    
    #if isEnableKey1
    if(Key1_Press)
        return 1;
    #endif
    
    #if isEnableKey2
    if(Key2_Press)
        return 2;
    #endif
    
    #if isEnableKey3
    if(Key3_Press)
        return 3;
    #endif
    
    #if isEnableKey4
    if(Key4_Press)
        return 4;
    #endif
    
    return 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 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) && (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通用定时器,只是当时笔者没想那么多,使用两个定时器确实造成资源浪费😂,大家只需要再设置一个记录时间的变量,每次进入中断要记录此次进入的时间,在调用时间查函数判断按键持续了多久,很简单的就能实现同样的功能,大家有兴趣的话可以再这个代码的基础上实现逻辑层的应用即可,也就是中断函数里面的部分,不需要大家大改动。

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

相关推荐
嵌入式详谈8 分钟前
基于STM32的智能风扇控制系统设计
stm32·单片机·嵌入式硬件
小小怪大梦想27 分钟前
RTC实时时钟
stm32·单片机·嵌入式硬件
水饺编程5 小时前
【英特尔IA-32架构软件开发者开发手册第3卷:系统编程指南】2001年版翻译,1-2
linux·嵌入式硬件·fpga开发
电子科技圈5 小时前
IAR全面支持国科环宇AS32X系列RISC-V车规MCU
人工智能·嵌入式硬件·mcu·编辑器
SZPU领跑5 小时前
第十二届蓝桥杯嵌入式省赛程序设计题解析(基于HAL库)(第一套)
stm32·单片机·算法·职场和发展·蓝桥杯
逢生博客6 小时前
Rust 语言开发 ESP32C3 并在 Wokwi 电子模拟器上运行(esp-hal 非标准库、LCD1602、I2C)
开发语言·后端·嵌入式硬件·rust
Tlog嵌入式8 小时前
蓝桥杯【物联网】零基础到国奖之路:十六. 扩展模块之矩阵按键
arm开发·stm32·单片机·mcu·物联网·蓝桥杯·iot
打地基的小白8 小时前
UART通信—基于江科大源码基础进行的改进和解析
单片机·嵌入式硬件·uart通信·代码详解
黄小美3219 小时前
STM32(五)GPIO输入硬件电路及C语言知识复习
stm32·单片机·嵌入式硬件
py.鸽鸽9 小时前
STM32
stm32·单片机·嵌入式硬件