按键

[English]

按键组件实现了 GPIO 和 ADC 两种按键,并允许同时创建两种不同的按键。下图显示了两种按键的硬件设计:

../_images/button_hardware.png
  • GPIO 按键优点有:每一个按键占用独立的 IO,之间互不影响,稳定性高;缺点有:按键数量多时占用太多 IO 资源。

  • ADC 按键优点有:可多个按键共用一个 ADC 通道,占用 IO 资源少;缺点有:不能同时按下多按键,当按键因氧化等因素导致闭合电阻增大时,容易误触,稳定性不高。

备注

  • GPIO 按键需注意上下拉问题,组件内部会启用芯片内部的上下拉电阻,但是在仅支持输入的 IO 内部没有电阻, 需要外部连接

  • ADC 按键需注意电压不能超过 ADC 量程。

按键事件

每个按键拥有下表的 8 个事件:

事件

触发条件

BUTTON_PRESS_DOWN

按下

BUTTON_PRESS_UP

弹起

BUTTON_PRESS_REPEAT

按下弹起次数 >= 2次

BUTTON_PRESS_REPEAT_DONE

重复按下结束

BUTTON_SINGLE_CLICK

按下弹起 1 次

BUTTON_DOUBLE_CLICK

按下弹起 2 次

BUTTON_MULTIPLE_CLICK

指定重复按下次数 N 次,达成时触发

BUTTON_LONG_PRESS_START

按下时间达到阈值的瞬间

BUTTON_LONG_PRESS_HOLD

长按期间一直触发

BUTTON_LONG_PRESS_UP

长按弹起

BUTTON_PRESS_REPEAT_DONE

多次按下弹起结束

BUTTON_PRESS_END

表示 button 此次检测已结束

每个按键可以有 回调轮询 两种使用方式:

  • 回调:一个按键的每个事件都可以为其注册一个回调函数,产生事件时回调函数将会被调用。这种方式的效率和实时性高,不会丢失事件。

  • 轮询:在程序中周期性调用 iot_button_get_event() 查询按键当前的事件。这种方式使用简单,适合任务简单的场合

当然你也可以将以上两种方式组合使用。

注意

回调函数中不能有 TaskDelay 等阻塞的操作

Button

配置项

  • BUTTON_PERIOD_TIME_MS : 扫描周期

  • BUTTON_DEBOUNCE_TICKS : 消抖次数

  • BUTTON_SHORT_PRESS_TIME_MS : 连续短按有效时间

  • BUTTON_LONG_PRESS_TIME_MS : 长按有效时间

  • ADC_BUTTON_MAX_CHANNEL : ADC 按钮的最大通道数

  • ADC_BUTTON_MAX_BUTTON_PER_CHANNEL : ADC 一个通道最多的按钮数

  • ADC_BUTTON_SAMPLE_TIMES : 每次扫描的样本数

  • BUTTON_SERIAL_TIME_MS : 长按期间触发的 CALLBACK 间隔时间

  • BUTTON_LONG_PRESS_TOLERANCE_MS : 用于设置长按的容错时间。

应用示例

创建按键

