Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

C#でラズパイに接続したOLEDディスプレイに文字を描こう

C#ではじめるラズパイIoTプログラミング 第4回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2019/10/30 11:00

 C#と言えば、Windows環境だけと思われがちですが、.Net CoreをLinux環境にインストールすれば、C#で開発したアプリケーションが動作します。また、Raspberry Pi(ラズベリーパイ)でも、C#を使ってIoT開発を行うことができます。この連載では、C#によるIoTプログラミングを解説します。

目次

対象読者

 IoTに興味があり、C#とRaspberry Pi(Linux環境)の基本的な知識がある方を対象とします。Linuxや電子工作の初歩的な説明は割愛していますので、「Raspberry Piをつかったセンサープログラミング超入門」の記事なども併せて参照してください。

はじめに

 前回は、I2C接続でのOLEDディスプレイモジュールの基本的な制御を紹介しました。今回は、ディスプレイに直線や文字を表示してみましょう。

OLEDディスプレイの表示手法

 OLEDディスプレイモジュールの画面コントローラーSSD1306の表示仕様は、やや特殊なので、今回はこの仕様をあまり意識せずに、ドット単位の描画やテキスト表示を行う方法を解説します。

ダブルバッファリング

 前回説明したように、画面コントローラーのSSD1306では、画面データを縦方向に1バイト(8ビット)単位で、送信する必要があります。単純に0のデータを送信するなら、あまり問題ありませんが、グラフや文字などをドット単位で自由に表示したい場合、その都度1バイト単位のデータを作成するのは困難です。

 そのため、直接画像データを転送するのではなく、メモリ上の領域(画面バッファ)を介して画像データを送信するようにします。処理の流れは、次のようになります。

  1. 画面の解像度と同じビット数の仮想的な領域を、画面バッファとしてメモリ上に作成
  2. 画面バッファに対して、(仮想的に)描画
  3. 画面バッファのデータを、画像データとしてまるごと送信

 こうすれば、画面データの送信仕様に関係なく、自由に画像データが作成できます。

 ちなみにこのように、直接描画メモリに描画(送信)するのではなく、いったん別のメモリ領域に描画を行い、すべて書き終わった後にデータを転送する手法を「ダブルバッファリング」と呼びます。

BitArrayによる画面バッファ

 画面バッファとなるメモリ上の領域は、System.Collections.BitArrayクラスを利用することにしました。BitArrayは、ビット値をブール型の配列として管理するクラスです。

 前回作成したOledSsd1306クラスに、次のように、画面の解像度(横128ドット縦64ドット)と同じビット数のBitArrayをBitbufferという名前で追加します。ディスプレイモジュールの画面サイズは、他のクラスやコンストラクタでも参照できるように、public staticとして定義しています。

[リスト1]Program.csの一部
public class OledSsd1306
{
    // 画面サイズ
    public static int Width { get; } = 128;
    public static int Height { get; } = 64;
    public static int Unit { get; } = 8;

    // 画面バッファ
    private readonly BitArray Bitbuffer = new BitArray(Width * Height);

~略~

 画面バッファへの描画は、このBitArrayのビットを1にすることになります。ビットを1にするには、BitArrayクラスのSetメソッド、ビットを読み出すにはGetメソッドを用います。

 では、画面バッファに1ビットを読み書きするメソッドを定義しましょう。

[リスト2]Program.csの一部
// 画面バッファに1ビット書き込む
public void SetPixel(int x, int y, bool b = true)
{
    Bitbuffer.Set(x + y * Width, b);
}

// 画面バッファから1ビット読み出す
public bool GetPixel(int x, int y)
{
    return Bitbuffer.Get(x + y * Width);
}

// 画面バッファをすべて0(false)か1(true)にする(1)
public void SetAll(bool b = false)
{
    Bitbuffer.SetAll(b);
}

 BitArrayは、縦も横もない単なる配列ですが、メソッドの引数に縦(0~63)と横(0~127)の位置(座標)を指定して、該当のビットを操作できるようにしました。

 また、画面バッファをすべて0か1にするメソッドも作成します。こちらはBitArrayのSetAllメソッドを呼び出すだけです(1)。

 なお、すべてのメソッドで引数の範囲チェックなどは省略しています。必要に応じて追加してください。

画面バッファの送信

 次に画面バッファの送信、SendBufferメソッドを定義しましょう。画面バッファから、縦方向に8ビット分読み出し(1)、それを1バイトとして送信します。1バイトデータの組み立ては、1を縦の位置に応じてビットシフトした値をORして作成します(2)。

[リスト3]Program.csの一部
// 画面バッファのデータを送信する
public void SendBuffer()
{
    for (int p = 0; p < Height / Unit; p++)
    {
        for (int x = 0; x < Width; x++)
        {
            byte b = 0;
            for (int y = 0; y < Unit; y++)      // 縦に8ビット読み出す(1)
            {
                if (GetPixel(x, y + p * Unit))  // ビット値を読み出す
                {
                    b |= (byte)(1 << y);        // 1バイトデータを組み立てる(2)
                }
            }
            SendData(b);
        }
    }
}

 BitArrayの初期値はすべてfalseなので、画面バッファの初期値はすべて0となります。そのため、OledSsd1306クラスのインスタンス生成後、すぐにSendBufferメソッドを呼び出せば、画面クリアになります。

var oled = new OledSsd1306(fd1);
oled.SendBuffer(); // 画面バッファの送信(初期値0なので、画面クリアになる)

直線(ライン)の描画

 1ビット(ドット)単位での描画ができましたので、このメソッドを使えば、直線や文字などの描画も可能です。まずは、2つの点の間を直線で描くメソッドを作成してみましょう。

 コンピューターグラフィックスでの2点間の直線を描く方法は、DDA(Digital Differential Algorithm)やブレゼンハムのアルゴリズムが有名です(DDAとブレゼンハムのアルゴリズムとの違い)。

 上記のDDAを参考にメソッドを作成すると、次のようになります。

[リスト4]Program.csの一部
// 画面バッファに、始点(x0, y0)~終点(x1, y1)間の直線を描く
public void LineDDA(int x0, int y0, int x1, int y1)
{
    var dx = x1 - x0;
    var dy = y1 - y0;
    float steps = Math.Max(Math.Abs(dx), Math.Abs(dy));

    float xinc = dx / steps;
    float yinc = dy / steps;
    float x = x0, y = y0;
    for (int i = 0; i <= steps; i++)
    {
        SetPixel((int)Math.Round(x), (int)Math.Round(y));
        x += xinc;
        y += yinc;
    }
}

 この直線のメソッドを利用すると、次のような直線の描画も簡単です。

[リスト5]Program.csの一部
// 直線のアニメーション
for (int i = 0; i < OledSsd1306.Width; i += 8)
{
    oled.LineDDA(i, 0, OledSsd1306.Width -1 - i, OledSsd1306.Height-1);  // 直線の描画
    oled.SendBuffer();                                                   // 画面データ送信
    Thread.Sleep(300);
}
直線のサンプルプログラムの実行結果
直線のサンプルプログラムの実行結果

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 高江 賢(タカエ ケン)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

バックナンバー

連載:C#ではじめるラズパイIoTプログラミング
All contents copyright © 2005-2019 Shoeisha Co., Ltd. All rights reserved. ver.1.5