GPIO

先ずはExample: GPIOでGPIOの使い方を勉強しようと思ったのですが、余りにもサンプルが難しい。

gpio_example_main.c

/* GPIO Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gpio.h"

/**
 * Brief:
 * This test code shows how to configure gpio and how to use gpio interrupt.
 *
 * GPIO status:
 * GPIO18: output (ESP32C2/ESP32H2 uses GPIO8 as the second output pin)
 * GPIO19: output (ESP32C2/ESP32H2 uses GPIO9 as the second output pin)
 * GPIO4:  input, pulled up, interrupt from rising edge and falling edge
 * GPIO5:  input, pulled up, interrupt from rising edge.
 *
 * Note. These are the default GPIO pins to be used in the example. You can
 * change IO pins in menuconfig.
 *
 * Test:
 * Connect GPIO18(8) with GPIO4
 * Connect GPIO19(9) with GPIO5
 * Generate pulses on GPIO18(8)/19(9), that triggers interrupt on GPIO4/5
 *
 */

#define GPIO_OUTPUT_IO_0    CONFIG_GPIO_OUTPUT_0
#define GPIO_OUTPUT_IO_1    CONFIG_GPIO_OUTPUT_1
#define GPIO_OUTPUT_PIN_SEL  ((1ULL<<GPIO_OUTPUT_IO_0) | (1ULL<<GPIO_OUTPUT_IO_1))
#define GPIO_INPUT_IO_0     CONFIG_GPIO_INPUT_0
#define GPIO_INPUT_IO_1     CONFIG_GPIO_INPUT_1
#define GPIO_INPUT_PIN_SEL  ((1ULL<<GPIO_INPUT_IO_0) | (1ULL<<GPIO_INPUT_IO_1))
#define ESP_INTR_FLAG_DEFAULT 0

static QueueHandle_t gpio_evt_queue = NULL;

static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    uint32_t gpio_num = (uint32_t) arg;
    xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL);
}

static void gpio_task_example(void* arg)
{
    uint32_t io_num;
    for(;;) {
        if(xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) {
            printf("GPIO[%d] intr, val: %d\n", io_num, gpio_get_level(io_num));
        }
    }
}

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    //disable interrupt
    io_conf.intr_type = GPIO_INTR_DISABLE;
    //set as output mode
    io_conf.mode = GPIO_MODE_OUTPUT;
    //bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
    //disable pull-down mode
    io_conf.pull_down_en = 0;
    //disable pull-up mode
    io_conf.pull_up_en = 0;
    //configure GPIO with the given settings
    gpio_config(&io_conf);

    //interrupt of rising edge
    io_conf.intr_type = GPIO_INTR_POSEDGE;
    //bit mask of the pins, use GPIO4/5 here
    io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL;
    //set as input mode
    io_conf.mode = GPIO_MODE_INPUT;
    //enable pull-up mode
    io_conf.pull_up_en = 1;
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t));
    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(GPIO_INPUT_IO_1, gpio_isr_handler, (void*) GPIO_INPUT_IO_1);

    //remove isr handler for gpio number.
    gpio_isr_handler_remove(GPIO_INPUT_IO_0);
    //hook isr handler for specific gpio pin again
    gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void*) GPIO_INPUT_IO_0);

    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

    int cnt = 0;
    while(1) {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        gpio_set_level(GPIO_OUTPUT_IO_0, cnt % 2);
        gpio_set_level(GPIO_OUTPUT_IO_1, cnt % 2);
    }
}

GPIOのサンプルなのに、タスクの作成、割り込み、キューと盛りだくさん。とりあえず、出力ポートを設定して定期的にLEDをオンオフするだけのプログラムに書き換えて見ました。

gpio_example_main.c

// GPIO Example

#include <stdio.h>
#include "driver/gpio.h"

//	GPIO18: output

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = 1ULL << 18;		    	//bit mask of the pins GPIO18
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

  	int cnt = 0;
	int a,b;
    while(1) 
    {
        printf("cnt: %d\n", cnt++);
		for(a = 0; a < 0x10000; a ++)
			for(b = 0; b < 0x100; b ++) ;
	    gpio_set_level(18, cnt % 2);
    }
}
  • 11行:gpio_config_t型のインスタンスを宣言。ポートのパラメータを設定する。
    • io_conf.intr_type = GPIO_INTR_DISABLE; =>割り込み無し
    • io_conf.mode = GPIO_MODE_OUTPUT; => 出力設定
    • io_conf.pin_bit_mask = 1ULL << 18; => 使用するポートの指定。今回はGPIO18を使用。
    • io_conf.pull_down_en = 0; => プルダウン無し
    • io_conf.pull_up_en = 0; => プルアップ無し
  • 18行:gpio_config(&io_conf); => この関数でポートを設定する。
  • 22から28行:無限ループでGPIO18をHigh/Lowにする。
    • 25,26行:ウエイト
    • 27行:GPIO18をHigh/Low