// create gpio button
button_config_t gpio_btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .gpio_button_config = {
        .gpio_num = 0,
        .active_level = 0,
    },
};
button_handle_t gpio_btn = iot_button_create(&gpio_btn_cfg);
if(NULL == gpio_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create adc button
button_config_t adc_btn_cfg = {
    .type = BUTTON_TYPE_ADC,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .adc_button_config = {
        .adc_channel = 0,
        .button_index = 0,
        .min = 100,
        .max = 400,
    },
};
button_handle_t adc_btn = iot_button_create(&adc_btn_cfg);
if(NULL == adc_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create matrix keypad button
button_config_t matrix_button_cfg = {
    .type = BUTTON_TYPE_MATRIX,
    .long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
    .short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
    .matrix_button_config = {
        .row_gpio_num = 0,
        .col_gpio_num = 1,
    }
};
button_handle_t matrix_button = iot_button_create(&matrix_button_cfg);
if(NULL == matrix_button) {
    ESP_LOGE(TAG, "Button create failed");
}

备注

当 IDF 版本大于等于 release/5.0 时, ADC 按钮使用的是 ADC1 ,当项目中还有其他地方使用到了 ADC1 时,请传入 adc_handle 和 adc_channel 来配置 ADC 按钮。

注册回调函数

Button 组件支持为多个事件注册回调函数,每个事件都可以注册一个回调函数,当事件发生时,回调函数将会被调用。

其中,

  • BUTTON_LONG_PRESS_STARTBUTTON_LONG_PRESS_UP 支持设置特殊的长按时间。

  • BUTTON_MULTIPLE_CLICK 支持设置多次按下的次数。

  • 简单写法

    static void button_single_click_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_SINGLE_CLICK");
    }
    
    iot_button_register_cb(gpio_btn, BUTTON_SINGLE_CLICK, button_single_click_cb,NULL);
    
  • 多个回调函数写法

    static void button_long_press_1_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START_1");
    }
    
    static void button_long_press_2_cb(void *arg,void *usr_data)
    {
        ESP_LOGI(TAG, "BUTTON_LONG_PRESS_START_2");
    }
    button_event_config_t cfg = {
        .event = BUTTON_LONG_PRESS_START,
        .event_data.long_press.press_time = 2000,
    };
    
    iot_button_register_event_cb(gpio_btn, cfg, button_long_press_1_cb, NULL);
    
    cfg.event_data.long_press.press_time = 5000;
    iot_button_register_event_cb(gpio_btn, cfg, button_long_press_2_cb, NULL);
    

查询按键事件

button_event_t event;
event = iot_button_get_event(button_handle);

动态修改按键默认值

iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, 5000);

低功耗支持

在 light_sleep 模式下,esp_timer 定时器会定时触发,导致 cpu 整体功耗居高不下。为了解决这个问题,button 组件提供了低功耗模式。

所需配置:

  • 打开 CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE 选项, 会在组件中增加低功耗相关代码

  • 确保创建的所有按键类型为 GPIO 按键, 并且都开启了 enable_power_save,如存在其他按键,会导致低功耗模式失效

备注

该功能只保证 Button 组件只在使用中才唤醒 CPU, 不保证 CPU 一定会进入低功耗模式

功耗对比:

  • 未开启低功耗模式,按下一次按键

    未开启低功耗模式,一次按下
  • 开启低功耗模式,按下一次按键

    开启低功耗模式,一次按下

因为 GPIO 唤醒 CPU, 仅支持电平触发,所以当按键为工作电平时,CPU 会支持的被唤醒,取决于按下去的时长,因此在低功耗模式下,单次按下的平均电流高于未开启低功耗模式。但是在大的工作周期中,会比未开启低功耗模式更加省电。

  • 未开启低功耗模式下,在 4s 内按下三次按键

    非低功耗模式下,在 4s 内按下三次按键
  • 低功耗模式下,在 4s 内按下三次按键

    低功耗模式下,在 4s 内按下三次按键

如图,低功耗模式下更加的省电。

button_config_t btn_cfg = {
    .type = BUTTON_TYPE_GPIO,
    .gpio_button_config = {
        .gpio_num = button_num,
        .active_level = BUTTON_ACTIVE_LEVEL,
        .enable_power_save = true,
    },
};
button_handle_t btn = iot_button_create(&btn_cfg);

什么时候进入 Light Sleep

  • 使用 Auto Light Sleep: 会在 button 自动关闭 esp_timer 后进入 Light Sleep

  • 用户控制 Light Sleep: 需要在 enter_power_save_cb 回调到来时进入 Light Sleep

void btn_enter_power_save(void *usr_data)
{
    ESP_LOGI(TAG, "Can enter power save now");
}

button_power_save_config_t config = {
    .enter_power_save_cb = btn_enter_power_save,
};

iot_button_register_power_save_cb(&config);

