Updated dependencies.
This commit is contained in:
parent
d86c494d45
commit
58748fcef1
101 changed files with 5845 additions and 2391 deletions
|
|
@ -1 +1 @@
|
|||
30a3f495c3862d505ce6e41adbbd218b2750e9723ab2151feff00e9fe685b326
|
||||
fccb18c37f1cfe0797b74a53a44d3f400f5fd01f4993b40052dfb7f401915089
|
||||
|
|
@ -1,5 +1,61 @@
|
|||
# ChangeLog
|
||||
|
||||
## v4.1.5 - 2025-12-3
|
||||
|
||||
### Fix:
|
||||
|
||||
* Fixed the unreasonable function name `iot_button_get_ticks_time` and renamed it to `iot_button_get_pressed_time`
|
||||
|
||||
## v4.1.4 - 2025-10-08
|
||||
|
||||
### Fix:
|
||||
|
||||
* Fixed requires in CMake for IDF6.
|
||||
|
||||
## v4.1.3 - 2025-04-11
|
||||
|
||||
### Fix:
|
||||
|
||||
* Added initialization for gpio_config. [!485](https://github.com/espressif/esp-iot-solution/pull/485)
|
||||
|
||||
## v4.1.2 - 2025-03-24
|
||||
|
||||
### Fix:
|
||||
|
||||
* fix incorrect long press start and release check.
|
||||
|
||||
## v4.1.1 - 2025-03-13
|
||||
|
||||
### Improve:
|
||||
|
||||
* include stdbool.h before using bool
|
||||
|
||||
## v4.1.0 - 2025-02-28
|
||||
|
||||
### Improve:
|
||||
|
||||
* Update the version of dependent cmake_utilities to *
|
||||
|
||||
## v4.0.0 - 2025-1-9
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Use the factory pattern to reduce the build size.
|
||||
* Change the state machine to use enumerated values.
|
||||
|
||||
### Break change:
|
||||
|
||||
* Standardize the return value of the iot_button interface to esp_err_t.
|
||||
* Remove support for the old ADC driver.
|
||||
* Modify the callback registration interface to:
|
||||
```c
|
||||
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);
|
||||
```
|
||||
* Modify the callback unregistration interface to:
|
||||
```c
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args);
|
||||
```
|
||||
|
||||
## v3.5.0 - 2024-12-27
|
||||
|
||||
### Enhancements:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
set(PRIVREQ esp_timer)
|
||||
set(REQ driver)
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.3")
|
||||
set(REQ esp_driver_gpio)
|
||||
else()
|
||||
set(REQ driver)
|
||||
endif()
|
||||
set(SRC_FILES "button_gpio.c" "iot_button.c" "button_matrix.c")
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
|
|
@ -7,26 +11,12 @@ if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
|||
if(CONFIG_SOC_ADC_SUPPORTED)
|
||||
list(APPEND SRC_FILES "button_adc.c")
|
||||
endif()
|
||||
else()
|
||||
list(APPEND REQ esp_adc_cal)
|
||||
list(APPEND SRC_FILES "button_adc.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${SRC_FILES}
|
||||
INCLUDE_DIRS include
|
||||
INCLUDE_DIRS include interface
|
||||
REQUIRES ${REQ}
|
||||
PRIV_REQUIRES ${PRIVREQ})
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.0")
|
||||
# Add the macro CONFIG_SOC_ADC_SUPPORTED for the following chips.
|
||||
if(CONFIG_IDF_TARGET STREQUAL "esp32" OR
|
||||
CONFIG_IDF_TARGET STREQUAL "esp32s2" OR
|
||||
CONFIG_IDF_TARGET STREQUAL "esp32s3" OR
|
||||
CONFIG_IDF_TARGET STREQUAL "esp32c3" OR
|
||||
CONFIG_IDF_TARGET STREQUAL "esp32h2")
|
||||
target_compile_definitions(${COMPONENT_LIB} PUBLIC CONFIG_SOC_ADC_SUPPORTED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
|
|
|
|||
|
|
@ -24,22 +24,12 @@ menu "IoT Button"
|
|||
range 500 5000
|
||||
default 1500
|
||||
|
||||
config BUTTON_SERIAL_TIME_MS
|
||||
int "BUTTON SERIAL TIME (MS)"
|
||||
config BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS
|
||||
int "BUTTON LONG_PRESS_HOLD SERIAL TIME (MS)"
|
||||
range 2 1000
|
||||
default 20
|
||||
help
|
||||
"Serial trigger interval"
|
||||
|
||||
config GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
bool "GPIO BUTTON SUPPORT POWER SAVE"
|
||||
default n
|
||||
help
|
||||
Enable GPIO button power save
|
||||
|
||||
The function enables the use of GPIO buttons during light sleep,
|
||||
but enabling this function prevents the simultaneous use of other
|
||||
types of buttons.
|
||||
"Long press hold Serial trigger interval"
|
||||
|
||||
config ADC_BUTTON_MAX_CHANNEL
|
||||
int "ADC BUTTON MAX CHANNEL"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
|
@ -6,48 +6,38 @@
|
|||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_idf_version.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "esp_adc/adc_cali.h"
|
||||
#include "esp_adc/adc_cali_scheme.h"
|
||||
#else
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/adc.h"
|
||||
#include "esp_adc_cal.h"
|
||||
#endif
|
||||
#include "button_adc.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "adc button";
|
||||
|
||||
#define ADC_BTN_CHECK(a, str, ret_val) \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret_val); \
|
||||
}
|
||||
static const char *TAG = "adc_button";
|
||||
|
||||
#define DEFAULT_VREF 1100
|
||||
#define NO_OF_SAMPLES CONFIG_ADC_BUTTON_SAMPLE_TIMES //Multisampling
|
||||
|
||||
/*!< Using atten bigger than 6db by default, it will be 11db or 12db in different target */
|
||||
#define DEFAULT_ADC_ATTEN (ADC_ATTEN_DB_6 + 1)
|
||||
#define DEFAULT_ADC_ATTEN (ADC_ATTEN_DB_6 + 1)
|
||||
|
||||
#define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#define ADC_BUTTON_CHANNEL_MAX SOC_ADC_MAX_CHANNEL_NUM
|
||||
#define ADC_BUTTON_ATTEN DEFAULT_ADC_ATTEN
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#define ADC1_BUTTON_CHANNEL_MAX SOC_ADC_MAX_CHANNEL_NUM
|
||||
#define ADC_BUTTON_ATTEN DEFAULT_ADC_ATTEN
|
||||
#else
|
||||
#define ADC_BUTTON_WIDTH ADC_WIDTH_MAX-1
|
||||
#define ADC1_BUTTON_CHANNEL_MAX ADC1_CHANNEL_MAX
|
||||
#define ADC_BUTTON_ATTEN DEFAULT_ADC_ATTEN
|
||||
#endif
|
||||
#define ADC_BUTTON_ADC_UNIT ADC_UNIT_1
|
||||
#define ADC_BUTTON_MAX_CHANNEL CONFIG_ADC_BUTTON_MAX_CHANNEL
|
||||
#define ADC_BUTTON_MAX_BUTTON CONFIG_ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
|
||||
|
||||
// ESP32C3 ADC2 it has been deprecated.
|
||||
#if (SOC_ADC_PERIPH_NUM >= 2) && !CONFIG_IDF_TARGET_ESP32C3
|
||||
#define ADC_UNIT_NUM 2
|
||||
#else
|
||||
#define ADC_UNIT_NUM 1
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
|
|
@ -60,45 +50,56 @@ typedef struct {
|
|||
uint64_t last_time; /* the last time of adc sample */
|
||||
} btn_adc_channel_t;
|
||||
|
||||
typedef enum {
|
||||
ADC_NONE_INIT = 0,
|
||||
ADC_INIT_BY_ADC_BUTTON,
|
||||
ADC_INIT_BY_USER,
|
||||
} adc_init_info_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_configured;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
adc_cali_handle_t adc1_cali_handle;
|
||||
adc_oneshot_unit_handle_t adc1_handle;
|
||||
#else
|
||||
esp_adc_cal_characteristics_t adc_chars;
|
||||
#endif
|
||||
adc_init_info_t is_configured;
|
||||
adc_cali_handle_t adc_cali_handle;
|
||||
adc_oneshot_unit_handle_t adc_handle;
|
||||
btn_adc_channel_t ch[ADC_BUTTON_MAX_CHANNEL];
|
||||
uint8_t ch_num;
|
||||
} adc_button_t;
|
||||
} btn_adc_unit_t;
|
||||
|
||||
static adc_button_t g_button = {0};
|
||||
typedef struct {
|
||||
btn_adc_unit_t unit[ADC_UNIT_NUM];
|
||||
} button_adc_t;
|
||||
typedef struct {
|
||||
button_driver_t base;
|
||||
adc_unit_t unit_id;
|
||||
uint32_t ch;
|
||||
uint32_t index;
|
||||
} button_adc_obj;
|
||||
|
||||
static int find_unused_channel(void)
|
||||
static button_adc_t g_button = {0};
|
||||
|
||||
static int find_unused_channel(adc_unit_t unit_id)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (0 == g_button.ch[i].is_init) {
|
||||
if (0 == g_button.unit[unit_id].ch[i].is_init) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_channel(uint8_t channel)
|
||||
static int find_channel(adc_unit_t unit_id, uint8_t channel)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (channel == g_button.ch[i].channel) {
|
||||
if (channel == g_button.unit[unit_id].ch[i].channel) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
static bool adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
{
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
esp_err_t ret = ESP_ERR_NOT_SUPPORTED;
|
||||
bool calibrated = false;
|
||||
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
|
|
@ -136,177 +137,191 @@ static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_ca
|
|||
ESP_LOGI(TAG, "Calibration Success");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED) {
|
||||
ESP_LOGW(TAG, "Calibration not supported");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||
}
|
||||
|
||||
return calibrated ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t button_adc_init(const button_adc_config_t *config)
|
||||
{
|
||||
ADC_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
|
||||
ADC_BTN_CHECK(config->adc_channel < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", ESP_ERR_NOT_SUPPORTED);
|
||||
ADC_BTN_CHECK(config->button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_NOT_SUPPORTED);
|
||||
ADC_BTN_CHECK(config->max > 0, "key max voltage invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
int ch_index = find_channel(config->adc_channel);
|
||||
if (ch_index >= 0) { /**< the channel has been initialized */
|
||||
ADC_BTN_CHECK(g_button.ch[ch_index].btns[config->button_index].max == 0, "The button_index has been used", ESP_ERR_INVALID_STATE);
|
||||
} else { /**< this is a new channel */
|
||||
int unused_ch_index = find_unused_channel();
|
||||
ADC_BTN_CHECK(unused_ch_index >= 0, "exceed max channel number, can't create a new channel", ESP_ERR_INVALID_STATE);
|
||||
ch_index = unused_ch_index;
|
||||
}
|
||||
|
||||
/** initialize adc */
|
||||
if (0 == g_button.is_configured) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_err_t ret;
|
||||
if (NULL == config->adc_handle) {
|
||||
//ADC1 Init
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
};
|
||||
ret = adc_oneshot_new_unit(&init_config, &g_button.adc1_handle);
|
||||
ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot new unit fail!", ESP_FAIL);
|
||||
} else {
|
||||
g_button.adc1_handle = *config->adc_handle ;
|
||||
ESP_LOGI(TAG, "ADC1 has been initialized");
|
||||
}
|
||||
#else
|
||||
//Configure ADC
|
||||
adc1_config_width(ADC_BUTTON_WIDTH);
|
||||
//Characterize ADC
|
||||
esp_adc_cal_value_t val_type = esp_adc_cal_characterize(ADC_BUTTON_ADC_UNIT, ADC_BUTTON_ATTEN, ADC_BUTTON_WIDTH, DEFAULT_VREF, &g_button.adc_chars);
|
||||
if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) {
|
||||
ESP_LOGI(TAG, "Characterized using Two Point Value");
|
||||
} else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) {
|
||||
ESP_LOGI(TAG, "Characterized using eFuse Vref");
|
||||
} else {
|
||||
ESP_LOGI(TAG, "Characterized using Default Vref");
|
||||
}
|
||||
#endif
|
||||
g_button.is_configured = 1;
|
||||
}
|
||||
|
||||
/** initialize adc channel */
|
||||
if (0 == g_button.ch[ch_index].is_init) {
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
//ADC1 Config
|
||||
adc_oneshot_chan_cfg_t oneshot_config = {
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
.atten = ADC_BUTTON_ATTEN,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_config_channel(g_button.adc1_handle, config->adc_channel, &oneshot_config);
|
||||
ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot config channel fail!", ESP_FAIL);
|
||||
//-------------ADC1 Calibration Init---------------//
|
||||
ret = adc_calibration_init(ADC_BUTTON_ADC_UNIT, ADC_BUTTON_ATTEN, &g_button.adc1_cali_handle);
|
||||
ADC_BTN_CHECK(ret == ESP_OK, "ADC1 Calibration Init False", 0);
|
||||
#else
|
||||
adc1_config_channel_atten(config->adc_channel, ADC_BUTTON_ATTEN);
|
||||
#endif
|
||||
g_button.ch[ch_index].channel = config->adc_channel;
|
||||
g_button.ch[ch_index].is_init = 1;
|
||||
g_button.ch[ch_index].last_time = 0;
|
||||
}
|
||||
g_button.ch[ch_index].btns[config->button_index].max = config->max;
|
||||
g_button.ch[ch_index].btns[config->button_index].min = config->min;
|
||||
g_button.ch_num++;
|
||||
|
||||
return ESP_OK;
|
||||
return calibrated;
|
||||
}
|
||||
|
||||
esp_err_t button_adc_deinit(uint8_t channel, int button_index)
|
||||
static bool adc_calibration_deinit(adc_cali_handle_t handle)
|
||||
{
|
||||
ADC_BTN_CHECK(channel < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", ESP_ERR_INVALID_ARG);
|
||||
ADC_BTN_CHECK(button_index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", ESP_ERR_INVALID_ARG);
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (adc_cali_delete_scheme_curve_fitting(handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
int ch_index = find_channel(channel);
|
||||
ADC_BTN_CHECK(ch_index >= 0, "can't find the channel", ESP_ERR_INVALID_ARG);
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (adc_cali_delete_scheme_line_fitting(handle) == ESP_OK) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
g_button.ch[ch_index].btns[button_index].max = 0;
|
||||
g_button.ch[ch_index].btns[button_index].min = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t button_adc_del(button_driver_t *button_driver)
|
||||
{
|
||||
button_adc_obj *adc_btn = __containerof(button_driver, button_adc_obj, base);
|
||||
ESP_RETURN_ON_FALSE(adc_btn->ch < ADC_BUTTON_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_btn->index < ADC_BUTTON_MAX_BUTTON, ESP_ERR_INVALID_ARG, TAG, "button_index out of range");
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, adc_btn->ch);
|
||||
ESP_RETURN_ON_FALSE(ch_index >= 0, ESP_ERR_INVALID_ARG, TAG, "can't find the channel");
|
||||
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_btn->index].max = 0;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_btn->index].min = 0;
|
||||
|
||||
/** check button usage on the channel*/
|
||||
uint8_t unused_button = 0;
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_BUTTON; i++) {
|
||||
if (0 == g_button.ch[ch_index].btns[i].max) {
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[ch_index].btns[i].max) {
|
||||
unused_button++;
|
||||
}
|
||||
}
|
||||
if (unused_button == ADC_BUTTON_MAX_BUTTON && g_button.ch[ch_index].is_init) { /**< if all button is unused, deinit the channel */
|
||||
g_button.ch[ch_index].is_init = 0;
|
||||
g_button.ch[ch_index].channel = ADC1_BUTTON_CHANNEL_MAX;
|
||||
ESP_LOGD(TAG, "all button is unused on channel%d, deinit the channel", g_button.ch[ch_index].channel);
|
||||
if (unused_button == ADC_BUTTON_MAX_BUTTON && g_button.unit[adc_btn->unit_id].ch[ch_index].is_init) { /**< if all button is unused, deinit the channel */
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].is_init = 0;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].channel = ADC_BUTTON_CHANNEL_MAX;
|
||||
ESP_LOGD(TAG, "all button is unused on channel%d, deinit the channel", g_button.unit[adc_btn->unit_id].ch[ch_index].channel);
|
||||
}
|
||||
|
||||
/** check channel usage on the adc*/
|
||||
uint8_t unused_ch = 0;
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (0 == g_button.ch[i].is_init) {
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[i].is_init) {
|
||||
unused_ch++;
|
||||
}
|
||||
}
|
||||
if (unused_ch == ADC_BUTTON_MAX_CHANNEL && g_button.is_configured) { /**< if all channel is unused, deinit the adc */
|
||||
g_button.is_configured = false;
|
||||
memset(&g_button, 0, sizeof(adc_button_t));
|
||||
if (unused_ch == ADC_BUTTON_MAX_CHANNEL && g_button.unit[adc_btn->unit_id].is_configured) { /**< if all channel is unused, deinit the adc */
|
||||
if (g_button.unit[adc_btn->unit_id].is_configured == ADC_INIT_BY_ADC_BUTTON) {
|
||||
esp_err_t ret = adc_oneshot_del_unit(g_button.unit[adc_btn->unit_id].adc_handle);
|
||||
ESP_RETURN_ON_FALSE(ret == ESP_OK, ret, TAG, "adc oneshot del unit fail");
|
||||
adc_calibration_deinit(g_button.unit[adc_btn->unit_id].adc_cali_handle);
|
||||
}
|
||||
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_NONE_INIT;
|
||||
memset(&g_button.unit[adc_btn->unit_id], 0, sizeof(btn_adc_unit_t));
|
||||
ESP_LOGD(TAG, "all channel is unused, , deinit adc");
|
||||
}
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
esp_err_t ret = adc_oneshot_del_unit(g_button.adc1_handle);
|
||||
ADC_BTN_CHECK(ret == ESP_OK, "adc oneshot deinit fail", ESP_FAIL);
|
||||
#endif
|
||||
free(adc_btn);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint32_t get_adc_volatge(uint8_t channel)
|
||||
static uint32_t get_adc_voltage(adc_unit_t unit_id, uint8_t channel)
|
||||
{
|
||||
uint32_t adc_reading = 0;
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
int adc_raw = 0;
|
||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||
adc_oneshot_read(g_button.adc1_handle, channel, &adc_raw);
|
||||
adc_oneshot_read(g_button.unit[unit_id].adc_handle, channel, &adc_raw);
|
||||
adc_reading += adc_raw;
|
||||
}
|
||||
adc_reading /= NO_OF_SAMPLES;
|
||||
//Convert adc_reading to voltage in mV
|
||||
int voltage = 0;
|
||||
adc_cali_raw_to_voltage(g_button.adc1_cali_handle, adc_reading, &voltage);
|
||||
adc_cali_raw_to_voltage(g_button.unit[unit_id].adc_cali_handle, adc_reading, &voltage);
|
||||
ESP_LOGV(TAG, "Raw: %"PRIu32"\tVoltage: %dmV", adc_reading, voltage);
|
||||
#else
|
||||
//Multisampling
|
||||
for (int i = 0; i < NO_OF_SAMPLES; i++) {
|
||||
adc_reading += adc1_get_raw(channel);
|
||||
}
|
||||
adc_reading /= NO_OF_SAMPLES;
|
||||
//Convert adc_reading to voltage in mV
|
||||
uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, &g_button.adc_chars);
|
||||
ESP_LOGV(TAG, "Raw: %"PRIu32"\tVoltage: %"PRIu32"mV", adc_reading, voltage);
|
||||
#endif
|
||||
return voltage;
|
||||
}
|
||||
|
||||
uint8_t button_adc_get_key_level(void *button_index)
|
||||
uint8_t button_adc_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
button_adc_obj *adc_btn = __containerof(button_driver, button_adc_obj, base);
|
||||
static uint16_t vol = 0;
|
||||
uint32_t ch = ADC_BUTTON_SPLIT_CHANNEL(button_index);
|
||||
uint32_t index = ADC_BUTTON_SPLIT_INDEX(button_index);
|
||||
ADC_BTN_CHECK(ch < ADC1_BUTTON_CHANNEL_MAX, "channel out of range", 0);
|
||||
ADC_BTN_CHECK(index < ADC_BUTTON_MAX_BUTTON, "button_index out of range", 0);
|
||||
int ch_index = find_channel(ch);
|
||||
ADC_BTN_CHECK(ch_index >= 0, "The button_index is not init", 0);
|
||||
uint32_t ch = adc_btn->ch;
|
||||
uint32_t index = adc_btn->index;
|
||||
ESP_RETURN_ON_FALSE(ch < ADC_BUTTON_CHANNEL_MAX, 0, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(index < ADC_BUTTON_MAX_BUTTON, 0, TAG, "button_index out of range");
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, ch);
|
||||
ESP_RETURN_ON_FALSE(ch_index >= 0, 0, TAG, "The button_index is not init");
|
||||
|
||||
/** It starts only when the elapsed time is more than 1ms */
|
||||
if ((esp_timer_get_time() - g_button.ch[ch_index].last_time) > 1000) {
|
||||
vol = get_adc_volatge(ch);
|
||||
g_button.ch[ch_index].last_time = esp_timer_get_time();
|
||||
if ((esp_timer_get_time() - g_button.unit[adc_btn->unit_id].ch[ch_index].last_time) > 1000) {
|
||||
vol = get_adc_voltage(adc_btn->unit_id, ch);
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].last_time = esp_timer_get_time();
|
||||
}
|
||||
|
||||
if (vol <= g_button.ch[ch_index].btns[index].max &&
|
||||
vol >= g_button.ch[ch_index].btns[index].min) {
|
||||
return 1;
|
||||
if (vol <= g_button.unit[adc_btn->unit_id].ch[ch_index].btns[index].max &&
|
||||
vol >= g_button.unit[adc_btn->unit_id].ch[ch_index].btns[index].min) {
|
||||
return BUTTON_ACTIVE;
|
||||
}
|
||||
return 0;
|
||||
return BUTTON_INACTIVE;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_adc_device(const button_config_t *button_config, const button_adc_config_t *adc_config, button_handle_t *ret_button)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(button_config && adc_config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
ESP_RETURN_ON_FALSE(adc_config->unit_id < ADC_UNIT_NUM, ESP_ERR_INVALID_ARG, TAG, "adc_handle out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->adc_channel < ADC_BUTTON_CHANNEL_MAX, ESP_ERR_INVALID_ARG, TAG, "channel out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->button_index < ADC_BUTTON_MAX_BUTTON, ESP_ERR_INVALID_ARG, TAG, "button_index out of range");
|
||||
ESP_RETURN_ON_FALSE(adc_config->max > 0, ESP_ERR_INVALID_ARG, TAG, "key max voltage invalid");
|
||||
button_adc_obj *adc_btn = calloc(1, sizeof(button_adc_obj));
|
||||
ESP_RETURN_ON_FALSE(adc_btn, ESP_ERR_NO_MEM, TAG, "calloc fail");
|
||||
adc_btn->unit_id = adc_config->unit_id;
|
||||
|
||||
int ch_index = find_channel(adc_btn->unit_id, adc_config->adc_channel);
|
||||
if (ch_index >= 0) { /**< the channel has been initialized */
|
||||
ESP_GOTO_ON_FALSE(g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].max == 0, ESP_ERR_INVALID_STATE, err, TAG, "The button_index has been used");
|
||||
} else { /**< this is a new channel */
|
||||
int unused_ch_index = find_unused_channel(adc_config->unit_id);
|
||||
ESP_GOTO_ON_FALSE(unused_ch_index >= 0, ESP_ERR_INVALID_STATE, err, TAG, "exceed max channel number, can't create a new channel");
|
||||
ch_index = unused_ch_index;
|
||||
}
|
||||
|
||||
/** initialize adc */
|
||||
if (0 == g_button.unit[adc_btn->unit_id].is_configured) {
|
||||
esp_err_t ret;
|
||||
if (NULL == adc_config->adc_handle) {
|
||||
//ADC1 Init
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = adc_btn->unit_id,
|
||||
};
|
||||
ret = adc_oneshot_new_unit(&init_config, &g_button.unit[adc_btn->unit_id].adc_handle);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "adc oneshot new unit fail!");
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_INIT_BY_ADC_BUTTON;
|
||||
} else {
|
||||
g_button.unit[adc_btn->unit_id].adc_handle = *adc_config->adc_handle;
|
||||
ESP_LOGI(TAG, "ADC1 has been initialized");
|
||||
g_button.unit[adc_btn->unit_id].is_configured = ADC_INIT_BY_USER;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** initialize adc channel */
|
||||
if (0 == g_button.unit[adc_btn->unit_id].ch[ch_index].is_init) {
|
||||
//ADC1 Config
|
||||
adc_oneshot_chan_cfg_t oneshot_config = {
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
.atten = ADC_BUTTON_ATTEN,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_config_channel(g_button.unit[adc_btn->unit_id].adc_handle, adc_config->adc_channel, &oneshot_config);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "adc oneshot config channel fail!");
|
||||
//-------------ADC1 Calibration Init---------------//
|
||||
adc_calibration_init(adc_btn->unit_id, ADC_BUTTON_ATTEN, &g_button.unit[adc_btn->unit_id].adc_cali_handle);
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].channel = adc_config->adc_channel;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].is_init = 1;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].last_time = 0;
|
||||
}
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].max = adc_config->max;
|
||||
g_button.unit[adc_btn->unit_id].ch[ch_index].btns[adc_config->button_index].min = adc_config->min;
|
||||
g_button.unit[adc_btn->unit_id].ch_num++;
|
||||
|
||||
adc_btn->ch = adc_config->adc_channel;
|
||||
adc_btn->index = adc_config->button_index;
|
||||
adc_btn->base.get_key_level = button_adc_get_key_level;
|
||||
adc_btn->base.del = button_adc_del;
|
||||
ret = iot_button_create(button_config, &adc_btn->base, ret_button);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
|
||||
return ESP_OK;
|
||||
err:
|
||||
if (adc_btn) {
|
||||
free(adc_btn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,33 +4,96 @@
|
|||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_gpio.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "button_interface.h"
|
||||
#include "iot_button.h"
|
||||
|
||||
static const char *TAG = "gpio button";
|
||||
static const char *TAG = "gpio_button";
|
||||
|
||||
#define GPIO_BTN_CHECK(a, str, ret_val) \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret_val); \
|
||||
}
|
||||
typedef struct {
|
||||
button_driver_t base; /**< button driver */
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
bool enable_power_save; /**< enable power save */
|
||||
} button_gpio_obj;
|
||||
|
||||
esp_err_t button_gpio_init(const button_gpio_config_t *config)
|
||||
static esp_err_t button_gpio_del(button_driver_t *button_driver)
|
||||
{
|
||||
GPIO_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
|
||||
GPIO_BTN_CHECK(GPIO_IS_VALID_GPIO(config->gpio_num), "GPIO number error", ESP_ERR_INVALID_ARG);
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
esp_err_t ret = gpio_reset_pin(gpio_btn->gpio_num);
|
||||
free(gpio_btn);
|
||||
return ret;
|
||||
}
|
||||
|
||||
gpio_config_t gpio_conf;
|
||||
static uint8_t button_gpio_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
int level = gpio_get_level(gpio_btn->gpio_num);
|
||||
return level == gpio_btn->active_level ? 1 : 0;
|
||||
}
|
||||
|
||||
static esp_err_t button_gpio_enable_gpio_wakeup(uint32_t gpio_num, uint8_t active_level, bool enable)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (enable) {
|
||||
gpio_intr_enable(gpio_num);
|
||||
ret = gpio_wakeup_enable(gpio_num, active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
} else {
|
||||
gpio_intr_disable(gpio_num);
|
||||
ret = gpio_wakeup_disable(gpio_num);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static esp_err_t button_gpio_set_intr(int gpio_num, gpio_int_type_t intr_type, gpio_isr_t isr_handler)
|
||||
{
|
||||
static bool isr_service_installed = false;
|
||||
gpio_set_intr_type(gpio_num, intr_type);
|
||||
if (!isr_service_installed) {
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
|
||||
isr_service_installed = true;
|
||||
}
|
||||
gpio_isr_handler_add(gpio_num, isr_handler, (void *)gpio_num);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void button_power_save_isr_handler(void* arg)
|
||||
{
|
||||
/*!< resume the button */
|
||||
iot_button_resume();
|
||||
/*!< disable gpio wakeup not need active level*/
|
||||
button_gpio_enable_gpio_wakeup((uint32_t)arg, 0, false);
|
||||
}
|
||||
|
||||
static esp_err_t button_enter_power_save(button_driver_t *button_driver)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = __containerof(button_driver, button_gpio_obj, base);
|
||||
return button_gpio_enable_gpio_wakeup(gpio_btn->gpio_num, gpio_btn->active_level, true);
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_gpio_device(const button_config_t *button_config, const button_gpio_config_t *gpio_cfg, button_handle_t *ret_button)
|
||||
{
|
||||
button_gpio_obj *gpio_btn = NULL;
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(button_config && gpio_cfg && ret_button, ESP_ERR_INVALID_ARG, err, TAG, "Invalid argument");
|
||||
ESP_GOTO_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_cfg->gpio_num), ESP_ERR_INVALID_ARG, err, TAG, "GPIO number error");
|
||||
|
||||
gpio_btn = (button_gpio_obj *)calloc(1, sizeof(button_gpio_obj));
|
||||
ESP_GOTO_ON_FALSE(gpio_btn, ESP_ERR_NO_MEM, err, TAG, "No memory for gpio button");
|
||||
gpio_btn->gpio_num = gpio_cfg->gpio_num;
|
||||
gpio_btn->active_level = gpio_cfg->active_level;
|
||||
gpio_btn->enable_power_save = gpio_cfg->enable_power_save;
|
||||
|
||||
gpio_config_t gpio_conf = {0};
|
||||
gpio_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = (1ULL << config->gpio_num);
|
||||
if (config->disable_pull) {
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
} else {
|
||||
if (config->active_level) {
|
||||
gpio_conf.pin_bit_mask = (1ULL << gpio_cfg->gpio_num);
|
||||
if (!gpio_cfg->disable_pull) {
|
||||
if (gpio_cfg->active_level) {
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
} else {
|
||||
|
|
@ -40,75 +103,47 @@ esp_err_t button_gpio_init(const button_gpio_config_t *config)
|
|||
}
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
if (config->enable_power_save) {
|
||||
if (gpio_cfg->enable_power_save) {
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
if (!esp_sleep_is_valid_wakeup_gpio(config->gpio_num)) {
|
||||
ESP_LOGE(TAG, "GPIO %ld is not a valid wakeup source under CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE", config->gpio_num);
|
||||
if (!esp_sleep_is_valid_wakeup_gpio(gpio_cfg->gpio_num)) {
|
||||
ESP_LOGE(TAG, "GPIO %ld is not a valid wakeup source under CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE", gpio_cfg->gpio_num);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
gpio_hold_en(config->gpio_num);
|
||||
gpio_hold_en(gpio_cfg->gpio_num);
|
||||
#endif
|
||||
/* Enable wake up from GPIO */
|
||||
esp_err_t ret = gpio_wakeup_enable(config->gpio_num, config->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
GPIO_BTN_CHECK(ret == ESP_OK, "Enable gpio wakeup failed", ESP_FAIL);
|
||||
esp_err_t ret = gpio_wakeup_enable(gpio_cfg->gpio_num, gpio_cfg->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_ERR_INVALID_STATE, err, TAG, "Enable gpio wakeup failed");
|
||||
#if CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP
|
||||
#if SOC_PM_SUPPORT_EXT1_WAKEUP
|
||||
ret = esp_sleep_enable_ext1_wakeup_io((1ULL << config->gpio_num), config->active_level == 0 ? ESP_EXT1_WAKEUP_ANY_LOW : ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
ret = esp_sleep_enable_ext1_wakeup_io((1ULL << gpio_cfg->gpio_num), gpio_cfg->active_level == 0 ? ESP_EXT1_WAKEUP_ANY_LOW : ESP_EXT1_WAKEUP_ANY_HIGH);
|
||||
#else
|
||||
/*!< Not support etc: esp32c2, esp32c3. Target must support ext1 wakeup */
|
||||
ret = ESP_FAIL;
|
||||
GPIO_BTN_CHECK(ret == ESP_OK, "Target must support ext1 wakeup", ESP_FAIL);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Target must support ext1 wakeup");
|
||||
#endif
|
||||
#else
|
||||
ret = esp_sleep_enable_gpio_wakeup();
|
||||
#endif
|
||||
GPIO_BTN_CHECK(ret == ESP_OK, "Configure gpio as wakeup source failed", ESP_FAIL);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Configure gpio as wakeup source failed");
|
||||
|
||||
ret = button_gpio_set_intr(gpio_btn->gpio_num, gpio_cfg->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL, button_power_save_isr_handler);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Set gpio interrupt failed");
|
||||
|
||||
gpio_btn->base.enable_power_save = true;
|
||||
gpio_btn->base.enter_power_save = button_enter_power_save;
|
||||
}
|
||||
#endif
|
||||
|
||||
gpio_btn->base.get_key_level = button_gpio_get_key_level;
|
||||
gpio_btn->base.del = button_gpio_del;
|
||||
|
||||
ret = iot_button_create(button_config, &gpio_btn->base, ret_button);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t button_gpio_deinit(int gpio_num)
|
||||
{
|
||||
return gpio_reset_pin(gpio_num);;
|
||||
}
|
||||
|
||||
uint8_t button_gpio_get_key_level(void *gpio_num)
|
||||
{
|
||||
return (uint8_t)gpio_get_level((uint32_t)gpio_num);
|
||||
}
|
||||
|
||||
esp_err_t button_gpio_set_intr(int gpio_num, gpio_int_type_t intr_type, gpio_isr_t isr_handler, void *args)
|
||||
{
|
||||
static bool isr_service_installed = false;
|
||||
gpio_set_intr_type(gpio_num, intr_type);
|
||||
if (!isr_service_installed) {
|
||||
gpio_install_isr_service(ESP_INTR_FLAG_IRAM);
|
||||
isr_service_installed = true;
|
||||
}
|
||||
gpio_isr_handler_add(gpio_num, isr_handler, args);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t button_gpio_intr_control(int gpio_num, bool enable)
|
||||
{
|
||||
if (enable) {
|
||||
gpio_intr_enable(gpio_num);
|
||||
} else {
|
||||
gpio_intr_disable(gpio_num);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t button_gpio_enable_gpio_wakeup(uint32_t gpio_num, uint8_t active_level, bool enable)
|
||||
{
|
||||
esp_err_t ret;
|
||||
if (enable) {
|
||||
ret = gpio_wakeup_enable(gpio_num, active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL);
|
||||
} else {
|
||||
ret = gpio_wakeup_disable(gpio_num);
|
||||
err:
|
||||
if (gpio_btn) {
|
||||
free(gpio_btn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,59 +1,88 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_matrix.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "matrix button";
|
||||
static const char *TAG = "matrix_button";
|
||||
|
||||
#define MATRIX_BTN_CHECK(a, str, ret_val) \
|
||||
if (!(a)) \
|
||||
{ \
|
||||
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret_val); \
|
||||
}
|
||||
typedef struct {
|
||||
button_driver_t base; /**< base button driver */
|
||||
int32_t row_gpio_num; /**< row gpio */
|
||||
int32_t col_gpio_num; /**< col gpio */
|
||||
} button_matrix_obj;
|
||||
|
||||
esp_err_t button_matrix_init(const button_matrix_config_t *config)
|
||||
static esp_err_t button_matrix_gpio_init(int32_t gpio_num, gpio_mode_t mode)
|
||||
{
|
||||
MATRIX_BTN_CHECK(NULL != config, "Pointer of config is invalid", ESP_ERR_INVALID_ARG);
|
||||
MATRIX_BTN_CHECK(GPIO_IS_VALID_GPIO(config->row_gpio_num), "row GPIO number error", ESP_ERR_INVALID_ARG);
|
||||
MATRIX_BTN_CHECK(GPIO_IS_VALID_GPIO(config->col_gpio_num), "col GPIO number error", ESP_ERR_INVALID_ARG);
|
||||
|
||||
// set row gpio as output
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(gpio_num), ESP_ERR_INVALID_ARG, TAG, "gpio_num error");
|
||||
gpio_config_t gpio_conf = {0};
|
||||
gpio_conf.intr_type = GPIO_INTR_DISABLE;
|
||||
gpio_conf.mode = GPIO_MODE_OUTPUT;
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpio_conf.pin_bit_mask = (1ULL << config->row_gpio_num);
|
||||
gpio_conf.pin_bit_mask = (1ULL << gpio_num);
|
||||
gpio_conf.mode = mode;
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
// set col gpio as input
|
||||
gpio_conf.mode = GPIO_MODE_INPUT;
|
||||
gpio_conf.pin_bit_mask = (1ULL << config->col_gpio_num);
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t button_matrix_deinit(int row_gpio_num, int col_gpio_num)
|
||||
esp_err_t button_matrix_del(button_driver_t *button_driver)
|
||||
{
|
||||
button_matrix_obj *matrix_btn = __containerof(button_driver, button_matrix_obj, base);
|
||||
//Reset an gpio to default state (select gpio function, enable pullup and disable input and output).
|
||||
gpio_reset_pin(row_gpio_num);
|
||||
gpio_reset_pin(col_gpio_num);
|
||||
gpio_reset_pin(matrix_btn->row_gpio_num);
|
||||
gpio_reset_pin(matrix_btn->col_gpio_num);
|
||||
free(matrix_btn);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t button_matrix_get_key_level(void *hardware_data)
|
||||
uint8_t button_matrix_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
uint32_t row = MATRIX_BUTTON_SPLIT_ROW(hardware_data);
|
||||
uint32_t col = MATRIX_BUTTON_SPLIT_COL(hardware_data);
|
||||
gpio_set_level(row, 1);
|
||||
uint8_t level = gpio_get_level(col);
|
||||
gpio_set_level(row, 0);
|
||||
|
||||
button_matrix_obj *matrix_btn = __containerof(button_driver, button_matrix_obj, base);
|
||||
gpio_set_level(matrix_btn->row_gpio_num, 1);
|
||||
uint8_t level = gpio_get_level(matrix_btn->col_gpio_num);
|
||||
gpio_set_level(matrix_btn->row_gpio_num, 0);
|
||||
return level;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_new_matrix_device(const button_config_t *button_config, const button_matrix_config_t *matrix_config, button_handle_t *ret_button, size_t *size)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(button_config && matrix_config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
ESP_RETURN_ON_FALSE(matrix_config->col_gpios && matrix_config->row_gpios, ESP_ERR_INVALID_ARG, TAG, "Invalid matrix config");
|
||||
ESP_RETURN_ON_FALSE(matrix_config->col_gpio_num > 0 && matrix_config->row_gpio_num > 0, ESP_ERR_INVALID_ARG, TAG, "Invalid matrix config");
|
||||
ESP_RETURN_ON_FALSE(*size == matrix_config->row_gpio_num * matrix_config->col_gpio_num, ESP_ERR_INVALID_ARG, TAG, "Invalid size");
|
||||
|
||||
button_matrix_obj *matrix_btn = calloc(*size, sizeof(button_matrix_obj));
|
||||
for (int i = 0; i < matrix_config->row_gpio_num; i++) {
|
||||
button_matrix_gpio_init(matrix_config->row_gpios[i], GPIO_MODE_OUTPUT);
|
||||
}
|
||||
|
||||
for (int i = 0; i < matrix_config->col_gpio_num; i++) {
|
||||
button_matrix_gpio_init(matrix_config->col_gpios[i], GPIO_MODE_INPUT);
|
||||
}
|
||||
|
||||
for (int i = 0; i < *size; i++) {
|
||||
matrix_btn[i].base.get_key_level = button_matrix_get_key_level;
|
||||
matrix_btn[i].base.del = button_matrix_del;
|
||||
matrix_btn[i].row_gpio_num = matrix_config->row_gpios[i / matrix_config->col_gpio_num];
|
||||
matrix_btn[i].col_gpio_num = matrix_config->col_gpios[i % matrix_config->col_gpio_num];
|
||||
ESP_LOGD(TAG, "row_gpio_num: %"PRId32", col_gpio_num: %"PRId32"", matrix_btn[i].row_gpio_num, matrix_btn[i].col_gpio_num);
|
||||
ret = iot_button_create(button_config, &matrix_btn[i].base, &ret_button[i]);
|
||||
ESP_GOTO_ON_FALSE(ret == ESP_OK, ESP_FAIL, err, TAG, "Create button failed");
|
||||
}
|
||||
*size = matrix_config->row_gpio_num * matrix_config->col_gpio_num;
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
if (matrix_btn) {
|
||||
free(matrix_btn);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(button_power_save)
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
## Button Power Save Example
|
||||
|
||||
This example demonstrates how to utilize the `button` component in conjunction with the light sleep low-power mode.
|
||||
|
||||
* `button` [Component Introduction](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html)
|
||||
|
||||
## Hardware
|
||||
|
||||
* Any GPIO on any development board can be used in this example.
|
||||
|
||||
## Build and Flash
|
||||
|
||||
Build the project and flash it to the board, then run the monitor tool to view the serial output:
|
||||
|
||||
* Run `. ./export.sh` to set IDF environment
|
||||
* Run `idf.py set-target esp32xx` to set target chip
|
||||
* Run `idf.py -p PORT flash monitor` to build, flash and monitor the project
|
||||
|
||||
(To exit the serial monitor, type `Ctrl-]`.)
|
||||
|
||||
See the Getting Started Guide for all the steps to configure and use the ESP-IDF to build projects.
|
||||
|
||||
## Example Output
|
||||
|
||||
```
|
||||
I (1139) pm: Frequency switching config: CPU_MAX: 160, APB_MAX: 80, APB_MIN: 80, Light sleep: ENABLED
|
||||
I (1149) sleep: Code start at 42000020, total 119.03 KiB, data start at 3c000000, total 49152.00 KiB
|
||||
I (1159) button: IoT Button Version: 3.2.0
|
||||
I (1163) gpio: GPIO[0]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (2922) button_power_save: Button event BUTTON_PRESS_DOWN
|
||||
I (3017) button_power_save: Button event BUTTON_PRESS_UP
|
||||
I (3017) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3200) button_power_save: Button event BUTTON_SINGLE_CLICK
|
||||
I (3200) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3202) button_power_save: Button event BUTTON_PRESS_REPEAT_DONE
|
||||
I (3208) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3627) button_power_save: Button event BUTTON_PRESS_DOWN
|
||||
I (3702) button_power_save: Button event BUTTON_PRESS_UP
|
||||
I (3702) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3887) button_power_save: Button event BUTTON_SINGLE_CLICK
|
||||
I (3887) button_power_save: Wake up from light sleep, reason 4
|
||||
I (3889) button_power_save: Button event BUTTON_PRESS_REPEAT_DONE
|
||||
```
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
menu "Example Configuration"
|
||||
|
||||
choice ENTER_LIGHT_SLEEP_MODE
|
||||
prompt "Enter light sleep mode"
|
||||
default ENTER_LIGHT_SLEEP_AUTO
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Enable light sleep mode to save power.
|
||||
|
||||
config ENTER_LIGHT_SLEEP_AUTO
|
||||
bool "Auto enter Light Sleep"
|
||||
config ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
bool "Manually enter Light Sleep"
|
||||
endchoice
|
||||
|
||||
choice EXAMPLE_MAX_CPU_FREQ
|
||||
prompt "Maximum CPU frequency"
|
||||
default EXAMPLE_MAX_CPU_FREQ_80 if !IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_MAX_CPU_FREQ_96 if IDF_TARGET_ESP32H2
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Maximum CPU frequency to use for dynamic frequency scaling.
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_80
|
||||
bool "80 MHz"
|
||||
config EXAMPLE_MAX_CPU_FREQ_96
|
||||
bool "96 MHz"
|
||||
depends on IDF_TARGET_ESP32H2
|
||||
config EXAMPLE_MAX_CPU_FREQ_120
|
||||
bool "120 MHz"
|
||||
depends on IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_MAX_CPU_FREQ_160
|
||||
bool "160 MHz"
|
||||
depends on !IDF_TARGET_ESP32C2
|
||||
config EXAMPLE_MAX_CPU_FREQ_240
|
||||
bool "240 MHz"
|
||||
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MAX_CPU_FREQ_MHZ
|
||||
int
|
||||
default 80 if EXAMPLE_MAX_CPU_FREQ_80
|
||||
default 96 if EXAMPLE_MAX_CPU_FREQ_96
|
||||
default 120 if EXAMPLE_MAX_CPU_FREQ_120
|
||||
default 160 if EXAMPLE_MAX_CPU_FREQ_160
|
||||
default 240 if EXAMPLE_MAX_CPU_FREQ_240
|
||||
|
||||
choice EXAMPLE_MIN_CPU_FREQ
|
||||
prompt "Minimum CPU frequency"
|
||||
default EXAMPLE_MIN_CPU_FREQ_10M if !IDF_TARGET_ESP32H2
|
||||
default EXAMPLE_MIN_CPU_FREQ_32M if IDF_TARGET_ESP32H2
|
||||
depends on PM_ENABLE
|
||||
help
|
||||
Minimum CPU frequency to use for dynamic frequency scaling.
|
||||
Should be set to XTAL frequency or XTAL frequency divided by integer.
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_40M
|
||||
bool "40 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_20M
|
||||
bool "20 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_10M
|
||||
bool "10 MHz (use with 40MHz XTAL)"
|
||||
depends on XTAL_FREQ_40 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_40 || ESP32_XTAL_FREQ_AUTO || !IDF_TARGET_ESP32
|
||||
config EXAMPLE_MIN_CPU_FREQ_26M
|
||||
bool "26 MHz (use with 26MHz XTAL)"
|
||||
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_13M
|
||||
bool "13 MHz (use with 26MHz XTAL)"
|
||||
depends on XTAL_FREQ_26 || XTAL_FREQ_AUTO || ESP32_XTAL_FREQ_26 || ESP32_XTAL_FREQ_AUTO
|
||||
config EXAMPLE_MIN_CPU_FREQ_32M
|
||||
bool "32 MHz (use with 32MHz XTAL)"
|
||||
depends on IDF_TARGET_ESP32H2
|
||||
depends on XTAL_FREQ_32 || XTAL_FREQ_AUTO
|
||||
endchoice
|
||||
|
||||
config EXAMPLE_MIN_CPU_FREQ_MHZ
|
||||
int
|
||||
default 40 if EXAMPLE_MIN_CPU_FREQ_40M
|
||||
default 20 if EXAMPLE_MIN_CPU_FREQ_20M
|
||||
default 10 if EXAMPLE_MIN_CPU_FREQ_10M
|
||||
default 26 if EXAMPLE_MIN_CPU_FREQ_26M
|
||||
default 13 if EXAMPLE_MIN_CPU_FREQ_13M
|
||||
default 32 if EXAMPLE_MIN_CPU_FREQ_32M
|
||||
|
||||
endmenu
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
version: "0.1.0"
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
button:
|
||||
version: "*"
|
||||
override_path: "../../../../components/button"
|
||||
|
|
@ -1,125 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_pm.h"
|
||||
#include "iot_button.h"
|
||||
#include "esp_sleep.h"
|
||||
#include "esp_idf_version.h"
|
||||
|
||||
/* Most development boards have "boot" button attached to GPIO0.
|
||||
* You can also change this to another pin.
|
||||
*/
|
||||
#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32C2 || CONFIG_IDF_TARGET_ESP32H2 || CONFIG_IDF_TARGET_ESP32C6
|
||||
#define BOOT_BUTTON_NUM 9
|
||||
#else
|
||||
#define BOOT_BUTTON_NUM 0
|
||||
#endif
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static const char *TAG = "button_power_save";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
iot_button_print_event((button_handle_t)arg);
|
||||
esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause();
|
||||
if (cause != ESP_SLEEP_WAKEUP_UNDEFINED) {
|
||||
ESP_LOGI(TAG, "Wake up from light sleep, reason %d", cause);
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
void button_enter_power_save(void *usr_data)
|
||||
{
|
||||
ESP_LOGI(TAG, "Can enter power save now");
|
||||
esp_light_sleep_start();
|
||||
}
|
||||
#endif
|
||||
|
||||
void button_init(uint32_t button_num)
|
||||
{
|
||||
button_config_t btn_cfg = {
|
||||
.type = BUTTON_TYPE_GPIO,
|
||||
.gpio_button_config = {
|
||||
.gpio_num = button_num,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
.enable_power_save = true,
|
||||
#endif
|
||||
},
|
||||
};
|
||||
button_handle_t btn = iot_button_create(&btn_cfg);
|
||||
assert(btn);
|
||||
esp_err_t err = iot_button_register_cb(btn, BUTTON_PRESS_DOWN, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_PRESS_UP, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, button_event_cb, NULL);
|
||||
err |= iot_button_register_cb(btn, BUTTON_PRESS_END, button_event_cb, NULL);
|
||||
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY
|
||||
/*!< For enter Power Save */
|
||||
button_power_save_config_t config = {
|
||||
.enter_power_save_cb = button_enter_power_save,
|
||||
};
|
||||
err |= iot_button_register_power_save_cb(&config);
|
||||
#endif
|
||||
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0)
|
||||
void power_save_init(void)
|
||||
{
|
||||
esp_pm_config_t pm_config = {
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
.light_sleep_enable = true
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
}
|
||||
#else
|
||||
void power_save_init(void)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32
|
||||
esp_pm_config_esp32_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
esp_pm_config_esp32s2_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C3
|
||||
esp_pm_config_esp32c3_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
esp_pm_config_esp32s3_t pm_config = {
|
||||
#elif CONFIG_IDF_TARGET_ESP32C2
|
||||
esp_pm_config_esp32c2_t pm_config = {
|
||||
#endif
|
||||
.max_freq_mhz = CONFIG_EXAMPLE_MAX_CPU_FREQ_MHZ,
|
||||
.min_freq_mhz = CONFIG_EXAMPLE_MIN_CPU_FREQ_MHZ,
|
||||
#if CONFIG_FREERTOS_USE_TICKLESS_IDLE
|
||||
.light_sleep_enable = true
|
||||
#endif
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_pm_configure(&pm_config));
|
||||
}
|
||||
#endif
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
button_init(BOOT_BUTTON_NUM);
|
||||
#if CONFIG_ENTER_LIGHT_SLEEP_AUTO
|
||||
power_save_init();
|
||||
#else
|
||||
esp_light_sleep_start();
|
||||
#endif
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY=y\
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# Enable support for power management
|
||||
CONFIG_PM_ENABLE=y
|
||||
# Enable tickless idle mode
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
# Put related source code in IRAM
|
||||
CONFIG_PM_SLP_IRAM_OPT=y
|
||||
CONFIG_PM_RTOS_IDLE_OPT=y
|
||||
# Use 1000Hz freertos tick to lower sleep time threshold
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
# For button power save
|
||||
CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE=y
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
dependencies:
|
||||
cmake_utilities: 0.*
|
||||
cmake_utilities: '*'
|
||||
idf: '>=4.0'
|
||||
description: GPIO and ADC button driver
|
||||
description: GPIO and ADC and Matrix button driver
|
||||
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: git://github.com/espressif/esp-iot-solution.git
|
||||
repository_info:
|
||||
commit_sha: ef19f4a5524a0ea147c9eccde0438123b41aeeb1
|
||||
commit_sha: 91aeb7fb41e8a3e76aeb21371f9f83711c74cf3f
|
||||
path: components/button
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/button
|
||||
version: 3.5.0
|
||||
version: 4.1.5
|
||||
|
|
|
|||
|
|
@ -1,75 +1,56 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_idf_version.h"
|
||||
#include "driver/gpio.h"
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#else
|
||||
#include "driver/adc.h"
|
||||
#endif
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ADC_BUTTON_COMBINE(channel, index) ((channel)<<8 | (index))
|
||||
#define ADC_BUTTON_SPLIT_INDEX(data) ((uint32_t)(data)&0xff)
|
||||
#define ADC_BUTTON_SPLIT_CHANNEL(data) (((uint32_t)(data) >> 8) & 0xff)
|
||||
|
||||
/**
|
||||
* @brief adc button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
adc_oneshot_unit_handle_t *adc_handle; /**< handle of adc unit, if NULL will create new one internal, else will use the handle */
|
||||
adc_unit_t unit_id; /**< ADC unit */
|
||||
uint8_t adc_channel; /**< Channel of ADC */
|
||||
uint8_t button_index; /**< button index on the channel */
|
||||
uint16_t min; /**< min voltage in mv corresponding to the button */
|
||||
uint16_t max; /**< max voltage in mv corresponding to the button */
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
adc_oneshot_unit_handle_t *adc_handle; /**< handle of adc unit, if NULL will create new one internal, else will use the handle */
|
||||
#endif
|
||||
} button_adc_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize gpio button
|
||||
* @brief Create a new ADC button device
|
||||
*
|
||||
* @param config pointer of configuration struct
|
||||
* This function initializes and configures a new ADC button device using the given configuration parameters.
|
||||
* It manages the ADC unit, channels, and button-specific parameters, and ensures proper resource allocation
|
||||
* for the ADC button object.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] adc_config Configuration for the ADC channel and button, including the ADC unit, channel,
|
||||
* button index, and voltage range (min and max).
|
||||
* @param[out] ret_button Handle to the newly created button device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
* - ESP_ERR_NOT_SUPPORTED Arguments out of range.
|
||||
* - ESP_ERR_INVALID_STATE State is error.
|
||||
* - ESP_OK: Successfully created the ADC button device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_ERR_INVALID_STATE: The requested button index or channel is already in use, or no channels are available.
|
||||
* - ESP_FAIL: Failed to initialize or configure the ADC or button device.
|
||||
*
|
||||
* @note
|
||||
* - If the ADC unit is not already configured, it will be initialized with the provided or default settings.
|
||||
* - If the ADC channel is not initialized, it will be configured for the specified unit and calibrated.
|
||||
* - This function ensures that ADC resources are reused whenever possible to optimize resource allocation.
|
||||
*/
|
||||
esp_err_t button_adc_init(const button_adc_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize gpio button
|
||||
*
|
||||
* @param channel ADC channel
|
||||
* @param button_index Button index on the channel
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid.
|
||||
*/
|
||||
esp_err_t button_adc_deinit(uint8_t channel, int button_index);
|
||||
|
||||
/**
|
||||
* @brief Get the adc button level
|
||||
*
|
||||
* @param button_index It is compressed by ADC channel and button index, use the macro ADC_BUTTON_COMBINE to generate. It will be treated as a uint32_t variable.
|
||||
*
|
||||
* @return
|
||||
* - 0 Not pressed
|
||||
* - 1 Pressed
|
||||
*/
|
||||
uint8_t button_adc_get_key_level(void *button_index);
|
||||
esp_err_t iot_button_new_adc_device(const button_config_t *button_config, const button_adc_config_t *adc_config, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -18,74 +19,34 @@ extern "C" {
|
|||
typedef struct {
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
bool enable_power_save; /**< enable power save mode */
|
||||
#endif
|
||||
bool disable_pull; /**< disable internal pull or not */
|
||||
bool disable_pull; /**< disable internal pull up or down */
|
||||
} button_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize gpio button
|
||||
* @brief Create a new GPIO button device
|
||||
*
|
||||
* @param config pointer of configuration struct
|
||||
* This function initializes and configures a GPIO-based button device using the given configuration parameters.
|
||||
* It sets up the GPIO pin, configures its input mode, and optionally enables power-saving features or wake-up functionality.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] gpio_cfg Configuration for the GPIO, including the pin number, active level, and power-save options.
|
||||
* @param[out] ret_button Handle to the newly created GPIO button device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
* - ESP_OK: Successfully created the GPIO button device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided, such as an invalid GPIO number.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_ERR_INVALID_STATE: Failed to configure GPIO wake-up or interrupt settings.
|
||||
* - ESP_FAIL: General failure, such as unsupported wake-up configuration on the target.
|
||||
*
|
||||
* @note
|
||||
* - If power-saving is enabled, the GPIO will be configured as a wake-up source for light sleep.
|
||||
* - Pull-up or pull-down resistors are configured based on the `active_level` and the `disable_pull` flag.
|
||||
* - This function checks for the validity of the GPIO as a wake-up source when power-saving is enabled.
|
||||
* - If power-saving is not supported by the hardware or configuration, the function will return an error.
|
||||
*/
|
||||
esp_err_t button_gpio_init(const button_gpio_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize gpio button
|
||||
*
|
||||
* @param gpio_num gpio number of button
|
||||
*
|
||||
* @return Always return ESP_OK
|
||||
*/
|
||||
esp_err_t button_gpio_deinit(int gpio_num);
|
||||
|
||||
/**
|
||||
* @brief Get current level on button gpio
|
||||
*
|
||||
* @param gpio_num gpio number of button, it will be treated as a uint32_t variable.
|
||||
*
|
||||
* @return Level on gpio
|
||||
*/
|
||||
uint8_t button_gpio_get_key_level(void *gpio_num);
|
||||
|
||||
/**
|
||||
* @brief Sets up interrupt for GPIO button.
|
||||
*
|
||||
* @param gpio_num gpio number of button
|
||||
* @param intr_type The type of GPIO interrupt.
|
||||
* @param isr_handler The ISR (Interrupt Service Routine) handler function.
|
||||
* @param args Arguments to be passed to the ISR handler function.
|
||||
* @return Always return ESP_OK
|
||||
*/
|
||||
esp_err_t button_gpio_set_intr(int gpio_num, gpio_int_type_t intr_type, gpio_isr_t isr_handler, void *args);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable interrupt for GPIO button.
|
||||
*
|
||||
* @param gpio_num gpio number of button
|
||||
* @param enable enable or disable
|
||||
* @return Always return ESP_OK
|
||||
*/
|
||||
esp_err_t button_gpio_intr_control(int gpio_num, bool enable);
|
||||
|
||||
/**
|
||||
* @brief Enable or disable GPIO wakeup functionality.
|
||||
*
|
||||
* This function allows enabling or disabling GPIO wakeup feature.
|
||||
*
|
||||
* @param gpio_num GPIO number for wakeup functionality.
|
||||
* @param active_level Active level of the GPIO when triggered.
|
||||
* @param enable Enable or disable the GPIO wakeup.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if trigger was not active or in conflict.
|
||||
*/
|
||||
esp_err_t button_gpio_enable_gpio_wakeup(uint32_t gpio_num, uint8_t active_level, bool enable);
|
||||
esp_err_t iot_button_new_gpio_device(const button_config_t *button_config, const button_gpio_config_t *gpio_config, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,18 +1,17 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MATRIX_BUTTON_COMBINE(row_gpio, col_gpio) ((row_gpio)<<8 | (col_gpio))
|
||||
#define MATRIX_BUTTON_SPLIT_COL(data) ((uint32_t)(data)&0xff)
|
||||
#define MATRIX_BUTTON_SPLIT_ROW(data) (((uint32_t)(data) >> 8) & 0xff)
|
||||
|
||||
/**
|
||||
* @brief Button matrix key configuration.
|
||||
* Just need to configure the GPIO associated with this GPIO in the matrix keyboard.
|
||||
|
|
@ -34,46 +33,37 @@ extern "C" {
|
|||
* but buttons within the same row can be detected without conflicts.
|
||||
*/
|
||||
typedef struct {
|
||||
int32_t row_gpio_num; /**< GPIO number associated with the row */
|
||||
int32_t col_gpio_num; /**< GPIO number associated with the column */
|
||||
int32_t *row_gpios; /**< GPIO number list for the row */
|
||||
int32_t *col_gpios; /**< GPIO number list for the column */
|
||||
uint32_t row_gpio_num; /**< Number of GPIOs associated with the row */
|
||||
uint32_t col_gpio_num; /**< Number of GPIOs associated with the column */
|
||||
} button_matrix_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a button matrix keyboard.
|
||||
* @brief Create a new button matrix device
|
||||
*
|
||||
* This function initializes and configures a button matrix device using the specified row and column GPIOs.
|
||||
* Each button in the matrix is represented as an independent button object, and its handle is returned in the `ret_button` array.
|
||||
*
|
||||
* @param[in] button_config Configuration for the button device, including callbacks and debounce parameters.
|
||||
* @param[in] matrix_config Configuration for the matrix, including row and column GPIOs and their counts.
|
||||
* @param[out] ret_button Array of handles for the buttons in the matrix.
|
||||
* @param[inout] size Pointer to the total number of buttons in the matrix. Must match the product of row and column GPIO counts.
|
||||
* On success, this value is updated to reflect the size of the button matrix.
|
||||
*
|
||||
* @param config Pointer to the button matrix key configuration.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if the argument is NULL.
|
||||
* - ESP_OK: Successfully created the button matrix device.
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument provided, such as null pointers or mismatched matrix dimensions.
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed.
|
||||
* - ESP_FAIL: General failure, such as button creation failure for one or more buttons.
|
||||
*
|
||||
* @note When initializing the button matrix keyboard, the row GPIO pins will be set as outputs,
|
||||
* and the column GPIO pins will be set as inputs, both with pull-down resistors enabled.
|
||||
* @note
|
||||
* - Each row GPIO is configured as an output, while each column GPIO is configured as an input.
|
||||
* - The total number of buttons in the matrix must equal the product of the row and column GPIO counts.
|
||||
* - The `ret_button` array must be large enough to store handles for all buttons in the matrix.
|
||||
* - If any button creation fails, the function will free all allocated resources and return an error.
|
||||
*/
|
||||
esp_err_t button_matrix_init(const button_matrix_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize a button in the matrix keyboard.
|
||||
*
|
||||
* @param row_gpio_num GPIO number of the row where the button is located.
|
||||
* @param col_gpio_num GPIO number of the column where the button is located.
|
||||
* @return
|
||||
* - ESP_OK if the button is successfully deinitialized
|
||||
*
|
||||
* @note When deinitializing a button, please exercise caution and avoid deinitializing a button individually, as it may affect the proper functioning of other buttons in the same row or column.
|
||||
*/
|
||||
esp_err_t button_matrix_deinit(int row_gpio_num, int col_gpio_num);
|
||||
|
||||
/**
|
||||
* @brief Get the key level from the button matrix hardware.
|
||||
*
|
||||
* @param hardware_data Pointer to hardware-specific data containing information about row GPIO and column GPIO.
|
||||
* @return uint8_t[out] The key level read from the hardware.
|
||||
*
|
||||
* @note This function retrieves the key level from the button matrix hardware.
|
||||
* The `hardware_data` parameter should contain information about the row and column GPIO pins,
|
||||
* and you can access this information using the `MATRIX_BUTTON_SPLIT_COL` and `MATRIX_BUTTON_SPLIT_ROW` macros.
|
||||
*/
|
||||
uint8_t button_matrix_get_key_level(void *hardware_data);
|
||||
esp_err_t iot_button_new_matrix_device(const button_config_t *button_config, const button_matrix_config_t *matrix_config, button_handle_t *ret_button, size_t *size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
57
managed_components/espressif__button/include/button_types.h
Normal file
57
managed_components/espressif__button/include/button_types.h
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum {
|
||||
BUTTON_INACTIVE = 0,
|
||||
BUTTON_ACTIVE,
|
||||
};
|
||||
|
||||
typedef struct button_dev_t *button_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
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_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a new IoT button instance
|
||||
*
|
||||
* This function initializes a new button instance with the specified configuration
|
||||
* and driver. It also sets up internal resources such as the button timer if not
|
||||
* already initialized.
|
||||
*
|
||||
* @param[in] config Pointer to the button configuration structure
|
||||
* @param[in] driver Pointer to the button driver structure
|
||||
* @param[out] ret_button Pointer to where the handle of the created button will be stored
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully created the button
|
||||
* - ESP_ERR_INVALID_ARG: Invalid arguments passed to the function
|
||||
* - ESP_ERR_NO_MEM: Memory allocation failed
|
||||
*
|
||||
* @note
|
||||
* - The first call to this function logs the IoT Button version.
|
||||
* - The function initializes a global button timer if it is not already running.
|
||||
* - Timer is started only if the driver does not enable power-saving mode.
|
||||
*/
|
||||
esp_err_t iot_button_create(const button_config_t *config, const button_driver_t *driver, button_handle_t *ret_button);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
|
@ -6,12 +6,8 @@
|
|||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_SOC_ADC_SUPPORTED
|
||||
#include "button_adc.h"
|
||||
#endif
|
||||
#include "button_gpio.h"
|
||||
#include "button_matrix.h"
|
||||
#include "esp_err.h"
|
||||
#include "button_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
@ -19,7 +15,6 @@ extern "C" {
|
|||
|
||||
typedef void (* button_cb_t)(void *button_handle, void *usr_data);
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
typedef void (* button_power_save_cb_t)(void *usr_data);
|
||||
|
||||
/**
|
||||
|
|
@ -27,12 +22,9 @@ typedef void (* button_power_save_cb_t)(void *usr_data);
|
|||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_power_save_cb_t enter_power_save_cb;
|
||||
void *usr_data;
|
||||
button_power_save_cb_t enter_power_save_cb; /**< Callback function when entering power save mode */
|
||||
void *usr_data; /**< User data for the callback */
|
||||
} button_power_save_config_t;
|
||||
#endif
|
||||
|
||||
typedef void *button_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Button events
|
||||
|
|
@ -55,7 +47,7 @@ typedef enum {
|
|||
} button_event_t;
|
||||
|
||||
/**
|
||||
* @brief Button events data
|
||||
* @brief Button events arg
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
|
|
@ -74,27 +66,7 @@ typedef union {
|
|||
struct multiple_clicks_t {
|
||||
uint16_t clicks; /**< number of clicks, to trigger the callback */
|
||||
} multiple_clicks; /**< multiple clicks struct, for event BUTTON_MULTIPLE_CLICK */
|
||||
} button_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Button events configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_event_t event; /**< button event type */
|
||||
button_event_data_t event_data; /**< event data corresponding to the event */
|
||||
} button_event_config_t;
|
||||
|
||||
/**
|
||||
* @brief Supported button type
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
BUTTON_TYPE_GPIO,
|
||||
BUTTON_TYPE_ADC,
|
||||
BUTTON_TYPE_MATRIX,
|
||||
BUTTON_TYPE_CUSTOM
|
||||
} button_type_t;
|
||||
} button_event_args_t;
|
||||
|
||||
/**
|
||||
* @brief Button parameter
|
||||
|
|
@ -106,45 +78,6 @@ typedef enum {
|
|||
BUTTON_PARAM_MAX,
|
||||
} button_param_t;
|
||||
|
||||
/**
|
||||
* @brief custom button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
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*/
|
||||
} button_custom_config_t;
|
||||
|
||||
/**
|
||||
* @brief Button configuration
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
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 */
|
||||
union {
|
||||
button_gpio_config_t gpio_button_config; /**< gpio button configuration */
|
||||
#if CONFIG_SOC_ADC_SUPPORTED
|
||||
button_adc_config_t adc_button_config; /**< adc button configuration */
|
||||
#endif
|
||||
button_matrix_config_t matrix_button_config; /**< matrix key button configuration */
|
||||
button_custom_config_t custom_button_config; /**< custom button configuration */
|
||||
}; /**< button configuration */
|
||||
} button_config_t;
|
||||
|
||||
/**
|
||||
* @brief Create a button
|
||||
*
|
||||
* @param config pointer of button configuration, must corresponding the button type
|
||||
*
|
||||
* @return A handle to the created button, or NULL in case of error.
|
||||
*/
|
||||
button_handle_t iot_button_create(const button_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Delete a button
|
||||
*
|
||||
|
|
@ -161,6 +94,7 @@ esp_err_t iot_button_delete(button_handle_t btn_handle);
|
|||
*
|
||||
* @param btn_handle A button handle to register
|
||||
* @param event Button event
|
||||
* @param event_args Button event arguments
|
||||
* @param cb Callback function.
|
||||
* @param usr_data user data
|
||||
*
|
||||
|
|
@ -170,51 +104,21 @@ esp_err_t iot_button_delete(button_handle_t btn_handle);
|
|||
* - 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_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data);
|
||||
|
||||
/**
|
||||
* @brief Register the button event callback function.
|
||||
*
|
||||
* @param btn_handle A button handle to register
|
||||
* @param event_cfg Button event configuration
|
||||
* @param cb Callback function.
|
||||
* @param usr_data user data
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Unregister the button event callback function.
|
||||
* In case event_data is also passed it will unregister function for that particular event_data only.
|
||||
*
|
||||
* @param btn_handle A button handle to unregister
|
||||
* @param event_cfg Button event
|
||||
* @param cb callback to unregister
|
||||
*
|
||||
* @return
|
||||
* - 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_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb);
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Unregister all the callbacks associated with the event.
|
||||
*
|
||||
* @param btn_handle A button handle to unregister
|
||||
* @param event Button event
|
||||
* @param event_args Used for unregistering a specific callback.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid.
|
||||
* - ESP_ERR_INVALID_STATE No callbacks registered for the event
|
||||
*/
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event);
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args);
|
||||
|
||||
/**
|
||||
* @brief counts total callbacks registered
|
||||
|
|
@ -238,7 +142,7 @@ size_t iot_button_count_cb(button_handle_t btn_handle);
|
|||
* - 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);
|
||||
size_t iot_button_count_event_cb(button_handle_t btn_handle, button_event_t event);
|
||||
|
||||
/**
|
||||
* @brief Get button event
|
||||
|
|
@ -286,12 +190,25 @@ esp_err_t iot_button_print_event(button_handle_t btn_handle);
|
|||
uint8_t iot_button_get_repeat(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button ticks time
|
||||
* @brief Get button pressed time
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Actual time from press down to up (ms).
|
||||
*/
|
||||
uint32_t iot_button_get_pressed_time(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button ticks time
|
||||
*
|
||||
* @deprecated This function is deprecated and will be removed in a future release.
|
||||
* Please use iot_button_get_pressed_time() instead.
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Actual time from press down to up (ms).
|
||||
*/
|
||||
__attribute__((deprecated("Use iot_button_get_pressed_time() instead")))
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
|
|
@ -343,7 +260,6 @@ esp_err_t iot_button_resume(void);
|
|||
*/
|
||||
esp_err_t iot_button_stop(void);
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
/**
|
||||
* @brief Register a callback function for power saving.
|
||||
* The config->enter_power_save_cb function will be called when all keys stop working.
|
||||
|
|
@ -356,7 +272,6 @@ esp_err_t iot_button_stop(void);
|
|||
* - ESP_ERR_NO_MEM Not enough memory
|
||||
*/
|
||||
esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct button_driver_t button_driver_t; /*!< Type of button object */
|
||||
|
||||
struct button_driver_t {
|
||||
/*!< (optional) Need Support Power Save */
|
||||
bool enable_power_save;
|
||||
|
||||
/*!< (necessary) Get key level */
|
||||
uint8_t (*get_key_level)(button_driver_t *button_driver);
|
||||
|
||||
/*!< (optional) Enter Power Save cb */
|
||||
esp_err_t (*enter_power_save)(button_driver_t *button_driver);
|
||||
|
||||
/*!< (optional) Del the hardware driver and cleanup */
|
||||
esp_err_t (*del)(button_driver_t *button_driver);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
|
@ -12,11 +12,10 @@
|
|||
#include "driver/gpio.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
#include "esp_pm.h"
|
||||
#endif
|
||||
#include "esp_check.h"
|
||||
#include "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "button_interface.h"
|
||||
|
||||
static const char *TAG = "button";
|
||||
static portMUX_TYPE s_button_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
|
|
@ -45,6 +44,14 @@ static const char *button_event_str[] = {
|
|||
"BUTTON_NONE_PRESS",
|
||||
};
|
||||
|
||||
enum {
|
||||
PRESS_DOWN_CHECK = 0,
|
||||
PRESS_UP_CHECK,
|
||||
PRESS_REPEAT_DOWN_CHECK,
|
||||
PRESS_REPEAT_UP_CHECK,
|
||||
PRESS_LONG_PRESS_UP_CHECK,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structs to store callback info
|
||||
*
|
||||
|
|
@ -52,48 +59,41 @@ static const char *button_event_str[] = {
|
|||
typedef struct {
|
||||
button_cb_t cb;
|
||||
void *usr_data;
|
||||
button_event_data_t event_data;
|
||||
button_event_args_t event_args;
|
||||
} button_cb_info_t;
|
||||
|
||||
/**
|
||||
* @brief Structs to record individual key parameters
|
||||
*
|
||||
*/
|
||||
typedef struct Button {
|
||||
uint32_t ticks; /*!< Count for the current button state. */
|
||||
uint32_t long_press_ticks; /*!< Trigger ticks for long press, */
|
||||
uint32_t short_press_ticks; /*!< Trigger ticks for repeat press */
|
||||
uint32_t long_press_hold_cnt; /*!< Record long press hold count */
|
||||
uint8_t repeat;
|
||||
uint8_t state: 3;
|
||||
uint8_t debounce_cnt: 3;
|
||||
uint8_t active_level: 1;
|
||||
uint8_t button_level: 1;
|
||||
uint8_t enable_power_save: 1;
|
||||
button_event_t event;
|
||||
uint8_t (*hal_button_Level)(void *hardware_data);
|
||||
esp_err_t (*hal_button_deinit)(void *hardware_data);
|
||||
void *hardware_data;
|
||||
button_type_t type;
|
||||
button_cb_info_t *cb_info[BUTTON_EVENT_MAX];
|
||||
size_t size[BUTTON_EVENT_MAX];
|
||||
int count[2];
|
||||
struct Button *next;
|
||||
typedef struct button_dev_t {
|
||||
uint32_t ticks; /*!< Count for the current button state. */
|
||||
uint32_t long_press_ticks; /*!< Trigger ticks for long press, */
|
||||
uint32_t short_press_ticks; /*!< Trigger ticks for repeat press */
|
||||
uint32_t long_press_hold_cnt; /*!< Record long press hold count */
|
||||
uint8_t repeat;
|
||||
uint8_t state: 3;
|
||||
uint8_t debounce_cnt: 4; /*!< Max 15 */
|
||||
uint8_t button_level: 1;
|
||||
button_event_t event;
|
||||
button_driver_t *driver;
|
||||
button_cb_info_t *cb_info[BUTTON_EVENT_MAX];
|
||||
size_t size[BUTTON_EVENT_MAX];
|
||||
int count[2];
|
||||
struct button_dev_t *next;
|
||||
} button_dev_t;
|
||||
|
||||
//button handle list head.
|
||||
static button_dev_t *g_head_handle = NULL;
|
||||
static esp_timer_handle_t g_button_timer_handle = NULL;
|
||||
static bool g_is_timer_running = false;
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
static button_power_save_config_t power_save_usr_cfg = {0};
|
||||
#endif
|
||||
|
||||
#define TICKS_INTERVAL CONFIG_BUTTON_PERIOD_TIME_MS
|
||||
#define DEBOUNCE_TICKS CONFIG_BUTTON_DEBOUNCE_TICKS //MAX 8
|
||||
#define SHORT_TICKS (CONFIG_BUTTON_SHORT_PRESS_TIME_MS /TICKS_INTERVAL)
|
||||
#define LONG_TICKS (CONFIG_BUTTON_LONG_PRESS_TIME_MS /TICKS_INTERVAL)
|
||||
#define SERIAL_TICKS (CONFIG_BUTTON_SERIAL_TIME_MS /TICKS_INTERVAL)
|
||||
#define SERIAL_TICKS (CONFIG_BUTTON_LONG_PRESS_HOLD_SERIAL_TIME_MS /TICKS_INTERVAL)
|
||||
#define TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS*4)
|
||||
|
||||
#define CALL_EVENT_CB(ev) \
|
||||
|
|
@ -110,7 +110,7 @@ static button_power_save_config_t power_save_usr_cfg = {0};
|
|||
*/
|
||||
static void button_handler(button_dev_t *btn)
|
||||
{
|
||||
uint8_t read_gpio_level = btn->hal_button_Level(btn->hardware_data);
|
||||
uint8_t read_gpio_level = btn->driver->get_key_level(btn->driver);
|
||||
|
||||
/** ticks counter working.. */
|
||||
if ((btn->state) > 0) {
|
||||
|
|
@ -129,54 +129,54 @@ static void button_handler(button_dev_t *btn)
|
|||
|
||||
/** State machine */
|
||||
switch (btn->state) {
|
||||
case 0:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
case PRESS_DOWN_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
|
||||
btn->ticks = 0;
|
||||
btn->repeat = 1;
|
||||
btn->state = 1;
|
||||
btn->state = PRESS_UP_CHECK;
|
||||
} else {
|
||||
btn->event = (uint8_t)BUTTON_NONE_PRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (btn->button_level != btn->active_level) {
|
||||
case PRESS_UP_CHECK:
|
||||
if (btn->button_level != BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->ticks = 0;
|
||||
btn->state = 2;
|
||||
btn->state = PRESS_REPEAT_DOWN_CHECK;
|
||||
|
||||
} else if (btn->ticks >= btn->long_press_ticks) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
btn->state = 4;
|
||||
btn->state = PRESS_LONG_PRESS_UP_CHECK;
|
||||
/** Calling callbacks for BUTTON_LONG_PRESS_START */
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(btn);
|
||||
int32_t diff = ticks_time - btn->long_press_ticks * TICKS_INTERVAL;
|
||||
uint32_t pressed_time = iot_button_get_pressed_time(btn);
|
||||
int32_t diff = pressed_time - btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (btn->cb_info[btn->event] && btn->count[0] == 0) {
|
||||
if (abs(diff) <= TOLERANCE && btn->cb_info[btn->event][btn->count[0]].event_data.long_press.press_time == (btn->long_press_ticks * TICKS_INTERVAL)) {
|
||||
if (abs(diff) <= TOLERANCE && btn->cb_info[btn->event][btn->count[0]].event_args.long_press.press_time == (btn->long_press_ticks * TICKS_INTERVAL)) {
|
||||
do {
|
||||
btn->cb_info[btn->event][btn->count[0]].cb(btn, btn->cb_info[btn->event][btn->count[0]].usr_data);
|
||||
btn->count[0]++;
|
||||
if (btn->count[0] >= btn->size[btn->event]) {
|
||||
break;
|
||||
}
|
||||
} while (btn->cb_info[btn->event][btn->count[0]].event_data.long_press.press_time == btn->long_press_ticks * TICKS_INTERVAL);
|
||||
} while (btn->cb_info[btn->event][btn->count[0]].event_args.long_press.press_time == btn->long_press_ticks * TICKS_INTERVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
case PRESS_REPEAT_DOWN_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
|
||||
btn->event = (uint8_t)BUTTON_PRESS_REPEAT;
|
||||
btn->repeat++;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_REPEAT); // repeat hit
|
||||
btn->ticks = 0;
|
||||
btn->state = 3;
|
||||
btn->state = PRESS_REPEAT_UP_CHECK;
|
||||
} else if (btn->ticks > btn->short_press_ticks) {
|
||||
if (btn->repeat == 1) {
|
||||
btn->event = (uint8_t)BUTTON_SINGLE_CLICK;
|
||||
|
|
@ -190,14 +190,8 @@ static void button_handler(button_dev_t *btn)
|
|||
|
||||
/** Calling the callbacks for MULTIPLE BUTTON CLICKS */
|
||||
for (int i = 0; i < btn->size[btn->event]; i++) {
|
||||
if (btn->repeat == btn->cb_info[btn->event][i].event_data.multiple_clicks.clicks) {
|
||||
do {
|
||||
btn->cb_info[btn->event][i].cb(btn, btn->cb_info[btn->event][i].usr_data);
|
||||
i++;
|
||||
if (i >= btn->size[btn->event]) {
|
||||
break;
|
||||
}
|
||||
} while (btn->cb_info[btn->event][i].event_data.multiple_clicks.clicks == btn->repeat);
|
||||
if (btn->repeat == btn->cb_info[btn->event][i].event_args.multiple_clicks.clicks) {
|
||||
btn->cb_info[btn->event][i].cb(btn, btn->cb_info[btn->event][i].usr_data);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -211,75 +205,75 @@ static void button_handler(button_dev_t *btn)
|
|||
break;
|
||||
|
||||
case 3:
|
||||
if (btn->button_level != btn->active_level) {
|
||||
if (btn->button_level != BUTTON_ACTIVE) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
if (btn->ticks < btn->short_press_ticks) {
|
||||
btn->ticks = 0;
|
||||
btn->state = 2; //repeat press
|
||||
btn->state = PRESS_REPEAT_DOWN_CHECK; //repeat press
|
||||
} else {
|
||||
btn->state = 0;
|
||||
btn->state = PRESS_DOWN_CHECK;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
case PRESS_LONG_PRESS_UP_CHECK:
|
||||
if (btn->button_level == BUTTON_ACTIVE) {
|
||||
//continue hold trigger
|
||||
if (btn->ticks >= (btn->long_press_hold_cnt + 1) * SERIAL_TICKS + btn->long_press_ticks) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_HOLD;
|
||||
btn->long_press_hold_cnt++;
|
||||
CALL_EVENT_CB(BUTTON_LONG_PRESS_HOLD);
|
||||
}
|
||||
|
||||
/** Calling callbacks for BUTTON_LONG_PRESS_START based on press_time */
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(btn);
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_START];
|
||||
uint16_t time = cb_info[btn->count[0]].event_data.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[0] + 1; i < btn->size[BUTTON_LONG_PRESS_START]; i++) {
|
||||
time = cb_info[i].event_data.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[0] = i;
|
||||
break;
|
||||
}
|
||||
/** Calling callbacks for BUTTON_LONG_PRESS_START based on press_time */
|
||||
uint32_t pressed_time = iot_button_get_pressed_time(btn);
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_START];
|
||||
uint16_t time = cb_info[btn->count[0]].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[0] + 1; i < btn->size[BUTTON_LONG_PRESS_START]; i++) {
|
||||
time = cb_info[i].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[0] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (btn->count[0] < btn->size[BUTTON_LONG_PRESS_START] && abs((int)ticks_time - (int)time) <= TOLERANCE) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
do {
|
||||
cb_info[btn->count[0]].cb(btn, cb_info[btn->count[0]].usr_data);
|
||||
btn->count[0]++;
|
||||
if (btn->count[0] >= btn->size[BUTTON_LONG_PRESS_START]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[0]].event_data.long_press.press_time);
|
||||
}
|
||||
}
|
||||
if (btn->count[0] < btn->size[BUTTON_LONG_PRESS_START] && abs((int)pressed_time - (int)time) <= TOLERANCE) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
do {
|
||||
cb_info[btn->count[0]].cb(btn, cb_info[btn->count[0]].usr_data);
|
||||
btn->count[0]++;
|
||||
if (btn->count[0] >= btn->size[BUTTON_LONG_PRESS_START]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[0]].event_args.long_press.press_time);
|
||||
}
|
||||
}
|
||||
|
||||
/** Updating counter for BUTTON_LONG_PRESS_UP press_time */
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_UP]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_UP];
|
||||
uint16_t time = cb_info[btn->count[1] + 1].event_data.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[1] + 1; i < btn->size[BUTTON_LONG_PRESS_UP]; i++) {
|
||||
time = cb_info[i].event_data.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[1] = i;
|
||||
break;
|
||||
}
|
||||
/** Updating counter for BUTTON_LONG_PRESS_UP press_time */
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_UP]) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[BUTTON_LONG_PRESS_UP];
|
||||
uint16_t time = cb_info[btn->count[1] + 1].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL > time) {
|
||||
for (int i = btn->count[1] + 1; i < btn->size[BUTTON_LONG_PRESS_UP]; i++) {
|
||||
time = cb_info[i].event_args.long_press.press_time;
|
||||
if (btn->long_press_ticks * TICKS_INTERVAL <= time) {
|
||||
btn->count[1] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (btn->count[1] + 1 < btn->size[BUTTON_LONG_PRESS_UP] && abs((int)ticks_time - (int)time) <= TOLERANCE) {
|
||||
do {
|
||||
btn->count[1]++;
|
||||
if (btn->count[1] + 1 >= btn->size[BUTTON_LONG_PRESS_UP]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[1] + 1].event_data.long_press.press_time);
|
||||
}
|
||||
}
|
||||
if (btn->count[1] + 1 < btn->size[BUTTON_LONG_PRESS_UP] && abs((int)pressed_time - (int)time) <= TOLERANCE) {
|
||||
do {
|
||||
btn->count[1]++;
|
||||
if (btn->count[1] + 1 >= btn->size[BUTTON_LONG_PRESS_UP]) {
|
||||
break;
|
||||
}
|
||||
} while (time == cb_info[btn->count[1] + 1].event_args.long_press.press_time);
|
||||
}
|
||||
}
|
||||
} else { //releasd
|
||||
|
|
@ -295,7 +289,7 @@ static void button_handler(button_dev_t *btn)
|
|||
break;
|
||||
}
|
||||
btn->count[1]--;
|
||||
} while (cb_info[btn->count[1]].event_data.long_press.press_time == cb_info[btn->count[1] + 1].event_data.long_press.press_time);
|
||||
} while (cb_info[btn->count[1]].event_args.long_press.press_time == cb_info[btn->count[1] + 1].event_args.long_press.press_time);
|
||||
|
||||
/** Reset the counter */
|
||||
btn->count[1] = -1;
|
||||
|
|
@ -307,7 +301,7 @@ static void button_handler(button_dev_t *btn)
|
|||
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->state = 0; //reset
|
||||
btn->state = PRESS_DOWN_CHECK; //reset
|
||||
btn->long_press_hold_cnt = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
|
|
@ -320,18 +314,13 @@ static void button_cb(void *args)
|
|||
{
|
||||
button_dev_t *target;
|
||||
/*!< When all buttons enter the BUTTON_NONE_PRESS state, the system enters low-power mode */
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
bool enter_power_save_flag = true;
|
||||
#endif
|
||||
for (target = g_head_handle; target; target = target->next) {
|
||||
button_handler(target);
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
if (!(target->enable_power_save && target->debounce_cnt == 0 && target->event == BUTTON_NONE_PRESS)) {
|
||||
if (!(target->driver->enable_power_save && target->debounce_cnt == 0 && target->event == BUTTON_NONE_PRESS)) {
|
||||
enter_power_save_flag = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
if (enter_power_save_flag) {
|
||||
/*!< Stop esp timer for power save */
|
||||
if (g_is_timer_running) {
|
||||
|
|
@ -339,9 +328,8 @@ static void button_cb(void *args)
|
|||
g_is_timer_running = false;
|
||||
}
|
||||
for (target = g_head_handle; target; target = target->next) {
|
||||
if (target->type == BUTTON_TYPE_GPIO && target->enable_power_save) {
|
||||
button_gpio_intr_control((int)(target->hardware_data), true);
|
||||
button_gpio_enable_gpio_wakeup((uint32_t)(target->hardware_data), target->active_level, true);
|
||||
if (target->driver->enable_power_save && target->driver->enter_power_save) {
|
||||
target->driver->enter_power_save(target->driver);
|
||||
}
|
||||
}
|
||||
/*!< Notify the user that the Button has entered power save mode by calling this callback function. */
|
||||
|
|
@ -349,212 +337,21 @@ static void button_cb(void *args)
|
|||
power_save_usr_cfg.enter_power_save_cb(power_save_usr_cfg.usr_data);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
static void IRAM_ATTR button_power_save_isr_handler(void* arg)
|
||||
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)
|
||||
{
|
||||
if (!g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
button_gpio_intr_control((int)arg, false);
|
||||
/*!< disable gpio wakeup not need active level*/
|
||||
button_gpio_enable_gpio_wakeup((uint32_t)arg, 0, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static button_dev_t *button_create_com(uint8_t active_level, uint8_t (*hal_get_key_state)(void *hardware_data), void *hardware_data, uint16_t long_press_ticks, uint16_t short_press_ticks)
|
||||
{
|
||||
BTN_CHECK(NULL != hal_get_key_state, "Function pointer is invalid", NULL);
|
||||
|
||||
button_dev_t *btn = (button_dev_t *) calloc(1, sizeof(button_dev_t));
|
||||
BTN_CHECK(NULL != btn, "Button memory alloc failed", NULL);
|
||||
btn->hardware_data = hardware_data;
|
||||
btn->event = BUTTON_NONE_PRESS;
|
||||
btn->active_level = active_level;
|
||||
btn->hal_button_Level = hal_get_key_state;
|
||||
btn->button_level = !active_level;
|
||||
btn->long_press_ticks = long_press_ticks;
|
||||
btn->short_press_ticks = short_press_ticks;
|
||||
|
||||
/** Add handle to list */
|
||||
btn->next = g_head_handle;
|
||||
g_head_handle = btn;
|
||||
|
||||
if (!g_button_timer_handle) {
|
||||
esp_timer_create_args_t button_timer = {0};
|
||||
button_timer.arg = NULL;
|
||||
button_timer.callback = button_cb;
|
||||
button_timer.dispatch_method = ESP_TIMER_TASK;
|
||||
button_timer.name = "button_timer";
|
||||
esp_timer_create(&button_timer, &g_button_timer_handle);
|
||||
}
|
||||
|
||||
return btn;
|
||||
}
|
||||
|
||||
static esp_err_t button_delete_com(button_dev_t *btn)
|
||||
{
|
||||
BTN_CHECK(NULL != btn, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
button_dev_t **curr;
|
||||
for (curr = &g_head_handle; *curr;) {
|
||||
button_dev_t *entry = *curr;
|
||||
if (entry == btn) {
|
||||
*curr = entry->next;
|
||||
free(entry);
|
||||
} else {
|
||||
curr = &entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* count button number */
|
||||
uint16_t number = 0;
|
||||
button_dev_t *target = g_head_handle;
|
||||
while (target) {
|
||||
target = target->next;
|
||||
number++;
|
||||
}
|
||||
ESP_LOGD(TAG, "remain btn number=%d", number);
|
||||
|
||||
if (0 == number && g_is_timer_running) { /**< if all button is deleted, stop the timer */
|
||||
esp_timer_stop(g_button_timer_handle);
|
||||
esp_timer_delete(g_button_timer_handle);
|
||||
g_button_timer_handle = NULL;
|
||||
g_is_timer_running = false;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
button_handle_t iot_button_create(const button_config_t *config)
|
||||
{
|
||||
ESP_LOGI(TAG, "IoT Button Version: %d.%d.%d", BUTTON_VER_MAJOR, BUTTON_VER_MINOR, BUTTON_VER_PATCH);
|
||||
BTN_CHECK(config, "Invalid button config", NULL);
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
button_dev_t *btn = NULL;
|
||||
uint16_t long_press_time = 0;
|
||||
uint16_t short_press_time = 0;
|
||||
long_press_time = TIME_TO_TICKS(config->long_press_time, LONG_TICKS);
|
||||
short_press_time = TIME_TO_TICKS(config->short_press_time, SHORT_TICKS);
|
||||
switch (config->type) {
|
||||
case BUTTON_TYPE_GPIO: {
|
||||
const button_gpio_config_t *cfg = &(config->gpio_button_config);
|
||||
ret = button_gpio_init(cfg);
|
||||
BTN_CHECK(ESP_OK == ret, "gpio button init failed", NULL);
|
||||
btn = button_create_com(cfg->active_level, button_gpio_get_key_level, (void *)cfg->gpio_num, long_press_time, short_press_time);
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
if (cfg->enable_power_save) {
|
||||
btn->enable_power_save = cfg->enable_power_save;
|
||||
button_gpio_set_intr(cfg->gpio_num, cfg->active_level == 0 ? GPIO_INTR_LOW_LEVEL : GPIO_INTR_HIGH_LEVEL, button_power_save_isr_handler, (void *)cfg->gpio_num);
|
||||
}
|
||||
#endif
|
||||
} break;
|
||||
#if CONFIG_SOC_ADC_SUPPORTED
|
||||
case BUTTON_TYPE_ADC: {
|
||||
const button_adc_config_t *cfg = &(config->adc_button_config);
|
||||
ret = button_adc_init(cfg);
|
||||
BTN_CHECK(ESP_OK == ret, "adc button init failed", NULL);
|
||||
btn = button_create_com(1, button_adc_get_key_level, (void *)ADC_BUTTON_COMBINE(cfg->adc_channel, cfg->button_index), long_press_time, short_press_time);
|
||||
} break;
|
||||
#endif
|
||||
case BUTTON_TYPE_MATRIX: {
|
||||
const button_matrix_config_t *cfg = &(config->matrix_button_config);
|
||||
ret = button_matrix_init(cfg);
|
||||
BTN_CHECK(ESP_OK == ret, "matrix button init failed", NULL);
|
||||
btn = button_create_com(1, button_matrix_get_key_level, (void *)MATRIX_BUTTON_COMBINE(cfg->row_gpio_num, cfg->col_gpio_num), long_press_time, short_press_time);
|
||||
} break;
|
||||
case BUTTON_TYPE_CUSTOM: {
|
||||
if (config->custom_button_config.button_custom_init) {
|
||||
ret = config->custom_button_config.button_custom_init(config->custom_button_config.priv);
|
||||
BTN_CHECK(ESP_OK == ret, "custom button init failed", NULL);
|
||||
}
|
||||
|
||||
btn = button_create_com(config->custom_button_config.active_level,
|
||||
config->custom_button_config.button_custom_get_key_value,
|
||||
config->custom_button_config.priv,
|
||||
long_press_time, short_press_time);
|
||||
if (btn) {
|
||||
btn->hal_button_deinit = config->custom_button_config.button_custom_deinit;
|
||||
}
|
||||
} break;
|
||||
|
||||
default:
|
||||
ESP_LOGE(TAG, "Unsupported button type");
|
||||
break;
|
||||
}
|
||||
BTN_CHECK(NULL != btn, "button create failed", NULL);
|
||||
btn->type = config->type;
|
||||
if (!btn->enable_power_save && !g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
return (button_handle_t)btn;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *)btn_handle;
|
||||
switch (btn->type) {
|
||||
case BUTTON_TYPE_GPIO:
|
||||
ret = button_gpio_deinit((int)(btn->hardware_data));
|
||||
break;
|
||||
#if CONFIG_SOC_ADC_SUPPORTED
|
||||
case BUTTON_TYPE_ADC:
|
||||
ret = button_adc_deinit(ADC_BUTTON_SPLIT_CHANNEL(btn->hardware_data), ADC_BUTTON_SPLIT_INDEX(btn->hardware_data));
|
||||
break;
|
||||
#endif
|
||||
case BUTTON_TYPE_MATRIX:
|
||||
ret = button_matrix_deinit(MATRIX_BUTTON_SPLIT_ROW(btn->hardware_data), MATRIX_BUTTON_SPLIT_COL(btn->hardware_data));
|
||||
break;
|
||||
case BUTTON_TYPE_CUSTOM:
|
||||
if (btn->hal_button_deinit) {
|
||||
ret = btn->hal_button_deinit(btn->hardware_data);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BTN_CHECK(ESP_OK == ret, "button deinit failed", ESP_FAIL);
|
||||
for (int i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (btn->cb_info[i]) {
|
||||
free(btn->cb_info[i]);
|
||||
}
|
||||
}
|
||||
button_delete_com(btn);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_register_cb(button_handle_t btn_handle, button_event_t event, button_cb_t cb, void *usr_data)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
BTN_CHECK(event != BUTTON_MULTIPLE_CLICK, "event argument is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_event_config_t event_cfg = {
|
||||
.event = event,
|
||||
};
|
||||
ESP_RETURN_ON_FALSE(event < BUTTON_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
ESP_RETURN_ON_FALSE(NULL != cb, ESP_ERR_INVALID_ARG, TAG, "Pointer of cb is invalid");
|
||||
ESP_RETURN_ON_FALSE(event != BUTTON_MULTIPLE_CLICK || event_args, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && !event_cfg.event_data.long_press.press_time) {
|
||||
event_cfg.event_data.long_press.press_time = btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (event_args) {
|
||||
ESP_RETURN_ON_FALSE(!(event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) || event_args->long_press.press_time > btn->short_press_ticks * TICKS_INTERVAL, ESP_ERR_INVALID_ARG, TAG, "event_args is invalid");
|
||||
ESP_RETURN_ON_FALSE(event != BUTTON_MULTIPLE_CLICK || event_args->multiple_clicks.clicks, ESP_ERR_INVALID_ARG, TAG, "event_args is invalid");
|
||||
}
|
||||
|
||||
return iot_button_register_event_cb(btn_handle, event_cfg, cb, usr_data);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
button_event_t event = event_cfg.event;
|
||||
BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
|
||||
BTN_CHECK(!(event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) || event_cfg.event_data.long_press.press_time > btn->short_press_ticks * TICKS_INTERVAL, "event_data is invalid", ESP_ERR_INVALID_ARG);
|
||||
BTN_CHECK(event != BUTTON_MULTIPLE_CLICK || event_cfg.event_data.multiple_clicks.clicks, "event_data is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
if (!btn->cb_info[event]) {
|
||||
btn->cb_info[event] = calloc(1, sizeof(button_cb_info_t));
|
||||
BTN_CHECK(NULL != btn->cb_info[event], "calloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
|
|
@ -573,27 +370,30 @@ esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_
|
|||
btn->cb_info[event][btn->size[event]].usr_data = usr_data;
|
||||
btn->size[event]++;
|
||||
|
||||
/** Inserting the event_data in sorted manner */
|
||||
/** Inserting the event_args in sorted manner */
|
||||
if (event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) {
|
||||
uint16_t press_time = event_cfg.event_data.long_press.press_time;
|
||||
BTN_CHECK(press_time / TICKS_INTERVAL > btn->short_press_ticks, "press_time event_data is less than short_press_ticks", ESP_ERR_INVALID_ARG);
|
||||
uint16_t press_time = btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (event_args) {
|
||||
press_time = event_args->long_press.press_time;
|
||||
}
|
||||
BTN_CHECK(press_time / TICKS_INTERVAL > btn->short_press_ticks, "press_time event_args is less than short_press_ticks", ESP_ERR_INVALID_ARG);
|
||||
if (btn->size[event] >= 2) {
|
||||
for (int i = btn->size[event] - 2; i >= 0; i--) {
|
||||
if (btn->cb_info[event][i].event_data.long_press.press_time > press_time) {
|
||||
if (btn->cb_info[event][i].event_args.long_press.press_time > press_time) {
|
||||
btn->cb_info[event][i + 1] = btn->cb_info[event][i];
|
||||
|
||||
btn->cb_info[event][i].event_data.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i].event_args.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i].cb = cb;
|
||||
btn->cb_info[event][i].usr_data = usr_data;
|
||||
} else {
|
||||
btn->cb_info[event][i + 1].event_data.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i + 1].event_args.long_press.press_time = press_time;
|
||||
btn->cb_info[event][i + 1].cb = cb;
|
||||
btn->cb_info[event][i + 1].usr_data = usr_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
btn->cb_info[event][btn->size[event] - 1].event_data.long_press.press_time = press_time;
|
||||
btn->cb_info[event][btn->size[event] - 1].event_args.long_press.press_time = press_time;
|
||||
}
|
||||
|
||||
int32_t press_ticks = press_time / TICKS_INTERVAL;
|
||||
|
|
@ -603,35 +403,52 @@ esp_err_t iot_button_register_event_cb(button_handle_t btn_handle, button_event_
|
|||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK) {
|
||||
uint16_t clicks = btn->long_press_ticks * TICKS_INTERVAL;
|
||||
if (event_args) {
|
||||
clicks = event_args->multiple_clicks.clicks;
|
||||
}
|
||||
if (btn->size[event] >= 2) {
|
||||
for (int i = btn->size[event] - 2; i >= 0; i--) {
|
||||
if (btn->cb_info[event][i].event_data.multiple_clicks.clicks > event_cfg.event_data.multiple_clicks.clicks) {
|
||||
if (btn->cb_info[event][i].event_args.multiple_clicks.clicks > clicks) {
|
||||
btn->cb_info[event][i + 1] = btn->cb_info[event][i];
|
||||
|
||||
btn->cb_info[event][i].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
|
||||
btn->cb_info[event][i].event_args.multiple_clicks.clicks = clicks;
|
||||
btn->cb_info[event][i].cb = cb;
|
||||
btn->cb_info[event][i].usr_data = usr_data;
|
||||
} else {
|
||||
btn->cb_info[event][i + 1].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
|
||||
btn->cb_info[event][i + 1].event_args.multiple_clicks.clicks = clicks;
|
||||
btn->cb_info[event][i + 1].cb = cb;
|
||||
btn->cb_info[event][i + 1].usr_data = usr_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
btn->cb_info[event][btn->size[event] - 1].event_data.multiple_clicks.clicks = event_cfg.event_data.multiple_clicks.clicks;
|
||||
btn->cb_info[event][btn->size[event] - 1].event_args.multiple_clicks.clicks = clicks;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event, button_event_args_t *event_args)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
ESP_RETURN_ON_FALSE(event < BUTTON_EVENT_MAX, ESP_ERR_INVALID_ARG, TAG, "event is invalid");
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
BTN_CHECK(NULL != btn->cb_info[event], "No callbacks registered for the event", ESP_ERR_INVALID_STATE);
|
||||
ESP_RETURN_ON_FALSE(btn->cb_info[event], ESP_ERR_INVALID_STATE, TAG, "No callbacks registered for the event");
|
||||
|
||||
int check = -1;
|
||||
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_args) {
|
||||
if (event_args->long_press.press_time != 0) {
|
||||
goto unregister_event;
|
||||
}
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK && event_args) {
|
||||
if (event_args->multiple_clicks.clicks != 0) {
|
||||
goto unregister_event;
|
||||
}
|
||||
}
|
||||
|
||||
if (btn->cb_info[event]) {
|
||||
free(btn->cb_info[event]);
|
||||
|
|
@ -648,52 +465,40 @@ esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t ev
|
|||
btn->cb_info[event] = NULL;
|
||||
btn->size[event] = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_unregister_event(button_handle_t btn_handle, button_event_config_t event_cfg, button_cb_t cb)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_event_t event = event_cfg.event;
|
||||
BTN_CHECK(event < BUTTON_EVENT_MAX, "event is invalid", ESP_ERR_INVALID_ARG);
|
||||
BTN_CHECK(NULL != cb, "Pointer to function callback is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
|
||||
int check = -1;
|
||||
unregister_event:
|
||||
|
||||
for (int i = 0; i < btn->size[event]; i++) {
|
||||
if (cb == btn->cb_info[event][i].cb) {
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_cfg.event_data.long_press.press_time) {
|
||||
if (event_cfg.event_data.long_press.press_time != btn->cb_info[event][i].event_data.long_press.press_time) {
|
||||
continue;
|
||||
}
|
||||
if ((event == BUTTON_LONG_PRESS_START || event == BUTTON_LONG_PRESS_UP) && event_args->long_press.press_time) {
|
||||
if (event_args->long_press.press_time != btn->cb_info[event][i].event_args.long_press.press_time) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK && event_cfg.event_data.multiple_clicks.clicks) {
|
||||
if (event_cfg.event_data.multiple_clicks.clicks != btn->cb_info[event][i].event_data.multiple_clicks.clicks) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
check = i;
|
||||
for (int j = i; j <= btn->size[event] - 1; j++) {
|
||||
btn->cb_info[event][j] = btn->cb_info[event][j + 1];
|
||||
}
|
||||
|
||||
if (btn->size[event] != 1) {
|
||||
button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] - 1));
|
||||
BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
btn->cb_info[event] = p;
|
||||
btn->size[event]--;
|
||||
} else {
|
||||
free(btn->cb_info[event]);
|
||||
btn->cb_info[event] = NULL;
|
||||
btn->size[event] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK && event_args->multiple_clicks.clicks) {
|
||||
if (event_args->multiple_clicks.clicks != btn->cb_info[event][i].event_args.multiple_clicks.clicks) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
check = i;
|
||||
for (int j = i; j <= btn->size[event] - 1; j++) {
|
||||
btn->cb_info[event][j] = btn->cb_info[event][j + 1];
|
||||
}
|
||||
|
||||
if (btn->size[event] != 1) {
|
||||
button_cb_info_t *p = realloc(btn->cb_info[event], sizeof(button_cb_info_t) * (btn->size[event] - 1));
|
||||
BTN_CHECK(NULL != p, "realloc cb_info failed", ESP_ERR_NO_MEM);
|
||||
btn->cb_info[event] = p;
|
||||
btn->size[event]--;
|
||||
} else {
|
||||
free(btn->cb_info[event]);
|
||||
btn->cb_info[event] = NULL;
|
||||
btn->size[event] = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
BTN_CHECK(check != -1, "No such callback registered for the event", ESP_ERR_INVALID_STATE);
|
||||
|
||||
ESP_RETURN_ON_FALSE(check != -1, ESP_ERR_NOT_FOUND, TAG, "No such callback registered for the event");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
|
@ -710,7 +515,7 @@ size_t iot_button_count_cb(button_handle_t btn_handle)
|
|||
return ret;
|
||||
}
|
||||
|
||||
size_t iot_button_count_event(button_handle_t btn_handle, button_event_t event)
|
||||
size_t iot_button_count_event_cb(button_handle_t btn_handle, button_event_t event)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
|
|
@ -745,13 +550,18 @@ uint8_t iot_button_get_repeat(button_handle_t btn_handle)
|
|||
return btn->repeat;
|
||||
}
|
||||
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle)
|
||||
uint32_t iot_button_get_pressed_time(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return (btn->ticks * TICKS_INTERVAL);
|
||||
}
|
||||
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle)
|
||||
{
|
||||
return iot_button_get_pressed_time(btn_handle);
|
||||
}
|
||||
|
||||
uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
|
|
@ -782,18 +592,19 @@ uint8_t iot_button_get_key_level(button_handle_t btn_handle)
|
|||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", 0);
|
||||
button_dev_t *btn = (button_dev_t *)btn_handle;
|
||||
uint8_t level = btn->hal_button_Level(btn->hardware_data);
|
||||
return (level == btn->active_level) ? 1 : 0;
|
||||
uint8_t level = btn->driver->get_key_level(btn->driver);
|
||||
return level;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_resume(void)
|
||||
{
|
||||
BTN_CHECK(g_button_timer_handle, "Button timer handle is invalid", ESP_ERR_INVALID_STATE);
|
||||
BTN_CHECK(!g_is_timer_running, "Button timer is already running", ESP_ERR_INVALID_STATE);
|
||||
|
||||
esp_err_t err = esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
BTN_CHECK(ESP_OK == err, "Button timer start failed", ESP_FAIL);
|
||||
g_is_timer_running = true;
|
||||
if (!g_button_timer_handle) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
if (!g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
|
@ -808,7 +619,6 @@ esp_err_t iot_button_stop(void)
|
|||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config)
|
||||
{
|
||||
BTN_CHECK(g_head_handle, "No button registered", ESP_ERR_INVALID_STATE);
|
||||
|
|
@ -818,4 +628,83 @@ esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *co
|
|||
power_save_usr_cfg.usr_data = config->usr_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
esp_err_t iot_button_create(const button_config_t *config, const button_driver_t *driver, button_handle_t *ret_button)
|
||||
{
|
||||
if (!g_head_handle) {
|
||||
ESP_LOGI(TAG, "IoT Button Version: %d.%d.%d", BUTTON_VER_MAJOR, BUTTON_VER_MINOR, BUTTON_VER_PATCH);
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(driver && config && ret_button, ESP_ERR_INVALID_ARG, TAG, "Invalid argument");
|
||||
button_dev_t *btn = (button_dev_t *) calloc(1, sizeof(button_dev_t));
|
||||
ESP_RETURN_ON_FALSE(btn, ESP_ERR_NO_MEM, TAG, "Button memory alloc failed");
|
||||
|
||||
btn->driver = (button_driver_t *)driver;
|
||||
btn->long_press_ticks = TIME_TO_TICKS(config->long_press_time, LONG_TICKS);
|
||||
btn->short_press_ticks = TIME_TO_TICKS(config->short_press_time, SHORT_TICKS);
|
||||
btn->event = BUTTON_NONE_PRESS;
|
||||
btn->button_level = BUTTON_INACTIVE;
|
||||
|
||||
btn->next = g_head_handle;
|
||||
g_head_handle = btn;
|
||||
|
||||
if (!g_button_timer_handle) {
|
||||
esp_timer_create_args_t button_timer = {0};
|
||||
button_timer.arg = NULL;
|
||||
button_timer.callback = button_cb;
|
||||
button_timer.dispatch_method = ESP_TIMER_TASK;
|
||||
button_timer.name = "button_timer";
|
||||
esp_timer_create(&button_timer, &g_button_timer_handle);
|
||||
}
|
||||
|
||||
if (!driver->enable_power_save && !g_is_timer_running) {
|
||||
esp_timer_start_periodic(g_button_timer_handle, TICKS_INTERVAL * 1000U);
|
||||
g_is_timer_running = true;
|
||||
}
|
||||
|
||||
*ret_button = (button_handle_t)btn;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(NULL != btn_handle, ESP_ERR_INVALID_ARG, TAG, "Pointer of handle is invalid");
|
||||
button_dev_t *btn = (button_dev_t *)btn_handle;
|
||||
|
||||
for (int i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (btn->cb_info[i]) {
|
||||
free(btn->cb_info[i]);
|
||||
}
|
||||
}
|
||||
|
||||
ret = btn->driver->del(btn->driver);
|
||||
ESP_RETURN_ON_FALSE(ESP_OK == ret, ret, TAG, "Failed to delete button driver");
|
||||
|
||||
button_dev_t **curr;
|
||||
for (curr = &g_head_handle; *curr;) {
|
||||
button_dev_t *entry = *curr;
|
||||
if (entry == btn) {
|
||||
*curr = entry->next;
|
||||
free(entry);
|
||||
} else {
|
||||
curr = &entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
/* count button number */
|
||||
uint16_t number = 0;
|
||||
button_dev_t *target = g_head_handle;
|
||||
while (target) {
|
||||
target = target->next;
|
||||
number++;
|
||||
}
|
||||
ESP_LOGD(TAG, "remain btn number=%d", number);
|
||||
|
||||
if (0 == number && g_is_timer_running) { /**< if all button is deleted, stop the timer */
|
||||
esp_timer_stop(g_button_timer_handle);
|
||||
esp_timer_delete(g_button_timer_handle);
|
||||
g_button_timer_handle = NULL;
|
||||
g_is_timer_running = false;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND PRIVREQ esp_adc)
|
||||
else()
|
||||
list(APPEND PRIVREQ esp_adc_cal)
|
||||
endif()
|
||||
|
||||
idf_component_register(SRC_DIRS "."
|
||||
PRIV_INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES unity test_utils button ${PRIVREQ})
|
||||
PRIV_REQUIRES esp_event unity test_utils button ${PRIVREQ}
|
||||
WHOLE_ARCHIVE)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,134 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_adc.h"
|
||||
|
||||
static const char *TAG = "ADC BUTTON TEST";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "BTN[%d] %s", (int)data, iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tPressed Time[%"PRIu32"]", iot_button_get_pressed_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("adc button test", "[button][adc]")
|
||||
{
|
||||
/** ESP32-S3-Korvo2 board */
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_adc_config_t btn_adc_cfg = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel = 4,
|
||||
};
|
||||
|
||||
button_handle_t btns[6] = {NULL};
|
||||
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
btn_adc_cfg.button_index = i;
|
||||
if (i == 0) {
|
||||
btn_adc_cfg.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
btn_adc_cfg.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
esp_err_t ret = iot_button_new_adc_device(&btn_cfg, &btn_adc_cfg, &btns[i]);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btns[i]);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_END, NULL, button_event_cb, (void *)i);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(btns[i]);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("adc button test memory leak", "[button][adc][memory leak]")
|
||||
{
|
||||
/** ESP32-S3-Korvo2 board */
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_adc_config_t btn_adc_cfg = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
.adc_channel = 4,
|
||||
};
|
||||
|
||||
button_handle_t btns[6] = {NULL};
|
||||
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
btn_adc_cfg.button_index = i;
|
||||
if (i == 0) {
|
||||
btn_adc_cfg.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
btn_adc_cfg.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
btn_adc_cfg.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
esp_err_t ret = iot_button_new_adc_device(&btn_cfg, &btn_adc_cfg, &btns[i]);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(btns[i]);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)i);
|
||||
iot_button_register_cb(btns[i], BUTTON_PRESS_END, NULL, button_event_cb, (void *)i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(btns[i]);
|
||||
}
|
||||
}
|
||||
288
managed_components/espressif__button/test_apps/main/auto_test.c
Normal file
288
managed_components/espressif__button/test_apps/main/auto_test.c
Normal file
|
|
@ -0,0 +1,288 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_gpio.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "BUTTON AUTO TEST";
|
||||
|
||||
#define GPIO_OUTPUT_IO_45 45
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static EventGroupHandle_t g_check = NULL;
|
||||
static SemaphoreHandle_t g_auto_check_pass = NULL;
|
||||
|
||||
static button_event_t state = BUTTON_PRESS_DOWN;
|
||||
|
||||
static void button_auto_press_test_task(void *arg)
|
||||
{
|
||||
// test BUTTON_PRESS_DOWN
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// // test BUTTON_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT_DONE
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_SINGLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_DOUBLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_MULTIPLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_LONG_PRESS_START
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(1600));
|
||||
|
||||
// test BUTTON_LONG_PRESS_HOLD and BUTTON_LONG_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
ESP_LOGI(TAG, "Auto Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
static void button_auto_check_cb_1(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(arg) == state) {
|
||||
xEventGroupSetBits(g_check, BIT(1));
|
||||
}
|
||||
}
|
||||
static void button_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(arg) == state) {
|
||||
ESP_LOGI(TAG, "Auto check: button event %s pass", iot_button_get_event_str(state));
|
||||
xEventGroupSetBits(g_check, BIT(0));
|
||||
if (++state >= BUTTON_EVENT_MAX) {
|
||||
xSemaphoreGive(g_auto_check_pass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button auto-test", "[button][iot][auto]")
|
||||
{
|
||||
state = BUTTON_PRESS_DOWN;
|
||||
g_check = xEventGroupCreate();
|
||||
g_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xEventGroupSetBits(g_check, BIT(0) | BIT(1));
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
/* register iot_button callback for all the button_event */
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 4,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_auto_check_cb, NULL);
|
||||
} else {
|
||||
iot_button_register_cb(btn, i, NULL, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_cb(btn, i, NULL, button_auto_check_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, (void *)1500));
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
xTaskCreate(button_auto_press_test_task, "button_auto_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(g_auto_check_pass, pdMS_TO_TICKS(6000)));
|
||||
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
button_event_args_t args;
|
||||
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
args.multiple_clicks.clicks = 4;
|
||||
iot_button_unregister_cb(btn, i, &args);
|
||||
} else if (i == BUTTON_LONG_PRESS_UP || i == BUTTON_LONG_PRESS_START) {
|
||||
args.long_press.press_time = 1500;
|
||||
iot_button_unregister_cb(btn, i, &args);
|
||||
} else {
|
||||
iot_button_unregister_cb(btn, i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(btn));
|
||||
vEventGroupDelete(g_check);
|
||||
vSemaphoreDelete(g_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
#define TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS * 4)
|
||||
|
||||
uint16_t long_press_time[5] = {2000, 2500, 3000, 3500, 4000};
|
||||
static SemaphoreHandle_t long_press_check = NULL;
|
||||
static SemaphoreHandle_t long_press_auto_check_pass = NULL;
|
||||
unsigned int status = 0;
|
||||
|
||||
static void button_auto_long_press_test_task(void *arg)
|
||||
{
|
||||
// Test for BUTTON_LONG_PRESS_START
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
status = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
|
||||
if (i > 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] - long_press_time[i - 1]));
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i]));
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// Test for BUTTON_LONG_PRESS_UP
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
status = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] + 10));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Auto Long Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void button_long_press_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
uint32_t value = (uint32_t)data;
|
||||
uint16_t event = (0xffff0000 & value) >> 16;
|
||||
uint16_t time = 0xffff & value;
|
||||
uint32_t pressed_time = iot_button_get_pressed_time(arg);
|
||||
int32_t diff = pressed_time - time;
|
||||
if (status == value && abs(diff) <= TOLERANCE) {
|
||||
ESP_LOGI(TAG, "Auto check: button event: %s and time: %d pass", iot_button_get_event_str(event), time);
|
||||
|
||||
if (event == BUTTON_LONG_PRESS_UP && time == long_press_time[4]) {
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
}
|
||||
|
||||
xSemaphoreGive(long_press_check);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button long_press auto-test", "[button][long_press][auto]")
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting the test");
|
||||
long_press_check = xSemaphoreCreateBinary();
|
||||
long_press_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(long_press_check);
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
button_event_args_t args = {
|
||||
.long_press.press_time = long_press_time[i],
|
||||
};
|
||||
|
||||
uint32_t data = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, &args, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xTaskCreate(button_auto_long_press_test_task, "button_auto_long_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
xSemaphoreTake(long_press_auto_check_pass, portMAX_DELAY);
|
||||
iot_button_unregister_cb(btn, BUTTON_LONG_PRESS_START, NULL);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
button_event_args_t args = {
|
||||
.long_press.press_time = long_press_time[i]
|
||||
};
|
||||
|
||||
uint32_t data = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, &args, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(long_press_auto_check_pass, pdMS_TO_TICKS(17000)));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(btn));
|
||||
vSemaphoreDelete(long_press_check);
|
||||
vSemaphoreDelete(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
|
@ -1,743 +0,0 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/timers.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "BUTTON TEST";
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-400)
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
#define BUTTON_NUM 16
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
static button_handle_t g_btns[BUTTON_NUM] = {0};
|
||||
|
||||
static int get_btn_index(button_handle_t btn)
|
||||
{
|
||||
for (size_t i = 0; i < BUTTON_NUM; i++) {
|
||||
if (btn == g_btns[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void button_press_down_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_DOWN, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_DOWN", get_btn_index((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_press_up_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_UP, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_UP[%"PRIu32"]", get_btn_index((button_handle_t)arg), iot_button_get_ticks_time((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_press_repeat_cb(void *arg, void *data)
|
||||
{
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT[%d]", get_btn_index((button_handle_t)arg), iot_button_get_repeat((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_single_click_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_SINGLE_CLICK, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_SINGLE_CLICK", get_btn_index((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_double_click_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_DOUBLE_CLICK, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_DOUBLE_CLICK", get_btn_index((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_long_press_start_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_START, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_START", get_btn_index((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_long_press_hold_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_LONG_PRESS_HOLD, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_LONG_PRESS_HOLD[%"PRIu32"],count is [%d]", get_btn_index((button_handle_t)arg), iot_button_get_ticks_time((button_handle_t)arg), iot_button_get_long_press_hold_cnt((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static void button_press_repeat_done_cb(void *arg, void *data)
|
||||
{
|
||||
TEST_ASSERT_EQUAL_HEX(BUTTON_PRESS_REPEAT_DONE, iot_button_get_event(arg));
|
||||
ESP_LOGI(TAG, "BTN%d: BUTTON_PRESS_REPEAT_DONE[%d]", get_btn_index((button_handle_t)arg), iot_button_get_repeat((button_handle_t)arg));
|
||||
}
|
||||
|
||||
static esp_err_t custom_button_gpio_init(void *param)
|
||||
{
|
||||
button_gpio_config_t *cfg = (button_gpio_config_t *)param;
|
||||
|
||||
return button_gpio_init(cfg);
|
||||
}
|
||||
|
||||
static uint8_t custom_button_gpio_get_key_value(void *param)
|
||||
{
|
||||
button_gpio_config_t *cfg = (button_gpio_config_t *)param;
|
||||
|
||||
return button_gpio_get_key_level((void *)cfg->gpio_num);
|
||||
}
|
||||
|
||||
static esp_err_t custom_button_gpio_deinit(void *param)
|
||||
{
|
||||
button_gpio_config_t *cfg = (button_gpio_config_t *)param;
|
||||
|
||||
return button_gpio_deinit(cfg->gpio_num);
|
||||
}
|
||||
|
||||
TEST_CASE("custom button test", "[button][iot]")
|
||||
{
|
||||
button_gpio_config_t *gpio_cfg = calloc(1, sizeof(button_gpio_config_t));
|
||||
gpio_cfg->active_level = 0;
|
||||
gpio_cfg->gpio_num = 0;
|
||||
|
||||
button_config_t cfg = {
|
||||
.type = BUTTON_TYPE_CUSTOM,
|
||||
.long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS,
|
||||
.short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS,
|
||||
.custom_button_config = {
|
||||
.button_custom_init = custom_button_gpio_init,
|
||||
.button_custom_deinit = custom_button_gpio_deinit,
|
||||
.button_custom_get_key_value = custom_button_gpio_get_key_value,
|
||||
.active_level = 0,
|
||||
.priv = gpio_cfg,
|
||||
},
|
||||
};
|
||||
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(g_btns[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test", "[button][iot]")
|
||||
{
|
||||
button_config_t 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,
|
||||
},
|
||||
};
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(g_btns[0]);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(g_btns[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button get event test", "[button][iot]")
|
||||
{
|
||||
button_config_t 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,
|
||||
},
|
||||
};
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(g_btns[0]);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
button_event_t event = iot_button_get_event(g_btns[0]);
|
||||
if (event != BUTTON_NONE_PRESS) {
|
||||
ESP_LOGI(TAG, "event is %s", iot_button_get_event_str(event));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
iot_button_delete(g_btns[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test power save", "[button][iot][power save]")
|
||||
{
|
||||
button_config_t 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,
|
||||
},
|
||||
};
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[0], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_stop());
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_resume());
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(g_btns[0]);
|
||||
}
|
||||
|
||||
TEST_CASE("matrix keyboard button test", "[button][matrix key]")
|
||||
{
|
||||
int32_t row_gpio[4] = {4, 5, 6, 7};
|
||||
int32_t col_gpio[4] = {3, 8, 16, 15};
|
||||
button_config_t 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 = 0,
|
||||
}
|
||||
};
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
cfg.matrix_button_config.row_gpio_num = row_gpio[i];
|
||||
for (int j = 0; j < 4; j++) {
|
||||
cfg.matrix_button_config.col_gpio_num = col_gpio[j];
|
||||
g_btns[i * 4 + j] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[i * 4 + j]);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i * 4 + j], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
iot_button_delete(g_btns[i * 4 + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if CONFIG_SOC_ADC_SUPPORTED
|
||||
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
TEST_CASE("adc button test", "[button][iot]")
|
||||
{
|
||||
/** ESP32-S3-Korvo board */
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
button_config_t cfg = {0};
|
||||
cfg.type = BUTTON_TYPE_ADC;
|
||||
cfg.long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS;
|
||||
cfg.short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS;
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
cfg.adc_button_config.adc_channel = 7,
|
||||
cfg.adc_button_config.button_index = i;
|
||||
if (i == 0) {
|
||||
cfg.adc_button_config.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
cfg.adc_button_config.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
g_btns[i] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[i]);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(g_btns[i]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
|
||||
#include "esp_adc/adc_cali.h"
|
||||
|
||||
static esp_err_t adc_calibration_init(adc_unit_t unit, adc_atten_t atten, adc_cali_handle_t *out_handle)
|
||||
{
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
#define ADC_BUTTON_WIDTH SOC_ADC_RTC_MAX_BITWIDTH
|
||||
#else
|
||||
#define ADC_BUTTON_WIDTH ADC_WIDTH_MAX - 1
|
||||
#endif
|
||||
|
||||
adc_cali_handle_t handle = NULL;
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
bool calibrated = false;
|
||||
|
||||
#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting");
|
||||
adc_cali_curve_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED
|
||||
if (!calibrated) {
|
||||
ESP_LOGI(TAG, "calibration scheme version is %s", "Line Fitting");
|
||||
adc_cali_line_fitting_config_t cali_config = {
|
||||
.unit_id = unit,
|
||||
.atten = atten,
|
||||
.bitwidth = ADC_BUTTON_WIDTH,
|
||||
};
|
||||
ret = adc_cali_create_scheme_line_fitting(&cali_config, &handle);
|
||||
if (ret == ESP_OK) {
|
||||
calibrated = true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
*out_handle = handle;
|
||||
if (ret == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Calibration Success");
|
||||
} else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) {
|
||||
ESP_LOGW(TAG, "eFuse not burnt, skip software calibration");
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid arg or no memory");
|
||||
}
|
||||
|
||||
return calibrated ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
TEST_CASE("adc button idf5 drive test", "[button][iot]")
|
||||
{
|
||||
adc_oneshot_unit_handle_t adc1_handle;
|
||||
adc_cali_handle_t adc1_cali_handle;
|
||||
adc_oneshot_unit_init_cfg_t init_config = {
|
||||
.unit_id = ADC_UNIT_1,
|
||||
};
|
||||
esp_err_t ret = adc_oneshot_new_unit(&init_config, &adc1_handle);
|
||||
TEST_ASSERT_TRUE(ret == ESP_OK);
|
||||
/*!< use atten 11db or 12db */
|
||||
adc_calibration_init(ADC_UNIT_1, 3, &adc1_cali_handle);
|
||||
|
||||
/** ESP32-S3-Korvo board */
|
||||
const uint16_t vol[6] = {380, 820, 1180, 1570, 1980, 2410};
|
||||
button_config_t cfg = {0};
|
||||
cfg.type = BUTTON_TYPE_ADC;
|
||||
cfg.long_press_time = CONFIG_BUTTON_LONG_PRESS_TIME_MS;
|
||||
cfg.short_press_time = CONFIG_BUTTON_SHORT_PRESS_TIME_MS;
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
cfg.adc_button_config.adc_handle = &adc1_handle;
|
||||
cfg.adc_button_config.adc_channel = 7,
|
||||
cfg.adc_button_config.button_index = i;
|
||||
if (i == 0) {
|
||||
cfg.adc_button_config.min = (0 + vol[i]) / 2;
|
||||
} else {
|
||||
cfg.adc_button_config.min = (vol[i - 1] + vol[i]) / 2;
|
||||
}
|
||||
|
||||
if (i == 5) {
|
||||
cfg.adc_button_config.max = (vol[i] + 3000) / 2;
|
||||
} else {
|
||||
cfg.adc_button_config.max = (vol[i] + vol[i + 1]) / 2;
|
||||
}
|
||||
|
||||
g_btns[i] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[i]);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_DOWN, button_press_down_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_UP, button_press_up_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT, button_press_repeat_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_SINGLE_CLICK, button_single_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_DOUBLE_CLICK, button_double_click_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_START, button_long_press_start_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_LONG_PRESS_HOLD, button_long_press_hold_cb, NULL);
|
||||
iot_button_register_cb(g_btns[i], BUTTON_PRESS_REPEAT_DONE, button_press_repeat_done_cb, NULL);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
for (size_t i = 0; i < 6; i++) {
|
||||
iot_button_delete(g_btns[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif // CONFIG_SOC_ADC_SUPPORTED
|
||||
|
||||
#define GPIO_OUTPUT_IO_45 45
|
||||
static EventGroupHandle_t g_check = NULL;
|
||||
static SemaphoreHandle_t g_auto_check_pass = NULL;
|
||||
|
||||
static button_event_t state = BUTTON_PRESS_DOWN;
|
||||
|
||||
static void button_auto_press_test_task(void *arg)
|
||||
{
|
||||
// test BUTTON_PRESS_DOWN
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// // test BUTTON_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_PRESS_REPEAT_DONE
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_SINGLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_DOUBLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(200));
|
||||
|
||||
// test BUTTON_MULTIPLE_CLICK
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
for (int i = 0; i < 4; i++) {
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
// test BUTTON_LONG_PRESS_START
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(1600));
|
||||
|
||||
// test BUTTON_LONG_PRESS_HOLD and BUTTON_LONG_PRESS_UP
|
||||
xEventGroupWaitBits(g_check, BIT(0) | BIT(1), pdTRUE, pdTRUE, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
ESP_LOGI(TAG, "Auto Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
static void button_auto_check_cb_1(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(g_btns[0]) == state) {
|
||||
xEventGroupSetBits(g_check, BIT(1));
|
||||
}
|
||||
}
|
||||
static void button_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
if (iot_button_get_event(g_btns[0]) == state) {
|
||||
ESP_LOGI(TAG, "Auto check: button event %s pass", iot_button_get_event_str(state));
|
||||
xEventGroupSetBits(g_check, BIT(0));
|
||||
if (++state >= BUTTON_EVENT_MAX) {
|
||||
xSemaphoreGive(g_auto_check_pass);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button auto-test", "[button][iot][auto]")
|
||||
{
|
||||
state = BUTTON_PRESS_DOWN;
|
||||
g_check = xEventGroupCreate();
|
||||
g_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xEventGroupSetBits(g_check, BIT(0) | BIT(1));
|
||||
button_config_t 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,
|
||||
},
|
||||
};
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
|
||||
/* register iot_button callback for all the button_event */
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
button_event_config_t btn_cfg;
|
||||
btn_cfg.event = i;
|
||||
btn_cfg.event_data.multiple_clicks.clicks = 4;
|
||||
iot_button_register_event_cb(g_btns[0], btn_cfg, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_event_cb(g_btns[0], btn_cfg, button_auto_check_cb, NULL);
|
||||
} else {
|
||||
iot_button_register_cb(g_btns[0], i, button_auto_check_cb_1, NULL);
|
||||
iot_button_register_cb(g_btns[0], i, button_auto_check_cb, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_set_param(g_btns[0], BUTTON_LONG_PRESS_TIME_MS, (void *)1500));
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
|
||||
xTaskCreate(button_auto_press_test_task, "button_auto_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(g_auto_check_pass, pdMS_TO_TICKS(6000)));
|
||||
|
||||
for (uint8_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
button_event_config_t btn_cfg;
|
||||
btn_cfg.event = i;
|
||||
if (i == BUTTON_MULTIPLE_CLICK) {
|
||||
btn_cfg.event_data.multiple_clicks.clicks = 4;
|
||||
} else if (i == BUTTON_LONG_PRESS_UP || i == BUTTON_LONG_PRESS_START) {
|
||||
btn_cfg.event_data.long_press.press_time = 1500;
|
||||
}
|
||||
iot_button_unregister_event(g_btns[0], btn_cfg, button_auto_check_cb);
|
||||
iot_button_unregister_event(g_btns[0], btn_cfg, button_auto_check_cb_1);
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(g_btns[0]));
|
||||
vEventGroupDelete(g_check);
|
||||
vSemaphoreDelete(g_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
#define TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS * 4)
|
||||
|
||||
uint16_t long_press_time[5] = {2000, 2500, 3000, 3500, 4000};
|
||||
static SemaphoreHandle_t long_press_check = NULL;
|
||||
static SemaphoreHandle_t long_press_auto_check_pass = NULL;
|
||||
unsigned int status = 0;
|
||||
|
||||
static void button_auto_long_press_test_task(void *arg)
|
||||
{
|
||||
// Test for BUTTON_LONG_PRESS_START
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
status = (BUTTON_LONG_PRESS_START << 16) | long_press_time[i];
|
||||
if (i > 0) {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] - long_press_time[i - 1]));
|
||||
} else {
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i]));
|
||||
}
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
// Test for BUTTON_LONG_PRESS_UP
|
||||
for (int i = 0; i < 5; i++) {
|
||||
xSemaphoreTake(long_press_check, portMAX_DELAY);
|
||||
status = (BUTTON_LONG_PRESS_UP << 16) | long_press_time[i];
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 0);
|
||||
vTaskDelay(pdMS_TO_TICKS(long_press_time[i] + 10));
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Auto Long Press Success!");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void button_long_press_auto_check_cb(void *arg, void *data)
|
||||
{
|
||||
uint32_t value = (uint32_t)data;
|
||||
uint16_t event = (0xffff0000 & value) >> 16;
|
||||
uint16_t time = 0xffff & value;
|
||||
uint32_t ticks_time = iot_button_get_ticks_time(g_btns[0]);
|
||||
int32_t diff = ticks_time - time;
|
||||
if (status == value && abs(diff) <= TOLERANCE) {
|
||||
ESP_LOGI(TAG, "Auto check: button event: %s and time: %d pass", iot_button_get_event_str(state), time);
|
||||
|
||||
if (event == BUTTON_LONG_PRESS_UP && time == long_press_time[4]) {
|
||||
xSemaphoreGive(long_press_auto_check_pass);
|
||||
}
|
||||
|
||||
xSemaphoreGive(long_press_check);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button long_press auto-test", "[button][long_press][auto]")
|
||||
{
|
||||
ESP_LOGI(TAG, "Starting the test");
|
||||
long_press_check = xSemaphoreCreateBinary();
|
||||
long_press_auto_check_pass = xSemaphoreCreateBinary();
|
||||
xSemaphoreGive(long_press_check);
|
||||
button_config_t 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,
|
||||
},
|
||||
};
|
||||
g_btns[0] = iot_button_create(&cfg);
|
||||
TEST_ASSERT_NOT_NULL(g_btns[0]);
|
||||
|
||||
button_event_config_t btn_cfg;
|
||||
btn_cfg.event = BUTTON_LONG_PRESS_START;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
btn_cfg.event_data.long_press.press_time = long_press_time[i];
|
||||
uint32_t data = (btn_cfg.event << 16) | long_press_time[i];
|
||||
iot_button_register_event_cb(g_btns[0], btn_cfg, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = (1ULL << GPIO_OUTPUT_IO_45),
|
||||
.pull_down_en = 0,
|
||||
.pull_up_en = 0,
|
||||
};
|
||||
gpio_config(&io_conf);
|
||||
gpio_set_level(GPIO_OUTPUT_IO_45, 1);
|
||||
xTaskCreate(button_auto_long_press_test_task, "button_auto_long_press_test_task", 1024 * 4, NULL, 10, NULL);
|
||||
|
||||
xSemaphoreTake(long_press_auto_check_pass, portMAX_DELAY);
|
||||
iot_button_unregister_cb(g_btns[0], BUTTON_LONG_PRESS_START);
|
||||
btn_cfg.event = BUTTON_LONG_PRESS_UP;
|
||||
for (int i = 0; i < 5; i++) {
|
||||
btn_cfg.event_data.long_press.press_time = long_press_time[i];
|
||||
uint32_t data = (btn_cfg.event << 16) | long_press_time[i];
|
||||
iot_button_register_event_cb(g_btns[0], btn_cfg, button_long_press_auto_check_cb, (void*)data);
|
||||
}
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xSemaphoreTake(long_press_auto_check_pass, pdMS_TO_TICKS(17000)));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, iot_button_delete(g_btns[0]));
|
||||
vSemaphoreDelete(long_press_check);
|
||||
vSemaphoreDelete(long_press_auto_check_pass);
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* ____ _ _ _______ _
|
||||
*| _ \ | | | | |__ __| | |
|
||||
*| |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_
|
||||
*| _ < | | | || __|| __|/ _ \ | '_ \ | | / _ \/ __|| __|
|
||||
*| |_) || |_| || |_ | |_| (_) || | | | | || __/\__ \| |_
|
||||
*|____/ \__,_| \__| \__|\___/ |_| |_| |_| \___||___/ \__|
|
||||
*/
|
||||
printf(" ____ _ _ _______ _ \n");
|
||||
printf(" | _ \\ | | | | |__ __| | | \n");
|
||||
printf(" | |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_ \n");
|
||||
printf(" | _ < | | | || __|| __|/ _ \\ | '_ \\ | | / _ \\/ __|| __|\n");
|
||||
printf(" | |_) || |_| || |_ | |_| (_) || | | | | || __/\\__ \\| |_ \n");
|
||||
printf(" |____/ \\__,_| \\__| \\__|\\___/ |_| |_| |_| \\___||___/ \\__|\n");
|
||||
unity_run_menu();
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#define LEAKS (400)
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
unity_utils_evaluate_leaks_direct(LEAKS);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
/*
|
||||
* ____ _ _ _______ _
|
||||
*| _ \ | | | | |__ __| | |
|
||||
*| |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_
|
||||
*| _ < | | | || __|| __|/ _ \ | '_ \ | | / _ \/ __|| __|
|
||||
*| |_) || |_| || |_ | |_| (_) || | | | | || __/\__ \| |_
|
||||
*|____/ \__,_| \__| \__|\___/ |_| |_| |_| \___||___/ \__|
|
||||
*/
|
||||
printf(" ____ _ _ _______ _ \n");
|
||||
printf(" | _ \\ | | | | |__ __| | | \n");
|
||||
printf(" | |_) | _ _ | |_ | |_ ___ _ __ | | ___ ___ | |_ \n");
|
||||
printf(" | _ < | | | || __|| __|/ _ \\ | '_ \\ | | / _ \\/ __|| __|\n");
|
||||
printf(" | |_) || |_| || |_ | |_| (_) || | | | | || __/\\__ \\| |_ \n");
|
||||
printf(" |____/ \\__,_| \\__| \\__|\\___/ |_| |_| |_| \\___||___/ \\__|\n");
|
||||
unity_run_menu();
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "driver/gpio.h"
|
||||
|
||||
static const char *TAG = "CUSTOM BUTTON TEST";
|
||||
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "%s", iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tPressed Time[%"PRIu32"]", iot_button_get_pressed_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
button_driver_t base;
|
||||
int32_t gpio_num; /**< num of gpio */
|
||||
uint8_t active_level; /**< gpio level when press down */
|
||||
} custom_gpio_obj;
|
||||
|
||||
static uint8_t button_get_key_level(button_driver_t *button_driver)
|
||||
{
|
||||
custom_gpio_obj *custom_btn = __containerof(button_driver, custom_gpio_obj, base);
|
||||
int level = gpio_get_level(custom_btn->gpio_num);
|
||||
return level == custom_btn->active_level ? 1 : 0;
|
||||
}
|
||||
|
||||
static esp_err_t button_del(button_driver_t *button_driver)
|
||||
{
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
TEST_CASE("custom button test", "[button][custom]")
|
||||
{
|
||||
gpio_config_t gpio_conf = {
|
||||
.pin_bit_mask = 1ULL << BUTTON_IO_NUM,
|
||||
.mode = GPIO_MODE_INPUT,
|
||||
.pull_up_en = 1,
|
||||
.pull_down_en = 0,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
custom_gpio_obj *custom_btn = (custom_gpio_obj *)calloc(1, sizeof(custom_gpio_obj));
|
||||
TEST_ASSERT_NOT_NULL(custom_btn);
|
||||
custom_btn->active_level = BUTTON_ACTIVE_LEVEL;
|
||||
custom_btn->gpio_num = BUTTON_IO_NUM;
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
const button_config_t btn_cfg = {0};
|
||||
custom_btn->base.get_key_level = button_get_key_level;
|
||||
custom_btn->base.del = button_del;
|
||||
esp_err_t ret = iot_button_create(&btn_cfg, &custom_btn->base, &btn);
|
||||
TEST_ASSERT(ESP_OK == ret);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
|
@ -0,0 +1,186 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_gpio.h"
|
||||
|
||||
static const char *TAG = "GPIO BUTTON TEST";
|
||||
|
||||
#define BUTTON_IO_NUM 0
|
||||
#define BUTTON_ACTIVE_LEVEL 0
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "%s", iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tPressed Time[%"PRIu32"]", iot_button_get_pressed_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test", "[button][gpio]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(btn);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button get event test", "[button][gpio][event test]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
uint8_t level = 0;
|
||||
level = iot_button_get_key_level(btn);
|
||||
ESP_LOGI(TAG, "button level is %d", level);
|
||||
|
||||
while (1) {
|
||||
button_event_t event = iot_button_get_event(btn);
|
||||
if (event != BUTTON_NONE_PRESS) {
|
||||
ESP_LOGI(TAG, "event is %s", iot_button_get_event_str(event));
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test power save", "[button][gpio][power save]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
.enable_power_save = true,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
||||
TEST_CASE("gpio button test memory leak", "[button][gpio][memory leak]")
|
||||
{
|
||||
const button_config_t btn_cfg = {0};
|
||||
const button_gpio_config_t btn_gpio_cfg = {
|
||||
.gpio_num = BUTTON_IO_NUM,
|
||||
.active_level = BUTTON_ACTIVE_LEVEL,
|
||||
};
|
||||
|
||||
button_handle_t btn = NULL;
|
||||
esp_err_t ret = iot_button_new_gpio_device(&btn_cfg, &btn_gpio_cfg, &btn);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
TEST_ASSERT_NOT_NULL(btn);
|
||||
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_DOWN, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_SINGLE_CLICK, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_DOUBLE_CLICK, NULL, button_event_cb, NULL);
|
||||
|
||||
/*!< Multiple Click must provide button_event_args_t */
|
||||
/*!< Double Click */
|
||||
button_event_args_t args = {
|
||||
.multiple_clicks.clicks = 2,
|
||||
};
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)2);
|
||||
/*!< Triple Click */
|
||||
args.multiple_clicks.clicks = 3;
|
||||
iot_button_register_cb(btn, BUTTON_MULTIPLE_CLICK, &args, button_event_cb, (void *)3);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_START, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_LONG_PRESS_UP, NULL, button_event_cb, NULL);
|
||||
iot_button_register_cb(btn, BUTTON_PRESS_END, NULL, button_event_cb, NULL);
|
||||
|
||||
iot_button_delete(btn);
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_log.h"
|
||||
#include "unity.h"
|
||||
#include "iot_button.h"
|
||||
#include "button_matrix.h"
|
||||
|
||||
static const char *TAG = "MATRIX BUTTON TEST";
|
||||
|
||||
static void button_event_cb(void *arg, void *data)
|
||||
{
|
||||
button_event_t event = iot_button_get_event(arg);
|
||||
ESP_LOGI(TAG, "BUTTON[%d] %s", (int)data, iot_button_get_event_str(event));
|
||||
if (BUTTON_PRESS_REPEAT == event || BUTTON_PRESS_REPEAT_DONE == event) {
|
||||
ESP_LOGI(TAG, "\tREPEAT[%d]", iot_button_get_repeat(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_PRESS_UP == event || BUTTON_LONG_PRESS_HOLD == event || BUTTON_LONG_PRESS_UP == event) {
|
||||
ESP_LOGI(TAG, "\tPressed Time[%"PRIu32"]", iot_button_get_pressed_time(arg));
|
||||
}
|
||||
|
||||
if (BUTTON_MULTIPLE_CLICK == event) {
|
||||
ESP_LOGI(TAG, "\tMULTIPLE[%d]", (int)data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("matrix keyboard button test", "[button][matrix key]")
|
||||
{
|
||||
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 btns[16] = {0};
|
||||
size_t btn_num = 16;
|
||||
esp_err_t ret = iot_button_new_matrix_device(&btn_cfg, &matrix_cfg, btns, &btn_num);
|
||||
TEST_ASSERT(ret == ESP_OK);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
int index = i * 4 + j;
|
||||
TEST_ASSERT_NOT_NULL(btns[index]);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_DOWN, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_UP, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_REPEAT, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_REPEAT_DONE, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_SINGLE_CLICK, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_DOUBLE_CLICK, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_START, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_HOLD, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_LONG_PRESS_UP, NULL, button_event_cb, (void *)index);
|
||||
iot_button_register_cb(btns[index], BUTTON_PRESS_END, NULL, button_event_cb, (void *)index);
|
||||
}
|
||||
}
|
||||
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = 0; j < 4; j++) {
|
||||
iot_button_delete(btns[i * 4 + j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE=y
|
||||
Loading…
Add table
Add a link
Reference in a new issue