ポートの機能は1つづつ設定出来ますが、(例えば入出力の設定ー>gpio_set_direction())このプログラムの様に諸パラメターを一気に設定する事も可能です。

コンパイルと実行

プログラムの変更に合わせてプロジェクトの構成を変更しています。例題ではKconfig等有りましたがこのプログラムでは使わないので削除しています。


- ~esp/generic_gpio/
      - CMakeLists.txt
      - main/
                   - CMakeLists.txt
                   - gpio_example_main.c

プロジェクト直下のCMakeLists.txt


# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)

include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(gpio)

使用するCPUはESP32。LEDを下記の様に配線しています。

モニターで、idf.py -p[portNo.] flash monitorを実行して下さい。実行結果がモニターに表示され、LEDが点滅します。

点滅はするんですが

確かにLEDは点滅するんですが、モニターの表示が定期的に変になります。


I (274) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (281) heap_init: At 4008B160 len 00014EA0 (83 KiB): IRAM
I (288) spi_flash: detected chip: generic
I (292) spi_flash: flash io: dio
W (296) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (310) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (320) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
cnt: 0
cnt: 1
cnt: 2
cnt: 3
cnt: 4
E (5320) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
E (5320) task_wdt:  - IDLE (CPU 0)
E (5320) task_wdt: Tasks currently running:
E (5320) task_wdt: CPU 0: main
E (5320) task_wdt: CPU 1: IDLE
E (5320) task_wdt: Print CPU 0 (current core) backtrace


Backtrace:0x400D74AB:0x3FFB0BA00x40082869:0x3FFB0BC0 0x400D4BAF:0x3FFB5580 0x400E578C:0x3FFB55C0 0x40087941:0x3FFB55E0 
0x400d74ab: task_wdt_isr at /home/kita_note/esp/esp-idf/components/esp_system/task_wdt.c:182 (discriminator 3)

0x40082869: _xt_lowint1 at /home/kita_note/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/xtensa_vectors.S:1117

0x400d4baf: app_main at /home/kita_note/esp/samp/gpio/gpio_1/build/../main/gpio_example_main.c:25

0x400e578c: main_task at /home/kita_note/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/port_common.c:127 (discriminator 2)

0x40087941: vPortTaskWrapper at /home/kita_note/esp/esp-idf/components/freertos/FreeRTOS-Kernel/portable/xtensa/port.c:141

こんなエラーがモニタに表示されます。

E (5320) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:

”タスクがウォッチドッグをリセットしていない”。Webで検索かけると、プログラムにループが有る場合、ウォッチドッグをリセット出来なくてこの警告が出るようです。今回のプログラムで、22から28行のループがそれに当たる様です。対策は、

  • ウォッチドッグをオフする。
    • menuconfigでウォッチドッグをオフ出来る様ですが、そのそのオフして良いものか分かりません。
  • 10msec位のウエイトをループに入れる。
    • ウエイトを入れる事によりリセットが可能になる様です。

という事で、ウエイトを入れる事にしました。元々サンプルプログラムにはウエイトが入っていたのでその部分を戻しました。

gpio_example_main.c

// GPIO Example

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

//	GPIO18: output 

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = 1ULL << 18;		    	//bit mask of the pins GPIO18
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

  	int cnt = 0;
    while(1) 
    {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
	    gpio_set_level(18, cnt % 2);
    }
}
  • 前回whileループでforを使ったウエイトを vTaskDelay(1000 / portTICK_PERIOD_MS); に変更しました。これで1000msec 1秒のウエイトです。
  • これにより、5,6行のヘッダーをファイルを追加しています。

今度は問題無く実行出来ました。forを使ったウエイトとvTaskDelay()の何が違うのでしょうか。良く分からない点ですが、注意が必要です。

GPIO04を入力設定

