見出し画像

ESP32でOLEDディスプレイ試す(I2C SSD1306 SCL SDAの設定方法)


  • 0.96インチの有機ELディスプレイ(OLED)をAmazonで購入してみた

  • データシートや説明書が、ほとんどなかったが

  • ESP32-S3で無事に動作確認できたので、情報をまとめる


商品仕様

  • 商品ページの説明は以下のみ

    • アンドロイド > Arduinoのこと?

    • STIM32/2 > STM32のこと?

    • ドライバーチップはSSD1306

  • 色々、不安要素はあるが、ESP32向け(Arduino向け)のSSD1306ドライバがありそうなので、動くはず、ポチっ

セット内容:OLEDディスプレイモジュール2個入り。
サイズ:0.96インチ。
解像度:128*64。
表示色:白色、視野角160度以上。
サポートプラットフォーム:アンドロイド、51シリーズ、MSP430シリーズ、STIM32/2、CSRチップに対応。
ドライバーチップはSSD1306です。

https://amzn.to/49ShuqK

データシート

  • 参考となるデータシートを探す

  • 以下の「秋月電子通商で販売しているOLEDディスプレイ」の「互換品」と考えて、以下のリンクにある参考のデータシートを閲覧する

  • 「秋月電子通商の商品」も、販売している商品のそのものの資料ではなく、参考資料扱い


I2C通信のアドレスを探す

  • I2C通信するのに必要なアドレスの明記がない

  • 自分でスキャンする必要がある

    • アドレスは0~127なので、全て試すのは大変ではない

  • スキャンした結果、購入したものは、0x3Cであった

    • Adafruitのライブラリのコメントには、128x32が0x3C、その他が0x3Dという記述がある

    • 購入した商品は128x64だが、0x3C(Adafruit商品ではないので仕方ないが、文脈を理解しないと勘違いする)

アドレス変更用のスイッチ

  • 商品の写真を見ると、抵抗の付け替えで、アドレスを変更できるような仕組みが基板にありそうだった

    • 秋月電子通商の商品の写真にもありそう(Addressと書いてありそう)

  • しかし、届いたものには、写真にあるパターンが存在しなかった(残念)

    • チップ抵抗を持っていないので、やることもなかったかも

  • 1つのMCUのI2CのBusで、2つのOLEDを利用したい場合は向いていない


ディスプレイ制御用のドライバ


ソースコード

  • 付属のサンプルがてんこ盛りなので、

  • 入門しやすいように、理解しやすいようにシンプルなサンプルを用意した(シンプルさを優先して、エラー処理を省いてる)

  • 設定は以下の2行なので、理解してしまえば簡単

    • Adafruit_SSD1306 コンストラクタ

    • Adafruit_SSD1306.begin()メソッド

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);:
  • 以下のサンプルは、アルファベットを表示するコード(Arduino向け)

    • テキストを描画するメソッドtestdrawchar()を除けば、とても簡単だとわかる(4行)

  • コードの意味を次の項目で説明する

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1       // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
#define OLED_SCL 47
#define OLED_SDA 21

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 400000UL, 100000UL);

void testdrawchar(uint8_t offset = 0) {
    display.clearDisplay();
    display.display();
    display.setTextSize(1);              // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);             // Start at top-left corner
    display.cp437(true);                 // Use full 256 char 'Code Page 437' font
    for (uint8_t i = 0; i < 170; i++)
        display.write((uint8_t)((i + offset) % 26 + 65));// 65:A to 90:Z
    display.display();
}

void setup()
{
    Wire.begin(OLED_SDA, OLED_SCL);

    display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));

    display.clearDisplay();
    
    testdrawchar();
}

void loop()
{   
}

SSD1306の設定

以下、OLEDディスプレイを動かすためのAdafruit SSD1306の設定についての情報

ディスプレイサイズの設定

  • Adafruit_SSD1306 のコンストラクタで指定

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

リセットピンの設定

  • Adafruit_SSD1306 のコンストラクタで指定

  • リセットピンはないので Adafruit_SSD1306 のコンストラクタの第4引数に-1を渡せばよさそう

  • 以下、参考コード

#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

Reset pin (using Arduino pin numbering), or -1 if not used

https://github.com/adafruit/Adafruit_SSD1306

周波数の設定

  • Adafruit_SSD1306 のコンストラクタで指定

  • デフォルト値の400kHzに設定(clkDuring)

    • ライブラリのコメントを読むと、ESP32のときは、800kHzも使われるという記述がある

  • SSD1306の通信後の周波数も設定できるようだが、デフォルト値のままにした(clkAfter)

    • 他の低速なI2Cデバイスのために、SSD1306の通信後、周波数を変更すると思われる

    • 今回は、SSD1306しか接続しないので無関係のはず

  • 以下、参考コード、第5,6引数は何も渡さないと、デフォルト値になる

  • 今は理解を深めるフェーズなので、あえて、明示的に指定するコードにした

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 400000UL, 100000UL);
Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi = &Wire,
int8_t rst_pin = -1, uint32_t clkDuring = 400000UL,
uint32_t clkAfter = 100000UL);


SDAとSCLの設定

  • Adafruit_SSD1306 のコンストラクタで指定するが、少し設定が複雑

    • 初心者にはハードルが高いと思う

    • 筆者も入門したばかりだったら、自力では(解説サイトがなければ)辿りつけなかった気がする

  • まず、ESP32-S3の開発ボードのI2CのデフォルトのSDAとSCLのピン番号をみつけるのに苦労した

  • 以下のページには、ESP32のデフォルトが書いてある(S3では違う可能性大)