开启 CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP 选项后,如何正常使用按键?

  • 开启这个宏后,GPIO 模块会下电,如果需要使用按键功能,必须选用 RTC/LP GPIO,并将唤醒源修改为 EXT 1

GPIO 类型

是否开启 CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP

唤醒源

数字管脚

N

GPIO 电平触发

数字管脚

Y

RTC/LP 管脚

N

GPIO 电平触发 / EXT 1

RTC/LP 管脚

Y

EXT 1

备注

ESP32-C5, ESP32-C6 的 LP GPIO 可以支持 GPIO 电平唤醒和 EXT 1 唤醒,同时也需要开启 gpio_hold_en

开启和关闭

组件支持在任意时刻开启和关闭。

// stop button
iot_button_stop();
// resume button
iot_button_resume();

API Reference

Header File

Functions

button_handle_t iot_button_create(const button_config_t *config)

Create a button.

参数

config – pointer of button configuration, must corresponding the button type

返回

A handle to the created button, or NULL in case of error.

esp_err_t iot_button_delete(button_handle_t btn_handle)

Delete a button.

参数

btn_handle – A button handle to delete

返回

  • ESP_OK Success

  • ESP_FAIL Failure

esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data)

Register the button event callback function.

参数
  • btn_handle – A button handle to register

  • event – Button event

  • cb – Callback function.

  • usr_data – user data

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

  • ESP_ERR_NO_MEM No more memory allocation for the event

esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb, void *usr_data)

Register the button event callback function.

参数
  • btn_handle – A button handle to register

  • event_cfg – Button event configuration

  • cb – Callback function.

  • usr_data – user data

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback is already registered. No free Space for another Callback.

  • ESP_ERR_NO_MEM No more memory allocation for the event

esp_err_t iot_button_unregister_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb)

Unregister the button event callback function. In case event_data is also passed it will unregister function for that particular event_data only.

参数
  • btn_handle – A button handle to unregister

  • event_cfg – Button event

  • cb – callback to unregister

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE The Callback was never registered with the event

esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)

Unregister all the callbacks associated with the event.

参数
  • btn_handle – A button handle to unregister

  • event – Button event

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

  • ESP_ERR_INVALID_STATE No callbacks registered for the event

size_t iot_button_count_cb(button_handle_t btn_handle)

counts total callbacks registered

参数

btn_handle – A button handle to the button

返回

  • 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.

  • ESP_ERR_INVALID_ARG if btn_handle is invalid

size_t iot_button_count_event(button_handle_t btn_handle, button_event_t event)

how many callbacks are registered for the event

参数
  • btn_handle – A button handle to the button

  • event – Button event

返回

  • 0 if no callbacks registered, or 1 .. (BUTTON_EVENT_MAX-1) for the number of Registered Buttons.

  • ESP_ERR_INVALID_ARG if btn_handle is invalid

button_event_t iot_button_get_event(button_handle_t btn_handle)

Get button event.

参数

btn_handle – Button handle

返回

Current button event. See button_event_t

const char *iot_button_get_event_str(button_event_t event)

Get the string representation of a button event.

This function returns the corresponding string for a given button event. If the event value is outside the valid range, the function returns error string “event value is invalid”.

参数

event[in] The button event to be converted to a string.

返回

  • Pointer to the event string if the event is valid.

  • ”invalid event” if the event value is invalid.

esp_err_t iot_button_print_event(button_handle_t btn_handle)

Log the current button event as a string.

This function prints the string representation of the current event associated with the button.

参数

btn_handle[in] Handle to the button object.

返回

  • ESP_OK: Successfully logged the event string.

  • ESP_FAIL: Invalid button handle.

uint8_t iot_button_get_repeat(button_handle_t btn_handle)

Get button repeat times.

参数

btn_handle – Button handle

返回

button pressed times. For example, double-click return 2, triple-click return 3, etc.

uint32_t iot_button_get_ticks_time(button_handle_t btn_handle)

Get button ticks time.

参数

btn_handle – Button handle

返回

