按键乐章:单片机按键应用框架探究

引言

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

按键开发的难点和痛点

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

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

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

  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值