« 2010年2月 | トップページ | 2010年4月 »

2010年3月の記事

mbedでNMEA-0183 Libraryを動かす

NMEA-0183メッセージの解析」で書いた通り、さてこれからSTM32 Primer2でGPSモジュールを動かすぞ!と意気込んだ矢先に電源ショートでGPSモジュールがお亡くなりになっていました。スイッチサイエンスさんからの取り寄せで、めでたく(?)GPSモジュールが復活したため、NMEA Libraryを使って実際にGPSモジュールが出力するNMEA-0183メッセージの解析を行ってみました。

ただ3月初めからmbedにはまっており、STM32 Primer2でなく、mbedで動かしています。使用したライブラリは、前回記事と同様のNMEA Libraryです。

■ mbedプロジェクトへの外部ライブラリコードの取り込み

以下のキャプチャー画面に示すように、NMEA Libraryのコードを格納するフォルダー(nmea)を作成して、ライブラリファイルを格納します。

mbed_NMEA_project

xxx.hファイルはImport Fileメニューから取り込めるのですが、xxx.cファイルはImport Fileで取り込めないため(x.cppは対応)、空のファイルを作ってコピペでインポートしました。

■ コーディングとコンパイル

main.cppとgps.hのみが今回コーディングした部分になります。ソース一式はmbedのwebページに格納してありますので、こちらを参照してください。

前回も書きましたが、NMEA Libraryはメッセージの行単位に依存せず、一定の文字数単位でパーサーにデーターを渡して解析ができます。今回のプログラムでは、シリアル接続したGPSモジュールからデーターを受信する毎に割り込み処理でバッファーに受信データーを蓄積し、バッファー長が200Byteを超えた時点でパーサーを呼び出します。

パーサーを呼び出す瞬間もデーターを受信していますので、バッファを2面用意して、割り込み停止中にバッファー面の切り替えを行っています(いわゆるダブル・バッファー)。割り込みの停止はmbedのSerialライブラリ関数呼び出しだけで行っています。そのため、割り込みハンドラーの登録機能を使い、過渡的にハンドラーのポインターをnullに書き換えることで割り込みON/OFFの制御を行っています。このあたりの制御は、通常コントロールレジスターの割り込み許可フラグクリアクリアで行いますが、今回は手っ取り早く開発することを優先し(LPC1768のデーターシートを読むのが面倒で、、)、標準ライブラリの機能で実装しています。

コンパイルしてみると、NMEA Libraryの部分は、殆ど無修正でコンパイルが通りました。直したのは、malloc関数の戻り値をポインターに代入するコードが型の不一致でエラーになったため、キャストを追加しただけです。当初VC++でコンパイルした際は全くエラーがなかったので、mbedのコンパイラの方が型チェックを厳しく行っているのでしょうか。

メッセージの解析が完了すると、nmeaINFO info構造体に結果が格納されます。構造体からのデーターの取り出しは前回の記事に示した通りです。

パーサーの動作とは直接関係がありませんが、ブレッドボード上でmbedとGPSモジュールを隣あわせに配置すると、ノイズのせいかGPSの受信感度が低下しました。そのため、両者の距離を離して配置しています。mbedのクロック周波数を47MHzに下げるとさらに感度が改善できました(厳密な比較はできていませんが)。MACチップのクロックもノイズ源だと思うため、ついでにMACチップの停止も行っています。クロック周波数の変更とMACチップの停止もmbed.orgのNotebook(blog相当)で公開されているmbed Clock Controlmbed Power Controlを使用しました。

■ 動作イメージ

今回作成したプログラムは、以下の2つの出力に対応しています。

1) USBシリアルに以下のような結果を出力(gps.hの#define SERIAL_OUTPUTをuncommentする)

Trace: $GPGSA,A,2,29,18,22,,,,,,,,,,5.0,4.8,1.0*39
Trace: $GPGSV,3,1,11,22,45,225,32,18,24,186,34,29,16,148,32,30,82,102,*73
Trace: $GPGSV,3,2,11,14,59,327,,05,53,049,,01,47,321,31,12,47,048,*75
9140, Lat: 35.xxxxxx, Lon: 139.xxxxxx, Sig:1, Fix:2, Inuse:3
  sat_id:22, sig:32, Inuse:1
  sat_id:18, sig:34, Inuse:1
  sat_id:29, sig:32, Inuse:1
  sat_id:01, sig:31, Inuse:0

2) 「mbedのライブラリ作成」で使用したeDISPライブラリを使ってLCDに結果を表示する(SERIAL_OUTPUTをcomment outする)。表示イメージを以下に示します。

