Skip to content

Commit

Permalink
feature(button): Add choice to select whether to use esp_timer or fre…
Browse files Browse the repository at this point in the history
…ertos timer.

Sometimes when the CPU is very busy, timer task could be blocked, if the timer queue gets full, there would be a deadlock in timer task.
So, we should not use blocking method in FreeRTOS timer callback function.
  • Loading branch information
costaud committed Jul 24, 2019
1 parent c679921 commit 42d4a72
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 12 deletions.
10 changes: 10 additions & 0 deletions components/__config/Kconfig.projbuild
Expand Up @@ -114,6 +114,16 @@ menu "IoT Solution settings"

menu "Button"
depends on IOT_BUTTON_ENABLE
choice TIMER_IMPLEMENT
bool "Button Timer Mode"
default BUTTON_USE_ESP_TIMER
help
Choose a implementation of timer for button instance.
config BUTTON_USE_RTOS_TIMER
bool "Use FreeRTOS Timer"
config BUTTON_USE_ESP_TIMER
bool "Use ESP Timer"
endchoice
config IO_GLITCH_FILTER_TIME_MS
int "IO glitch filter timer ms (10~100)"
range 10 100
Expand Down
11 changes: 11 additions & 0 deletions components/general/button/button/Kconfig
@@ -1,4 +1,15 @@
menu "Button"
choice TIMER_IMPLEMENT
bool "Button Timer Mode"
default BUTTON_USE_ESP_TIMER
help
Choose a implementation of timer for button instance.
config BUTTON_USE_RTOS_TIMER
bool "Use FreeRTOS Timer"
config BUTTON_USE_ESP_TIMER
bool "Use ESP Timer"
endchoice

config IO_GLITCH_FILTER_TIME_MS
int "IO glitch filter timer ms (10~100)"
range 10 100
Expand Down
141 changes: 129 additions & 12 deletions components/general/button/button/button.c
Expand Up @@ -19,6 +19,16 @@
#include "esp_log.h"
#include "driver/gpio.h"
#include "iot_button.h"
#include "esp_timer.h"

#define USE_ESP_TIMER CONFIG_BUTTON_USE_ESP_TIMER
#if USE_ESP_TIMER
#define STOP_TIMER(tmr) esp_timer_stop(tmr)
#define DELETE_TIMER(tmr) esp_timer_delete(tmr)
#else
#define STOP_TIMER(tmr) xTimerStop(tmr, portMAX_DELAY)
#define DELETE_TIMER(tmr) xTimerDelete(tmr, portMAX_DELAY);
#endif

#define IOT_CHECK(tag, a, ret) if(!(a)) { \
ESP_LOGE(tag,"%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \
Expand All @@ -40,7 +50,11 @@ struct btn_cb{
TickType_t interval;
button_cb cb;
void* arg;
#if !USE_ESP_TIMER
TimerHandle_t tmr;
#else
esp_timer_handle_t tmr;
#endif
button_dev_t *pbtn;
button_cb_t *next_cb;
};
Expand All @@ -60,9 +74,15 @@ struct button_dev{
#define BUTTON_GLITCH_FILTER_TIME_MS CONFIG_IO_GLITCH_FILTER_TIME_MS
static const char* TAG = "button";

static void button_press_cb(xTimerHandle tmr)
// static void button_press_cb(xTimerHandle tmr)
static void button_press_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif

button_dev_t* btn = btn_cb->pbtn;
// low, then restart
if (btn->active_level == gpio_get_level(btn->io_num)) {
Expand All @@ -73,50 +93,72 @@ static void button_press_cb(xTimerHandle tmr)
}
}

static void button_tap_psh_cb(xTimerHandle tmr)
// static void button_tap_psh_cb(xTimerHandle tmr)
static void button_tap_psh_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif

button_dev_t* btn = btn_cb->pbtn;
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
STOP_TIMER(btn->tap_rls_cb.tmr);

int lv = gpio_get_level(btn->io_num);

