深入探究单片机按键应用框架的按键乐章

引言

在嵌入式系统中,按键作为用户与设备交互的重要接口,其稳定可靠的响应是保证用户体验的关键。然而,按键开发面临着诸多挑战,其中包括需求不确定导致的频繁修改和各种按键触发方式的实现。为了解决这些问题,我们提出了一种创新的单片机按键应用框架,旨在简化按键开发流程、提高开发效率,以及增强按键系统的稳定性和灵活性。

按键开发的难点和痛点

在当今的嵌入式系统开发中,按键作为最常用的外设之一,扮演着与用户交互的关键角色。然而,如果在按键的前期开发过程中,框架的设计和实现不合理,往往会导致后续开发阶段面临一系列挑战和痛点。

1. 需求不确定性导致的频繁修改

在嵌入式系统开发中,按键功能的需求经常受到项目进展和用户反馈等因素的影响,这导致按键功能需求具有一定的不确定性。因此,在开发过程中经常需要对按键逻辑进行频繁修改,以满足新的需求和变化的要求。这种频繁修改按键逻辑的现象,不仅增加了开发人员的工作量,还可能导致开发进度的延迟和项目成本的增加。

2. 多样化的按键触发方式需求

在嵌入式系统中,不同的应用场景可能需要实现多种按键触发方式,这些触发方式包括短按、长按、双击等。每种触发方式都有其特定的应用场景和功能需求,因此,为了满足用户的多样化需求,按键应用框架需要具备灵活的触发方式设置功能

框架特性

我们提出的单片机按键应用框架具有以下特性:

