From d576b4d42d7c560c60fb9652b56954b387abb0df Mon Sep 17 00:00:00 2001 From: localhorst Date: Tue, 6 Jan 2026 18:15:53 +0100 Subject: [PATCH] handle BTN with interrupt --- main/localbtn.c | 195 +++++++++++++++++++++++++++++++++++++++--------- main/localbtn.h | 9 ++- 2 files changed, 163 insertions(+), 41 deletions(-) diff --git a/main/localbtn.c b/main/localbtn.c index 429c284..491cb2f 100644 --- a/main/localbtn.c +++ b/main/localbtn.c @@ -1,6 +1,6 @@ /** * @file localbtn.c - * @brief Local GPIO0 BTN reading implementation using edge capture + * @brief Local GPIO button reading using interrupt-based edge detection */ #include "localbtn.h" @@ -10,78 +10,199 @@ #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/queue.h" #include static const char *TAG = "LOCALBTN"; -uint8_t current_mode; -int8_t current_localBtnPin; -bool initialized; -TaskHandle_t localbtnTaskhandle; -localbtn_mode_change_callback_t callback; +#define DEBOUNCE_TIME_MS 50 // Debounce time in milliseconds + +// Button state +static struct +{ + int8_t gpio_pin; + bool initialized; + TaskHandle_t task_handle; + QueueHandle_t event_queue; + localbtn_mode_change_callback_t callback; + int64_t last_press_time; // For debouncing +} button_state = { + .gpio_pin = -1, + .initialized = false, + .task_handle = NULL, + .event_queue = NULL, + .callback = NULL, + .last_press_time = 0}; + +/** + * @brief GPIO interrupt handler (ISR) + * Minimal work in ISR - just send event to task + */ +static void IRAM_ATTR gpio_isr_handler(void *arg) +{ + int64_t now = esp_timer_get_time(); + + // Send timestamp to queue for debouncing in task + BaseType_t high_priority_task_woken = pdFALSE; + xQueueSendFromISR(button_state.event_queue, &now, &high_priority_task_woken); + + if (high_priority_task_woken) + { + portYIELD_FROM_ISR(); + } +} + +/** + * @brief Button handling task + * Handles debouncing and callback execution + */ static void localbtn_task(void *arg) { - bool lastState = false; + int64_t event_time; + + ESP_LOGI(TAG, "Button task started, monitoring GPIO%d", button_state.gpio_pin); + while (1) { - vTaskDelay(pdMS_TO_TICKS(100)); // TODO:Get btn state via interrupt instead of polling - - bool currentState = (gpio_get_level(current_localBtnPin) == 0); - if ((currentState) && (lastState != currentState)) + // Wait for button press event from ISR + if (xQueueReceive(button_state.event_queue, &event_time, portMAX_DELAY)) { - ESP_LOGI(TAG, "Local button pressed"); + // Debouncing: Check if enough time has passed since last press + int64_t time_since_last_press = (event_time - button_state.last_press_time) / 1000; // Convert to ms - if (callback) + if (time_since_last_press >= DEBOUNCE_TIME_MS) { - callback(current_mode); + // Valid button press - verify button is still pressed + vTaskDelay(pdMS_TO_TICKS(10)); // Small delay to ensure stable state + + if (gpio_get_level(button_state.gpio_pin) == 0) + { + ESP_LOGI(TAG, "Button press detected on GPIO%d", button_state.gpio_pin); + + button_state.last_press_time = event_time; + + // Execute callback + if (button_state.callback) + { + button_state.callback(); + } + } } } - - lastState = currentState; } } esp_err_t localbtn_init(int8_t pin_localbtn) { - current_localBtnPin = pin_localbtn; - gpio_config_t io_conf = { - .pin_bit_mask = 1ULL << current_localBtnPin, - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLUP_ENABLE, // safe even if external pull-up exists - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE}; - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - // Create monitor task - BaseType_t ret = xTaskCreate(localbtn_task, "localbtn_task", 2048, NULL, 5, &localbtnTaskhandle); - if (ret != pdPASS) + if (pin_localbtn < 0) { + ESP_LOGW(TAG, "Button disabled (invalid pin: %d)", pin_localbtn); + return ESP_ERR_NOT_SUPPORTED; + } + + if (button_state.initialized) + { + ESP_LOGW(TAG, "Button already initialized"); + return ESP_ERR_INVALID_STATE; + } + + button_state.gpio_pin = pin_localbtn; + button_state.last_press_time = 0U; + + // Create event queue for ISR->Task communication + button_state.event_queue = xQueueCreate(10, sizeof(int64_t)); + if (button_state.event_queue == NULL) + { + ESP_LOGE(TAG, "Failed to create event queue"); + return ESP_ERR_NO_MEM; + } + + // Configure GPIO + gpio_config_t io_conf = { + .pin_bit_mask = (1ULL << pin_localbtn), + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLUP_ENABLE, // Enable internal pull-up (safe even with external) + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_NEGEDGE // Interrupt on falling edge (button press) + }; + + esp_err_t ret = gpio_config(&io_conf); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "GPIO config failed: %s", esp_err_to_name(ret)); + vQueueDelete(button_state.event_queue); + return ret; + } + + // Add ISR handler for this GPIO + ret = gpio_isr_handler_add(pin_localbtn, gpio_isr_handler, NULL); + if (ret != ESP_OK) + { + ESP_LOGE(TAG, "ISR handler add failed: %s", esp_err_to_name(ret)); + vQueueDelete(button_state.event_queue); + return ret; + } + + // Create button handling task + BaseType_t task_ret = xTaskCreate( + localbtn_task, + "localbtn_task", + 2048, + NULL, + 5, // Priority 5 (same as other tasks) + &button_state.task_handle); + + if (task_ret != pdPASS) + { + ESP_LOGE(TAG, "Failed to create button task"); + gpio_isr_handler_remove(pin_localbtn); + vQueueDelete(button_state.event_queue); return ESP_FAIL; } - initialized = true; - ESP_LOGI(TAG, "local btn initialized on GPIO%d", current_localBtnPin); + button_state.initialized = true; + ESP_LOGI(TAG, "Button initialized on GPIO%d with interrupt-based detection", pin_localbtn); + ESP_LOGI(TAG, "Debounce time: %d ms", DEBOUNCE_TIME_MS); return ESP_OK; } void localbtn_deinit(void) { - if (!initialized) + if (!button_state.initialized) { return; } - if (localbtnTaskhandle) + // Remove ISR handler + if (button_state.gpio_pin >= 0) { - vTaskDelete(localbtnTaskhandle); - localbtnTaskhandle = NULL; + gpio_isr_handler_remove(button_state.gpio_pin); } - initialized = false; + + // Delete task + if (button_state.task_handle) + { + vTaskDelete(button_state.task_handle); + button_state.task_handle = NULL; + } + + // Delete queue + if (button_state.event_queue) + { + vQueueDelete(button_state.event_queue); + button_state.event_queue = NULL; + } + + button_state.initialized = false; + button_state.callback = NULL; + + ESP_LOGI(TAG, "Button deinitialized"); } void localbtn_register_callback(localbtn_mode_change_callback_t cb) { - callback = cb; -} + button_state.callback = cb; + ESP_LOGI(TAG, "Callback registered"); +} \ No newline at end of file diff --git a/main/localbtn.h b/main/localbtn.h index 0b87671..4a9c81e 100644 --- a/main/localbtn.h +++ b/main/localbtn.h @@ -1,6 +1,6 @@ /** * @file localbtn.h - * @brief Local GPIO0 BTN reading implementation using edge capture + * @brief Local GPIO button reading using interrupt-based edge detection */ #ifndef LOCALBTN_H @@ -17,19 +17,20 @@ typedef void (*localbtn_mode_change_callback_t)(); /** - * @brief Initialize local btn reading + * @brief Initialize local button with interrupt-based detection + * @param pin_localbtn GPIO pin number for button (active low) * @return ESP_OK on success */ esp_err_t localbtn_init(int8_t pin_localbtn); /** - * @brief Deinitialize local btn reading + * @brief Deinitialize local button reading */ void localbtn_deinit(void); /** * @brief Register callback for mode changes - * @param callback Callback function + * @param cb Callback function */ void localbtn_register_callback(localbtn_mode_change_callback_t cb);