目次 [ Contents ]
前回ではu8glibでサンプルスケッチを動かしてみるところまでやってみましたが、今回は実際に自分で描画していきたいと思います。
配線
今回はI2Cのディスプレイを元に説明します。SPIの場合は前回を参照してください。また、可変抵抗器も使ってみます。
基本
まずはサンプルスケッチ「HelloWorld」を開いてみます。例によって自分の設定をコメントアウトから外しArduinoに流し込めば、ディスプレイにまんま「Hello World」と出てきます。
そこそこ長いスケッチに見えますが、前回説明した設定のコメントアウト、諸々の関数などを整理して必要な要素に絞ると下記のようになります。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); void setup(void) { u8g.setColorIndex(1); // pixel on } void loop(void) { u8g.firstPage(); do { u8g.setFont(u8g_font_unifont); u8g.drawStr( 0, 22, "Hello World!"); } while ( u8g.nextPage() ); }
たったこれだけ。シンプルです。
setup内に書かれているu8g.setColorIndex(1)は描画の色指定です。詳しくは後述します。
大切なのはloop内。
u8g.firstPage()とu8g.nextPage()
loop内では最初にu8g.first.Page()という関数があります。これは「今から描きまっせ」という合図です(と僕は認識しています)。
その後do~whole文に入りますが、この中に実際描きたい命令文を描いていきます。このサンプルでは文字表示の命令をしています。
文字の描画に関してはまた次回説明しますが、簡単にいうとset.Fontでフォントの指定、drawStrではxy座標の指定と表示したい文言を入れているだけです。
その後whileを抜け出す条件としてu8g.nextPage()が出てきます。これは「もう描き終わったか」かどうか判断しています。つまりI2Cなり、SPIで描画に関するデータのやり取りが行われて、まだ描き終わっていなければ、この関数が「まだダメ」と返事し、また括弧の中の描画命令を繰り返すわけです。
u8glibライブラリをインクルード 使う設定を有効にする u8g.firstPage(); do { この中に描画したいことをかく } while ( u8g.nextPage() );
この仕組みが分かれば難しくはありません。後は何を描くかだけです。
図形描画のための関数
u8glibではグラフィックを描くのに使う関数が結構用意されていますが、いくつか基本的なものを紹介します。
図形を描くには基本的に横(x)と縦(y)の座標を指定します。
点を描く
u8g.drawPixel(x座標, y座標);
一番簡単な描画、縦と横の位置を指定するだけです。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); void setup(void) { u8g.setColorIndex(1); // pixel on } void loop(void) { u8g.firstPage(); do { u8g.drawpixel(63, 31); } while ( u8g.nextPage() ); }
真ん中にポツンと点が描かれますが、認識しづらいかもしれません。
線を描く
u8g.drawLine(始点x座標, 始点y座標, 終点x座標, 終点y座標);
u8g.drawLine(0, 31, 127, 31);
線を描くには始まりのxy座標と終わりのxy座標を指定します。始点と終点をずらせば斜線も描けます。また始点終点は逆でも大丈夫です。
u8g.drawLine(127, 63, 0, 0);
四角を描く
u8g.drawFrame(始点x座標, 始点y座標, 長さ, 高さ);
u8g.drawFrame(31, 15, 64, 32);
四角で塗りつぶす
u8g.drawBox(始点x座標, 始点y座標, 長さ, 高さ);
u8g.drawBox(31, 15, 64, 32);
引数の後半は座標ではなく、大きさ(Pixel数)です。線の描き方と指定方法が違うのにご注意。
丸を描く
u8g.drawEllipse(始点x座標, 始点y座標, 横幅, 縦幅);
u8g.drawEllipse(63, 31, 20, 20);
丸で塗りつぶす
drawFilledEllipse(始点x座標, 始点y座標, 横幅, 縦幅);
u8g.drawFilledEllipse(63, 31, 20, 20);
横幅と縦幅の値が違うと楕円になります。
他にも図形の描画に関しての関数は多数あります。こちらに詳しく載っているので気になる方は調べてください(英語ですが…)。
やってみて分かったと思いますが、u8glibは1回の描画ごとにまっさらから描いています。画面のクリアみたいなのは必要ありません。でも、場合によっては一部のピクセルを消したいこともあるかもしれません。
u8g.setColorIndex()
上記で説明を省略したColorIndexですが、要は白で描くか黒で描くかという話です。サンプルを見てもらうほうが早いので、こちらを。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); void setup(void) {} void loop(void) { u8g.firstPage(); do { u8g.setColorIndex(1); // pixel on u8g.drawBox(9, 4, 109, 55); // 四角 u8g.setColorIndex(0); // pixel off u8g.drawFilledEllipse(63, 31, 15, 15); // 丸 u8g.setColorIndex(1); // pixel on } while ( u8g.nextPage() ); }
白(ON)で四角を書いた後、黒(OFF)で丸を描くのでこうなります。
組み合わせることで、いろんな形を作れると思います。
ピクセルをオフにしたことを忘れて絵が出ないなんてことがよくあります。基本的に画面は黒なんで、間違えのないようオフの後はオンに戻しておくのが無難です。
可変抵抗の値から図形を動かす
基本的な図形の描き方は分かりました。で、これを利用して、可変抵抗器の値で図形が動くようにしてみます。
「可変抵抗器に反応するメータ」スケッチ
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); byte val = map(vol_val, 0, VMAX, 0, 127); u8g.firstPage(); do { u8g.drawBox(0, 28, val, 5); } while (u8g.nextPage()); }
VMAXはこの可変抵抗器読み取り方法での誤差用定数なので、自分用に調整してください。詳細はこちらで説明しています。
可変抵抗器の値を0~127の値に収めるのにmap関数を使っています。これをそのまま図形描画の座標値として当てはめるだけで動かすことが可能になります。
u8glibの問題点
可変抵抗器の反応がもっさり感じるかもしれせん。というか鈍いです。でもこれはu8glibの構造的な問題です。具体的に言うとdo~whileが意外に長く繰り返されているので、次のanalogReadまでタイムラグができるからです。
でも、これは裏技的に回避する方法もあります。
#include "U8glib.h" //U8GLIB_SSD1306_128X64 u8g(4, 5, 6, 7); U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_DEV_0 | U8G_I2C_OPT_NO_ACK | U8G_I2C_OPT_FAST); #define VOL A0 #define VMAX 1013 int vol_val = 0; void setup() { pinMode(VOL, INPUT); u8g.setColorIndex(1); } void loop() { u8g.firstPage(); do { vol_val = 0.9 * vol_val + 0.1 * analogRead(VOL); byte val = map(vol_val, 0, VMAX, 0, 127); u8g.drawBox(0, 28, val, 5); } while (u8g.nextPage()); }
「描画ループ中にアナログ読み取りも組み込んでしまえ」という強引な方法です。これはこれでまた別の問題を孕んでくるので完全な解決策ではないですけど、「こういう方法もあるんだな」くらいで覚えておくと良いかもしれないです。
次回は文字、数値の表示について書いてみたいと思います。