単色OLEDモジュールを使う(SSD1306 / SPI)

コンピュータ、組み込み

Arduino環境で、RP2040(Raspberry Pi pico)のSPI接続、ESP32(BananaPi PicoW)のSPI接続での表示をします。
I2Cとの表示速度比較、OLEDを複数接続してTripple Monitorで使ってみます。

紹介するもの

OLED SSD1306 0.96inch <SPI>

特徴

同じSSD1306を使用しますが通信方式には4線式I2Cと、7線のSPI接続の方式があります。
購入時にはどちらかの通信方式を使用するか確認してください。
本記事ではSPI単色表示について記載します。

I2C接続でのSSD1306についてはこちら。

製品情報
サイズ0.96 ~ 2.42inch 各種
当記事では 0.96を使用します。
解像度(X, Y)128, 64
表示色白、青、黄、黄+青
黄+青の場合、それぞれのエリアが決まっていて、
色の変更はできないようです。
電源電圧3.3 – 5.0V
通信方式購入時にどちらで通信するか選択
I2C
SPI
ドライバSSD1306
入手電子部品ショップ、ネットショップ
入手性は非常に良く、安く大量買いする場合はAli Express
外観

前面

背面

比較

vsI2C

SSD1306の0.96inchには、I2CモデルとSPIモデルがあります。
同時に同ストアで購入したものではないので単純な製品比較にはならないかもしれませんが並べてみます。(画像左がSPI, 右がI2C)

画面の大きさは同じ0.96inchですがSPIのほうが若干大きいですが、画素の有効範囲や大きさは同じに見えます。
ダイナミック点灯なので、撮影のタイミングで色合いに変化が出てしまいますが、輝度には差異が感じられませんでした。

表示速度はSPIのほうが早いです。
一瞬でAdafruitロゴスクロールまで到達しました。

同じ命令セットですが、単純な通信速度だけでもない気もします。
SCROLL命令では、SPIは縦軸にも動きますが、I2Cでは横方向にしか動きませんでした。

使用したサンプルは以下のサンプルに、ピン番号の変更とdelay()はすべて消去しています。
ファイル(F) > スケッチ例 > Adafruit SSD1306 > ssd1306_128x64_i2c
ファイル(F) > スケッチ例 > Adafruit SSD1306 > ssd1306_128x64_spi

使用感

I2Cのデフォルト表示速度と比べると速いです。

I2Cより配線の量が多くて煩わしいですが、いくつでもOLEDを接続できるメリットがあります。
マイコン側のI/Oピンの少ないものではSPIが多くのピンを占有するので、最終的な完成系に必要なI/Oが確保できるかを検討する必要があります。

SPIで接続する場合は、同じ配線数で表現力の豊かなカラーOLEDを使用することもできます。
このサイズでは価格にそれほど差はありません。

準備

ライブラリ

ライブラリは以下2つのライブラリを使用します。
I2Cと同じライブラリを使用します。

ライブラリAdafruit SSD1306 by Adafruit Ver2.5.1
Adafruit GFX Library Ver 1.11.1
※動作確認では使用したバージョンです。
赤太字箇所をライブラリマネージャの検索欄に入力することで検索できます。

使い方

SPIによる表示(RP2040系 Raspberry Pi Pico)

SPIを使ったOLEDの表示です。

配線

Raspberry Pi Pico配線SSD1306
GNDGND
3V3VDD
GPIO18(SPI0 SCK)SCK
GPIO19(SPI0 MOSI)SDA
GPIO22RES
GPIO28DC
GPIO17(SPI0 CS)CS

スケッチ

/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
Raspberry Pi Pico

【スケッチの説明】
SSD1306 OLEDの制御をします。

【ライブラリ】
Raspberry Pi Pico/RP2040 > Raspberry Pi Pico
Adafruit SSD1306 by Adafruit
Adafruit GFX Library by Adafruit

【準備】
マイコン基板       <-> SSD1306(SPI)
GND               <-> GND
3V3               <-> VDD
GPIO18(SPI0 SCK)  <-> SCK
GPIO19(SPI0 MOSI) <-> SDA
GPIO22            <-> RES
GPIO28            <-> DC
GPIO17(SPI0 CS)   <-> CS

【バージョン情報】
2023/03/20 : 新規
**********************************************************************/

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

#define SCREEN_WIDTH 128                //解像度 128 x 64 で使用します。
#define SCREEN_HEIGHT 64                //SCREEN_HEIGHTは 32 に設定することができます。

#define OLED_SCK    18
#define OLED_MOSI   19
#define OLED_RST    22
#define OLED_DC     28
#define OLED_CS     17

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC, OLED_RST, OLED_CS);

// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
//   OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

void setup() {
  
  //RP2040系では、この命令セットでSPIピンを設定する。
  SPI.setTX(OLED_MOSI);
  SPI.setSCK(OLED_SCK);

  if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();               //何か表示されている場合に備えて表示クリア

  display.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display.setCursor(20, 10);            //テキストの表示開始位置
  display.print(F("TAMANEGI"));         //表示文字列
  display.setCursor(25, 30);
  display.print(F("SSD1306"));
  display.setCursor(45, 50);
  display.print(F("SPI0"));

  display.display();                    //バッファ転送(表示)
}

