GPIO IN, OUT (ESP-IDF 環境 + C 言語)
プログラムの書き方
LED やスイッチを使う上でよく使われる関数については, ESP-IDF Programming Guide の API Reference の GPIO & RTC GPIO を参照して欲しい.
GPIO (IN) : L チカ
電子工作における hello world と言える L チカを実行する. まずはサンプルファイルをコピーし, 実行してみる.
$ cd ~/esp $ cp -r esp-idf/examples/get-started/blink . $ cd blink
サンプルでは, config menu で点灯させる LED の GPIO を指定するようになっている. その設定は, main/Kconfig.projbuild に書かれており, デフォルトで GPIO 5 が設定されている.
$ lv main/Kconfig.projbuild menu "Example Configuration" config BLINK_GPIO int "Blink GPIO number" range 0 34 default 5 help GPIO number (IOxx) to blink on and off. Some GPIOs are used for other purposes (flash connections, etc.) and cannot be used to blink. GPIOs 35-39 are input-only so cannot be used as outputs. endmenu
main プログラムでは congig menu で設定された LED GPIO が CONFIG_BLINK_GPIO に 代入され, それがマクロで BLINK_GPIO に置き換えられている. プログラム中では BLINK_GPIO のレベルを 0, 1 に変化させることで LED の点灯を実現している.
$ lv main/blink.c ...(中略).... #define BLINK_GPIO CONFIG_BLINK_GPIO ...(中略).... printf("Turning off the LED\n"); gpio_set_level(BLINK_GPIO, 0); ...(後略)....
ビルド
make コマンドを実行する. プロジェクトのディレクトリで最初に make コマンドを実行した時には 以下の例のように config menu が表示される.
- Serial flasher config を選択し, Default serial port にデバイス名 (ここでは /dev/ttyUSB0) を入力する.
- Example Configuration を選択し, Blink GPIO number に LED の GPIO の番号 (ここでは 13) を入力する.
マイコンへの書き込み
マイコンに書き込むのは make flash コマンド, 標準出力を表示するのは make monitor コマンドである. まとめて make flash monitor としても良い.
$ make flash monitor Turning off the LED Turning on the LED Turning off the LED Turning on the LED Turning off the LED ....
上記のように標準出力に LED の点灯・消灯のメッセージが表示され, さらに実際に実習ボードの LED の 1 つが点灯することが確認できるであろう.
プログラムの修正と実行
デフォルトの blink.c は make menuconfig で設定された GPIO の LED を点灯させるもの となっている. それを修正して 1 つ目の LED (GPIO 13) を点灯させるプログラムに修正する.
$ cp main/blink.c main/blink.c.bk $ vi main/blink.c
エディタを使って以下のように書く.
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h" 3 #include "freertos/task.h" 4 #include "driver/gpio.h" 5 6 #define BLINK_GPIO 13 7 8 void app_main(void) 9 { 10 /* ESP32 の各ピンは様々な役割がとれる. GPIO モードに切り替える*/ 11 gpio_pad_select_gpio(BLINK_GPIO); 12 13 /* GPIO を出力モードに切り替える (OUTPUT と INPUT の 2 つから選ぶ)*/ 14 gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); 15 16 while(1) { 17 /* Blink off (output low) */ 18 printf("Turning off the LED\n"); 19 gpio_set_level(BLINK_GPIO, 0); 20 vTaskDelay(1000 / portTICK_PERIOD_MS); 21 22 /* Blink on (output high) */ 23 printf("Turning on the LED\n"); 24 gpio_set_level(BLINK_GPIO, 1); 25 vTaskDelay(1000 / portTICK_PERIOD_MS); 26 } 27 }
- 4 行目: GPIO を利用する場合に driver/gpio.h を include する必要がある.
- 6 行目: LED の接続される GPIO を指定
- 11 行目: ピンの役割を GPIO モードに変更
- 14 行目: LED の接続されているピンを出力に設定.
- 19, 24 行目: LED の点灯・消灯. gpio_set_level の第二引数が 0 なら電圧を 0V に (= 消灯)
- 20, 25 行目: 1 秒 = 1000 ミリ秒だけ動作を止める. ESP-IDF 環境では, 待ち時間 (単位 ミリ秒) を portTICK_PERIOD_MS (FreeRTOS の割込み周期)で割り算した結果を vTaskDelay の引数に与える必要がある.
編集したのち, コンパイルとマイコンへの書き込みを行う. make flash すると LED の点滅が確認できるはずである.
$ make $ make flash
全ての LED を点灯させる.
LED を 1 つしか点灯できない. 実習ボードの全ての LED を点灯させてみよう. 実習基板には 8 つの LED があるが, それらの GPIO は 32,33,25,26,27,14,12,13 である.
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h" 3 #include "freertos/task.h" 4 #include "driver/gpio.h" 5 6 #define NUM 8 7 8 void app_main(void) 9 { 10 int BLINK_GPIO[NUM] = {32,33,25,26,27,14,12,13}; 11 int i; 12 13 /* 初期化 */ 14 for (i = 0; i < NUM; i++){ 15 /* ESP32 の各ピンは様々な役割がとれる. GPIO モードに切り替える*/ 16 gpio_pad_select_gpio(BLINK_GPIO[i]); 17 18 /* GPIO を出力モードに切り替える (OUTPUT と INPUT の 2 つから選ぶ)*/ 19 gpio_set_direction(BLINK_GPIO[i], GPIO_MODE_OUTPUT); 20 } 21 22 while(1) { 23 for (i = 0; i < NUM; i++){ 24 /* Blink off (output low) */ 25 gpio_set_level(BLINK_GPIO[i], 0); 26 vTaskDelay(1000 / portTICK_PERIOD_MS); 27 28 /* Blink on (output high) */ 29 gpio_set_level(BLINK_GPIO[i], 1); 30 vTaskDelay(1000 / portTICK_PERIOD_MS); 31 } 32 } 33 }
編集したのち, コンパイルとマイコンへの書き込みを行う.
$ make $ make flash
上記の操作で全ての LED が点灯することが確認できたと思う. 念のために, gpio_pad_select_gpio が必要であることを確認してみる. gpio_pad_select_gpio をコメントアウトして実行するとどうなるであろうか?
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h" 3 #include "freertos/task.h" 4 #include "driver/gpio.h" 5 6 #define NUM 8 7 8 void app_main(void) 9 { 10 int BLINK_GPIO[NUM] = {32,33,25,26,27,14,12,13}; 11 int i; 12 13 /* 初期化 */ 14 for (i = 0; i < NUM; i++){ 15 /* ESP32 の各ピンは様々な役割がとれる. GPIO モードに切り替える*/ 16// gpio_pad_select_gpio(BLINK_GPIO[i]); 17 18 /* GPIO を出力モードに切り替える (OUTPUT と INPUT の 2 つから選ぶ)*/ 19 gpio_set_direction(BLINK_GPIO[i], GPIO_MODE_OUTPUT); 20 } 21 22 while(1) { 23 for (i = 0; i < NUM; i++){ 24 /* Blink off (output low) */ 25 gpio_set_level(BLINK_GPIO[i], 0); 26 vTaskDelay(1000 / portTICK_PERIOD_MS); 27 28 /* Blink on (output high) */ 29 gpio_set_level(BLINK_GPIO[i], 1); 30 vTaskDelay(1000 / portTICK_PERIOD_MS); 31 } 32 } 33 }
改めてコンパイルとマイコンへの書き込みを行う.
$ make $ make flash
そうすると, LED が 5 個しか点灯しないことが確認できるはずである. ESP32 の Pin Description (データシート の 2.2 節を参照) を見ると, 各ピンに様々な役割があることがわかる. 上記のように, 初期化プロセスで各ピンを GPIO モードに切り替えることを忘れる, 思った通りの動作をさせられないことになる.
GPIO (OUT) : スイッチ
スイッチと LED を連動させたプログラムを書く. 以下のようなプログラムを書くと, SW4 を ON/OFF した時に LED1 が点灯/消灯する.
1 #include <stdio.h> 2 #include "freertos/FreeRTOS.h" 3 #include "freertos/task.h" 4 #include "driver/gpio.h" 5 6 #define buttonPin 19 7 #define ledPin 13 8 9 void app_main(void) 10 { 11 /* LED の初期化 */ 12 gpio_pad_select_gpio(ledPin); 13 gpio_set_direction(ledPin, GPIO_MODE_OUTPUT); 14 15 /* スイッチの初期化*/ 16 gpio_pad_select_gpio(buttonPin); 17 gpio_set_direction(buttonPin, GPIO_MODE_INPUT); 18 gpio_set_pull_mode(buttonPin, GPIO_PULLUP_ONLY); //PULLUP が必要 19 20 while(1) { 21 int buttonState = gpio_get_level(buttonPin); 22 23 if (buttonState == 1) { 24 gpio_set_level(ledPin, 1); // turn LED on: 25 } else { 26 gpio_set_level(ledPin, 0); // turn LED off: 27 } 28 } 29 }
- 6 行目: スイッチの接続される GPIO を指定.
- 16~18 行目: スイッチの接続されているピンを入力に設定
- GPIO をインプットモードにする. PULLUP しないといけない.
- 21 行目: スイッチの入力値を取得
- 23-24 行目: スイッチが入っていたら LED を ON に.
- 25~26 行目: スイッチが入ってなければ LED を OFF に.