handle BTN with interrupt

This commit is contained in:
2026-01-06 18:15:53 +01:00
parent 9ef50436a4
commit d576b4d42d
2 changed files with 163 additions and 41 deletions

View File

@ -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 <string.h>
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");
}

View File

@ -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);