跳转至

key开发指南

概述

按键系统支持两种类型按键:

  • Power Key按键:电源键,默认支持
  • Function Key按键:功能键,可通过GPIO外接按键实现,最多可扩展7个

按键类型示意

**按键类型示意**

支持的按键事件类型:

  • 单击(CLICKUP)
  • 双击(CLICKUP_2)
  • 短按(LONGPRESS_1S)
  • 长按(LONGPRESS_5S)

快速验证板载按键

社区固件的产品输入模块已在启动阶段初始化 Power Key 并注册按键消息链路,因此无需再创建一套会重复占用定时器和 GPIO 回调的 demo。烧录社区固件并进入应用列表后,依次进行以下验证:

  1. 单击 Power Key,确认屏幕亮灭状态发生变化。
  2. 双击 Power Key,确认系统进入应用列表或执行当前产品定义的双击动作。
  3. 长按 Power Key,确认系统产生长按动作;具体动作由产品层当前注册的 BUTTON_LONGPRESS_1SBUTTON_LONGPRESS_5S 处理逻辑决定。

板载 Power Key 不依赖额外硬件。Function Key 属于扩展 GPIO 按键,只有在接入按键、上拉/下拉和消抖电路并完成引脚映射后才能验证;不要为了测试 Function Key 改写 Power Key 的现有回调。

功能描述

按键模块架构

按键模块架构

按键模块提供的接口:

接口声明 功能说明
uapi_button_init() 初始化按键模块
uapi_button_register() 注册按键引脚映射
uapi_button_gpio_register() 注册 GPIO 按键
uapi_button_send_msg_register() 注册消息发送函数
uapi_timer_init() 初始化定时器
uapi_timer_adapter() 适配定时器
uapi_timer_create() 创建定时器

开发指引

按键初始化

errcode_t uapi_button_init(void)
{
    errcode_t ret = 0;
    ret = uapi_timer_init();
    ret |= uapi_timer_adapter(1, TIMER_1_IRQN, irq_prio(TIMER_1_IRQN)); /* 使用timer1 */
    if (ret != ERRCODE_SUCC) {
        PRINT("uapi_timer_init FAILED 0x%x", ret);
        return ret;
    }
    ret = uapi_timer_create(1, &g_button_timer); /* 使用timer1 */
    if (ret != ERRCODE_SUCC) {
        PRINT("uapi_timer_create FAILED 0x%x", ret);
        return ret;
    }
    return ERRCODE_SUCC;
}

注册按键引脚映射

errcode_t uapi_button_register(button_map_t* button_map)
{
    errcode_t ret = 0;
    if (button_map == NULL) {
        return ERRCODE_FAIL;
    }
    button_peripheral_api *button_api = button_port_get_api();
    for (uint8_t i = 0; i < BUTTON_PIN_MAP_MAX; i++) {
        g_button_pin_map[i].pin = button_map[i].pin;
        g_button_pin_map[i].button = button_map[i].button;
        if (button_map[i].pin != PIN_NONE && button_map[i].button != BUTTON_MAX) {
            if (button_map[i].button == BUTTON_PWR) {
                button_api->button_pmu_pwr_cfg(true);
            }
            ret |= uapi_button_gpio_register(g_button_pin_map[i].pin);
        } else if (button_map[i].pin == PIN_NONE && button_map[i].button == BUTTON_PWR) {
            ret = button_api->button_register_callback(g_button_pin_map[i].pin,
                                                      (osal_irq_handler)button_irq_callback);
            if (ret != ERRCODE_SUCC) {
                PRINT("button register failed\r\n");
                return ret;
            }
            continue;
        }
    }
    return ret;
}

注册按键回调

button_map_t g_brandy_button_pin_map[BUTTON_PIN_MAP_MAX] = {
    { PIN_NONE, BUTTON_PWR },    // 电源键
    { S_MGPIO25, BUTTON_1 },     // GPIO按键1
    { PIN_NONE, BUTTON_2 },      // 可继续扩展
    { PIN_NONE, BUTTON_3 },
    { PIN_NONE, BUTTON_4 },
    { PIN_NONE, BUTTON_5 },
    { PIN_NONE, BUTTON_6 },
    { PIN_NONE, BUTTON_7 },
};

// 调用注册
uapi_button_register(g_brandy_button_pin_map);

注册消息发送函数

void uapi_button_send_msg_register(msg_send_callback_t cb)
{
    if (cb != NULL) {
        g_msg_send_cb = cb;
    }
}

