Arduinoではオブジェクトの使い回しでメモリ不足を回避


2019年 07月 29日

128×64ドットで、多数jの文字を表示するのは、1画面ならOKだが、2画面だとダメだった。

ということで、次のような実験をした。

  1. 画面を128×64ではなく、128x32を指定してみた。
  2. 表示する文字数をぐっと半分以下に減らしてみた。

これだと、ディスプレイの上半分だけが使われるのだろうか。

プログラム全体を以下に示す。

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET     4

Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
Serial.begin(115200);
display1.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display2.begin(SSD1306_SWITCHCAPVCC, 0x3D);

displayText(display1, "Arduino is an open-source hardware and software company,"
"project and user community ");

displayText(display2, "that designs and manufactures "
"single-board microcontrollers and microcontroller kits " );
}

void loop() {

}

void displayText(Adafruit_SSD1306 disp, char* text) {
disp.setTextColor(WHITE);
disp.clearDisplay();
disp.setTextSize(1);
disp.setCursor(0,0);
disp.print(text);
disp.display();
}

すると、結果はこうなった。

OLED-128x32displa2.JPG

文字が縦に倍に伸ばされているようだ。1行の文字数には変化がない。

さて、どうして、128×64では動かず、128×32にしたら動いたのであろうか。

たぶん、以下のような理由ではないかと思う。

画面が128x64ドットということは、128×64 = 8192画素なので、モノクロ画面イメージを保持するには8192ビット、つまり1KBのメモリが必要になる。

すると、次の2行で、

Adafruit_SSD1306 display1(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
Adafruit_SSD1306 display2(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

2つのオブジェクトが作られ、各オブジェクトには画面イメージ1024バイト必要で、オブジェクトにはその他の値も少しは保持されるので、1オブジェクトが1KBよりやや大きいはずである。

ところで、Arduinoのメインメモリ(SRAM)は2048バイトしかない。

一部の特殊なものは多数のメモリを搭載している。例えば、Arduino Zeroは32ビットCPUのArmが載っていて、SRAM 32KBとなっている。Zeroがついていると、性能が落ちると思い込んではいけない。

ということは、それぞれの画面毎にオブジェクトを作ったら、それだけでメモリが足りなくなるので、動作しないのは当然だ。

それでも、それぞれの画面に、小さい字で8行表示したいと思い、次のように変更した。

#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     4

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

void setup() {
Serial.begin(115200);

displayText(0x3C,"Arduino is an open-source hardware and software company,"
"project and user community that designs and manufactures "
"single-board microcontrollers and microcontroller kits " );
displayText(0X3D,"for building digital devices and interactive objects "
"that can sense and control both physically and digitally.");
}

void loop() {
exit(0);
}

void displayText( int addr, char* text) {
display.begin(SSD1306_SWITCHCAPVCC, addr );
display.setTextColor(WHITE);
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0,0);
display.print(text);
display.display();
}

そして結果は、めでたくこうなった。

DSCN4799-400.JPG

要するに、オブジェクトは1つ作るだけで、displayText関数には、I2Cのアドレスと文字列を渡すことにした。

そして、displayText()では、最初に渡されたI2Cアドレスを指定して初期化してから表示処理をするようにした。

蛇足だが、このOLEDでは、送られてきた画面データはずっと保持されているので、最初のOLEDは放置して、次のOLEDの処理を行うことができる。

これを上手く使えば、多数のOLEDの表示を、1つのArduinoで制御できる。

OLEDの話が予定より長くなってしまった。次からは、別の話題にする。