handle BTN with interrupt
This commit is contained in:
195
main/localbtn.c
195
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 <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");
|
||||
}
|
||||
@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user