2017年9月16日土曜日

u8g2のFull BufferとPage Buffer


この間、u8g2で描画テストをしてみてSRAMを消費しすぎていたので原因を考えてみた。
#define RULER_STR "ABCDEFGHI012345678901234567890"
としていて、
u8g2.drawStr(0,10,RULER_STR); // write something to the internal memory
u8g2.drawStr(0,21,RULER_STR);  // write something to the internal memory
u8g2.drawStr(0,32,RULER_STR);  // write something to the internal memory
と、「#define」した文字列を3回も定義していることになるので、これが原因かも?と思って「#define」のところを「const char*」に変更してみた。

「const char*」なら定義は1回なので1/3になるはずだが・・・

Arduinoのスケッチ
<HiLetgo_IIC_OLED_SSD1306_128x32_u8g2_F_FontTest.ino>

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

U8G2_SSD1306_128X32_UNIVISION_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);  // Adafruit ESP8266/32u4/ARM Boards + FeatherWing OLED

void setup(void) {
  u8g2.begin();
}

const char* RULER_STR = "ABCDEFGHI012345678901234567890";

void loop(void) {
  u8g2.clearBuffer();                   // clear the internal memory
  u8g2.setFont(u8g2_font_8x13_tf);  // choose a suitable font  
  u8g2.drawStr(0,10,RULER_STR); // write something to the internal memory
  u8g2.drawStr(0,21,RULER_STR);  // write something to the internal memory
  u8g2.drawStr(0,32,RULER_STR);  // write something to the internal memory
  u8g2.sendBuffer();                    // transfer internal memory to the display
  delay(1000);

  u8g2.setDrawColor(1);
  u8g2.drawBox(0, 0, 128, 32);
  u8g2.sendBuffer();
  delay(1000);  
}

ビルドの結果は
最大30720バイトのフラッシュメモリのうち、スケッチが9650バイト(31%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が1512バイト(73%)を使っていて、ローカル変数で536バイト使うことができます。
となって、SRAMの消費量は「#define」の時と同じ1512バイト。この程度は最適化されるようだ。

Arduino IDEと言っても中でavr-gccでGCCを使っている。さすがGCCは賢い(^q^!

<追記:2017.09.18>

文字列定数はSRAMではなくフラッシュメモリに格納されると思うので、フラッシュメモリの使用状況も比較。

「#define」時
最大32256バイトのフラッシュメモリのうち、スケッチが9644バイト(29%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が1512バイト(73%)を使っていて、ローカル変数で536バイト使うことができます。

「const char*」時
最大30720バイトのフラッシュメモリのうち、スケッチが9650バイト(31%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が1512バイト(73%)を使っていて、ローカル変数で536バイト使うことができます。

フラッシュメモリの使用量は「const char*」の方が増えている(@@;

</追記>

Page Buffer


ということはFull Bufferだと転送用バッファでメモリ領域を専有しているのでは?と思って、Page単位のクラスを使ってみた。

Arduinoのスケッチ
<HiLetgo_IIC_OLED_SSD1306_128x32_1_FontTest.ino>

#include <Arduino.h>
#include <U8g2lib.h>
#include <Wire.h>

U8G2_SSD1306_128X32_UNIVISION_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);  // Adafruit ESP8266/32u4/ARM Boards + FeatherWing OLED

void setup(void) {
  u8g2.begin();
}

const char* RULER_STR = "ABCDEFGHI012345678901234567890";

void loop(void) {
  u8g2.firstPage();
  do {
    // u8g2.clearBuffer();         // clear the internal memory
    u8g2.setFont(u8g2_font_8x13_tf);  // choose a suitable font
    u8g2.drawStr(0,10,RULER_STR); // write something to the internal memory
    u8g2.drawStr(0,21,RULER_STR);  // write something to the internal memory
    u8g2.drawStr(0,32,RULER_STR);  // write something to the internal memory
    // u8g2.sendBuffer();          // transfer internal memory to the display
  } while (u8g2.nextPage());
  delay(1000);

  u8g2.firstPage();
  do {
    // u8g2.clearBuffer();         // clear the internal memory
    u8g2.setDrawColor(1);
    u8g2.drawBox(0, 0, 128, 32);
    // u8g2.sendBuffer();
  } while (u8g2.nextPage());
  delay(1000);
}


<追記:2017.10.03>

Page Buffer Modeで使う場合、do while()ループ内でu8g2.clearBuffer()、u8g2.sendBuffer()は不要とご指摘いただいたので、スケッチを修正しました。

</追記>

クラス指定を
U8G2_SSD1306_128X32_UNIVISION_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);  // Adafruit ESP8266/32u4/ARM Boards + FeatherWing OLED
に変更した(クラス名の途中のFを1にする)。描画の仕方もPage単位で「do while()」で回すように変更。
最大30720バイトのフラッシュメモリのうち、スケッチが9730バイト(31%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が616バイト(30%)を使っていて、ローカル変数で1432バイト使うことができます。
Full Bufferが1512Bなのに対して、Page Bufferだと616Bで済んでいる。

ただし、ページ単位の書き換えだと書き換えが上から下へとダラダラした描画になる。ベースマシンのシーケンサーのStep表示みたいなリアルタイム書き換えだと見栄えがよくない。

アニメーションさせようと思うと、昔のパソコンの裏VRAMやDirect Drawみたいな感じでFull Bufferを使ったほうがいいと思う。

メモ:


SSD1306はu8glibやu8g2の他にも専用のライブラリが多数公開されています。