サンプルではGPIO04を入力に設定して見ます。プログラムを下記の様に変更しました。

  • 現在の状態から、GPIO04を入力に設定する。
  • GPIOの18と04をつなぐ
  • GPIO04 の状態に合わせ、HIGH又はLOWをモニタに表示する。
gpio_example_main.c

// GPIO Example

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

//	GPIO18: output 
//  GPIO4:  input, pulled up

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = 1ULL << 18;		    	//bit mask of the pins GPIO18
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

    io_conf.mode = GPIO_MODE_INPUT;	    			//set as input mode
    io_conf.pin_bit_mask = 1ULL << 4;    			//bit mask of the pins GPIO4	
    io_conf.pull_up_en = 1;						    //enable pull-up mode
    gpio_config(&io_conf);

  	int cnt = 0;
    while(1) 
    {
        printf("cnt: %d: ", cnt++);
	    gpio_set_level(18, cnt % 2);
        if(gpio_get_level(4)) printf("Port is High\n");
        else printf("Port is Low\n");
        	
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
  • 23から26行でGPIO04を入力に設定しています。
  • 33,34行でGPIO04の状態を読み込みモニターに表示しています。

プログラムをidf.py -p[portNo.] flash monitorでコンパイルして実行して下さい。前回と同じくLEDが点滅し、モニターに以下の様に表示されます。


I (275) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (281) heap_init: At 4008B160 len 00014EA0 (83 KiB): IRAM
I (288) spi_flash: detected chip: generic
I (292) spi_flash: flash io: dio
W (296) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (310) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (320) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (330) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 
cnt: 0: Port is High
cnt: 1: Port is Low
cnt: 2: Port is High
cnt: 3: Port is Low
cnt: 4: Port is High
cnt: 5: Port is Low
cnt: 6: Port is High

通常のGPIOのサンプルはここまでですが、今回のサンプルはこれで終わりません。

GPIOピンを使った割り込み

GPIOピンを使った割り込み用にプログラムを以下の様に変更します。

  • GPIO04を入力で割り込み可に変更。
  • GPIO18を定期的にHigh/Lowさせ割り込みをかける
  • LEDをGPIO19につなぎ、割り込みがかかる度にHigh/Lowする。ー>LEDが点滅する。
gpio_example_main.c

// GPIO Example

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

//	GPIO18: output 
//  GPIO19: output 
//  GPIO4:  input, pulled up, interrupt 

#define ESP_INTR_FLAG_DEFAULT 0

int flg = 0;
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
	flg = !flg;
	gpio_set_level(19,flg);
}

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = ((1ULL << 18) | (1ULL << 19));
    										    	//bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

    io_conf.intr_type = GPIO_INTR_POSEDGE;		    //interrupt of rising edge
    io_conf.mode = GPIO_MODE_INPUT;	    			//set as input mode
    io_conf.pin_bit_mask = 1ULL << 4;    			//bit mask of the pins, use GPIO4/5 here	
    io_conf.pull_up_en = 1;						    //enable pull-up mode
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(4, GPIO_INTR_ANYEDGE);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(4, gpio_isr_handler, (void*) 4);

 	int cnt = 0;
    while(1) 
    {
        printf("cnt: %d: ", cnt++);
	    gpio_set_level(18, cnt % 2);
        if(gpio_get_level(4)) printf("Port is High\n");
        else printf("Port is Low\n");
        	
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}
  • 14から19行:割り込み関数に関する定義
    • 15行:static void IRAM_ATTR gpio_isr_handler(void* arg)
      • 割り込み関数の定義
        • 割り込み関数には、IRAM_ATTR を使用
        • 引数は、gpio_isr_handler_add()関数の第3引数
    • ここでは、割り込みがかかる度に外部変数の flg の値を変転し、GPIO19に出力している。
  • 28行:GPIO19を18と同じ条件で設定。
  • 34行:io_conf.intr_type = GPIO_INTR_POSEDGE;
    • 割り込みのタイプを指定。
      • GPIO_INTR_DISABLE:割り込み無し
      • GPIO_INTR_POSEDGE:エッジの立ち上がり
      • GPIO_INTR_NEGEDGE:エッジの立ち下がり
      • GPIO_INTR_ANYEDGE:両方
  • 44行:gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    • この関数で割り込みが使用出来る様になります。
    • int 型の引数(intr_alloc_flags)で割り込みのタイプを指定します。
    • サンプルでは、ESP_INTR_FLAG_DEFAULT(0)としています。
  • 46行:gpio_isr_handler_add(4, gpio_isr_handler, (void*) 4);
    • ここで割り込み時に実行される関数を指定しています。
    • 引数
      • 第一:gpio_num:  GPIO 番号
      • 第二:isr_handler:  割り込みが実行されるISR のハンドラ
      • 第三:args: ISRへ渡すデータのパラメータ