1. 多种按键触发方式支持

  • 短按: 用户按下按键
  • 短按松开: 用户按下按键一小段时间后松开
  • 长按: 用户按下按键一段时间(时间可设置)
  • 长按松开: 用户按下按键一段时间后松开
  • 持续按: 用户按下按键并保持按住(时间可设置)
  • 持续按松开: 用户按下按键并保持按住,随后触发持续按后松开
  • 双击: 用户快速两次按下按键(双击间隔时间可设置)
  • 可扩展多击: 框架支持将多击事件扩展到更多次数
  • 2. 支持传统按键和AD按键

    该框架不仅支持常见的数字按键,还能够兼容模拟量输入,例如通过模拟信号产生的按键操作。这种称为模拟数字(AD)按键的输入方式,在一些场景下非常有用,尤其是需要连续调节、精确控制或模拟信号采集的应用中。

    3. 二进制事件映射成按键

    除了按键输入外,框架还支持将其他二进制事件映射成按键进行处理,增强了按键系统的灵活性。例如,可以将传感器的触发事件映射为按键事件,从而实现传感器触发时的特定操作。

    4. 按键使能功能

    框架提供按键使能功能,可以灵活控制按键的使用状态,适用于各种按键需求动态变化的场景。通过按键使能功能,用户可以根据具体需求,在运行时动态地启用或禁用某些按键,以实现灵活的按键管理和控制。

    5. 可设置时间

    该框架可支持每个按键定制不同的长按时间和持续按时间,使得按键触发的灵敏度和响应速度可以根据具体需求进行灵活调整,进一步提升了按键系统的定制性和适用性。

    程序核心代码解析

    以下是按键框架的核心代码解析:

    // 按键初始化函数
    void keyParaInit(keyCategory_t *keys)
    {
        if (NULL == keys)
            return;
        
        if (KEY_NUM >= KEY_MAX_NUMBER)
        {
            keyNum = KEY_MAX_NUMBER;
        }
        memcpy(keyTable, keys, sizeof(keyCategory_t) * keyNum);
    }
    
    // 按键扫描函数
    static bool readKeyStatus(keyFSM_t *Key_Buf)
    {
    
        if (!getKeyLevel(Key_Buf))
            return false;
    
        switch (Key_Buf->keyStatus)
        {
        // 状态 0: 未按下按键
        case KEY_NULL:
            if (Key_Buf->keyLevel == Bit_SET) 		// 按键按下
            {
                Key_Buf->keyStatus = KEY_SURE;
            }
            break;
        // 状态 1: 确认按下
        case KEY_SURE:
            if (Key_Buf->keyLevel == Bit_SET) 		// 确认按下按键
            {
                Key_Buf->eventType = DOWN_Event; 	// 触发按下事件
                Key_Buf->keyStatus = KEY_DOWN;
                Key_Buf->keyCount = 0; 				// 重置按键计数值
                Key_Buf->keyLongFlag = true;
            }
            else
            {
                Key_Buf->keyStatus = KEY_NULL;
            }
            break;
        // 状态 2: 按下按键
        case KEY_DOWN:
            if (Key_Buf->keyLevel != Bit_SET) 			// 松开按键
            {
                if (Key_Buf->keyShortFlag == false) 	// 按下按键置位标志位
                {
                    Key_Buf->keyShortFlag = true;
                    Key_Buf->keyFrequency++;  			// 按键次数加 1
                    Key_Buf->keyInterval = 0; 			// 按键间隔清零
                }
                if (keyClickFrequency(Key_Buf)) 		// 多击判断
                {
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_NULL;
                }
                if ((Key_Buf->keyCount >= Key_Buf->keyLongTime / DEBOUNCE_TIME) &&
                    (Key_Buf->keyCount < Key_Buf->keyLastTime / DEBOUNCE_TIME))
                {	// 按键长按释放
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_NULL;
                    Key_Buf->eventType = RELEASE_Event; // 触发长按释放事件 
                }
                Key_Buf->keyCount = 0; 					// 重置按键计数器
                Key_Buf->keyLongFlag = true;
            }
            else
            {
                if ((++Key_Buf->keyCount >= Key_Buf->keyLastTime / DEBOUNCE_TIME)) 
                {	// 超过设定时间没有释放
                    Key_Buf->keyCount = 0; 				// 重置按键计数器
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_LONG;
                    Key_Buf->eventType = LAST_Event; 	// 触发持续按事件
                    Key_Buf->keyLongFlag = true;
                }
                if ((Key_Buf->keyCount >= Key_Buf->keyLongTime / DEBOUNCE_TIME) && 
                    (Key_Buf->keyCount < Key_Buf->keyLastTime / DEBOUNCE_TIME))
                {	// 按下按键达到设定时间
                    Key_Buf->keyFrequency = 0;
                    if (Key_Buf->keyLongFlag == true)
                    {
                        Key_Buf->eventType = LONG_Event;// 触发长按事件
                        Key_Buf->keyLongFlag = false;
                    }
                }
                Key_Buf->keyShortFlag = false; 			// 按下按键置位标志位
            }
            break;
        // 状态 3: 持续按键长按状态
        case KEY_LONG:
            if (Key_Buf->keyLevel != Bit_SET) 			// 松开按键
            {
                Key_Buf->keyStatus = KEY_NULL;
                Key_Buf->eventType = RELEASE_Event; 	// 触发长按释放事件
            }
            break;
        default:
            break;
        }
        return true;
    }
    
    // 按键事件处理函数
    void keyHandle(void)
    {
        for (size_t i = 0; i < keyNum; i++)
        {
            if (keyTable[i].fsm.eventType == NULL_Event)
                continue;
            switch (keyTable[i].fsm.eventType)
            {
            case RELEASE_Event:
                if (keyTable[i].func.releasePressCb == NULL)
                    break;
                keyTable[i].func.releasePressCb();
                break;
            case SHORT_Event:
                if (keyTable[i].func.ShortPressCb == NULL)
                    break;
                keyTable[i].func.ShortPressCb();
                break;
            case DOWN_Event:
                if (keyTable[i].func.downPressCb == NULL)
                    break;
                keyTable[i].func.downPressCb();
                break;
            case LONG_Event:
                if (keyTable[i].func.longPressCb == NULL)
                    break;
                keyTable[i].func.longPressCb();
                break;
            case LAST_Event:
                if (keyTable[i].func.lastPressCb == NULL)
                    break;
                keyTable[i].func.lastPressCb();
                break;
            case DBCL_Event:
                if (keyTable[i].func.dbclPressCb == NULL)
                    break;
                keyTable[i].func.dbclPressCb();
                break;
            default:
                break;
            }
            if (keyTable[i].fsm.eventType != LAST_Event)
            {   // 持续按事件需要一直执行不需要复位事件
                keyTable[i].fsm.eventType = NULL_Event;
            }
        }
    }
    

    程序解析

    以下是对按键框架核心代码的详细解析:

    按键参数初始化函数(keyParaInit)
  • keyParaInit 函数用于初始化按键参数。
  • 参数 keys 是一个指向按键配置结构体数组的指针,用于初始化按键的数量和相关配置。
  • 如果传入的按键数量超过最大支持数量,函数会将按键数量限制为最大支持数量。
  • 使用 memcpy 函数将传入的按键配置结构体数组复制到内部的按键配置表中。
  • 按键扫描函数(readKeyStatus)
  • readKeyStatus 函数用于扫描按键状态,并根据状态变化触发相应的事件。
  • 参数 Key_Buf 是一个指向按键状态结构体的指针,用于保存按键的当前状态信息。
  • 函数首先通过 getKeyLevel 函数获取按键的电平状态,如果按键未按下则返回,否则根据按键状态执行相应的逻辑。
  • 根据按键状态的不同,分别处理按键的按下、释放、长按等事件,并触发相应的事件类型。
  • 按键事件处理函数(keyHandle)
  • keyHandle 函数用于处理按键事件,根据事件类型调用相应的回调函数进行处理。
  • 遍历按键配置表,对每个按键的事件类型进行判断,并根据事件类型调用相应的回调函数。
  • 如果事件类型为持续按事件,则不需要复位事件类型,保持事件类型不变。
  • 按键程序框架

    key.c

    /**
     * ***********************************************************
     * @file key.c
     * @author cyWU
     * @brief 按键应用框架
     * @version 0.2
     * @date 2024-01-29
     * @copyright 版权所有 2024
     * ***********************************************************
     */
    
    #include "key.h"
    #include <math.h>
    
    uint32_t keyCountTime; 				// 计时器
    uint8_t keyNum = KEY_NUM; 			// 按键数量
    keyCategory_t keyTable[KEY_NUM]; 	// 按键数组
    
    /**
     * @brief 判断按键是否被按下
     * @param [in] key :按键状态机全局结构指针
     * @return :  true,按键状态读取成功;
     *            false,按键未使能;
     */
    static bool getKeyLevel(keyFSM_t *key)
    {
        if (key->keyShield == KEY_DISABLE)
            return false;
        
        if (key->keyReadValue() == key->keyDownLevel)
        {
            key->keyLevel = Bit_SET;
        }
        else
        {
            key->keyLevel = Bit_RESET;
        }
        return true;
    }
    
    /**
     * @brief 判断按键是否单次或多次被按下
     * @param [in] click :按键状态机全局结构指针
     * @return : true,如果按键间隔时间超过,返回事件;
     *           false,如果按键不超过按键间隔,则不返回事件;
     */
    static bool keyClickFrequency(keyFSM_t *click)
    {
        click->keyInterval++;
        switch (click->keyFrequency)
        {
        case CLICK:
            if (click->keyInterval >= (KEY_INTERVAL / (DEBOUNCE_TIME * KEY_TIMER_MS)))
            {
                click->eventType = SHORT_Event; // 单击事件
                return true;
            }
            break;
        case DbLCLICK:
            if (click->keyInterval >= (KEY_INTERVAL / (DEBOUNCE_TIME * KEY_TIMER_MS)))
            {
                click->eventType = DBCL_Event; // 双击事件
                return true;
            }
            break;
        /*
        根据需要添加更多的点击事件
        case XXXX:
            if (click->keyInterval >= (KEY_INTERVAL / (DEBOUNCE_TIME * KEY_TIMER_MS)))
            {
                click->eventType = XXXXXX;
                return true;
            }
            break;
        */
        default:
            return true;
        }
        return false;
    }
    
    /**
     * @brief 读取按键值
     * @param [in] Key_Buf :按键状态机全局结构指针
     * @return : true,按键值获取成功;
     *           false,按键未使能;
     */
    static bool readKeyStatus(keyFSM_t *Key_Buf)
    {
    
        if (!getKeyLevel(Key_Buf))
            return false;
    
        switch (Key_Buf->keyStatus)
        {
        // 状态 0: 未按下按键
        case KEY_NULL:
            if (Key_Buf->keyLevel == Bit_SET) 		// 按键按下
            {
                Key_Buf->keyStatus = KEY_SURE;
            }
            break;
        // 状态 1: 确认按下
        case KEY_SURE:
            if (Key_Buf->keyLevel == Bit_SET) 		// 确认按下按键
            {
                Key_Buf->eventType = DOWN_Event; 	// 触发按下事件
                Key_Buf->keyStatus = KEY_DOWN;
                Key_Buf->keyCount = 0; 				// 重置按键计数值
                Key_Buf->keyLongFlag = true;
            }
            else
            {
                Key_Buf->keyStatus = KEY_NULL;
            }
            break;
        // 状态 2: 按下按键
        case KEY_DOWN:
            if (Key_Buf->keyLevel != Bit_SET) 			// 松开按键
            {
                if (Key_Buf->keyShortFlag == false) 	// 按下按键置位标志位
                {
                    Key_Buf->keyShortFlag = true;
                    Key_Buf->keyFrequency++;  			// 按键次数加 1
                    Key_Buf->keyInterval = 0; 			// 按键间隔清零
                }
                if (keyClickFrequency(Key_Buf)) 		// 多击判断
                {
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_NULL;
                }
                if ((Key_Buf->keyCount >= Key_Buf->keyLongTime / DEBOUNCE_TIME) &&
                    (Key_Buf->keyCount < Key_Buf->keyLastTime / DEBOUNCE_TIME))
                {	// 按键长按释放
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_NULL;
                    Key_Buf->eventType = RELEASE_Event; // 触发长按释放事件 
                }
                Key_Buf->keyCount = 0; 					// 重置按键计数器
                Key_Buf->keyLongFlag = true;
            }
            else
            {
                if ((++Key_Buf->keyCount >= Key_Buf->keyLastTime / DEBOUNCE_TIME)) 
                {	// 超过设定时间没有释放
                    Key_Buf->keyCount = 0; 				// 重置按键计数器
                    Key_Buf->keyFrequency = 0;
                    Key_Buf->keyStatus = KEY_LONG;
                    Key_Buf->eventType = LAST_Event; 	// 触发持续按事件
                    Key_Buf->keyLongFlag = true;
                }
                if ((Key_Buf->keyCount >= Key_Buf->keyLongTime / DEBOUNCE_TIME) && 
                    (Key_Buf->keyCount < Key_Buf->keyLastTime / DEBOUNCE_TIME))
                {	// 按下按键达到设定时间
                    Key_Buf->keyFrequency = 0;
                    if (Key_Buf->keyLongFlag == true)
                    {
                        Key_Buf->eventType = LONG_Event;// 触发长按事件
                        Key_Buf->keyLongFlag = false;
                    }
                }
                Key_Buf->keyShortFlag = false; 			// 按下按键置位标志位
            }
            break;
        // 状态 3: 持续按键长按状态
        case KEY_LONG:
            if (Key_Buf->keyLevel != Bit_SET) 			// 松开按键
            {
                Key_Buf->keyStatus = KEY_NULL;
                Key_Buf->eventType = RELEASE_Event; 	// 触发长按释放事件
            }
            break;
        default:
            break;
        }
        return true;
    }
    
    /**
     * @brief 按键事件处理函数
     * @param  None
     * @retval None
     */
    void keyEventProcess(void)
    {
        for (size_t i = 0; i < keyNum; i++)
        {
            if (!readKeyStatus(&keyTable[i].fsm))
                continue;
        }
    }
    
    /**
     * @brief 按键处理函数
     * @param  None
     * @retval None
     */
    void keyCheckProcess(void)
    {
        keyCountTime++;
        if (keyCountTime >= (DEBOUNCE_TIME / KEY_TIMER_MS))
        {
            keyCountTime = 0;
            keyEventProcess();
        }
    }
    
    /**
     * @brief 按键参数初始化函数
     * @param [in] keys :按键全局结构指针
     * @return none
     */
    void keyParaInit(keyCategory_t *keys)
    {
        if (NULL == keys)
        {
            return;
        }
        if (KEY_NUM >= KEY_MAX_NUMBER)
        {
            keyNum = KEY_MAX_NUMBER;
        }
        
        memcpy(keyTable, keys, sizeof(keyCategory_t) * keyNum);
    }
    
    /**
     * @brief 按键处理函数
     * @param  None
     * @retval None
     */
    void keyHandle(void)
    {
        for (size_t i = 0; i < keyNum; i++)
        {
            if (keyTable[i].fsm.eventType == NULL_Event)
                continue;
            switch (keyTable[i].fsm.eventType)
            {
            case RELEASE_Event:
                if (keyTable[i].func.releasePressCb == NULL)
                    break;
                keyTable[i].func.releasePressCb();
                break;
            case SHORT_Event:
                if (keyTable[i].func.ShortPressCb == NULL)
                    break;
                keyTable[i].func.ShortPressCb();
                break;
            case DOWN_Event:
                if (keyTable[i].func.downPressCb == NULL)
                    break;
                keyTable[i].func.downPressCb();
                break;
            case LONG_Event:
                if (keyTable[i].func.longPressCb == NULL)
                    break;
                keyTable[i].func.longPressCb();
                break;
            case LAST_Event:
                if (keyTable[i].func.lastPressCb == NULL)
                    break;
                keyTable[i].func.lastPressCb();
                break;
            case DBCL_Event:
                if (keyTable[i].func.dbclPressCb == NULL)
                    break;
                keyTable[i].func.dbclPressCb();
                break;
            default:
                break;
            }
            if (keyTable[i].fsm.eventType != LAST_Event)
            {   // 持续按事件需要一直执行不需要复位事件
                keyTable[i].fsm.eventType = NULL_Event;
            }
        }
    }
    

    key.h

    /**
     * ***********************************************************
     * @file key.h
     * @author cyWU
     * @brief 按键应用框架
     * @version 0.2
     * @date 2024-01-29
     * @copyright Copyright (c) 2024
     * ***********************************************************
     */
    
    #ifndef __KEY_H
    #define __KEY_H
    
    #include <stdint.h>
    #include <string.h>
    #include <stdbool.h>
    
    #define KEY_TIMER_MS 1     // 定时器周期(毫秒)
    #define KEY_MAX_NUMBER 12  // 最大支持按键数量
    #define DEBOUNCE_TIME 30   // 消抖时间(毫秒)
    #define KEY_INTERVAL 160   // 按键间隔时间(毫秒)
    
    #define CLICK 1    // 按键单击
    #define DbLCLICK 2 // 按键双击
    
    typedef enum
    {
        KEY_POWER,       // 电源按键
        KEY_CHARGE,      // 将充电检测口模拟成按键
        KEY_NUM,         // 按键数量,必须放在注册表的底部
    } keyList;
    
    typedef enum
    {
        Bit_RESET = 0,
        Bit_SET
    } BitAction_t;
    
    /* 按键状态枚举 */
    typedef enum
    {
        KEY_NULL,    // 按键无动作
        KEY_RELEASE, // 按键释放
        KEY_SURE,    // 按键抖动消除
        KEY_UP,      // 按键释放
        KEY_DOWN,    // 按键按下
        KEY_LONG,    // 按键长按
    } keyStatus_t;
    
    /* 按键事件枚举 */
    typedef enum
    {
        NULL_Event,    // 空事件
        DOWN_Event,    // 按下事件
        SHORT_Event,   // 短按事件
        LONG_Event,    // 长按事件
        LAST_Event,    // 连按事件
        DBCL_Event,    // 双击事件
        RELEASE_Event, // 释放事件
    } keyEvent_t;
    
    typedef enum
    {
        KEY_DISABLE = 0,
        KEY_ENABLE = !KEY_DISABLE
    } keyEnable_t;
    
    /* 按键状态机结构体 */
    __packed typedef struct
    {
        bool keyShortFlag;           // 按键短按标志
        bool keyLongFlag;            // 按键长按标志
        uint8_t keyInterval;         // 按键间隔时间
        uint8_t keyFrequency;        // 按键按下次数
        uint16_t keyLongTime;        // 按键长按时间
        uint16_t keyLastTime;        // 按键连按间隔时间
        uint32_t keyCount;           // 长按计数
        keyEnable_t keyShield;   	 // 按键使能状态
        BitAction_t keyLevel;        // 判断按键是否按下,按下: 1,松开: 0
        BitAction_t keyDownLevel;    // 按键按下时 IO 端口的电平状态
        keyStatus_t keyStatus;       // 按键当前状态
        keyEvent_t eventType;        // 按键事件类型
        uint8_t (*keyReadValue)(void); // 按键读取值函数指针
    } keyFSM_t;
    
    /* 不同事件对应的回调函数 */
    __packed typedef struct
    {
        void (*nullPressCb)(void);    // 无动作事件回调函数
        void (*releasePressCb)(void); // 释放事件回调函数
        void (*downPressCb)(void);    // 按下事件回调函数
        void (*ShortPressCb)(void);   // 短按事件回调函数
        void (*longPressCb)(void);    // 长按事件回调函数
        void (*lastPressCb)(void);    // 连按事件回调函数
        void (*dbclPressCb)(void);    // 双击事件回调函数
    } keyFunc_t;
    
    /* 按键类结构体 */
    __packed typedef struct
    {
        keyFSM_t fsm;     // 按键状态机
        keyFunc_t func;   // 按键事件回调函数
    } keyCategory_t;
    
    void keyParaInit(keyCategory_t *keys); // 按键参数初始化函数
    void keyCheckProcess(void);            // 按键检测处理函数
    void keyHandle(void);                   // 按键事件处理函数
    
    #endif
    

    使用示例

    // 读取电源按键值
    uint8_t key_power_read(void)
    {	// 返回电源按键的电平值
        return HAL_GPIO_ReadPin(KEY_POWER_GPIO_Port, KEY_POWER_Pin);
    }
    // 读取充电按键值
    uint8_t key_charge_read(void)
    {	// 返回充电按键的电平值
        return HAL_GPIO_ReadPin(CHARGE_GPIO_PORT, CHARGE_PIN);
    }
    // 电源按键短按回调函数
    void key_power_ShortPress(void)
    {
        printf("key_power_ShortPress");
    }
    // 电源按键长按回调函数
    void key_power_LongPress(void)
    {
        printf("key_power_LongPress");
    }
    // 充电按键按下回调函数
    void key_charge_InsertPress(void)
    {
        printf("key_charge_InsertPress");
    }
    // 充电按键释放回调函数
    void key_charge_ExtractPress(void)
    {
        printf("key_charge_ExtractPress");
    }
    
    // 按键初始化
    void user_key_Init(void)
    {
        /* 初始化KEY GPIO */
        // 在这里进行按键的GPIO初始化设置
    
        // 初始化KEY EVENT
        // 定义一个keyCategory_t类型的数组keys,用于存储每个按键的状态机和回调函数信息
        keyCategory_t keys[KEY_NUM] = {
            [KEY_POWER] = {
                // 电源按键 状态机初始化
                .fsm.keyShield = KEY_ENABLE,       // 按键使能
                .fsm.keyDownLevel = Bit_RESET,     // 按键按下时的电平
                .fsm.eventType = NULL_Event,       // 按键事件类型
                .fsm.keyLongTime = 2000,           // 长按时间阈值(毫秒)
                .fsm.keyLastTime = 5000,           // 持续按下时间阈值(毫秒)
                .fsm.keyReadValue = key_power_read,// 读取按键值的函数指针
                // 电源按键 回调函数初始化
                .func.ShortPressCb = key_power_ShortPress, // 短按回调函数
                .func.longPressCb = key_power_LongPress,   // 长按回调函数
            },
            [KEY_CHARGE] = {
                // 充电 状态机初始化(将充电检测模拟成按键)
                .fsm.keyShield = KEY_ENABLE,         // 按键使能
                .fsm.keyDownLevel = Bit_SET,         // 按键按下时的电平
                .fsm.eventType = RELEASE_Event,      // 按键事件类型
                .fsm.keyLongTime = 1000,             // 长按时间阈值(毫秒)
                .fsm.keyLastTime = 5000,             // 持续按下时间阈值(毫秒)
                .fsm.keyReadValue = key_charge_read, // 读取按键值的函数指针
                // 充电 回调函数初始化
                .func.downPressCb = key_charge_InsertPress, 	// 按键按下回调函数
                .func.releasePressCb = key_charge_ExtractPress, // 按键释放回调函数
            },
        };
    
        // 调用keyParaInit函数进行按键参数初始化
        keyParaInit(keys);
    }
    
    // 定时器1ms中断
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {	// 定时器14初始化1ms中断
        if (htim->Instance == TIM14)
        {	// 在中断中进行按键扫描
            keyCheckProcess();
        }
    }
    
    int main(void)
    {
        user_key_Init();	// 初始化按键
        while(1)
        {
            keyHandle();    // 按键处理函数
        }
    }
    

    程序解析

    以下是对新增的按键处理函数、按键初始化函数以及主函数的详细解析:

    读取电源按键值函数(key_power_read)
  • key_power_read 函数用于读取电源按键的电平值。
  • 通过调用 HAL_GPIO_ReadPin 函数获取电源按键的电平值,并返回该值。
  • 读取充电按键值函数(key_charge_read)
  • key_charge_read 函数用于读取充电按键的电平值。
  • 通过调用 HAL_GPIO_ReadPin 函数获取充电按键的电平值,并返回该值。
  • 电源按键短按回调函数(key_power_ShortPress)
  • key_power_ShortPress 函数用于处理电源按键的短按事件。
  • 当电源按键短按时,将打印 “key_power_ShortPress” 字符串。
  • 电源按键长按回调函数(key_power_LongPress)
  • key_power_LongPress 函数用于处理电源按键的长按事件。
  • 当电源按键长按时,将打印 “key_power_LongPress” 字符串。
  • 充电按键按下回调函数(key_charge_InsertPress)
  • key_charge_InsertPress 函数用于处理充电按键按下事件。
  • 当充电按键按下时,将打印 “key_charge_InsertPress” 字符串。
  • 充电按键释放回调函数(key_charge_ExtractPress)
  • key_charge_ExtractPress 函数用于处理充电按键释放事件。
  • 当充电按键释放时,将打印 “key_charge_ExtractPress” 字符串。
  • 按键初始化函数(user_key_Init)
  • user_key_Init 函数用于初始化按键。
  • 首先进行按键的GPIO初始化设置。
  • 然后定义了一个包含按键状态机和回调函数信息的数组 keys,并对每个按键进行了状态机参数和回调函数的初始化。
  • 最后调用 keyParaInit 函数进行按键参数的初始化。
  • 定时器1ms中断回调函数(HAL_TIM_PeriodElapsedCallback)
  • HAL_TIM_PeriodElapsedCallback 函数用于定时器1ms中断的回调处理。
  • 在该函数中进行了按键扫描,通过调用 keyCheckProcess 函数实现。
  • 主函数(main)
  • 主函数中首先调用 user_key_Init 函数,初始化按键。
  • 然后进入一个无限循环,在循环中持续调用 keyHandle 函数来处理按键事件。
  • 以上程序是一个按键处理的示例程序,包括了按键的初始化、按键状态检测、按键事件回调等功能。用户可以根据自己的需求进行修改和扩展,以实现特定的按键应用逻辑。

    结论

    通过以上按键处理框架,我们实现了一个可靠、灵活的按键系统,具有以下优势和特点:

    1. 简化开发流程: 提供了按键初始化函数和事件处理函数,用户只需简单调用这些函数,即可快速搭建起按键系统,无需从头编写复杂的按键处理逻辑,大大节省了开发时间和精力。

    2. 提高开发效率: 按键框架封装了按键状态机的实现细节,用户只需关注按键的功能和回调函数的实现,无需过多关注底层细节,使得开发过程更加高效。

    3. 增强系统稳定性: 按键状态机设计合理,采用了状态转换机制和事件驱动模型,能够有效地处理按键的各种状态和事件,提高了系统的稳定性和可靠性。

    4. 提升系统灵活性: 用户可以根据实际需求自定义按键的状态机参数和回调函数,灵活配置按键的功能和行为,满足不同场景下的按键应用需求,具有较高的灵活性和可定制性。

    综上所述,通过以上框架,为用户提供了一个可靠、灵活的按键处理方案,帮助用户快速搭建按键系统,提高了开发效率,增强了系统稳定性和灵活性,为用户提供了更好的按键体验。

    作者:阿胡不秃头

    物联沃分享整理
    物联沃-IOTWORD物联网 » 深入探究单片机按键应用框架的按键乐章

    发表评论