if (btn->active_level == lv) {
// high, then key is up
btn->state = BUTTON_STATE_PUSH;
if (btn->press_serial_cb.tmr) {
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
#else
esp_timer_stop(btn->press_serial_cb.tmr);
esp_timer_start_once(btn->press_serial_cb.tmr, btn->serial_thres_sec * 1000 * 1000);
#endif

}
if (btn->tap_psh_cb.cb) {
btn->tap_psh_cb.cb(btn->tap_psh_cb.arg);
}
} else {
// 50ms, check if this is a real key up
if (btn->tap_rls_cb.tmr) {
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
STOP_TIMER(btn->tap_rls_cb.tmr);
#if !USE_ESP_TIMER
xTimerReset(btn->tap_rls_cb.tmr, portMAX_DELAY);
#else
esp_timer_start_once(btn->tap_rls_cb.tmr, btn->tap_rls_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
}
}

static void button_tap_rls_cb(xTimerHandle tmr)
static void button_tap_rls_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr);
#else
button_cb_t* btn_cb = (button_cb_t*)(tmr);
#endif
button_dev_t* btn = btn_cb->pbtn;
xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY);
STOP_TIMER(btn->tap_rls_cb.tmr);

if (btn->active_level == gpio_get_level(btn->io_num)) {

} else {
// high, then key is up
button_cb_t *pcb = btn->cb_head;
while (pcb != NULL) {
if (pcb->tmr != NULL) {
xTimerStop(pcb->tmr, portMAX_DELAY);
STOP_TIMER(pcb->tmr);
}
pcb = pcb->next_cb;
}
if (btn->press_serial_cb.tmr && btn->press_serial_cb.tmr != NULL) {
xTimerStop(btn->press_serial_cb.tmr, portMAX_DELAY);
STOP_TIMER(btn->press_serial_cb.tmr);
}
if (btn->tap_short_cb.cb && btn->state == BUTTON_STATE_PUSH) {
btn->tap_short_cb.cb(btn->tap_short_cb.arg);
Expand All @@ -128,14 +170,24 @@ static void button_tap_rls_cb(xTimerHandle tmr)
}
}

static void button_press_serial_cb(xTimerHandle tmr)
static void button_press_serial_cb(void* tmr)
{
#if !USE_ESP_TIMER
button_dev_t* btn = (button_dev_t*) pvTimerGetTimerID(tmr);
#else
button_dev_t* btn = (button_dev_t*)(tmr);
#endif

if (btn->press_serial_cb.cb) {
btn->press_serial_cb.cb(btn->press_serial_cb.arg);
}
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->press_serial_cb.interval, portMAX_DELAY);
xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY);
#else
esp_timer_stop(btn->press_serial_cb.tmr);
esp_timer_start_once(btn->press_serial_cb.tmr, btn->press_serial_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}

static void button_gpio_isr_handler(void* arg)
Expand All @@ -145,35 +197,51 @@ static void button_gpio_isr_handler(void* arg)
int level = gpio_get_level(btn->io_num);
if (level == btn->active_level) {
if (btn->tap_psh_cb.tmr) {
#if !USE_ESP_TIMER
xTimerStopFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
xTimerResetFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken);
#else
esp_timer_stop(btn->tap_psh_cb.tmr);
esp_timer_start_once(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}

button_cb_t *pcb = btn->cb_head;
while (pcb != NULL) {
if (pcb->tmr != NULL) {
#if !USE_ESP_TIMER
xTimerStopFromISR(pcb->tmr, &HPTaskAwoken);
xTimerResetFromISR(pcb->tmr, &HPTaskAwoken);
#else
esp_timer_stop(pcb->tmr);
esp_timer_start_once(pcb->tmr, pcb->interval * portTICK_PERIOD_MS * 1000);
#endif
}
pcb = pcb->next_cb;
}
} else {
// 50ms, check if this is a real key up
if (btn->tap_rls_cb.tmr) {
#if !USE_ESP_TIMER
xTimerStopFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
xTimerResetFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken);
#else
esp_timer_stop(btn->tap_rls_cb.tmr);
esp_timer_start_once(btn->tap_rls_cb.tmr, btn->tap_rls_cb.interval * portTICK_PERIOD_MS * 1000);
#endif
}
}
if(HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
}
}

static void button_free_tmr(xTimerHandle* tmr)
// static void button_free_tmr(xTimerHandle* tmr)
static void button_free_tmr(esp_timer_handle_t *tmr)
{
if (tmr && *tmr) {
xTimerStop(*tmr, portMAX_DELAY);
xTimerDelete(*tmr, portMAX_DELAY);
STOP_TIMER(*tmr);
DELETE_TIMER(*tmr);
*tmr = NULL;
}
}
Expand Down Expand Up @@ -203,6 +271,11 @@ esp_err_t iot_button_delete(button_handle_t btn_handle)

