按键

[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 : 每次 ADC 扫描的样本数

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

应用示例

创建按键

// create gpio button
const button_config_t btn_cfg = {0};
const button_gpio_config_t btn_gpio_cfg = {
    .gpio_num = 0,
    .active_level = 0,
};
button_handle_t gpio_btn = NULL;
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &gpio_btn);
if(NULL == gpio_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create adc button
const button_config_t btn_cfg = {0};
button_adc_config_t btn_adc_cfg = {
    .unit_id = ADC_UNIT_1,
    .adc_channel = 0,
    .button_index = 0,
    .min = 100,
    .max = 400,
};

button_handle_t adc_btn = NULL;
esp_err_t ret = iot_button_new_adc_device(&btn_cfg, &btn_adc_cfg, &adc_btn);
if(NULL == adc_btn) {
    ESP_LOGE(TAG, "Button create failed");
}

// create matrix keypad button
const button_config_t btn_cfg = {0};
const button_matrix_config_t matrix_cfg = {
    .row_gpios = (int32_t[]){4, 5, 6, 7},
    .col_gpios = (int32_t[]){3, 8, 16, 15},
    .row_gpio_num = 4,
    .col_gpio_num = 4,
};
button_handle_t matrix_button = NULL;
esp_err_t ret = iot_button_new_matrix_device(&btn_cfg, &matrix_cfg, btns, &matrix_button);
if(NULL == matrix_button) {
    ESP_LOGE(TAG, "Button create failed");
}

备注

当 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, NULL, 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_args_t args = {
        .long_press.press_time = 2000,
    };
    
    iot_button_register_cb(gpio_btn, BUTTON_LONG_PRESS_START, &args, button_auto_check_cb_1, NULL);
    
    args.long_press.press_time = 5000;
    iot_button_register_cb(gpio_btn, BUTTON_LONG_PRESS_START, &args, 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 组件提供了低功耗模式。

所需配置:

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

备注

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

功耗对比:

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

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

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

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

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

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

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

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

button_config_t btn_cfg = {0};
button_gpio_config_t gpio_cfg = {
    .gpio_num = button_num,
    .active_level = BUTTON_ACTIVE_LEVEL,
    .enable_power_save = true,
};

button_handle_t btn;
iot_button_new_gpio_device(&btn_cfg, &gpio_cfg, &btn);

什么时候进入 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

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_event_args_t *event_args, button_cb_t cb, void *usr_data)

Register the button event callback function.

参数
  • btn_handle – A button handle to register

  • event – Button event

  • event_args – Button event arguments

  • 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_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args)

Unregister all the callbacks associated with the event.

参数
  • btn_handle – A button handle to unregister

  • event – Button event

  • event_args – Used for unregistering a specific callback.

返回

  • 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_cb(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

esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config)

Register a callback function for power saving. The config->enter_power_save_cb function will be called when all keys stop working.

参数

config – Button power save config

返回

  • ESP_OK on success

  • ESP_ERR_INVALID_STATE No button registered

  • ESP_ERR_INVALID_ARG Arguments is invalid

  • ESP_ERR_NO_MEM Not enough memory

Unions

union button_event_args_t
#include <iot_button.h>

Button events arg.

Public Members

struct button_event_args_t::long_press_t long_press

long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP

struct button_event_args_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_power_save_config_t

Structs to store power save callback info.

Public Members

button_power_save_cb_t enter_power_save_cb

Callback function when entering power save mode

void *usr_data

User data for the callback

Type Definitions

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

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_param_t

Button parameter.

Values:

enumerator BUTTON_LONG_PRESS_TIME_MS
enumerator BUTTON_SHORT_PRESS_TIME_MS
enumerator BUTTON_PARAM_MAX