void loop() {
}

結果

SPIによる表示(ESP32系 Banana Pi PicoW)

配線

Banana Pi PicoW配線SSD1306
GNDGND
3V3VDD
GPIO18(SPI0 SCK)SCK
GPIO19(SPI0 MOSI)SDA
GPIO22RES
GPIO28DC
GPIO17(SPI0 CS)CS

スケッチ

/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
micro:bit

【スケッチの説明】
SSD1306 OLEDの制御をします。

【ライブラリ】
Nordic Semiconductor nRF5 Boards > BBC micro:bit
Adafruit SSD1306 by Adafruit
Adafruit GFX Library by Adafruit

【準備】
マイコン基板       <-> SSD1306(SPI)
GND               <-> GND
3V3               <-> VDD
GPIO3(SPI0 SCK)   <-> SCK
GPIO4(SPI0 MOSI)  <-> SDA
GPIO7             <-> RES
GPIO10            <-> DC
GPIO2(SPI0 CS)    <-> CS

【バージョン情報】
2023/03/20 : 新規
**********************************************************************/

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

#define SCREEN_WIDTH 128                //解像度 128 x 64 で使用します。
#define SCREEN_HEIGHT 64                //SCREEN_HEIGHTは 32 に設定することができます。

#define OLED_SCK    3
#define OLED_MOSI   4
#define OLED_RST    7
#define OLED_DC     10
#define OLED_CS1    2

Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC, OLED_RST, OLED_CS);

// Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
//   OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);

void setup() {
  
  //ESP32系では、この命令セットでSPIピンを設定する。
  SPI.begin(OLED_SCK, -1, OLED_MOSI, OLED_CS);
 
  if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();               //何か表示されている場合に備えて表示クリア

  display.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display.setCursor(20, 10);            //テキストの表示開始位置
  display.print(F("TAMANEGI"));         //表示文字列
  display.setCursor(25, 30);
  display.print(F("SSD1306"));
  display.setCursor(45, 50);
  display.print(F("SPI0"));

  display.display();                    //バッファ転送(表示)
}

void loop() {
}

結果

SPIを使ったトリプルモニタ

同じI2C0 ライン上には異なるI2Cアドレスのモジュールを配線します。
スケッチにはそれぞれのアドレスに対して個別の表示ができます。

配線

SPI0を使用し、MOSIとSCKは共通で使用します。
CSとRESとDCは各モニタ用に割り当てます。

マイコンのピンアサイン表にはSPI CSの記載がありますが、デフォルトから変更して使用する場合はどのピンを使用しても良いようです。

RaspberryPiPico配線SSD1306(0.96)配線SSD1306(1.54)配線SSD1306(2.42)
GNDVCCVCCVCC
3V3GNDGNDGND
GPIO18 (SPI0 SCK)SCKSCKSCK
GPIO19 (SPI0 MOSI)SDASDASDA
GPIO22RES
GPIO28DC
GPIO17 (SPI0 CS)CS
GPIO15RES
GPIO14DC
GPIO13CS
GPIO2RES
GPIO1DC
GPIO0CS

スケッチ

/**********************************************************************
【ライセンスについて】
Copyright(c) 2022 by tamanegi
Released under the MIT license
'http://tamanegi.digick.jp/about-licence/

【マイコン基板】
Raspberry Pi Pico

【スケッチの説明】
SSD1306 OLED (SPI)を3個チェーン制御をします。

【ライブラリ】
Raspberry Pi Pico/RP2040 > Raspberry Pi Pico
Adafruit SSD1306 by Adafruit
Adafruit GFX Library by Adafruit

【準備】
マイコン基板       <-> SSD1306(SPI) 1台目
GND               <-> GND
3V3               <-> VDD
GPIO18(SPI0 SCK)  <-> SCK
GPIO19(SPI0 MOSI) <-> SDA
GPIO22            <-> RES
GPIO28            <-> DC
GPIO17(SPI0 CS)   <-> CS

マイコン基板       <-> SSD1306(SPI) 2台目
GND               <-> GND
3V3               <-> VDD
GPIO18(SPI0 SCK)  <-> SCK
GPIO19(SPI0 MOSI) <-> SDA
GPIO15            <-> RES
GPIO14            <-> DC
GPIO13            <-> CS

マイコン基板       <-> SSD1306(SPI) 3台目
GND               <-> GND
3V3               <-> VDD
GPIO18(SPI0 SCK)  <-> SCK
GPIO19(SPI0 MOSI) <-> SDA
GPIO2             <-> RES
GPIO1             <-> DC
GPIO0             <-> CS


【バージョン情報】
2023/03/20 : 新規
**********************************************************************/

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

#define SCREEN_WIDTH 128                //解像度 128 x 64 で使用します。
#define SCREEN_HEIGHT 64                //SCREEN_HEIGHTは 32 に設定することができます。

//共通で使用するSPIのピン定義
#define OLED_SCK    18
#define OLED_MOSI   19

