ESP32でgetPinMode()相当を作る

概要

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マトリクスを含めて情報を取得しています。

コメントする

メールアドレスが公開されることはありません。

管理者承認後にページに追加されます。公開されたくない相談はその旨本文に記載するかTwitterなどでDM投げてください。またスパム対策として、日本語が含まれない投稿は無視されますのでご注意ください。