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 * @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" #include "localbtn.h"
@ -10,78 +10,199 @@
#include "esp_log.h" #include "esp_log.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/queue.h"
#include <string.h> #include <string.h>
static const char *TAG = "LOCALBTN"; 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) 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) while (1)
{ {
vTaskDelay(pdMS_TO_TICKS(100)); // TODO:Get btn state via interrupt instead of polling // Wait for button press event from ISR
if (xQueueReceive(button_state.event_queue, &event_time, portMAX_DELAY))
bool currentState = (gpio_get_level(current_localBtnPin) == 0);
if ((currentState) && (lastState != currentState))
{ {
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) esp_err_t localbtn_init(int8_t pin_localbtn)
{ {
current_localBtnPin = pin_localbtn; if (pin_localbtn < 0)
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)
{ {
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; return ESP_FAIL;
} }
initialized = true; button_state.initialized = true;
ESP_LOGI(TAG, "local btn initialized on GPIO%d", current_localBtnPin); 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; return ESP_OK;
} }
void localbtn_deinit(void) void localbtn_deinit(void)
{ {
if (!initialized) if (!button_state.initialized)
{ {
return; return;
} }
if (localbtnTaskhandle) // Remove ISR handler
if (button_state.gpio_pin >= 0)
{ {
vTaskDelete(localbtnTaskhandle); gpio_isr_handler_remove(button_state.gpio_pin);
localbtnTaskhandle = NULL;
} }
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) 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 * @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 #ifndef LOCALBTN_H
@ -17,19 +17,20 @@
typedef void (*localbtn_mode_change_callback_t)(); 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 * @return ESP_OK on success
*/ */
esp_err_t localbtn_init(int8_t pin_localbtn); esp_err_t localbtn_init(int8_t pin_localbtn);
/** /**
* @brief Deinitialize local btn reading * @brief Deinitialize local button reading
*/ */
void localbtn_deinit(void); void localbtn_deinit(void);
/** /**
* @brief Register callback for mode changes * @brief Register callback for mode changes
* @param callback Callback function * @param cb Callback function
*/ */
void localbtn_register_callback(localbtn_mode_change_callback_t cb); void localbtn_register_callback(localbtn_mode_change_callback_t cb);