button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level)
{
#if USE_ESP_TIMER
ets_printf("use esp timer !!!\n");
esp_timer_init();
#endif

IOT_CHECK(TAG, gpio_num < GPIO_NUM_MAX, NULL);
button_dev_t* btn = (button_dev_t*) calloc(1, sizeof(button_dev_t));
POINT_ASSERT(TAG, btn, NULL);
Expand All @@ -213,14 +286,33 @@ button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_le
btn->tap_rls_cb.cb = NULL;
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
btn->tap_rls_cb.pbtn = btn;
#if !USE_ESP_TIMER
btn->tap_rls_cb.tmr = xTimerCreate("btn_rls_tmr", btn->tap_rls_cb.interval, pdFALSE,
&btn->tap_rls_cb, button_tap_rls_cb);
#else
esp_timer_create_args_t tmr_param_rls;
tmr_param_rls.arg = &btn->tap_rls_cb;
tmr_param_rls.callback = button_tap_rls_cb;
tmr_param_rls.dispatch_method = ESP_TIMER_TASK;
tmr_param_rls.name = "btn_rls_tmr";
esp_timer_create(&tmr_param_rls, &btn->tap_rls_cb.tmr);
#endif

btn->tap_psh_cb.arg = NULL;
btn->tap_psh_cb.cb = NULL;
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS;
btn->tap_psh_cb.pbtn = btn;
#if !USE_ESP_TIMER
btn->tap_psh_cb.tmr = xTimerCreate("btn_psh_tmr", btn->tap_psh_cb.interval, pdFALSE,
&btn->tap_psh_cb, button_tap_psh_cb);
#else
esp_timer_create_args_t tmr_param_psh;
tmr_param_psh.arg = &btn->tap_psh_cb;
tmr_param_psh.callback = button_tap_psh_cb;
tmr_param_psh.dispatch_method = ESP_TIMER_TASK;
tmr_param_psh.name = "btn_psh_tmr";
esp_timer_create(&tmr_param_psh, &btn->tap_psh_cb.tmr);
#endif
gpio_install_isr_service(0);
gpio_config_t gpio_conf;
gpio_conf.intr_type = GPIO_INTR_ANYEDGE;
Expand Down Expand Up @@ -257,15 +349,27 @@ esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_af
{
button_dev_t* btn = (button_dev_t*) btn_handle;
btn->serial_thres_sec = start_after_sec;

if (btn->press_serial_cb.tmr == NULL) {
#if !USE_ESP_TIMER
btn->press_serial_cb.tmr = xTimerCreate("btn_serial_tmr", btn->serial_thres_sec*1000 / portTICK_PERIOD_MS,
pdFALSE, btn, button_press_serial_cb);
#else
esp_timer_create_args_t tmr_param_ser;
tmr_param_ser.arg = btn;
tmr_param_ser.callback = button_press_serial_cb;
tmr_param_ser.dispatch_method = ESP_TIMER_TASK;
tmr_param_ser.name = "btn_serial_tmr";
esp_timer_create(&tmr_param_ser, &btn->press_serial_cb.tmr);
#endif
}
btn->press_serial_cb.arg = arg;
btn->press_serial_cb.cb = cb;
btn->press_serial_cb.interval = interval_tick;
btn->press_serial_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY);
#endif
return ESP_OK;
}

Expand All @@ -278,13 +382,17 @@ esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t typ
btn->tap_psh_cb.cb = cb;
btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS;
btn->tap_psh_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
#endif
} else if (type == BUTTON_CB_RELEASE) {
btn->tap_rls_cb.arg = arg;
btn->tap_rls_cb.cb = cb;
btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_RATE_MS;
btn->tap_rls_cb.pbtn = btn;
#if !USE_ESP_TIMER
xTimerChangePeriod(btn->tap_rls_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY);
#endif
} else if (type == BUTTON_CB_TAP) {
btn->tap_short_cb.arg = arg;
btn->tap_short_cb.cb = cb;
Expand All @@ -307,7 +415,16 @@ esp_err_t iot_button_add_custom_cb(button_handle_t btn_handle, uint32_t press_se
cb_new->cb = cb;
cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS;
cb_new->pbtn = btn;
#if !USE_ESP_TIMER
cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb);
#else
esp_timer_create_args_t tmr_param_cus;
tmr_param_cus.arg = cb_new;
tmr_param_cus.callback = button_press_cb;
tmr_param_cus.dispatch_method = ESP_TIMER_TASK;
tmr_param_cus.name = "btn_press_custom_tmr";
esp_timer_create(&tmr_param_cus, &cb_new->tmr);
#endif
cb_new->next_cb = btn->cb_head;
btn->cb_head = cb_new;
return ESP_OK;
Expand Down

0 comments on commit 42d4a72

Please sign in to comment.