« mbed Get | トップページ | mbedでNMEA-0183 Libraryを動かす »

mbedのライブラリ作成

mbedを使って、眠っていたeDISP用のライブラリを作ってみました。mbedのライブラリはArduinoと同様にC++のクラスとして作成します。今回改めてC++のクラスや継承の概念をお勉強し直しました。クラスの初期化方法など、分かったことを含めて記載します。加えて、mbed IDEを使ったユーザーライブラリの管理方法についても記載します。

eDISPについて

eDISPは携帯電話用LCDのリユースプロダクトで以下の特徴があります:

  • 程よい大きさと解像度(320x240)
  • MCUとの接続はシリアル(UART)となっておりシンプル
  • LCD本体は5V駆動ですが、ロジックレベルは3.3Vなのでmbed(ARM)との接続にぴったり
  • グラフィック機能(矩形・ライン描画)をサポート(エスケープシーケンスを使ったコマンドを使用)
  • 漢字ROMを内蔵(文字コードはSJIS/Unicodeに対応) しており日本語対応が容易
  • 消費電力は250mAとちょっと大きめ(バックライトが若干暖かくなる)

ライブラリの雛形

mbedのCookbookで公開されているTextLCDを雛形にして、コンストラクターなどクラスライブラリに必要な部分を実装しました。TextLCDはStreamクラス(抽象クラス)を継承しており、Streamクラスで純粋仮想関数となっている_putc()メンバー関数をLCDにあわせて作成することで文字出力を実現します。

即ち、_putc()関数をターゲットハードにあわせて作ってしまえば、上位のprintf()関数が自動的に使用可能となります。このあたりがオブジェクト指向の便利なところですね。

ライブラリの機能とコード

作成したライブラリのコードと、サンプルプログラムをmbedの個人スペース(Notebook)においてあります。
eDispライブラリのコード

ライブラリの機能は以下です:

  • locate: カーソルを指定位置に移動
  • textColor: 表示文字色を指定(8色)
  • backgroundColor: 文字の背景色を指定
  • cls: テキスト画面の消去
  • printf: 指定した書式で文字列を表示(親クラスから継承しているためコーディングはなし)
  • reset: テキスト画面の消去と色指定のリセット
  • fillRect: 矩形の描画と塗りつぶし
  • drawLine: 線の描画
  • eDisp: コンストラクター(シリアルポートのピン番号とボーレートを指定)

ライブラリの本体部分(eDisp.cpp)のコードを以下に示します。

/* mbed eDISP Library
 * Copyright (c) 2010 todotani
 * Version 0.1 (March 6, 2010)
 * Released under the MIT License: http://mbed.org/license/mit
 */

#include "eDisp.h"
#include "error.h"

using namespace mbed;

eDisp::eDisp(PinName tx, PinName rx, int baud, int columns, int rows)
     :_serial(tx, rx), _columns(columns), _rows(rows) {
    _serial.baud(baud);
    cls();
}

int eDisp::_putc(int value) {
    if(value == '\n') {
        newline();
    } else {
        _serial.putc(value);
    }
    return value;
}

int eDisp::_getc() {
    return 0;
}

void eDisp::newline() {
    _column = 0;
    _row++;
    if (_row >= _rows) {
        _row = 0;
    }
    locate(_column, _row);
}

void eDisp::locate(int column, int row) {
    if (column < 0 || column >= _columns || row < 0 || row >= _rows) {
        error("locate(%d,%d) out of range on %dx%d display", column, row, _columns, _rows);
        return;
    }

    _row = row;
    _column = column;
    _serial.printf("\x1B[%d;%dH", _row, _column);
}

void eDisp::cls() {
    locate(0, 0);
    _serial.printf("\x1B[J");
}

void eDisp::reset() {
    _serial.printf("\x1B[m");
    cls();
}

void eDisp::textColor (int color) {
    _serial.printf("\x1B[%dm", color);
}

void eDisp::backgroundColor (int color) {
    _serial.printf("\x1B[%dm", color + 10);
}

void eDisp::fillRect (int buffer, int width, int hight, int x, int y, int color ) {
    _serial.printf("\x1B@0;%d;%d;%d;%d;%d;%dz", buffer, width, hight, x, y, color);
}

void eDisp::drawLine (int buffer, int x0, int y0, int x1, int y1, int color) {
    _serial.printf("\x1B@2;%d;%d;%d;%d;%d;%dz", buffer, x0, y0, x1, y1, color);
}

13行目のコンストラクター初期化子で、シリアルポートを制御するためのSerialクラスのインスタンス生成を行っています(_serial(tx, rx)の部分)。この部分が今回お勉強した点でした。

Serialクラスのインスタンス生成を、以下のようにコンストラクターの本体部分で行うとコンパイルエラーになります。Arduinoでも同様のエラーに遭遇したことがありました。

