ボタンとタイマー

ボタンとタイマー

ボタンとタイマー

ボタン押したら何かするとか、タイマーで一定時間経ったらLED点灯するとか、そういうのやったことないのでやってみます。

ボタンはDevKitCに一つついてます。二つついてますけど一つはリセットボタンなので自由に使えるのは一つだけ。GPIO0に接続されています。

GPIO0のボタンが押されたら10秒間だけLEDを点滅させる。
これだけです。

プログラム

こんな感じになりました。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"

#define BTN_GPIO 0          // オンボードSW (ONでLOW、OFFでHI)
#define LED_GPIO 5          // LED

// GPIOの入力ハンドラ
void IRAM_ATTR gpio_isr_handler(void* arg)
{
    TaskHandle_t xHandle = *(TaskHandle_t*)arg;
    eTaskState state = eTaskGetState(xHandle);
    if (state == eRunning) {
        // 既に実行中の場合は終了
        return;
    }
    vTaskResume(xHandle);   // 10秒間LED点滅タスクを再開する
}

// 10秒間LED点滅タスク
portTASK_FUNCTION(led_10sec_blinking, pvParam) {
    while(1) {
        // 500ms間隔でLED点滅
        int ledState = 1;
        for(int i=0; i<20; i++) {
            gpio_set_level(LED_GPIO, ledState);
            ledState = ledState == 0 ? 1 : 0;
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
        vTaskSuspend(NULL);     // タスクを停止状態へ
    }
    vTaskDelete(NULL);
}

void app_main(void)
{
    // LED用のピン初期化 (出力)
    gpio_reset_pin(LED_GPIO);
    gpio_set_direction(LED_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(LED_GPIO, 0);

    // ボタン用のピン初期化 (入力)
    gpio_reset_pin(BTN_GPIO);
    gpio_set_intr_type (BTN_GPIO, GPIO_INTR_NEGEDGE);       // 立ち下がりエッジ。ボタンが押された瞬間に割り込み発生
    gpio_set_direction(BTN_GPIO, GPIO_MODE_INPUT);
    gpio_pulldown_dis(BTN_GPIO);
    gpio_pullup_en(BTN_GPIO);
    
    // LED点滅タスク登録
    TaskHandle_t xHandle = NULL;
    xTaskCreate(led_10sec_blinking, "ATask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle);
    vTaskSuspend(xHandle);          // タスクは停止状態にしておく。

    // ボタン用のピンの入力割り込みハンドラ設定
    gpio_install_isr_service(0);
    gpio_isr_handler_add(BTN_GPIO, gpio_isr_handler, (void*)&xHandle);
    
    vTaskSuspend(NULL);		// app_mainは永久に停止
}

一応動きます。

解説

最初のLEDの出力設定については省略。
ボタン入力の部分は入力に変化があった場合に割り込みを発生させます。
タイマ割り込みとかは使ってません。(ESP-IDFのタイマ割り込みどうやってやるのかわからない)

LEDを10秒間点滅させる部分をFreeRTOSのタスクとして作成します。

// 10秒間LED点滅タスク
portTASK_FUNCTION(led_10sec_blinking, pvParam) {
    while(1) {
        // 500ms間隔でLED点滅
        int ledState = 1;
        for(int i=0; i<20; i++) {
            gpio_set_level(LED_GPIO, ledState);
            ledState = ledState == 0 ? 1 : 0;
            vTaskDelay(500 / portTICK_PERIOD_MS);
        }
        vTaskSuspend(NULL);     // タスクを停止状態へ
    }
    vTaskDelete(NULL);
}

10秒間の処理が終わったらまたvTaskSuspendで一時停止状態に戻るようにしています。

portTSK_FUNCTIONはタスク登録のためのマクロです。
以下と同じです。

void led_10sec_blinking(void* pvParam)

app_mainの中でタスクを登録して一旦停止状態にします。

TaskHandle_t xHandle = NULL;
xTaskCreate(led_10sec_blinking, "ATask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle);
vTaskSuspend(xHandle);          // タスクは停止状態にしておく。

ボタンが押されたらタスクを再開させてます。

void IRAM_ATTR gpio_isr_handler(void* arg)
{
    TaskHandle_t xHandle = *(TaskHandle_t*)arg;
    eTaskState state = eTaskGetState(xHandle);
    if (state == eRunning) {
        // 既に実行中の場合は終了
        return;
    }
    vTaskResume(xHandle);   // 10秒間LED点滅タスクを再開する
}

タスクがeSuspended(一時停止)ではなくeRunning(実行中)の場合は処理を中断させてます。


ESP-IDFのタイマー割り込みを探したんですが、何故か発見できずFreeRTOSのvTaskDelayを代わりに使ってる感じになってます。
一応意図通りに動きます。本当にこれでいいのかというモヤっとした感は残りますね。

コメント

このブログの人気の投稿

(真)ESP32の開発環境をVSCodeで作る

ESP32にmicroSD接続

ESP32でデバッグ