概要
ESP32はpinMode()でINPUTやOUTPUTの状態を設定しますが、取得することができません。なので、ESP32 Technical Reference Manualを確認しながらレジスタから取得してみました。
仕組み
過去にpinMode()で何をしているのかを調べました。これを元にレジスタから現在の状態を取得してみたいと思います。
スケッチ
#include "soc/rtc_periph.h" typedef struct { bool enable: 1; bool pullup: 1; bool pulldown: 1; bool openDrain: 1; bool adc: 1; bool output: 1; bool level: 1; } gpio_info_t; gpio_info_t getPinMode(uint8_t pin) { gpio_info_t info = {}; if (!digitalPinIsValid(pin)) { return info; } info.enable = true; uint32_t rtc_reg = rtc_gpio_desc[pin].reg; if (rtc_reg) { uint32_t reg_val = ESP_REG(rtc_reg); if (reg_val & rtc_gpio_desc[pin].mux) { info.adc = true; } if (reg_val & rtc_gpio_desc[pin].pullup) { info.pullup = true; } if (reg_val & rtc_gpio_desc[pin].pulldown) { info.pulldown = true; } } if (pin > 33) { } else if (pin < 32) { if (ESP_REG(GPIO_ENABLE_REG) & ((uint32_t)1 << pin)) { info.output = true; } } else { if (ESP_REG(GPIO_ENABLE1_REG) & ((uint32_t)1 << (pin - 32))) { info.output = true; } } if (ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) & FUN_PU) { info.pullup = true; } if (ESP_REG(DR_REG_IO_MUX_BASE + esp32_gpioMux[pin].reg) & FUN_PD) { info.pulldown = true; } if (GPIO.pin[pin].val & (1 << GPIO_PIN0_PAD_DRIVER_S)) { info.openDrain = true; } if (pin < 32) { if (ESP_REG(GPIO_IN_REG) & ((uint32_t)1 << pin)) { info.level = true; } else { info.level = false; } } else { if (ESP_REG(GPIO_IN1_REG) & ((uint32_t)1 << (pin - 32))) { info.level = true; } else { info.level = false; } } return info; } void setup() { Serial.begin(115200); delay(1000); Serial.println("------------------------------------------------"); pinMode(9, OUTPUT_OPEN_DRAIN); digitalWrite(9, HIGH); pinMode(10, OUTPUT_OPEN_DRAIN); digitalWrite(10, LOW); pinMode(32, ANALOG); pinMode(33, OUTPUT); digitalWrite(33, HIGH); gpio_info_t gpioList[40]; for (int i = 0; i < 40; i++) { gpioList[i] = getPinMode(i); } Serial.println("GPIO LEVEL OUTPUT ADC PULLUP PULLDOWN OPEN_DRAIN"); Serial.println("------------------------------------------------"); for (int i = 0; i < 40; i++) { Serial.printf("%4d ", i); if (!gpioList[i].enable) { Serial.println(); continue; } if (gpioList[i].level) { Serial.print("HIGH "); } else { Serial.print("LOW "); } if (gpioList[i].output) { Serial.print("OUTPUT "); } else { Serial.print(" "); } if (gpioList[i].adc) { Serial.print("ADC "); } else { Serial.print(" "); } if (gpioList[i].pullup) { Serial.print("PULLUP "); } else { Serial.print(" "); } if (gpioList[i].pulldown) { Serial.print("PULLDOWN "); } else { Serial.print(" "); } if (gpioList[i].openDrain) { Serial.print("OPEN_DRAIN "); } else { Serial.print(" "); } Serial.println(); } } void loop() { }
こんな感じになりました。アナログなどRTC経由でアクセスするものと、直接レジスタに書き込むもので処理が別れています。
------------------------------------------------ GPIO LEVEL OUTPUT ADC PULLUP PULLDOWN OPEN_DRAIN ------------------------------------------------ 0 HIGH PULLUP 1 LOW OUTPUT 2 LOW PULLDOWN 3 HIGH 4 LOW PULLDOWN 5 LOW PULLUP 6 LOW OUTPUT PULLUP 7 HIGH PULLUP 8 LOW OUTPUT PULLUP 9 HIGH OUTPUT OPEN_DRAIN 10 LOW OUTPUT OPEN_DRAIN 11 HIGH PULLUP 12 LOW PULLDOWN 13 LOW PULLDOWN 14 HIGH PULLUP 15 LOW PULLUP 16 HIGH OUTPUT PULLUP 17 HIGH OUTPUT PULLUP 18 LOW 19 LOW 20 LOW 21 HIGH 22 HIGH 23 LOW 24 25 LOW 26 LOW 27 LOW 28 29 30 31 32 LOW ADC 33 HIGH OUTPUT 34 LOW 35 LOW 36 LOW 37 LOW 38 LOW 39 LOW
こんな感じの結果になりました。一番左のレベルはINPUT結果です。どんな状態でもピンの状態は取得できるようです。pinMode()を見ても、INPUTにしたときはOUTPUTの出力をやめているだけで、特別何もしていませんでした。
OUTPUTはアウトプットが有効になっている場合に表示されます。digitalWrite()で出力しても、pinMode()でOUTPUTに設定しないと実際には出力されないみたいでした。
ADCはMUX経由の場合にADCと表示されます。
PULL系の設定はRTC経由と、直接で別々に取得しています。両方フラグがあがっているものと、片方しかないものがありました。
まとめ
GPIO Matrixをもう少し研究すれば、もっと深いところの情報が取れそうですがまずはこんなところにしてみたいと思います。
これもM5Liteのデバッグ機能に埋め込みたいと思います。この手の情報はあったほうが楽だと思うのですが、なかなかないんですよね。
続編
上記で関数化と、GPIOマトリクスを含めて情報を取得しています。
コメント