Actual time from press down to up (ms).

uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)

Get button long press hold count.

参数

btn_handle – Button handle

返回

Count of trigger cb(BUTTON_LONG_PRESS_HOLD)

esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value)

Dynamically change the parameters of the iot button.

参数
  • btn_handle – Button handle

  • param – Button parameter

  • value – new value

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is invalid.

uint8_t iot_button_get_key_level(button_handle_t btn_handle)

Get button key level.

参数

btn_handle – Button handle

返回

  • 1 if key is pressed

  • 0 if key is released or invalid button handle

esp_err_t iot_button_resume(void)

resume button timer, if button timer is stopped. Make sure iot_button_create() is called before calling this API.

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE timer state is invalid.

esp_err_t iot_button_stop(void)

stop button timer, if button timer is running. Make sure iot_button_create() is called before calling this API.

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE timer state is invalid

Unions

union button_event_data_t
#include <iot_button.h>

Button events data.

Public Members

struct button_event_data_t::long_press_t long_press

long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP

struct button_event_data_t::multiple_clicks_t multiple_clicks

multiple clicks struct, for event BUTTON_MULTIPLE_CLICK

struct long_press_t
#include <iot_button.h>

Long press time event data.

Public Members

uint16_t press_time

press time(ms) for the corresponding callback to trigger

struct multiple_clicks_t
#include <iot_button.h>

Multiple clicks event data.

Public Members

uint16_t clicks

number of clicks, to trigger the callback

Structures

struct button_event_config_t

Button events configuration.

Public Members

button_event_t event

button event type

button_event_data_t event_data

event data corresponding to the event

struct button_custom_config_t

custom button configuration

Public Members

uint8_t active_level

active level when press down

esp_err_t (*button_custom_init)(void *param)

user defined button init

uint8_t (*button_custom_get_key_value)(void *param)

user defined button get key value

esp_err_t (*button_custom_deinit)(void *param)

user defined button deinit

void *priv

private data used for custom button, MUST be allocated dynamically and will be auto freed in iot_button_delete

struct button_config_t

Button configuration.

Public Members

button_type_t type

button type, The corresponding button configuration must be filled

uint16_t long_press_time

Trigger time(ms) for long press, if 0 default to BUTTON_LONG_PRESS_TIME_MS

uint16_t short_press_time

Trigger time(ms) for short press, if 0 default to BUTTON_SHORT_PRESS_TIME_MS

button_gpio_config_t gpio_button_config

gpio button configuration

button_matrix_config_t matrix_button_config

matrix key button configuration

button_custom_config_t custom_button_config

custom button configuration

union button_config_t::[anonymous] [anonymous]

button configuration

Type Definitions

typedef void (*button_cb_t)(void *button_handle, void *usr_data)
typedef void *button_handle_t

Enumerations

enum button_event_t

Button events.

Values:

enumerator BUTTON_PRESS_DOWN
enumerator BUTTON_PRESS_UP
enumerator BUTTON_PRESS_REPEAT
enumerator BUTTON_PRESS_REPEAT_DONE
enumerator BUTTON_SINGLE_CLICK
enumerator BUTTON_DOUBLE_CLICK
enumerator BUTTON_MULTIPLE_CLICK
enumerator BUTTON_LONG_PRESS_START
enumerator BUTTON_LONG_PRESS_HOLD
enumerator BUTTON_LONG_PRESS_UP
enumerator BUTTON_PRESS_END
enumerator BUTTON_EVENT_MAX
enumerator BUTTON_NONE_PRESS
enum button_type_t

Supported button type.

Values:

enumerator BUTTON_TYPE_GPIO
enumerator BUTTON_TYPE_ADC
enumerator BUTTON_TYPE_MATRIX
enumerator BUTTON_TYPE_CUSTOM
enum button_param_t

Button parameter.

Values:

enumerator BUTTON_LONG_PRESS_TIME_MS
enumerator BUTTON_SHORT_PRESS_TIME_MS
enumerator BUTTON_PARAM_MAX