Initial public release of the 2024A software.
This commit is contained in:
parent
7b9ad3edfd
commit
303e9e1dad
361 changed files with 60083 additions and 2 deletions
1
managed_components/espressif__button/.component_hash
Normal file
1
managed_components/espressif__button/.component_hash
Normal file
|
@ -0,0 +1 @@
|
|||
30a3f495c3862d505ce6e41adbbd218b2750e9723ab2151feff00e9fe685b326
|
157
managed_components/espressif__button/CHANGELOG.md
Normal file
157
managed_components/espressif__button/CHANGELOG.md
Normal file
|
@ -0,0 +1,157 @@
|
|||
# ChangeLog
|
||||
|
||||
## v3.5.0 - 2024-12-27
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add config to disable gpio button internal pull resistor.
|
||||
|
||||
## v3.4.1 - 2024-12-6
|
||||
|
||||
### Fix:
|
||||
|
||||
* Fix the issue where `BUTTON_LONG_PRESS_START` is not triggered when the polling interval exceeds 20ms.
|
||||
* Remove the `BUTTON_LONG_PRESS_TOLERANCE_MS` configuration option.
|
||||
|
||||
## v3.4.0 - 2024-10-22
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Supports a maximum button polling interval of 500ms.
|
||||
* Fixed a potential counter overflow issue.
|
||||
|
||||
### Break change:
|
||||
|
||||
* The return value of `iot_button_get_ticks_time` has been changed from `uint16_t` to `uint32_t`.
|
||||
|
||||
## v3.3.2 - 2024-8-28
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support macro CONFIG_PM_POWER_DOWN_PERIPHERAL_IN_LIGHT_SLEEP in power save mode.
|
||||
* Supports retrieving and printing the string corresponding to a button event.
|
||||
* Fixed the bug where the event was not assigned to `BUTTON_LONG_PRESS_START` before the `BUTTON_LONG_PRESS_START` event occurred.
|
||||
|
||||
## v3.3.1 - 2024-8-8
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add Button Event **BUTTON_PRESS_END**.
|
||||
|
||||
## v3.3.0 - 2024-8-7
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add Callback **button_power_save_cb_t** to support enter power save manually.
|
||||
* Increase the maximum polling interval supported by the button from 20ms to 50ms.
|
||||
|
||||
## v3.2.3 - 2024-7-2
|
||||
|
||||
* Fixed the issue where the GPIO button in low-power mode continuously woke up the CPU after being pressed, causing abnormal power consumption.
|
||||
|
||||
## v3.2.2 - 2024-6-17
|
||||
|
||||
* Fix the compilation error for chips that do not support ADC.
|
||||
|
||||
## v3.2.1 - 2024-6-17
|
||||
|
||||
### bugfix
|
||||
|
||||
- Fixed ignored ADC button tied to GND. thanks `demianzenkov` for the fix.
|
||||
|
||||
## v3.2.0 - 2023-11-13
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* The power consumption of GPIO buttons is lower during light sleep mode.
|
||||
|
||||
## v3.1.3 - 2023-11-13
|
||||
|
||||
* Resolved issue 'ADC_ATTEN_DB_11 is deprecated'.
|
||||
|
||||
## v3.1.2 - 2023-10-24
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where iot_button_delete feature crashes for custom button
|
||||
|
||||
## v3.1.1 - 2023-10-18
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where multiple callbacks feature crashes for BUTTON_MULTIPLE_CLICK
|
||||
|
||||
## v3.1.0 - 2023-10-9
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support matrix keypad
|
||||
|
||||
## v3.0.1 - 2023-9-1
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Resolves bug for iot_button_unregister_event function returned error when reallocating with 0 byte.
|
||||
* Update Test cases to test iot_button_unregister_event_cb
|
||||
* Add api iot_button_stop & iot_button_resume for power save.
|
||||
|
||||
## v3.0.0 - 2023-8-15
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add support to register multiple callbacks for a button_event
|
||||
|
||||
* Update iot_button_unregister_cb, to unregister all the callbacks for that event
|
||||
* Add iot_button_unregister_event to unregister specific callbacks of that event
|
||||
* Add iot_button_count_event to return number of callbacks registered for the event.
|
||||
* Update iot_button_count_cb, to return sum of number of registered callbacks.
|
||||
|
||||
* Add support for Long press on specific time
|
||||
|
||||
* Add iot_button_register_event, which takes specific event_config_t data as input.
|
||||
* Add BUTTON_LONG_PRESS_UP to trigger callback at the latest time of button release
|
||||
* Update BUTTON_LONG_PRESS_START to trigger callback as the time passes for long_press.
|
||||
|
||||
* Add support to trigger callback for specified number of clicks.
|
||||
|
||||
## v2.5.6 - 2023-8-22
|
||||
|
||||
### bugfix
|
||||
|
||||
* Fixed a bug where the Serial trigger interval in button_long_press_hold event fires at an incorrect time
|
||||
|
||||
## v2.5.5 - 2023-8-3
|
||||
|
||||
* Add modify api which can change long_press_time and short_press_time
|
||||
|
||||
## v2.5.4 - 2023-7-27
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Add test apps and ci auto test
|
||||
|
||||
## v2.5.3 - 2023-7-26
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* `repeat` defined in struct button_dev_t is reset to 0 after event `BUTTON_PRESS_REPEAT_DONE`
|
||||
|
||||
## v2.5.2 - 2023-7-18
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Set "event" member to BUTTON_PRESS_REPEAT before calling the BUTTON_PRESS_REPEAT callback
|
||||
|
||||
## v2.5.1 - 2023-3-14
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Update doc and code specification
|
||||
* Avoid overwriting callback by @franz-ms-muc in #252
|
||||
|
||||
## v2.5.0 - 2023-2-1
|
||||
|
||||
### Enhancements:
|
||||
|
||||
* Support custom button
|
||||
* Add BUTTON_PRESS_REPEAT_DONE event
|
32
managed_components/espressif__button/CMakeLists.txt
Normal file
32
managed_components/espressif__button/CMakeLists.txt
Normal file
|
@ -0,0 +1,32 @@
|
|||
set(PRIVREQ esp_timer)
|
||||
set(REQ driver)
|
||||
set(SRC_FILES "button_gpio.c" "iot_button.c" "button_matrix.c")
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND REQ esp_adc)
|
||||
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
|
||||
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})
|
65
managed_components/espressif__button/Kconfig
Normal file
65
managed_components/espressif__button/Kconfig
Normal file
|
@ -0,0 +1,65 @@
|
|||
menu "IoT Button"
|
||||
|
||||
config BUTTON_PERIOD_TIME_MS
|
||||
int "BUTTON PERIOD TIME (MS)"
|
||||
range 2 500
|
||||
default 5
|
||||
help
|
||||
"Button scan interval"
|
||||
|
||||
config BUTTON_DEBOUNCE_TICKS
|
||||
int "BUTTON DEBOUNCE TICKS"
|
||||
range 1 7
|
||||
default 2
|
||||
help
|
||||
"One CONFIG_BUTTON_DEBOUNCE_TICKS equal to CONFIG_BUTTON_PERIOD_TIME_MS"
|
||||
|
||||
config BUTTON_SHORT_PRESS_TIME_MS
|
||||
int "BUTTON SHORT PRESS TIME (MS)"
|
||||
range 50 800
|
||||
default 180
|
||||
|
||||
config BUTTON_LONG_PRESS_TIME_MS
|
||||
int "BUTTON LONG PRESS TIME (MS)"
|
||||
range 500 5000
|
||||
default 1500
|
||||
|
||||
config BUTTON_SERIAL_TIME_MS
|
||||
int "BUTTON 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.
|
||||
|
||||
config ADC_BUTTON_MAX_CHANNEL
|
||||
int "ADC BUTTON MAX CHANNEL"
|
||||
range 1 5
|
||||
default 3
|
||||
help
|
||||
"Maximum number of channels for ADC buttons"
|
||||
|
||||
config ADC_BUTTON_MAX_BUTTON_PER_CHANNEL
|
||||
int "ADC BUTTON MAX BUTTON PER CHANNEL"
|
||||
range 1 10
|
||||
default 8
|
||||
help
|
||||
"Maximum number of buttons per channel"
|
||||
|
||||
config ADC_BUTTON_SAMPLE_TIMES
|
||||
int "ADC BUTTON SAMPLE TIMES"
|
||||
range 1 4
|
||||
default 1
|
||||
help
|
||||
"Number of samples per scan"
|
||||
|
||||
endmenu
|
42
managed_components/espressif__button/README.md
Normal file
42
managed_components/espressif__button/README.md
Normal file
|
@ -0,0 +1,42 @@
|
|||
[](https://components.espressif.com/components/espressif/button)
|
||||
|
||||
# Component: Button
|
||||
[Online documentation](https://docs.espressif.com/projects/esp-iot-solution/en/latest/input_device/button.html)
|
||||
|
||||
After creating a new button object by calling function `button_create()`, the button object can create press events, every press event can have its own callback.
|
||||
|
||||
List of supported events:
|
||||
* Button pressed
|
||||
* Button released
|
||||
* Button pressed repeat
|
||||
* Button press repeat done
|
||||
* Button single click
|
||||
* Button double click
|
||||
* Button multiple click
|
||||
* Button long press start
|
||||
* Button long press hold
|
||||
* Button long press up
|
||||
* Button Press end
|
||||
|
||||

|
||||
|
||||
There are three ways this driver can handle buttons:
|
||||
1. Buttons connected to standard digital GPIO
|
||||
2. Multiple buttons connected to single ADC channel
|
||||
3. Matrix keyboard employs multiple GPIOs for operation.
|
||||
4. Custom button connect to any driver
|
||||
|
||||
The component supports the following functionalities:
|
||||
1. Creation of an unlimited number of buttons, accommodating various types simultaneously.
|
||||
2. Multiple callback functions for a single event.
|
||||
3. Allowing customization of the consecutive key press count to any desired number.
|
||||
4. Facilitating the setup of callbacks for any specified long-press duration.
|
||||
5. Support power save mode (Only for gpio button)
|
||||
|
||||
## Add component to your project
|
||||
|
||||
Please use the component manager command `add-dependency` to add the `button` to your project's dependency, during the `CMake` step the component will be downloaded automatically
|
||||
|
||||
```
|
||||
idf.py add-dependency "espressif/button=*"
|
||||
```
|
312
managed_components/espressif__button/button_adc.c
Normal file
312
managed_components/espressif__button/button_adc.c
Normal file
|
@ -0,0 +1,312 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.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"
|
||||
|
||||
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); \
|
||||
}
|
||||
|
||||
#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)
|
||||
|
||||
#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
|
||||
|
||||
typedef struct {
|
||||
uint16_t min;
|
||||
uint16_t max;
|
||||
} button_data_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t channel;
|
||||
uint8_t is_init;
|
||||
button_data_t btns[ADC_BUTTON_MAX_BUTTON]; /* all button on the channel */
|
||||
uint64_t last_time; /* the last time of adc sample */
|
||||
} btn_adc_channel_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
|
||||
btn_adc_channel_t ch[ADC_BUTTON_MAX_CHANNEL];
|
||||
uint8_t ch_num;
|
||||
} adc_button_t;
|
||||
|
||||
static adc_button_t g_button = {0};
|
||||
|
||||
static int find_unused_channel(void)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (0 == g_button.ch[i].is_init) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int find_channel(uint8_t channel)
|
||||
{
|
||||
for (size_t i = 0; i < ADC_BUTTON_MAX_CHANNEL; i++) {
|
||||
if (channel == g_button.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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#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;
|
||||
}
|
||||
|
||||
esp_err_t button_adc_deinit(uint8_t channel, int button_index)
|
||||
{
|
||||
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);
|
||||
|
||||
int ch_index = find_channel(channel);
|
||||
ADC_BTN_CHECK(ch_index >= 0, "can't find the channel", ESP_ERR_INVALID_ARG);
|
||||
|
||||
g_button.ch[ch_index].btns[button_index].max = 0;
|
||||
g_button.ch[ch_index].btns[button_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) {
|
||||
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);
|
||||
}
|
||||
|
||||
/** 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) {
|
||||
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));
|
||||
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
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static uint32_t get_adc_volatge(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_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);
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
/** 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 (vol <= g_button.ch[ch_index].btns[index].max &&
|
||||
vol >= g_button.ch[ch_index].btns[index].min) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
114
managed_components/espressif__button/button_gpio.c
Normal file
114
managed_components/espressif__button/button_gpio.c
Normal file
|
@ -0,0 +1,114 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_gpio.h"
|
||||
#include "esp_sleep.h"
|
||||
|
||||
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); \
|
||||
}
|
||||
|
||||
esp_err_t button_gpio_init(const button_gpio_config_t *config)
|
||||
{
|
||||
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);
|
||||
|
||||
gpio_config_t gpio_conf;
|
||||
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.pull_down_en = GPIO_PULLDOWN_ENABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_DISABLE;
|
||||
} else {
|
||||
gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
|
||||
gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE;
|
||||
}
|
||||
}
|
||||
gpio_config(&gpio_conf);
|
||||
|
||||
#if CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE
|
||||
if (config->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);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
gpio_hold_en(config->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);
|
||||
#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);
|
||||
#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);
|
||||
#endif
|
||||
#else
|
||||
ret = esp_sleep_enable_gpio_wakeup();
|
||||
#endif
|
||||
GPIO_BTN_CHECK(ret == ESP_OK, "Configure gpio as wakeup source failed", ESP_FAIL);
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
}
|
||||
return ret;
|
||||
}
|
59
managed_components/espressif__button/button_matrix.c
Normal file
59
managed_components/espressif__button/button_matrix.c
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "button_matrix.h"
|
||||
|
||||
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); \
|
||||
}
|
||||
|
||||
esp_err_t button_matrix_init(const button_matrix_config_t *config)
|
||||
{
|
||||
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
|
||||
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_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)
|
||||
{
|
||||
//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);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t button_matrix_get_key_level(void *hardware_data)
|
||||
{
|
||||
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);
|
||||
|
||||
return level;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
# 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)
|
|
@ -0,0 +1,43 @@
|
|||
## 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
|
||||
```
|
|
@ -0,0 +1,2 @@
|
|||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
|
@ -0,0 +1,87 @@
|
|||
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
|
|
@ -0,0 +1,6 @@
|
|||
version: "0.1.0"
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
button:
|
||||
version: "*"
|
||||
override_path: "../../../../components/button"
|
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
CONFIG_ENTER_LIGHT_SLEEP_MODE_MANUALLY=y\
|
|
@ -0,0 +1,11 @@
|
|||
# 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
|
12
managed_components/espressif__button/idf_component.yml
Normal file
12
managed_components/espressif__button/idf_component.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
dependencies:
|
||||
cmake_utilities: 0.*
|
||||
idf: '>=4.0'
|
||||
description: GPIO and ADC 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
|
||||
path: components/button
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/button
|
||||
version: 3.5.0
|
76
managed_components/espressif__button/include/button_adc.h
Normal file
76
managed_components/espressif__button/include/button_adc.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2023 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
|
||||
|
||||
#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 {
|
||||
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
|
||||
*
|
||||
* @param config pointer of configuration struct
|
||||
*
|
||||
* @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_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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
92
managed_components/espressif__button/include/button_gpio.h
Normal file
92
managed_components/espressif__button/include/button_gpio.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "driver/gpio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief gpio button configuration
|
||||
*
|
||||
*/
|
||||
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 */
|
||||
} button_gpio_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize gpio button
|
||||
*
|
||||
* @param config pointer of configuration struct
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is NULL.
|
||||
*/
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
80
managed_components/espressif__button/include/button_matrix.h
Normal file
80
managed_components/espressif__button/include/button_matrix.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#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.
|
||||
*
|
||||
* Matrix Keyboard Layout (3x3):
|
||||
* ----------------------------------------
|
||||
* | Button 1 | Button 2 | Button 3 |
|
||||
* | (R1-C1) | (R1-C2) | (R1-C3) |
|
||||
* |--------------------------------------|
|
||||
* | Button 4 | Button 5 | Button 6 |
|
||||
* | (R2-C1) | (R2-C2) | (R2-C3) |
|
||||
* |--------------------------------------|
|
||||
* | Button 7 | Button 8 | Button 9 |
|
||||
* | (R3-C1) | (R3-C2) | (R3-C3) |
|
||||
* ----------------------------------------
|
||||
*
|
||||
* - Button matrix key is driven using row scanning.
|
||||
* - Buttons within the same column cannot be detected simultaneously,
|
||||
* 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 */
|
||||
} button_matrix_config_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a button matrix keyboard.
|
||||
*
|
||||
* @param config Pointer to the button matrix key configuration.
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if the argument is NULL.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
363
managed_components/espressif__button/include/iot_button.h
Normal file
363
managed_components/espressif__button/include/iot_button.h
Normal file
|
@ -0,0 +1,363 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
/**
|
||||
* @brief Structs to store power save callback info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_power_save_cb_t enter_power_save_cb;
|
||||
void *usr_data;
|
||||
} button_power_save_config_t;
|
||||
#endif
|
||||
|
||||
typedef void *button_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Button events
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
BUTTON_PRESS_DOWN = 0,
|
||||
BUTTON_PRESS_UP,
|
||||
BUTTON_PRESS_REPEAT,
|
||||
BUTTON_PRESS_REPEAT_DONE,
|
||||
BUTTON_SINGLE_CLICK,
|
||||
BUTTON_DOUBLE_CLICK,
|
||||
BUTTON_MULTIPLE_CLICK,
|
||||
BUTTON_LONG_PRESS_START,
|
||||
BUTTON_LONG_PRESS_HOLD,
|
||||
BUTTON_LONG_PRESS_UP,
|
||||
BUTTON_PRESS_END,
|
||||
BUTTON_EVENT_MAX,
|
||||
BUTTON_NONE_PRESS,
|
||||
} button_event_t;
|
||||
|
||||
/**
|
||||
* @brief Button events data
|
||||
*
|
||||
*/
|
||||
typedef union {
|
||||
/**
|
||||
* @brief Long press time event data
|
||||
*
|
||||
*/
|
||||
struct long_press_t {
|
||||
uint16_t press_time; /**< press time(ms) for the corresponding callback to trigger */
|
||||
} long_press; /**< long press struct, for event BUTTON_LONG_PRESS_START and BUTTON_LONG_PRESS_UP */
|
||||
|
||||
/**
|
||||
* @brief Multiple clicks event data
|
||||
*
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @brief Button parameter
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
BUTTON_LONG_PRESS_TIME_MS = 0,
|
||||
BUTTON_SHORT_PRESS_TIME_MS,
|
||||
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
|
||||
*
|
||||
* @param btn_handle A button handle to delete
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK Success
|
||||
* - ESP_FAIL Failure
|
||||
*/
|
||||
esp_err_t iot_button_delete(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Register the button event callback function.
|
||||
*
|
||||
* @param btn_handle A button handle to register
|
||||
* @param event Button event
|
||||
* @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_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);
|
||||
|
||||
/**
|
||||
* @brief Unregister all the callbacks associated with the event.
|
||||
*
|
||||
* @param btn_handle A button handle to unregister
|
||||
* @param event Button event
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief counts total callbacks registered
|
||||
*
|
||||
* @param btn_handle A button handle to the button
|
||||
*
|
||||
* @return
|
||||
* - 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_cb(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief how many callbacks are registered for the event
|
||||
*
|
||||
* @param btn_handle A button handle to the button
|
||||
*
|
||||
* @param event Button event
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
/**
|
||||
* @brief Get button event
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Current button event. See button_event_t
|
||||
*/
|
||||
button_event_t iot_button_get_event(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get the string representation of a button event.
|
||||
*
|
||||
* This function returns the corresponding string for a given button event.
|
||||
* If the event value is outside the valid range, the function returns error string "event value is invalid".
|
||||
*
|
||||
* @param[in] event The button event to be converted to a string.
|
||||
*
|
||||
* @return
|
||||
* - Pointer to the event string if the event is valid.
|
||||
* - "invalid event" if the event value is invalid.
|
||||
*/
|
||||
const char *iot_button_get_event_str(button_event_t event);
|
||||
|
||||
/**
|
||||
* @brief Log the current button event as a string.
|
||||
*
|
||||
* This function prints the string representation of the current event associated with the button.
|
||||
*
|
||||
* @param[in] btn_handle Handle to the button object.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully logged the event string.
|
||||
* - ESP_FAIL: Invalid button handle.
|
||||
*/
|
||||
esp_err_t iot_button_print_event(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button repeat times
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return button pressed times. For example, double-click return 2, triple-click return 3, etc.
|
||||
*/
|
||||
uint8_t iot_button_get_repeat(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button ticks time
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Actual time from press down to up (ms).
|
||||
*/
|
||||
uint32_t iot_button_get_ticks_time(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Get button long press hold count
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
*
|
||||
* @return Count of trigger cb(BUTTON_LONG_PRESS_HOLD)
|
||||
*/
|
||||
uint16_t iot_button_get_long_press_hold_cnt(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief Dynamically change the parameters of the iot button
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
* @param param Button parameter
|
||||
* @param value new value
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid.
|
||||
*/
|
||||
esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value);
|
||||
|
||||
/**
|
||||
* @brief Get button key level
|
||||
*
|
||||
* @param btn_handle Button handle
|
||||
* @return
|
||||
* - 1 if key is pressed
|
||||
* - 0 if key is released or invalid button handle
|
||||
*/
|
||||
uint8_t iot_button_get_key_level(button_handle_t btn_handle);
|
||||
|
||||
/**
|
||||
* @brief resume button timer, if button timer is stopped. Make sure iot_button_create() is called before calling this API.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE timer state is invalid.
|
||||
*/
|
||||
esp_err_t iot_button_resume(void);
|
||||
|
||||
/**
|
||||
* @brief stop button timer, if button timer is running. Make sure iot_button_create() is called before calling this API.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE timer state is invalid
|
||||
*/
|
||||
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.
|
||||
*
|
||||
* @param config Button power save config
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE No button registered
|
||||
* - ESP_ERR_INVALID_ARG Arguments is invalid
|
||||
* - ESP_ERR_NO_MEM Not enough memory
|
||||
*/
|
||||
esp_err_t iot_button_register_power_save_cb(const button_power_save_config_t *config);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
821
managed_components/espressif__button/iot_button.c
Normal file
821
managed_components/espressif__button/iot_button.c
Normal file
|
@ -0,0 +1,821 @@
|
|||
/* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/timers.h"
|
||||
#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 "iot_button.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "button";
|
||||
static portMUX_TYPE s_button_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
#define BUTTON_ENTER_CRITICAL() portENTER_CRITICAL(&s_button_lock)
|
||||
#define BUTTON_EXIT_CRITICAL() portEXIT_CRITICAL(&s_button_lock)
|
||||
|
||||
#define BTN_CHECK(a, str, ret_val) \
|
||||
if (!(a)) { \
|
||||
ESP_LOGE(TAG, "%s(%d): %s", __FUNCTION__, __LINE__, str); \
|
||||
return (ret_val); \
|
||||
}
|
||||
|
||||
static const char *button_event_str[] = {
|
||||
"BUTTON_PRESS_DOWN",
|
||||
"BUTTON_PRESS_UP",
|
||||
"BUTTON_PRESS_REPEAT",
|
||||
"BUTTON_PRESS_REPEAT_DONE",
|
||||
"BUTTON_SINGLE_CLICK",
|
||||
"BUTTON_DOUBLE_CLICK",
|
||||
"BUTTON_MULTIPLE_CLICK",
|
||||
"BUTTON_LONG_PRESS_START",
|
||||
"BUTTON_LONG_PRESS_HOLD",
|
||||
"BUTTON_LONG_PRESS_UP",
|
||||
"BUTTON_PRESS_END",
|
||||
"BUTTON_EVENT_MAX",
|
||||
"BUTTON_NONE_PRESS",
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Structs to store callback info
|
||||
*
|
||||
*/
|
||||
typedef struct {
|
||||
button_cb_t cb;
|
||||
void *usr_data;
|
||||
button_event_data_t event_data;
|
||||
} 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;
|
||||
} 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 TOLERANCE (CONFIG_BUTTON_PERIOD_TIME_MS*4)
|
||||
|
||||
#define CALL_EVENT_CB(ev) \
|
||||
if (btn->cb_info[ev]) { \
|
||||
for (int i = 0; i < btn->size[ev]; i++) { \
|
||||
btn->cb_info[ev][i].cb(btn, btn->cb_info[ev][i].usr_data); \
|
||||
} \
|
||||
} \
|
||||
|
||||
#define TIME_TO_TICKS(time, congfig_time) (0 == (time))?congfig_time:(((time) / TICKS_INTERVAL))?((time) / TICKS_INTERVAL):1
|
||||
|
||||
/**
|
||||
* @brief Button driver core function, driver state machine.
|
||||
*/
|
||||
static void button_handler(button_dev_t *btn)
|
||||
{
|
||||
uint8_t read_gpio_level = btn->hal_button_Level(btn->hardware_data);
|
||||
|
||||
/** ticks counter working.. */
|
||||
if ((btn->state) > 0) {
|
||||
btn->ticks++;
|
||||
}
|
||||
|
||||
/**< button debounce handle */
|
||||
if (read_gpio_level != btn->button_level) {
|
||||
if (++(btn->debounce_cnt) >= DEBOUNCE_TICKS) {
|
||||
btn->button_level = read_gpio_level;
|
||||
btn->debounce_cnt = 0;
|
||||
}
|
||||
} else {
|
||||
btn->debounce_cnt = 0;
|
||||
}
|
||||
|
||||
/** State machine */
|
||||
switch (btn->state) {
|
||||
case 0:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_DOWN;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_DOWN);
|
||||
btn->ticks = 0;
|
||||
btn->repeat = 1;
|
||||
btn->state = 1;
|
||||
} else {
|
||||
btn->event = (uint8_t)BUTTON_NONE_PRESS;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
if (btn->button_level != btn->active_level) {
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->ticks = 0;
|
||||
btn->state = 2;
|
||||
|
||||
} else if (btn->ticks >= btn->long_press_ticks) {
|
||||
btn->event = (uint8_t)BUTTON_LONG_PRESS_START;
|
||||
btn->state = 4;
|
||||
/** 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;
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
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;
|
||||
} else if (btn->ticks > btn->short_press_ticks) {
|
||||
if (btn->repeat == 1) {
|
||||
btn->event = (uint8_t)BUTTON_SINGLE_CLICK;
|
||||
CALL_EVENT_CB(BUTTON_SINGLE_CLICK);
|
||||
} else if (btn->repeat == 2) {
|
||||
btn->event = (uint8_t)BUTTON_DOUBLE_CLICK;
|
||||
CALL_EVENT_CB(BUTTON_DOUBLE_CLICK); // repeat hit
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_MULTIPLE_CLICK;
|
||||
|
||||
/** 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);
|
||||
}
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_PRESS_REPEAT_DONE;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_REPEAT_DONE); // repeat hit
|
||||
btn->repeat = 0;
|
||||
btn->state = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
if (btn->button_level != btn->active_level) {
|
||||
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
|
||||
} else {
|
||||
btn->state = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 4:
|
||||
if (btn->button_level == btn->active_level) {
|
||||
//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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { //releasd
|
||||
|
||||
btn->event = BUTTON_LONG_PRESS_UP;
|
||||
|
||||
/** calling callbacks for BUTTON_LONG_PRESS_UP press_time */
|
||||
if (btn->cb_info[btn->event] && btn->count[1] >= 0) {
|
||||
button_cb_info_t *cb_info = btn->cb_info[btn->event];
|
||||
do {
|
||||
cb_info[btn->count[1]].cb(btn, cb_info[btn->count[1]].usr_data);
|
||||
if (!btn->count[1]) {
|
||||
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);
|
||||
|
||||
/** Reset the counter */
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
/** Reset counter */
|
||||
if (btn->cb_info[BUTTON_LONG_PRESS_START]) {
|
||||
btn->count[0] = 0;
|
||||
}
|
||||
|
||||
btn->event = (uint8_t)BUTTON_PRESS_UP;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_UP);
|
||||
btn->state = 0; //reset
|
||||
btn->long_press_hold_cnt = 0;
|
||||
btn->event = (uint8_t)BUTTON_PRESS_END;
|
||||
CALL_EVENT_CB(BUTTON_PRESS_END);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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)) {
|
||||
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) {
|
||||
esp_timer_stop(g_button_timer_handle);
|
||||
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);
|
||||
}
|
||||
}
|
||||
/*!< Notify the user that the Button has entered power save mode by calling this callback function. */
|
||||
if (power_save_usr_cfg.enter_power_save_cb) {
|
||||
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)
|
||||
{
|
||||
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);
|
||||
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,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
if (event == BUTTON_LONG_PRESS_START) {
|
||||
btn->count[0] = 0;
|
||||
} else if (event == BUTTON_LONG_PRESS_UP) {
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
} else {
|
||||
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->cb_info[event][btn->size[event]].cb = cb;
|
||||
btn->cb_info[event][btn->size[event]].usr_data = usr_data;
|
||||
btn->size[event]++;
|
||||
|
||||
/** Inserting the event_data 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);
|
||||
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) {
|
||||
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].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].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;
|
||||
}
|
||||
|
||||
int32_t press_ticks = press_time / TICKS_INTERVAL;
|
||||
if (btn->short_press_ticks < press_ticks && press_ticks < btn->long_press_ticks) {
|
||||
iot_button_set_param(btn, BUTTON_LONG_PRESS_TIME_MS, (void*)(intptr_t)press_time);
|
||||
}
|
||||
}
|
||||
|
||||
if (event == BUTTON_MULTIPLE_CLICK) {
|
||||
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) {
|
||||
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].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].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;
|
||||
}
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_unregister_cb(button_handle_t btn_handle, button_event_t event)
|
||||
{
|
||||
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);
|
||||
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);
|
||||
|
||||
if (btn->cb_info[event]) {
|
||||
free(btn->cb_info[event]);
|
||||
|
||||
/** Reset the counter */
|
||||
if (event == BUTTON_LONG_PRESS_START) {
|
||||
btn->count[0] = 0;
|
||||
} else if (event == BUTTON_LONG_PRESS_UP) {
|
||||
btn->count[1] = -1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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_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;
|
||||
}
|
||||
}
|
||||
|
||||
BTN_CHECK(check != -1, "No such callback registered for the event", ESP_ERR_INVALID_STATE);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
size_t iot_button_count_cb(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
size_t ret = 0;
|
||||
for (size_t i = 0; i < BUTTON_EVENT_MAX; i++) {
|
||||
if (btn->cb_info[i]) {
|
||||
ret += btn->size[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t iot_button_count_event(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;
|
||||
return btn->size[event];
|
||||
}
|
||||
|
||||
button_event_t iot_button_get_event(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", BUTTON_NONE_PRESS);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->event;
|
||||
}
|
||||
|
||||
const char *iot_button_get_event_str(button_event_t event)
|
||||
{
|
||||
BTN_CHECK(event <= BUTTON_NONE_PRESS && event >= BUTTON_PRESS_DOWN, "event value is invalid", "invalid event");
|
||||
return button_event_str[event];
|
||||
}
|
||||
|
||||
esp_err_t iot_button_print_event(button_handle_t btn_handle)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_FAIL);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
ESP_LOGI(TAG, "%s", button_event_str[btn->event]);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
uint8_t iot_button_get_repeat(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->repeat;
|
||||
}
|
||||
|
||||
uint32_t iot_button_get_ticks_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);
|
||||
}
|
||||
|
||||
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);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
return btn->long_press_hold_cnt;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_set_param(button_handle_t btn_handle, button_param_t param, void *value)
|
||||
{
|
||||
BTN_CHECK(NULL != btn_handle, "Pointer of handle is invalid", ESP_ERR_INVALID_ARG);
|
||||
button_dev_t *btn = (button_dev_t *) btn_handle;
|
||||
BUTTON_ENTER_CRITICAL();
|
||||
switch (param) {
|
||||
case BUTTON_LONG_PRESS_TIME_MS:
|
||||
btn->long_press_ticks = (int32_t)value / TICKS_INTERVAL;
|
||||
break;
|
||||
case BUTTON_SHORT_PRESS_TIME_MS:
|
||||
btn->short_press_ticks = (int32_t)value / TICKS_INTERVAL;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BUTTON_EXIT_CRITICAL();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t iot_button_stop(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 not running", ESP_ERR_INVALID_STATE);
|
||||
|
||||
esp_err_t err = esp_timer_stop(g_button_timer_handle);
|
||||
BTN_CHECK(ESP_OK == err, "Button timer stop failed", ESP_FAIL);
|
||||
g_is_timer_running = false;
|
||||
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);
|
||||
BTN_CHECK(config->enter_power_save_cb, "Enter power save callback is invalid", ESP_ERR_INVALID_ARG);
|
||||
|
||||
power_save_usr_cfg.enter_power_save_cb = config->enter_power_save_cb;
|
||||
power_save_usr_cfg.usr_data = config->usr_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
202
managed_components/espressif__button/license.txt
Normal file
202
managed_components/espressif__button/license.txt
Normal file
|
@ -0,0 +1,202 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,9 @@
|
|||
|
||||
# The following 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)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components"
|
||||
"../../button")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(button_test)
|
|
@ -0,0 +1,9 @@
|
|||
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})
|
|
@ -0,0 +1,743 @@
|
|||
/* 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,29 @@
|
|||
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
'''
|
||||
Steps to run these cases:
|
||||
- Build
|
||||
- . ${IDF_PATH}/export.sh
|
||||
- pip install idf_build_apps
|
||||
- python tools/build_apps.py components/button/test_apps -t esp32s3
|
||||
- Test
|
||||
- pip install -r tools/requirements/requirement.pytest.txt
|
||||
- pytest components/button/test_apps --target esp32s3
|
||||
'''
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.env('button')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'defaults',
|
||||
],
|
||||
)
|
||||
def test_button(dut: Dut)-> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests.')
|
||||
dut.write('[auto]')
|
||||
dut.expect_unity_test_output(timeout = 300)
|
|
@ -0,0 +1 @@
|
|||
CONFIG_GPIO_BUTTON_SUPPORT_POWER_SAVE=y
|
|
@ -0,0 +1,9 @@
|
|||
# For IDF 5.0
|
||||
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
# For IDF4.4
|
||||
CONFIG_ESP32S2_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
|
||||
CONFIG_ESP_TASK_WDT=n
|
Loading…
Add table
Add a link
Reference in a new issue