The default pins may vary from board to board. On the Generic ESP32 the default I2C pins are:
sdaPin GPIO21
sclPin GPIO22

https://docs.espressif.com/projects/arduino-esp32/en/latest/api/i2c.html
  • Arduinoライブラリのソースを見てデフォルト値を探す

  • ボードによって異なる値になるような仕組みがある

  • 例えば、ESP32-S3だと以下のファイルで各ピンが定義されている

    • variantsフォルダにピン番号が定義されたヘッダファイルが、各ボード毎にある

    • 以下は、PlatformIOの場合のパス

    • Arduino IDE、IDFの場合のファイルの格納場所は、以前の記事で述べた

{install folder}\packages\framework-arduinoespressif32\variants\esp32s3\pins_arduino.h

static const uint8_t SDA = 8;
static const uint8_t SCL = 9;
  • 「ボード名」と「フォルダ名」が完全に一致するわけではなさそうなのが厄介

    • 「board = esp32-s3-devkitc-1」が、「esp32s3フォルダの中のヘッダファイル」が使われる、という理解でよいと思うが(確信が持てない)

    • PlatformIOのGo to Definition機能では、esp32s3フォルダに移動した

    • 名前が一致しないということは、1対1の関係ではないのかもしれない

  • ESP32-S3(esp32s3フォルダにあるヘッダファイルが使われる環境)は、8と9ピンがデフォルトのようだ

    • 実際に読み込まれる「pins_arduino.h」を確認する方法がわかっていない

  • 実は、テスト時は、ここまで深く理解できておらず、デフォルト値を21と22と思っていて、自分のESP32-S3には22がない、と思ってしまった

  • 情報を探した結果、「GPIO Matrix and Pin Mux」に行き着く

  • ESP32には「GPIO Matrix and Pin Mux」という仕組みがあるので、「任意のピンにI2CのSDAとSCLをアサインできる」

  • 上記のような背景があり、SDAとSCLを自分でアサインする方法の方が確実と思った

  • コンストラクタかdisplay.begin()メソッドの引数で設定できるのだろうと想像したのだが、設定項目がみつからない

  • どうやら、Wire.begin()で行う構成のようだ

  • そして、Wireの初期化を自分で行った旨をdisplay.begin()の引数で渡す仕組みのようだ

  • 以下の第4引数の「periphBegin」をflaseにすると、SSD1306ライブラリ側でWireのbegin()メソッドを呼ばなくなる

    • periphBeginが何を意味しているのか?

    • periph = peripheral = ペリフェラル = 周辺機器をbeginするかどうか、のフラグ

    • peripheralが見る頻度が高くない単語の上に、periphと省略されていて、さらにWire.begin()のことを示す、という推測が必要だった

    • SPIとI2Cのことを抽象して表現しているのでわかりづらい(wireBeginならすぐピンとくるが、SPIの場合もあるので仕方ない)

  • 以下のようなコードで、無事にI2CのSDAとSCLのピンを変更できた

  • ちなみに、第3引数は初期化時にリセットするかの設定(リセットピンの番号を-1にしているので無関係だが、一応、falseを渡している)

#define OLED_SCL 47
#define OLED_SDA 21

// 自分でピン番号を指定
Wire.begin(OLED_SDA, OLED_SCL);

// 第4引数の「periphBegin」で自動で、Wire.begin()が行われないようにする
display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);

追記:

  • setPings()で、begin()前にピンを指定できるようだ

  • 以下のGitHub上のIssueで、setPings()の追加について議論している

  • 「ESP系以外のArduinoライブラリ」との関係性(互換性やマナーなど)を考える必要があるようだ

  • ESP32しか使っていないと、その点の視点が欠けてしまうので、なんで、こういう構成になってるだろうと、と思うことがあるかもしれないと、考えさせられた

  • また、「multiple invocations of Wire.begin() are supported.」(複数回、begin()を呼び出してもよい)とあるので、display.begin()の第4引数は、特に考えなくても動くかもしれない


#define OLED_SCL 47
#define OLED_SDA 21

Wire.setPins(OLED_SDA, OLED_SCL);


電源の設定

  • display.begin()で指定

  • 電源の設定は、「外部電源」か「3.3Vから生成する」という2つのモードがあるようだ

  • サンプルにある「SSD1306_SWITCHCAPVCC」に設定した

  • 3.3ボルトから、必要な電源を作っているという理解でよいのだろうか

display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false);
#define SSD1306_EXTERNALVCC 0x01  ///< External display voltage source
#define SSD1306_SWITCHCAPVCC 0x02 ///< Gen. display voltage from 3.3V

アドレスの設定

  • 上記で探したアドレスを、display.begin()で指定

display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));

初期化時にリセットするかの設定

  • 繰り返しになるが、display.begin()の第3引数で、初期化時にリセットするかを指定する

display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, false, false));


動作確認

  • 設定と描画命令が正しければ、以下のような感じでOLEDディスプレイの表示される

    • (筆者のサンプルは訳があってアルファベットだけにしてしまったが…)

  • 実際やるのは、苦労しなかったが

  • 情報を整理して、まとめるのには苦労した


4ピンで配線が楽(しかもESP32なら、2ピンは任意のピンでOK)ちょっとしたデバッグ出力するのに便利そう!耐久性はまだ、わからないが、1個500円しないので試してみる価値はあると思う



試した環境

  • PlatformIO Core 6.1.16 / Home 3.4.4

  • framework-arduinoespressif32 3.20017

  • adafruit/Adafruit SSD1306@^2.5.13


いいなと思ったら応援しよう!