eDisp::eDisp(PinName tx, PinName rx, int baud, int columns, int rows)
     :_columns(columns), _rows(rows) {
     _serial = Serial(tx, rx);
    _serial.baud(baud);
}

/*上記のコードは以下のエラーとなる:
    No default constructor exists for class "mbed::Serial" (E291)
*/

エラーとなる理由は以下と思われます。ヘッダファイル(eDisp.h)にて;

protected:
    Serial _serial; // まだインスタンスを起こしていない

と定義しているため、コンストラクターの先頭で引数なしのデフォルトコンストラクターを実行しようとするが、定義されていないということでしょうか。(引数付きのコンストラクターを定義しているため、コンパイラがデフォルトコンストラクターを自動的に生成しない)

そこで、初期化子リストを使用して;

eDisp::eDisp(PinName tx, PinName rx, int baud, int columns, int rows)
     :_serial(tx, rx), _columns(columns), _rows(rows)

と指定すると、引数付きのSerialクラスコンストラクターが呼ばれて、インスタンス_serialが一発で初期化できるということだと思います。

エスケープシーケンス付きのコマンドはSerialクラスに対してprintf関数で文字列を送り込むことによって実現しています。printf()を使用することでコードサイズが大きくなりますが(sampleのbinファイルで27Kbyte)、flash ROM容量が512KBもありますのでここは気にせずにイケイケで、、

動作イメージ

サンプルプログラムを動かした際の写真を以下に示します。

fillRect()を使って16色の帯を表示。

eDispLib_test1 


drawLineを使った線の表示。

eDispLib_test2


漢字の表示。

eDispLib_test3

printfで漢字を指定するだけで、あっけなく表示できました。ライブラリの基本部分は既にUnicode対応なんですね。漢字対応のコーディングが必要だと思っていたのでちょっと拍子抜けです(嬉しい誤算ですが)。

ただ、IDEのマルチバイト対応はまだ不完全で、ソースに漢字を埋め込んだ後、IDEを一旦閉じて開き直すと漢字が文字化けしてしまいます(入力もIMEで直接できず、コピペで流し込む必要あり)。あと、コンパイル時に"Invalid multibyte character sequence"というワーニングが大量に発生します。

作成したライブラリの再利用

せっかく作ったライブラリなので、どこか共通の場所に保管して今後のプロジェクトで再利用したいものですが、ユーザーが作成したカスタムライブラリの管理については、まだ十分でないように思えます。

現状(2010/3/7時点)サポートされている機能は作成したコードをNotebookにPublishすることです。Publish機能を使ってライブラリとして再利用するまでの流れを以下に示します:

  1. ライブラリ作成のプロジェクトを起こし(library.h, libraly.cpp, main.cppを作成する)、ライブラリを試験する
  2. 出来上がったライブラリのコードのみ(library.h, library.cpp)を登録したダミープロジェクトを作ってpublish。
    ちなみに、Publish時に"Add to my public mbed profile"をチェックするとNotebookの内容が公開され、mbed.orgトップ画面のRecent Activityに投稿があったことが表示されます
  3. 新規プロジェクトのImport LibraryメニューからNotebookのurlを指定しインポート

mbed_Lib 

Publish自体はコンパイラの画面からプロジェクト指定と右クリック一発でできるため非常に簡単ですが、問題なのはバージョン管理の機能がなく、修正版をpublishすると別のプロジェクトとしてNotebookに登録されてしまうことです。バージョン管理機能については検討中とForumにあります。

その他

eDISPはコマンドを受信するのみで、レスポンスは返しません。そのため、画面全体を塗りつぶすように処理時間がかかるコマンドをノーウエイトで連続して投げつけると表示が乱れてしまいます(描画完了前に次のコマンドを受信すると、処理中の描画を中断して次のコマンド実行を始めるように見えます)。そのため、連続描画を行う場合はプログラム側でwaitを入れるなどの対応が必要です。

« mbed Get | トップページ | mbedでNMEA-0183 Libraryを動かす »

mbed」カテゴリの記事

コメント

こんにちは、mbedに関する内容で
勉強させていただいています。m(._.)m

edispのmbedライブラリを使用したいと思っているんですが、リンク先のpublished programsに無いようです。
もう一度公開していただけないでしょうか?
宜しくお願いします。

あらら、確かに見えなくなっていますね。Published Programs(http://mbed.org/users/todotani/programs/)に上げておきましたのでご確認下さい。

返事遅れてすいません。
公開ありがとうございました!

この記事へのコメントは終了しました。

« mbed Get | トップページ | mbedでNMEA-0183 Libraryを動かす »

2018年10月
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31      
無料ブログはココログ