//モニタ1 0.96inchのピン定義
#define OLED_CS1    17
#define OLED_RST1   22
#define OLED_DC1    28

//モニタ2 1.54inchのピン定義
#define OLED_CS2    13
#define OLED_RST2   15
#define OLED_DC2    14

//モニタ3 2.42inchのピン定義
#define OLED_CS3    0
#define OLED_RST3   2
#define OLED_DC3    1

Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC1, OLED_RST1, OLED_CS1);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC2, OLED_RST2, OLED_CS2);
Adafruit_SSD1306 display3(SCREEN_WIDTH, SCREEN_HEIGHT, &SPI, OLED_DC3, OLED_RST3, OLED_CS3);

void setup() {
  
  //RP2040系では、この命令セットでSPIピンを設定する。
  //共通で使用するSPI0
  SPI.setTX(OLED_MOSI);
  SPI.setSCK(OLED_SCK);

  //モニタ1 0.96inchの初期化
  if(!display1.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  //モニタ2 1.54inchの初期化
  if(!display2.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  //モニタ3 2.42inchの初期化
  if(!display3.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  //モニタ1 0.96inchの表示
  display1.clearDisplay();               //何か表示されている場合に備えて表示クリア
  display1.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display1.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display1.setCursor(20, 10);            //テキストの表示開始位置
  display1.print(F("TAMANEGI"));         //表示文字列
  display1.setCursor(25, 30);
  display1.print(F("SSD1306"));
  display1.setCursor(20, 50);
  display1.print(F("0.96inch"));
  display1.display();                    //バッファ転送(表示)

  //モニタ2 1.54inchの表示
  display2.clearDisplay();               //何か表示されている場合に備えて表示クリア
  display2.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display2.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display2.setCursor(20, 10);            //テキストの表示開始位置
  display2.print(F("TAMANEGI"));         //表示文字列
  display2.setCursor(25, 30);
  display2.print(F("SSD1306"));
  display2.setCursor(20, 50);
  display2.print(F("1.54inch"));
  display2.display();                    //バッファ転送(表示)

  //モニタ3 2.42inchの表示
  display3.clearDisplay();               //何か表示されている場合に備えて表示クリア
  display3.setTextSize(2);               //フォントサイズは2(番目に小さい)
  display3.setTextColor(SSD1306_WHITE);  //色指定はできないが必要
  display3.setCursor(20, 10);            //テキストの表示開始位置
  display3.print(F("TAMANEGI"));         //表示文字列
  display3.setCursor(25, 30);
  display3.print(F("SSD1306"));
  display3.setCursor(20, 50);
  display3.print(F("2.42inch"));
  display3.display();                    //バッファ転送(表示)

}

void loop() {
}

結果

同じサイズのSPI OLEDを複数所有していないので、サイズ違いのSPIを使用しています。
I2Cはアドレスによりスレーブの識別をしていますが、SPIではCS(SS)によりスレーブの識別をしているので、IOの多く使用できる基板では多くのスレーブを使用することができます。

関連リンク

コメント

  1. Haruo Yamashita より:

    初歩的な質問ですみませんが、
    Adafruit SSD1306 by Adafruit Ver2.5.1
    Adafruit GFX Library Ver 1.11.1
    をインストールしていますが、
    コンパイルすると、以下のようにAVR固有の表現らしいところで、多数エラーが出ます。
    Adafruit_SSD1306.cpp:45:6: error: expected unqualified-id before ‘const’
    45 | (*(const unsigned char *)(addr)) ///< PROGMEM workaround for non-AVR
    回避する方法はありませんでしょうか?

    • tamanegi より:

      この行は AVR系とESP32, ESP8266以外のSocを搭載したボードを選択してコンパイルした時に実行される行と思います。

      現象については使用されているライブラリバージョンと同じにしてRP2040搭載ボード(Raspberry Pi Pico)でコンパイルと実行をしましたが、当方にて同様のエラーは再現できていません。
      念のため、AVR系(Arduino nano (ATMEGA328P)), ESP8266, ESP32-C3)でのコンパイルと実行をしましたが、エラーは再現できませんでした。

      現在考えられる対策としては、エラーの再現ができていないのですが、
      C:\Users\\Documents\Arduino\libraries\Adafruit_SSD1306\Adafruit_SSD1306.cpp
      の 44行目を以下のようにコメントアウトすることで回避とはならないでしょうか? (フォルダの
      はご自身の環境のフォルダ名です)
      //#define pgm_read_byte(addr)
      (*(const unsigned char *)(addr)) ///< PROGMEM workaround for non-AVR もともとコンパイルエラーの無かった箇所ですが、Raspberry Pi Picoでは掲載サンプルが動作しました。 もしこの方法で回避できた場合、ライブラリ(Adafruit SSD1306)のバージョンを更新すると修正したファイルが上書きされてエラーが再発することが考えられますので、 同じ手順で修正されるのが良いかと思います。

  2. Haruo Yamashita より:

    ライブラリを再インストールすることで解消できました。ありがとうございました。

タイトルとURLをコピーしました