配置电源键模式

Power Key支持设置强制触发事件:

typedef enum {
    PDMODE_RE_UP,   // 重启
    PDMODE_DOWN,    // 关机
    PDMODE_MAX
} power_mode_t;

// 设置为关机模式
// 具体接口请参考电源管理模块源码

// 设置为重启模式(默认)
// 具体接口请参考电源管理模块源码

配置电源键触发时间

Power Key支持设置触发事件的时间阈值:

typedef enum {
    PDTIME_LEVEL0,  // 10秒
    PDTIME_LEVEL1,  // 11秒(默认)
    PDTIME_LEVEL2,  // 12秒
    PDTIME_LEVEL3,  // 13秒
    PDTIME_LEVELMAX
} power_time_t;

// 设置为12秒触发
// 具体接口请参考电源管理模块源码

说明: 由于受片内ULP 32K时钟源精度影响,在0℃~55℃温度范围内按键触发时间误差约为0%~16%。

GPIO按键适配步骤

1. 配置按键引脚映射

修改button_feature.c中的g_brandy_button_pin_map

button_map_t g_brandy_button_pin_map[BUTTON_PIN_MAP_MAX] = {
    { PIN_NONE, BUTTON_PWR },    // 电源键(默认支持)
    { S_MGPIO25, BUTTON_1 },     // GPIO按键1
    { S_MGPIO26, BUTTON_2 },     // GPIO按键2
    { PIN_NONE, BUTTON_3 },
    { PIN_NONE, BUTTON_4 },
    { PIN_NONE, BUTTON_5 },
    { PIN_NONE, BUTTON_6 },
    { PIN_NONE, BUTTON_7 },
};

2. 配置GPIO管脚IE(低功耗版本)

board_evb.h 中修改对应管脚的 IE 配置:

// 原始配置
{ HAL_PIO_FUNC_GPIO, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_DISABLE },

// 修改为(开启输入使能)
{ HAL_PIO_FUNC_GPIO, HAL_PIO_DRIVE_MAX, HAL_PIO_PULL_MAX, HAL_PIO_IE_ENABLE },

3. 新增按键事件枚举

button_feature.h 中新增按键事件枚举:

typedef enum {
    /* power key */
    BUTTON_PWR_START = 0,
    BUTTON_PWR_CLICKUP,
    BUTTON_PWR_CLICKUP_2,
    BUTTON_PWR_LONGPRESS_1S,
    BUTTON_PWR_LONGPRESS_5S,
    BUTTON_PWR_INVALID,

    /* function key */
    BUTTON1_START,
    BUTTON1_CLICKUP,
    BUTTON1_CLICKUP_2,
    BUTTON1_LONGPRESS_1S,
    BUTTON1_LONGPRESS_5S,
    BUTTON1_INVALID,

    /* new key */
    BUTTON2_START,
    BUTTON2_CLICKUP,
    BUTTON2_CLICKUP_2,
    BUTTON2_LONGPRESS_1S,
    BUTTON2_LONGPRESS_5S,
    BUTTON2_INVALID,

    BUTTON_BUTT
} event_button_t;

4. 实现按键事件处理

button_event_get 接口中新增事件处理如下,默认只有 BUTTON_1 事件。例如要新增两个 GPIO 按键,需要在 BUTTON1 事件处理之后新增一组 BUTTON2 的事件处理,以此类推:

static event_button_t button_event_get(button_id_t id, button_report_state_t state)
{
    event_button_t event = BUTTON_PWR_START;

    if (id == BUTTON_1) {
        event = BUTTON1_START;
    }

    /* new key */
    if (id == BUTTON_2) {
        event = BUTTON2_START;
    }
    // ... 更多按键处理
    return event;
}

说明: 若只适配一个GPIO按键,完成步骤1和2即可,否则需要全部步骤适配完成。

注意事项

说明: 按键模块接口详细说明请参考上方接口表格中的头文件声明。

常见问题

问题 解决方案
按键无响应 检查GPIO管脚配置和IE设置是否正确
多按键冲突 检查GPIO管脚是否被其他功能占用
低功耗下按键失效 确保GPIO管脚IE配置正确
事件误触发 调整消抖时间和按键事件阈值
Power Key无法关机 检查电源管理模块配置是否为关机模式
长按触发时间不准 检查电源管理模块配置,考虑32K时钟精度误差

按键事件时间说明

按键事件时间说明