#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h" // 任務通知已內建於 task.h 中
#include "driver/gpio.h"
#include "esp_log.h"
#define CONFIG_LOG_MAXIMUM_LEVEL 5
#define LED_PIN GPIO_NUM_13
#define BUTTON_PIN GPIO_NUM_35
#define DEBOUNCE_TIME 50 // 消除彈跳時間 (ms)
static const char *TAG = "SWITCH_LED";
// 宣告 Task 控制代碼 (Handle)
static TaskHandle_t led_task_handle = NULL;
// 中斷處理函式:直接向指定的 Task 發送通知
static void IRAM_ATTR isr_handler(void* task) {
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
// task本身就是 void* 指標,可以直接傳給 vTaskNotifyGiveFromISR
vTaskNotifyGiveFromISR(task, &xHigherPriorityTaskWoken);
// 必要的 context switch 機制
if (xHigherPriorityTaskWoken) {
portYIELD_FROM_ISR();
}
}
void led_task(void* arg) {
bool led_state = false;
while (1) {
// 等待任務通知
// 第一個參數設為 pdTRUE:代表取得通知後將通知值清零(行為等同二元訊號量 Take)
if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY) > 0) {
vTaskDelay(pdMS_TO_TICKS(DEBOUNCE_TIME)); // 等待電位穩定
// 確認電位是否真的為低,確保只有「按下」動作有效
if (gpio_get_level(BUTTON_PIN) == 0) {
led_state = !led_state;
gpio_set_level(LED_PIN, led_state);
ESP_LOGI(TAG, "LED:%s", led_state ? "開" : "關");
}
}
}
}
void app_main(void) {
esp_log_level_set(TAG, ESP_LOG_VERBOSE);
gpio_config_t led_conf = { // LED接腳配置
.pin_bit_mask = (1ULL << LED_PIN),
.mode = GPIO_MODE_OUTPUT,
};
gpio_config(&led_conf);
gpio_config_t btn_conf = { // 按鍵接腳配置
.pin_bit_mask = (1ULL << BUTTON_PIN),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.intr_type = GPIO_INTR_NEGEDGE, // 下降邊緣觸發
};
gpio_config(&btn_conf);
// 建立 Task,並傳入 &led_task_handle 取得 Task 控制代碼
xTaskCreate(led_task, "led_task", 3000, NULL, 10, &led_task_handle);
// 安裝 GPIO 中斷服務
gpio_install_isr_service(0);
// 註冊回呼:將 led_task_handle 當作第 3 個參數傳給 isr_handler
gpio_isr_handler_add(BUTTON_PIN, isr_handler, (void*)led_task_handle);
}