プログラムでは、GPIO4の割り込みを一度、GPIO_INTR_POSEDGE で設定していますが、41行で、GPIO_INTR_ANYEDGE に指定し直しているのでエッジの両方で割り込みがかかります。割り込み信号はGPIO18で、このポートは1秒間隔でHigh/Lowを繰り返します。よってGPIO19に繋がれたLEDは1秒間隔で点滅します。コンパイルして実行して下さい。予定通りにLEDが点滅します。

タスクの生成

オリジナルのサンプルではタスクの生成も行っています。現在のプログラムでは、メインのループでGPIO4の状態を表示している箇所が有ります。この部分を新しくタスクを生成してそちらで行う様に変更します。

gpio_example_main.c

/ GPIO Example

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

//	GPIO18: output 
//  GPIO19: output 
//  GPIO4:  input, pulled up, interrupt 

#define ESP_INTR_FLAG_DEFAULT 0

int flg = 0;
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
	flg = !flg;
	gpio_set_level(19,flg);
}

static void gpio_task_example(void* arg)
{
 	int cnt = 0;
    for(;;) 
    {
        printf("cnt: %d: ", cnt++);
	    gpio_set_level(18, cnt % 2);
        if(gpio_get_level(4)) printf("Port is High\n");
        else printf("Port is Low\n");
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = ((1ULL << 18) | (1ULL << 19));
    										    	//bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

    io_conf.intr_type = GPIO_INTR_POSEDGE;		    //interrupt of rising edge
    io_conf.mode = GPIO_MODE_INPUT;	    			//set as input mode
    io_conf.pin_bit_mask = 1ULL << 4;    			//bit mask of the pins, use GPIO4/5 here	
    io_conf.pull_up_en = 1;						    //enable pull-up mode
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(4, GPIO_INTR_ANYEDGE);

    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(4, gpio_isr_handler, (void*) 4);

    while(1) 
    {
        vTaskDelay(10 / portTICK_PERIOD_MS);
    }
}
  • 21行:static void gpio_task_example(void* arg)
    • 新しく作成されたタスクの関数。
    • メインループで行っていたGPIO4の状態を表示しています。
  • 57行:xTaskCreate(gpio_task_example, “gpio_task_example”, 2048, NULL, 10, NULL);
    • タスクを作成する関数。
    • 引数
      • 第1:pvTaskCode   タスク関数のエントリー・ポインタ。
      • 第2:pcName     タスクの説明的な名前。
      • 第3:usStackDepth  タスクスタックの大きさ
      • 第4:pvParameters  作成されるタスクのパラメータとして使用するポインタ。
      • 第5:uxPriority     タスクが走るべきである優先権
      • 第6:pvCreatedTask  これによって作成されたタスクのハンドルを返す。
  • 64行目からのループは内容無しのからループにしています。
    • 全くの空ループにするとウォッチドッグの問題が生じるので10msecのWaitを入れています。

コンパイルして実行して下さい。前回と同じ様にLEDが点滅しモニターに結果が生じますがモニターの出力は新しく作成したタスクを行っている点が前回と違う所です。

次は、queue

最後にqueueを追加します。変更内容は

  • 新しく、queueを作成。
  • GPIO4に割り込みがかかったら、ISR(割り込み関数)でqueueに値を追加
  • 新たに作成されたタスクがqueueを監視
  • queueに追加された要素の値に合わせてGPIO19を設定する。
gpio_example_main.c

// GPIO Example

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

//	GPIO18: output 
//  GPIO19: output 
//  GPIO4:  input, pulled up

#define ESP_INTR_FLAG_DEFAULT 0

static QueueHandle_t gpio_evt_queue = NULL;

int flg = 0;
static void IRAM_ATTR gpio_isr_handler(void* arg)
{
    flg = !flg;
    xQueueSendFromISR(gpio_evt_queue, &flg, NULL);
}

static void gpio_task_example(void* arg)
{
    int _data;
    for(;;) 
    {
        if(xQueueReceive(gpio_evt_queue, &_data, portMAX_DELAY)) 
            gpio_set_level(19, _data);
    }
}

void app_main(void)
{
    //zero-initialize the config structure.
    gpio_config_t io_conf = {};
    
    io_conf.intr_type = GPIO_INTR_DISABLE;		    //disable interrupt
    io_conf.mode = GPIO_MODE_OUTPUT;    			//set as output mode
    io_conf.pin_bit_mask = ((1ULL << 18) | (1ULL << 19));
    										    	//bit mask of the pins that you want to set,e.g.GPIO18/19
    io_conf.pull_down_en = 0;						//disable pull-down mode
    io_conf.pull_up_en = 0;						    //disable pull-up mode
    gpio_config(&io_conf);    						//configure GPIO with the given settings

    io_conf.intr_type = GPIO_INTR_POSEDGE;		    //interrupt of rising edge
    io_conf.mode = GPIO_MODE_INPUT;	    			//set as input mode
    io_conf.pin_bit_mask = 1ULL << 4;    			//bit mask of the pins, use GPIO4/5 here	
    io_conf.pull_up_en = 1;						    //enable pull-up mode
    gpio_config(&io_conf);

    //change gpio interrupt type for one pin
    gpio_set_intr_type(4, GPIO_INTR_ANYEDGE);

    //create a queue to handle gpio event from isr
    gpio_evt_queue = xQueueCreate(10, sizeof(int));

    //start gpio task
    xTaskCreate(gpio_task_example, "gpio_task_example", 2048, NULL, 10, NULL);

    //install gpio isr service
    gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT);
    //hook isr handler for specific gpio pin
    gpio_isr_handler_add(4, gpio_isr_handler, (void*) 4);

    int cnt = 0;
    while(1) 
    {
        printf("cnt: %d\n", cnt++);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        gpio_set_level(18, cnt % 2);
    }
}
  • 18行:static void IRAM_ATTR gpio_isr_handler(void* arg)
    • 割り込みがかかった時に実行される関数。
    • 割り込みの度に反転する flg の値を xQueueSendFromISR(gpio_evt_queue, &flg, NULL); 関数でqueueに追加
  • 24行:static void gpio_task_example(void* arg)
    • 追加されたタスク
    • if(xQueueReceive(gpio_evt_queue, &_data, portMAX_DELAY)) で queueに追加の有無を判断
    • 追加が有ればqueueからのデータを読み込みGPIO19に出力。
  • 57行:gpio_evt_queue = xQueueCreate(10, sizeof(int));
    • ここでqueueを作成。

