ESP32-WROOM-32 UART1の正しい使い方

UART1はあるけど使用不可という勘違い

ESP32-WROOM-32は、UART0/1/2の3つが使えるはずだったけど、ESP32のUART1 RXD/TXDピン周りが内部Flash用に占有されているから使用不可と知り、公式のdatasheetにも以下記載があったので、勝手に UART1はあっても使用不可 なんだと勘違いしてた。

しかし、本文にもある通り 普通のGPIOとしては使えませんよ と言っているに過ぎず、 UART1が使えない とは言っていない。

結論から言うと、もちろんUART1は使える。

ESP32-WROOM-32 integrates a 4 MB SPI flash, which is connected to GPIO6, GPIO7, GPIO8, GPIO9, GPIO10 and GPIO11. These six pins cannot be used as regular GPIOs.

esp32-wroom-32_datasheet_en.pdf

UART1の使い方

いきなり結論から言うと、以下のようにすれば、UART1のTX/RXピンをデフォルトピン以外に割り当てて使用することが可能。

以下例はUART1のデフォルトのRX/TXピンをGPIO26/27に変更して使用している。

void setup(void) {
    Serial1.begin(115200, SERIAL_8N1, 26, 27);
}

void loop(void) {
    Serial1.println("hoge");
    Serial1.flush();
    delay(1000);
}

UART1周辺情報の確認

まずはdatasheetを確認。以下のようにU1RXD/TXD/RTS/CTSがそれぞれGPIO9/10/11/6にアサインされている。

公式datasheet記載の通りデフォルトのU1* ピンは全てGPIOとして使用不可なので、どうにかして別のピンに割り当てる必要がある。

esp32-wroom-32_datasheet_en.pdf 2.2 Pin Description

HardwareSerial クラス定義

以降のコードはArduino Framework使用を前提とする。

framework-arduinoespressif32/cores/esp32/HardwareSerial.h を確認すると、以下のようなクラス定義となっている。begin()rxPin/txPin にGPIO9/10以外のピンを指定すれば良さそうに思える。

が、その前に HardwareSerial コンストラクタ引数 uart_nr が気になる…。なんだこれ。

class HardwareSerial: public Stream
{
public:
    HardwareSerial(int uart_nr);

    void begin(unsigned long baud, uint32_t config=SERIAL_8N1, int8_t rxPin=-1, int8_t txPin=-1, bool invert=false, unsigned long timeout_ms = 20000UL);
    ...
};

HardwareSerial コンストラクタ実装

コンストラクタや begin() framework-arduinoespressif32/cores/esp32/HardwareSerial.cpp に実装されている。

HardwareSerial Serialx(1); のように自前でインスタンス作っても良いが、Serial1(1) インスタンスに対してそのまま begin() しても良いことがわかる。

#ifndef RX1
#define RX1 9
#endif

#ifndef TX1
#define TX1 10
#endif

#ifndef RX2
#define RX2 16
#endif

#ifndef TX2
#define TX2 17
#endif

#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_SERIAL)
HardwareSerial Serial(0);
HardwareSerial Serial1(1);
HardwareSerial Serial2(2);
#endif

HardwareSerial::HardwareSerial(int uart_nr) : _uart_nr(uart_nr), _uart(NULL) {}

void HardwareSerial::begin(unsigned long baud, uint32_t config, int8_t rxPin, int8_t txPin, bool invert, unsigned long timeout_ms)
{
    if(0 > _uart_nr || _uart_nr > 2) {
        log_e("Serial number is invalid, please use 0, 1 or 2");
        return;
    }
    if(_uart) {
        end();
    }
    if(_uart_nr == 0 && rxPin < 0 && txPin < 0) {
        rxPin = 3;
        txPin = 1;
    }
    if(_uart_nr == 1 && rxPin < 0 && txPin < 0) {
        rxPin = RX1;
        txPin = TX1;
    }
    if(_uart_nr == 2 && rxPin < 0 && txPin < 0) {
        rxPin = RX2;
        txPin = TX2;
    }

    _uart = uartBegin(_uart_nr, baud ? baud : 9600, config, rxPin, txPin, 256, invert);
    ...
}

HardwareSerial::uartBegin() 実装

更にFrameworkのコードを遡っていくと、ピンのアサインは以下のように実装されている。

pinMode()INPUTOUTPUT や割り込みなど、必要な設定は全て実装されていることがわかる。

uart_t* uartBegin(uint8_t uart_nr, uint32_t baudrate, uint32_t config, int8_t rxPin, int8_t txPin, uint16_t queueLen, bool inverted)
{
    ...
    if(rxPin != -1) {
        uartAttachRx(uart, rxPin, inverted);
    }

    if(txPin != -1) {
        uartAttachTx(uart, txPin, inverted);
    }
}

void uartAttachRx(uart_t* uart, uint8_t rxPin, bool inverted)
{
    if(uart == NULL || rxPin > 39) {
        return;
    }
    pinMode(rxPin, INPUT);
    pinMatrixInAttach(rxPin, UART_RXD_IDX(uart->num), inverted);
    uartEnableInterrupt(uart);
}

void uartAttachTx(uart_t* uart, uint8_t txPin, bool inverted)
{
    if(uart == NULL || txPin > 39) {
        return;
    }
    pinMode(txPin, OUTPUT);
    pinMatrixOutAttach(txPin, UART_TXD_IDX(uart->num), inverted, false);
}

まとめ

公式ドキュメントとFrameworkのコードはちゃんと読もう。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする

Close Bitnami banner
Bitnami