Step01 Hello Arduboyのサンプルソース


#1

#include <Arduboy.h>

Arduboy ab;


void setup()
{
    ab.begin();
    // ab.beginNoLogo();

    ab.setFrameRate(15);
}

void loop()
{
    if(!(ab.nextFrame()))
    {
        return;
    }

    ab.clear();
    ab.setCursor(4, 9);
    ab.print(F("Hello, world!"));

    ab.display();
}

■ソースコードの場所

ファイル→スケッチの例→Arduboy→Hello World

こちらは英語のコメントを消して、インデントなどを変えてみました。
ヲレのインデントスタイルと違う!という方はぜひ変えてください。(^^;

目で追いかけているより、手を動かした方が頭に入ってくる情報量が違いますし、
こういうのは手に馴染むのに時間かかると思います。

■setup関数

Arduinoの作法とC++の説明はすっ飛ばします。

begin:
ライブラリの初期設定に必要です。

beginNoLogo:
コメントアウトしていますが、知っておいてほしいので書いてみました。
こちらはタイトルロゴの表示をしません。多少、メモリの節約になります。
beginと比べてみてください。

setFrameRate:
デフォルト60fpsですが、15fpsに変更しています。
今回のサンプルでは効果が現れません。

■loop関数

nextFrame:
次のフレームまで待ちます。必須です!ないと爆走します。

clear:
画面バッファの消去をしています。
画面のハードに対して消去しているわけではないです。

setCursor:
print:
個人的には以下の方がいいじゃないかなーと思いつつ黙って従っています。
この方式だとメニュー画面を作る時など、setCursorとprintだらけになりそうです。

ab.print(4, 9, F("Hello, world!"));

display:
今のままの説明だとつまらなすぎるのでライブラリ内部を覗いてみます。

https://github.com/Arduboy/Arduboy/blob/stable/src/Arduboy.cpp#L731

void Arduboy::display()
{
  this->paintScreen(sBuffer);
}

https://github.com/Arduboy/Arduboy/blob/stable/src/core/core.cpp#L218

void ArduboyCore::paintScreen(const unsigned char *image)
{
  for (int i = 0; i < (HEIGHT*WIDTH)/8; i++)
  {
    SPI.transfer(pgm_read_byte(image + i));
  }
}

ディスプレイへの通信にはSPIを使っています。
やっていることはそのまんまで、1024バイト(128*64/8)のバッファを転送しています。

■print再考

少し内部的なことがわかったのでもう一度printを考えてみます。
画面への書き込みは1024バイトのバッファ単位で行うことがわかりました。

そこで文字はどうやって書かれているのかというと、バッファに対して
文字の塊(バイト?ビット?)を書き込むことになります。

場所はglcdfont.cになりますが、詳しいフォーマットの話については
別のStep03でお知らせします。今はそんなものがあるのか、程度に留めてください。
https://github.com/Arduboy/Arduboy/blob/stable/src/glcdfont.c


(Ross) #2

トップのための画像はありますか? Twitterのための。

私も1を作ってみることができます。


#3

photo uploaded.
I hope this helps.

next step is button sample of Arduino IDE!


(Ross) #4

はい!ありがとうございます。


#7

はじめまして、6/10から参加の新参者ですがよろしくお願いします。

this->paintScreen(sBuffer);

ディスプレイへの通信にはSPIを使っています。
やっていることはそのまんまで、1024バイト(128*64/8)のバッファを転送しています。

ライブラリ読んだ限りだと ArduboyCore::paintScreen を実行しなければsBufferは転送されないんだと思うので、自前で SPI.transfer() するようにして画面の書き換え範囲を制限すれば(例えば左半分だけとか)残りのsBufferを何かの処理に転用できるんじゃないかなとかライブラリ読みながら考えてました。
※メモリの少なかったころの8 bit PCでVRAMをテンポラリに使うとかよくやってた
ほんとはsBuffer自体を確保しないで都度直接描画すれば最小限になるんですが、ライブラリの修正が必要そうなので難しいかなと。


#8

こちらこそよろしくお願いします。

まだまだ人の少ない日本語フォーラムですけど、
ゆっくりしていってください。
SSD1306については未開拓な部分も多いので、
面白いことをすればすぐ注目されると思います。

ところで少し前に、バッファ使わないで描画できるかどうか
調べてみたことがあります。

仕様書の34ページ目「Page addressing mode」の部分がソレっぽいです。
SSD1306のGDDRAMに書き込むのは1バイト単位。

一方、SPI通信の場合、
17、18ページ目に以下のような文章がありました。

Under serial mode, only write operations are allowed.  

簡単にいってしまうとGDDRAMに書き込みはできるけど、
読み込みはできない制約のようです。間違ってたらすみません。

たとえば以下のようなコードを書いた場合、
結果は後半のドットしか描画されないことになります。

OledDrawDot(0, 0);
OledDrawDot(0, 3);

前の書いた1バイト分(8ビット)がわからない(読み込めない)というのは
少々悩ましいところです。どう料理するか。

日本語フォントを表示したり、クイズゲームなら活用できるかもとは思いつつ、
そもそもクイズゲームでメモリが枯渇するかというと微妙ですけど・・・。

ちょっとあまり役に立たない情報でした。

自前で SPI.transfer() するようにして画面の書き換え範囲を制限

右半分をアニメーション専用にして
pc98の魔導物語ARSを再現・・・げふんげふん(汗。


#9

資料ありがとうございます。
SSD1306.pdf
の資料はちょっと読んでました、ハードウェアスクロールもできるような記載があるのでもうちょっと読み込んでみます。

たとえば以下のようなコードを書いた場合、
結果は後半のドットしか描画されないことになります。

制約が色々ありそうですね。

sBuffer 使わない直接描画はスプライトエンジンとか一通り作るのが面倒くさそう(苦笑)なので、まずはsBufferの確保を64x64 (左右を2回で描画) とか 128x32 (上下を2回で描画) とかでメモリ節約できないかライブラリを修正して試してみます。
上手く動作するようなら正式に取り込んでもらえるか交渉してみようかと。


#10

ライブラリの修正、よろしくお願いします。

こちらの資料も良さそうです。


(hisadg) #11

こんにちわです!

ハードウェアスクロールは以前にフォーラムで投稿がありました。ご参考まで!

http://community.arduboy.com/t/mega-man-greyscale-scrolling-demo-with-music/156/16


#12

情報ありがとうございます。
スクロール中の処理に制約があるみたいなので難しそうですね
いろいろ試してみます。


#13

SSD1306にドライバつかわずに直接描画するテストしてる記事見つけました、
ソース読んだ感じだとそんなに苦労せず描けるみたいなので、いろいろ夢が広がります(笑)

▼I2C極小OLED(有機EL)SSD1306をArduinoでライブラリを使わずに動作させてみました
https://www.mgo-tec.com/blog-entry-31.html

あと、漢字表示とか。

▼OLED ( 有機EL ) SSD1306 に16×16ドットのフリーの日本語漢字、東雲フォントを表示させてみました
https://www.mgo-tec.com/blog-entry-ssd1306-shinonome.html


#14
#include <Arduboy.h>
#include <SPI.h>

Arduboy ab;

// page 0-7, col 0-127, dat 0x00-0xff
void draw(uint8_t page, uint8_t col, uint8_t dat)
{
    ab.LCDCommandMode();
    SPI.transfer(0x22);
    SPI.transfer(page);
    SPI.transfer(7);

    SPI.transfer(0x21);
    SPI.transfer(col);
    SPI.transfer(127);

    ab.LCDDataMode();
    SPI.transfer(dat);
}

void cls()
{
  for(uint8_t page=0; page<=7; page++)
  {
      for(uint8_t col=0; col<=127; col++)
      {
          draw(page, col, 0x00);
      }
  }
}

void setup()
{
    ab.beginNoLogo();
    ab.setFrameRate(30);

    cls();

    draw(0, 0, 0xff);
    draw(0, 2, 0xf0);
    draw(0, 3, 0x0f);

    draw(0, 127, 0xff);
    draw(7, 127, 0xff);
}

void loop()
{
    if(!(ab.nextFrame()))
    {
        return;
    }

    // EMPTY
}

適当に作ったのでもっといい感じにできるかもしれないです。


#15

OLEDのドキュメント読んでるだけだとピンとこなかったので参考になります、ありがとうございます。
ソースおかりしてスクロールコマンド追加してみました、単にスクロールさせるだけなら簡単なようです。

#include <Arduboy.h>
#include <SPI.h>

Arduboy ab;

// page 0-7, col 0-127, dat 0x00-0xff
void draw(uint8_t page, uint8_t col, uint8_t dat)
{
    ab.LCDCommandMode();
    SPI.transfer(0x22);
    SPI.transfer(page);
    SPI.transfer(7);

    SPI.transfer(0x21);
    SPI.transfer(col);
    SPI.transfer(127);

    ab.LCDDataMode();
    SPI.transfer(dat);
}

void cls()
{
  for(uint8_t page=0; page<=7; page++)
  {
      for(uint8_t col=0; col<=127; col++)
      {
          draw(page, col, 0x00);
      }
  }
}

void setup()
{
    ab.beginNoLogo();
    ab.setFrameRate(30);

    cls();

    draw(0, 0, 0xff);
    draw(0, 2, 0xf0);
    draw(0, 3, 0x0f);

    draw(0, 127, 0xff);
    draw(7, 127, 0xff);

    //Horizontal scrolling
    //http://www.eimodule.com/download/SSD1306-OLED-Controller.pdf
    //46 page
    ab.LCDCommandMode();
    SPI.transfer(0x27);//Left horizontal scroll
    SPI.transfer(0x00);//Dummy byte
    SPI.transfer(0x00);//Define PAGE0 as startpage address
    SPI.transfer(0x00);//Set time interval between each scrolls sleep as 5 frames
    SPI.transfer(0x07);//Define PAGE7 as startpage address
    SPI.transfer(0x00);//Dummy byte
    SPI.transfer(0xff);//Dummy byte
    SPI.transfer(0x2f);//Activate scrolling    
}

void loop()
{
    if(!(ab.nextFrame()))
    {
        return;
    }

    // EMPTY
}