コンパイルして実行して下さい。同じ様にLEDが点滅しモニターに結果が表示されます。

でもちょっと

今回のプログラムで作成したタスクですが、中身はfor文の無限ループになっていてかつ、vTaskDelay() Waitを含みません。この様な場合、ウォッチドッグの問題が生じるはずですが今回は出ません。何ででしょうか。

最後に

これでやっとオリジナルのサンプルコードに戻ります。

  • ここまで色んな変更をして来たのでオリジナルのコードをコピーし直す。
  • CPUはESP32を使用します。
  • 各ポートを変数扱いしています。以下デフォルトで説明します。デフォルトは
    • 出力:GPIO18,19。
    • 入力:GPIO4,5。
  • GPIOの 18と4 19と5 をつなぐ。(下記参照)
  • 割り込みのタイミングは以下のように設定されています。
    • GPIO4:両エッジ
    • GPIO5:立ち上がり
  • 割り込み信号に使用されているGPIO18,19は共に同じタイミングでHigh/Lowになります。よって割り込みが発生するタイミングは
    • GPIO4は出力が変わる度
    • GPIO5は出力がLowlからHighになる度。

コンパイルして実行して下さい。モニターに以下の様に表示されます。


I (311) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
I (321) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (331) gpio: GPIO[19]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 
I (341) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:1 
I (351) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:1 
Minimum free heap size: 293220 bytes
cnt: 0
cnt: 1
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1
cnt: 2
GPIO[4] intr, val: 0
cnt: 3
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1
cnt: 4
GPIO[4] intr, val: 0
cnt: 5
GPIO[4] intr, val: 1
GPIO[5] intr, val: 1
cnt: 6
GPIO[4] intr, val: 0


最後に

かなり難しいGPIOのサンプルプログラムでした。まだ完全に理解では出来ていませんがかなり分かったつもりです。