I2CでOLEDモジュールの高速表示

コンピュータ、組み込み

SSD1306 OLEDモジュールにはI2Cで制御する方法があります。
I2Cには通信速度を決定するクロック信号があります。
このクロック信号を変更することでどれだけ高速表示ができるか調査してみます。

調べること

フレームバッファを2画面を100回転送した時にかかった時間を計測します。
2画面分にしたのは、画面に描画変化があることを確認するためです。

測り方

準備

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

ライブラリAdafruit SSD1306 by Adafruit Ver2.5.1
Adafruit GFX Library Ver 1.11.1
※Verは 記事編集時 2022/6/19 のバージョンです。
赤太字箇所をライブラリマネージャの検索欄に入力することで検索できます。

基板とモジュールはRP2040 picoとSSD1306(単色OLED 0.96inch)を使います。

RP2040 picoワイヤ色SSD1306(0x3c)
3.3VVCC
GNDGND
GP0SDA
GP1SCL
()はI2Cアドレス

測り方

  1. 時間の計測はプログラムで行いOLED画面に表示します。
    (スケッチは一番最後に載せています)
  2. ロジックアナライザはSDAを(0ch)とSCLを(1ch)でプローブする。
  3. SDAを(1ch)とSCLを(2ch)でプローブする。

結果

転送時間

1. 設定クロックと転送にかかった時間をグラフにプロットします。
 クロックを早くすると転送時間は短くなりますが、グラフで示す通り
 比例ではなく二次曲線になっています。

2.正常に転送できる速度は3,700kHz(3.7MHz)まででした。
  3,800kHz(3.8MHz)より大きな設定値の場合、画面が映らくなりました。

  こちらは、4,000kHz(4.0MHz)を設定した時のロジックアナライザの画面です。
  一番最初のI2Cアドレスの転送箇所を拡大しています。

  中央緑枠には、”Address write 3C”とあります。
  OLEDモジュールのI2Cアドレスを指定していますが、結果はNACK(NG)となっています。

  続いて、 3,400kHz(3.4MHz)の画面です。
  同じく、I2Cアドレスの転送箇所を拡大していますが、結果はACK(成功)となっています。

スケッチ

スケッチの説明

I2C Clockの変更は41行目を変更します。
単位はHz でサンプルのスケッチでは100,000Hz(100kHz)が設定されています。
Wire.setClock(100000);

Adafruit製ライブラリではオブジェクト内にフレームバッファがあり、表示命令により一括転送をしているようです。フレームバッファに文字や図形を書き込む時間が含まれないように、あらかじめフレームバッファのデータを2つ作成して、交互に転送命令を実行することで通信以外のオーバーヘッドがかからないようにしています。

スケッチ

//Programing by たまねぎ
//【スケッチの説明】
//RP2040 pico(以下 pico)で使用します。
//OLED画面に "TAMANEGI"と "YUMMI!!"を交互に表示させます。
//100回繰り返した時の時間を最後に表示します。
//
//【準備】
//・RP2040 pico側
//本スケッチでは SDA に GP0, SCL に GP1 を使用します。
//・SSD1306 OLED側
////I2Cアドレスは 0x3c 
//・配線
// RP2040 pico   SSD1306
// GP0           SDA
// GP1           SCL
// 3.3V          VCC
// GND           GND
//
//【バージョン情報】
// 2022/6/21 : 新規

#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_RESET     -1               //使用しないので -1を設定する。
#define SCREEN_ADDRESS 0x3C             //I2Cアドレスは 0x3C
Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
                                        //表示制御にはAdafruit製 SSD1306を使用する。
                                        //初期化時には I2C0を使用する (Wire)
                                        
void setup() {
  
  Wire.setSDA(0);                       //I2C0で使用するGPは SDA = 0, SCL = 1
  Wire.setSCL(1);
  Wire.setClock(100000);		            //I2Cクロックはここで変更する。

  if(!display1.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for(;;);
  }
  if(!display2.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
    for(;;);
  }

  //このオブジェクトは "TAMANEGI" をバッファする
  display1.clearDisplay();
  display1.setTextSize(2);
  display1.setTextColor(SSD1306_WHITE);
  display1.setCursor(20, 20);
  display1.print(F("TAMANEGI"));
  
  //このオブジェクトは "YUMMY!!" をバッファする
  display2.clearDisplay();
  display2.setTextSize(2);
  display2.setTextColor(SSD1306_WHITE);
  display2.setCursor(30, 30);
  display2.print(F("YUMMY!!"));
  
  int Start_ms = millis();                //実行前の時間を記録

  //1000回交互に表示する
  for(int Count = 1; Count < 100; Count ++)
  {
    display1.display();                    //バッファ転送(表示)
    display2.display();
  }

  int Period_ms = millis() - Start_ms;    //終了時間を計測
  
  //計測時間を表示
  display1.clearDisplay();
  display1.setTextSize(1);
  display1.setTextColor(SSD1306_WHITE);
  display1.setCursor(10, 0);
  display1.printf("Measured time is");
  display1.setTextSize(2);
  display1.setCursor(30, 20);
  display1.printf("%d", Period_ms);
  display1.display();
  
}

void loop() {
}

コメント

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