mbed_GPSr

■ mbed開発環境の感想

まだ2週間程度ですが、mbed開発環境を使ってみた感想は以下です。

利点:

  • お手軽さでは、Arduino以上。組み込みプログラミングやC++の心得のある方なら、箱から取り出してから数時間でそこそこの作品ができてしまいます
  • mbedの開発環境はいわゆるSaaSというヤツで、クラウド上に開発環境があり、ローカルPCに開発環境を構築する必要がありません。開発環境構築やメンテの工数が不要というのは、非常に便利なことがよく分かりました。これまで、Google Appのようなクラウド/SaaS系のサービスは、ローカルアプリに比べて使い勝手で劣るという先入観があり使ったことがなかったのですが、mbedでクラウドの可能性を実感した次第です
  • コンパイラ画面からワンアクションでコードを一般公開できます。また、Notebookと呼ばれるBlog機能もセットになっており、成果やノウハウをコミュニティー内で共有できるようになっている。クラウド開発環境に加えてこちらも興味深い試みだと思います
  • 今回のように、出来合いのライブラリを組み合わせて、ちょこちょこっと作品を作る用途では最強かも。ArduinoだとSRAM容量の制約にぶち当たることもありますが(328でNMEA Libraryは動かない)、mbedなら問題なしです

欠点:

  • ドキュメント類がまだ貧弱なため、ライブラリの挙動が分かりづらい
  • デバック環境が貧弱。というか、ないに等しいですが、今回のように出来合いライブラリの組み合わせでは特に問題にはなりませんでした
  • リンカーのメモリー割付結果が分からないため、ヒープをどの程度使っているかなど、メモリーの使用状況が分からない
  • コアライブラリのソースが非公開など、一部オープンでない部分がある。コアライブラリのソースが公開の理由は、APIのアーキを早く固めることが優先事項のためらしい。今オープン化してAPIのサブバージョンが一杯できるのを懸念しているのかな。将来的にはオープン化すると言ってはおりますが

メモリ管理の自由度が弱点と書きましたが、ヒープポインターをプログラムから取得することや、ペリフェラル用の16K + 16K SRAM領域をバッファとして使うことは、mbed開発環境上でもできそうなため、別途調べてみようと思います。

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をゲットいたしました。通販でもよかったのですが、秋葉原まで出向いて秋月さんで購入。使ってみた感想を記載します。

セットアップ

秋月さんらしく、箱を開けると日本語のセットアップガイドが出てきます。秋月さんで部品を買うとついてくる紙にコピーした手作りマニュアルで、裏面はLPCXpressoのマニュアルと兼ねていました。

セットアップは以下のように非常に簡単です:

  • mbedをUSBケーブルでPCにつなぐとリムーバブルディスクとして認識される
  • MBED.HTMを開く
  • Sign upからアカウントを登録する

これだけでコンパイラが使えるようになります。また、コンパイル済みのHelloWorld.binをダウンロードしてリムーバブルディスクとして認識しているmbedにコピーし、リセットボタンを押すとLEDチカチカが動き出します。

開発環境のセットアップが不要なため、ここまでは最速ですね、確かに。

TextLCDを使ってみる

開発環境の習熟用に何か動かしてみようと、mbed本体と一緒に16x2 LCDモジュール(SD1602VBWB)を購入。このLCDは端子がシングル・インラインのため、ブレッドボードに挿して使うのにもってこいです。

LCDに文字を表示させるライブラリとして、ArduinoのLiquidCrystal Library相当であるTextLCD Libraryがあります。DigitalIn, AnalogInのような標準ライブラリはプロジェクトを生成すると自動的に使えるようになっていますが、TextLCDはcontributed libraryの位置づけとなり、個々にインポートする必要があります。IDEにライブラリをインポートした画面キャプチャーを以下に示します。

mbedIDE

TextLCD Libraryを使ったサンプルコードを以下に示します。

