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 に.
課題
- LED とスイッチを使うプログラムを作成せよ. 内容は自由であるが, 特にアイデアが思いつかなけれ以下の例で示すプログラムを作成せよ.
- 例) 各スイッチに対して以下のように別々の LED を割り当て, スイッチを入れたら割り当てられた LED が点灯する (スイッチを切ったら LED が消灯する) プログラムを作成せよ.
-
- スイッチ 1 を ON/OFF -> LED1,2 が ON/OFF
- スイッチ 2 を ON/OFF -> LED3,4 が ON/OFF
- スイッチ 3 を ON/OFF -> LED5,6 が ON/OFF
- スイッチ 4 を ON/OFF -> LED7,8 が ON/OFF