#include "mbed.h"
#include "TextLCD.h"

TextLCD lcd(p24, p25, p26, p27, p28, p29, p30); // RS, R/W, E, DB4, DB5, DB6, DB7
BusOut myleds(LED1, LED2, LED3, LED4);

char msg1[] = "Hello World!";
char msg2[] = "Hello mbed!";

void showMsg(char *msg);
unsigned int count = 0;

int main() 
{
    while (1) 
    {
        lcd.cls();
        lcd.locate(0, 0);
        showMsg(msg1);
        wait(1);
        lcd.locate(0, 1);
        showMsg(msg2);
        wait(1);
    }
}

void showMsg(char *msg)
{
    int i;

    for (i = 0; i < strlen(msg); i++) 
    {
        lcd.printf("%c", msg[i]);
        myleds = count++;
        wait(0.1);
    }
}

プログラムがmain()で始まるのは(Arudinoのsetup(), loop()でなく)、ある意味ほっとした気持ち(?)になります。Text LCDはしばらく触っていなかったため、一点はまりました。mbed.orgのサンプルには以下のコメントがついています。

TextLCD lcd(p24, p25, p26, p27, p28, p29, p30); // rs, rw, e, d0, d1, d2, d3

d0~d3という記載を見て、データーバスをLCDのDB0~DB3につないでしまい、最初はLCDがうんともすんとも言わない状態でした。よく考えてみると、このライブラリはLCDを4-bitモードで使っているため、LCDのデーターバスはDB4~DB7を使う必要があります。Webドキュメントの配線図も確かにそうなっていますが、ちゃんと見ていませんでした、、、

気を取り直して配線を修正すると以下のようにしっかりと動作してくれます。

mbed_LCD

ArduinoのLiquidCrystal Library(0017で強化された)に比べると機能は最小限ですが、LCDの初期化はちゃんと行っており、過去のArduinoライブラリのようにLCDの初期化が不十分という問題はありません。

あと、ArduinoのI/Oにはない機能として、BusOutクラスをあわせて使ってみました。BusOutは複数の出力ピンをまとめてバスとして使えるようにする機能です。サンプルではLED1~LED4の制御ピン4 bitを使って、LEDを4個を同時に制御します(34行目)。

ちなみにこのLCD、「おしゃれな白抜きタイプ」ということですが、あまり視認性はよくありませんでした。スタンダードな黒表示のSD1602HUOB(-XA-G-R)の方が見やすいです(価格は100円高いです)。

Cの標準ライブラリサポート

TextLCDのサンプルでstrlen()関数を使っていますが、string.hをインクルードしなくてもエラーになりません。理由は、mbed.hで以下の標準ライブラリのヘッダをインクルードしているためです。すなわち、以下のヘッダファイルでサポートしている標準関数が使えることになります。

// Useful C libraries
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>

mbed上のbinファイルの管理

リムーバーバブルディスクとして認識されるmbedは2MBの容量があり、複数のbinファイルを格納できます。リセットボタンを押すと、最後に格納したbinファイルをMCU(LCP1768)の内臓Flashに書き込んで起動してくれます。

過去に書き込んだbinファイルを起動したい場合は、一旦消去してコピーし直すことで再コンパイルすることなく実行できます。ただ、使わないbinファイルは消してしまうのが一番確実だと思います。

おわりに

出来合いのライブラリを使う範囲内では、mbedはArduino同様非常に効率がよいプログラミングができます。買ってきた部品をチョチョット試してみるにはうってつけです。一方で、Handbook配下にある標準ライブラリはソースコードが公開されておらず(ヘッダファイルのみ)、一部がブラックボックスな点が気になります。Cookbook配下のcontributedライブラリのソースは全て公開されています。

現在カスタムライブラリ作成のお勉強中で、眠っていたeDISP(カラーディスプレー)のライブラリを作成中です。作成の作法はArduinoのライブラリと同様で、C++のクラスを定義します。最小限の半角文字表示と文字色指定までは動作しましたが、もう少し確認・拡張を行って別途公開します。

WebベースIDEの使い勝手とか気になる点もありますが({ }のペアマッチができななど)、しばらく楽しめそうです。

« 2010年2月 | トップページ | 2010年4月 »

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      
無料ブログはココログ