GR ファミリー用ライブラリを Visual Studio 2015 Update 2 対応しました

追記)4月27日に公開した API リファレンスの構成が壊れているという連絡をいただきました。29日未明に修正版をコミットしました。すでにダウンロード済みの方はお手数ですが、再度最新版をダウンロードしてください。
なおバージョン番号は変更していません。

.NET Micro Framework v4.3 QFE2 と Visual Studio 2015 Update 2 との微妙な問題が解決し、GR-Family ライブラリの既存分の動作確認を終え、(超手抜きながら・・・)API リファレンスもなんとか作りました。

ということで、昨年秋に公開して少しずつ手を加え続けている、GR-Family ライブラリを改めて宣伝します。

https://github.com/netmf-lib-grfamily/GrFamilyLibrary

中味は全然すごくなくて、いちいち GPIO 操作する面倒を隠蔽したり I2C の低レベルの操作をラッピングしているだけなんですが、よろしければご活用ください。
GR-PEACH や PinKit をお持ちの方には便利に使えると思います。

 

そろそろ Windows 10 IoT 用のライブラリにも着手しようかな。皆さんにとっては素のままでも十分簡単なのか、ライブラリ化する人が出てこないけど、やっぱりコーディングは少しでもラクできるほうがいいですよね。

広告

「IoT ALGYAN ハンズオンのソースコード解説します」セッション資料を公開しました(Techfair.jp 勉強会 2016年3月19日開催)

2016年3月19日(土) に、Techfair.jp 主催、IoT ALGYAN 協力の勉強会

「IoT ALGYAN ハンズオンのソースコード解説します」

を開催しました。

IoT ALGYAN のハンズオンのソースを起点に、.NET Micro Framework やマイコンボードのプログラミングについて解説するというものです。あわせて Windows 10 IoT ではどう書くのかも紹介しました。

セッション資料は docs.com で公開しています。参加した皆さんは復習のため、参加していない方はマイコンボードプログラミングの参考にお使いください。

マイコンボードプログラミングは決して難しくはないものの、未経験の方にはとっつきにくいかもしれません。
そのハードルを越えるために、資料を活用してください。

参加者は決して多くはなかったものの、今回は内容についての評価が高く、参加者の期待には答えることができたと思います。
今後の勉強会では、ハンズオン資料のクラウド側の部分の解説をしたいと思います。が、単にコード解説ではなくて知見のあるスピーカーを呼んでセッション形式のパートもあるといいかもしれませんね。役に立って楽しいイベントになるように少し企画を検討してみます。

 

PS. 今回残念だったのは、前日・当日のキャンセル、無断欠席が多かったこと。申込者の半数が会場に来てくれませんでした。・・・残念です。参加したくなる仕掛けも考えてみないといけないですね。

「GR Family 用ライブラリの紹介」LT資料を公開しました (IoT 祭 2016 / 2016年3月12日) #algyan

IoT 祭 2016」(IoT ALGYAN 1周年記念イベント、3月12日(土) 開催) で、

GR Family 用ライブラリの紹介

というテーマで LT 登壇しました。
今回から資料は docs.com で公開しています。これまでの資料は引き続き SlideShare で公開しています。

 

以前からブログでお知らせしていた内容ではあるんですが、改めて LT で紹介しました。
「GitHub で公開してます」だけでは面白くないので、ライブラリを作り始めた経緯とか、ライブラリ利用でどんな風に便利になるのかをお伝えしました。
ぜひ活用して IoT を楽しんでください!

幸い、面白かったと言ってくださる方がいらっしゃって、実は内心薄い内容だな~と思っていたのですが、時間をいただいてよかったです。

懇親会で「DLL ってどうやって使うの?」という質問をいただきました。
このあたりは説明なしでいいのかなと思っていたんですが、やはり使い方とかリファレンスはあるほうがよさそうですね。
用意していきますので、少しだけ長い目で見てください。

 

次の週末 19日(土) の勉強会に参加してくだされば、ライブラリの使い方も紹介できます。ぜひ参加を検討してください。
現在、キャンセル待ちが出ている状態ですが、どうも増席されるんじゃないかな~と。(決めるのは私ですが /笑)

NETMF : 複数の I2C モジュールを接続(三軸加速度センサー ADXL345+キャラクターディスプレイ)

I2C の使用例として、これまで二つ紹介しました。

今回は I2C モジュールを二つ接続してみます。

WP_20160118_10_18_49_Pro

I2C のメリットは、複数のモジュールがあっても信号線 2本+電源の接続で済むこと。
I2C の概要は こちら のエントリーを参照していただければ。


I2CDevice の継承クラス / ADXL345 固有のクラス

.NET Micro Framework の I2CDevice クラスを使いやすくする拡張、および ADXL345 (三軸加速度センサー)は、こちらのコード がそのまま使えます。
特に変更する必要はありません。名前空間が気に入らなければ適当に変更してください。


キャラクターディスプレイのクラス

キャラクターディスプレイクラスは こちらのコード が使える・・・のですが、今回は 8×2行の別のモジュール、AQM0802A-RN-GBW を使ってみました。
理由は、実験用シールドに加速度センサーとキャラクターディスプレイの両方を載せるため。

キャラクターディスプレイは多くがHD44780 互換なのですが、AQM0802A は拡張機能セットを使います。
標準的な機能セットと共通の部分が多いのですが、モジュールの初期化コマンドおよびキャラクター出力コマンドが標準とは異なります。

ということで、以下のコードにします。

public class Aqm0802A : I2CDeviceEx
{
    private bool _displayOn = true;         // ディスプレイをオンにするか
    private bool _cursorOn = false;         // カーソルを表示するかどうか
    private bool _blinkOn = false;          // カーソル位置でブリンクするか

    private readonly int _commandWait = 1;  // コマンド実行後のウェイト

    public Aqm0802A(ushort aqm0802A, int defaultClockRateKhz, int timeout) :
        base(aqm0802A, defaultClockRateKhz, timeout)
    {
        Thread.Sleep(1000);

        // AQM0802A は HD44780 の拡張機能セットを使用する
        // 以下は拡張機能セットでの初期化コマンド
        // 標準機能セットを使用するモジュールについては以下を参照
        //   yseosoft.wordpress.com/2015/12/31/i2c_clcd_demo/ の記事
        //   GrFamilyLibrary のソースコード (github.com/netmf-lib-grfamily/GrFamilyLibrary)
        WriteCommand(0x38);         // 標準機能セットを指定
        WriteCommand(0x39);         // 拡張機能セットを指定
        WriteCommand(0x14);         // 内部オシレーター周波数を指定
        WriteCommand(0x70);         // コントラストを指定(下位4ビット)
        WriteCommand(0x56);         // コントラストを指定 (上位4ビット)
        WriteCommand(0x6c);         // フォロワーコントロール
        WriteCommand(0x38);         // 標準機能セットに戻す
        WriteCommand(0x0c);         // 表示オン

        WriteCommand(0x01);         // 表示クリア
        WriteCommand(0x06);         // カーソルと表示のシフト設定
        WriteCommand(0x0c);         // カーソルとブリンクの表示をオフ

        Thread.Sleep(100);
    }

    public void Print(string msg)
    {
        for (var i = 0; i < msg.Length; i++)
        {
            WriteCharactor((byte)msg[i]);
        }
    }

    // 表示クリア
    public void Clear()
    {
        WriteCommand(0x01, 5);      // Clear Displayはウェイトが必要
    }

    // カーソルを初期位置に戻す
    public void Home()
    {
        WriteCommand(0x02, 5);      // Return Homeはウェイトが必要
    }

    public void DisplayOn(bool displayOn)
    {
        ControlDisplay(displayOn, _cursorOn, _blinkOn);
        _displayOn = displayOn;
    }

    // カーソル表示オン・オフを切り替える
    public void CursorOn(bool cursorOn)
    {
        ControlDisplay(_displayOn, cursorOn, _blinkOn);
        _cursorOn = cursorOn;
    }

    // カーソル位置のブリンクのオン・オフを切り替える
    public void BlinkOn(bool blinkOn)
    {
        ControlDisplay(_displayOn, _cursorOn, blinkOn);
        _blinkOn = blinkOn;
    }

    public void SetCursor(int row, int col)
    {
        var addr = (byte)(((byte)row) << 6) + (byte)col;
        WriteCommand((byte)(0x80 | addr));
    }

    private void ControlDisplay(bool displayOn, bool cursorOn, bool blinkOn)
    {
        var cmd = (byte)0x08;
        if (displayOn)
            cmd |= 0x04;
        if (cursorOn)
            cmd |= 0x02;
        if (blinkOn)
            cmd |= 0x01;

        WriteCommand(cmd, _commandWait);
    }

    public void WriteCharactor(byte data)
    {
        // AQM0802A は拡張機能セットを使うので 0x40
        // 標準機能セットの場合は 0x80
        RegWrite((byte)0x40, data);
    }

    public void WriteCommand(byte cmd, int wait = 1)
    {
        var reg = (byte)0x00;
        RegWrite(reg, cmd);
        Thread.Sleep(wait);
    }
}

アプリケーションのコード例

アプリケーションのコード例は以下の通り。

public class Program
{
    private const ushort Adxl345Address = 0x1d;     // ADXL345のアドレス
    private const ushort Aqm0802AAddress = 0x3e;    // AQM0802A-RN-GBWのアドレス
    private const int DefaultClockRateKhz = 100;    // 転送速度 (KHz)
    private const int Timeout = 1000;               // 送受信ごとのタイムアウト

    public static void Main()
    {
        var prog = new Program();
        prog.Run();
    }

    private void Run()
    {
        var accel = new Adxl345(Adxl345Address, DefaultClockRateKhz, Timeout);
        var lcd = new Aqm0802A(Aqm0802AAddress, DefaultClockRateKhz, Timeout);

        while (true)
        {
            Int16 x;
            Int16 y;
            Int16 z;

            accel.Measure();
            accel.GetXYZ(out x, out y, out z);

            lcd.Clear();
            lcd.Print(x.ToString() + "," + y.ToString());
            lcd.SetCursor(1, 0);
            lcd.Print(z.ToString());

            Thread.Sleep(1000);
        }
    }
}

加速度センサーの使い方、キャラクターディスプレイの使い方が分かれば、(当然ですが)特に不思議なところも難しいところもないですね。


結線

結線は以下の通り。

I2C モジュールは信号線をパーティーライン状に接続するだけです。通信時にモジュールのアドレスが含まれているので(.NET Micro Framework では隠蔽されていますが)、どちらに対しても正しくデータを送受信できます。

WP_20160118_10_17_03_Pro2016-01-18 16-06-59

NETMF : I2C 接続のセンサーモジュール活用例(三軸加速度センサー ADXL345 の場合)

I2C の使用例としてキャラクターディスプレイモジュールについてすでに紹介しました。
キャラクターディスプレイは出力モジュールなので、マイコンボードからのコマンドも出力方向のみ(キャラクターディスプレイからデータを読み打差宇ことはない)でした。

今回は双方向のデータ通信について紹介します。センサーデバイスですね。
センサーデータを要求するコマンドをマイコンボードからモジュールに送信し、センサーデータをマイコンボードで受け取るという通信です。
モジュールによって通信に使う領域(レジスター)は異なりますが、送受信ともに既定の領域を使ってデータをやりとりします。


今回使用するモジュール

センサーモジュールの例として、今回は ADXL345 を使用します。3軸の加速度センサーです。
安価で入手しやすいモジュールです。
PinKit のセンサーボードをお持ちの方は以下のコードがそのまま使えます。
GR-PEACH のみで購入して PinKit のセンサーボードを持っていない方は秋月あたりでモジュールを購入できます。

WP_20150313_006WP_20151103_003

※写真では実験用シールドに ADXL345 を載せていますが、もちろん普通のブレッドボードなどを使ってもかまいません。加速度センサーは持ち上げて傾けるのでブレッドボードが分かれているよりマイコンボードと一体化しているほうがやりやすいというだけの理由です。


ADXL345 固有の情報

ADXL345 を使うにあたって必要な(モジュール固有の情報)をまとめておきます。

I2C アドレスは、私が使った上記のモジュールでは 0x1d です。

レジスタ(通信のための領域)のうち、最低限必要なものは以下です。

  • 0x2d・・・電源管理(センサーデータ取得でも使います)
  • 0x31・・・データフォーマット(加速度の測定範囲を設定)
  • 0x32~0x37・・・x, y, x 方向の加速度データ

I2CDevice の継承クラス

.NET Micro Framework の I2CDevice クラスは少しだけ不便なところがあるので、少し拡張してあげます。
これは I2C キャラクターディスプレイの例で紹介したものと同じです。

using Microsoft.SPOT.Hardware;

namespace AccelTest01
{
    public class I2CDeviceEx
    {
        private readonly I2CDevice _i2C;
        private readonly int _timeout;

        private readonly byte[] _adata = new byte[1];   // アドレス指定用のバイトデータ領域
        private readonly byte[] _rdata = new byte[1];   // データ受信用のバイトデータ領域
        private readonly byte[] _wdata = new byte[2];   // データ送信用のバイトデータ領域

        private I2CDevice.I2CTransaction[] _trRegRead;      // データ受信用のトランザクションデータ
        private I2CDevice.I2CTransaction[] _trRegWrite;     // データ送信用のトランザクションデータ

        public I2CDeviceEx(ushort i2CAddress, int clockRateKhz = 100, int timeout = 1000)
        {
            _i2C = new I2CDevice(new I2CDevice.Configuration(i2CAddress, clockRateKhz));
            _timeout = timeout;

        }

        protected byte RegRead(byte reg)
        {
            _adata[0] = reg;
            _trRegRead = new I2CDevice.I2CTransaction[] {
                I2CDevice.CreateWriteTransaction(_adata),
                I2CDevice.CreateReadTransaction(_rdata) };
            _i2C.Execute(_trRegRead, _timeout);
            return _rdata[0];
        }

        protected void RegReads(byte reg, ref byte[] data)
        {
            _adata[0] = reg;
            _trRegRead = new I2CDevice.I2CTransaction[] {
                I2CDevice.CreateWriteTransaction(_adata),
                I2CDevice.CreateReadTransaction(data) };
            _i2C.Execute(_trRegRead, _timeout);
        }

        protected void RegWrite(byte reg, byte val)
        {
            _wdata[0] = reg;
            _wdata[1] = val;
            _trRegWrite = new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(_wdata) };
            _i2C.Execute(_trRegWrite, _timeout);
        }

        protected void RegWriteMask(byte reg, byte val, byte mask)
        {
            var tmp = RegRead(reg);
            _wdata[0] = reg;
            _wdata[1] = (byte)(tmp & ~(int)mask | ((int)val & (int)mask));
            _trRegWrite = new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(_wdata) };
            _i2C.Execute(_trRegWrite, _timeout);
        }
    }
}

 


ADXL345 固有のクラス

ADXL345 固有の機能をクラスにまとめるとこうなります。

アプリからは Measure メソッドを呼び出したあとに GetXYZ メソッドを呼び出すと加速度データが得られます。
なお ADXL345 では測定範囲は設定により変更できますが、今回はサンプルということで 4G に固定しています。
このあたりの変更は GrFamilyLibrary のソースコードを参考にしていただければ。

public class Adxl345 : I2CDeviceEx
{
    private const byte PowerCtl = 0x2d;
    private const byte DataFormat = 0x31;
    private const byte DataX0 = 0x32;

    private byte[] _xyz = new byte[6];

    public Adxl345(ushort adxl345Address, int defaultClockRateKhz, int timeout) :
        base(adxl345Address, defaultClockRateKhz, timeout)
    {
        Thread.Sleep(1000);

        SetRange4G();
        ToWakeup();
    }

    public void Measure()
    {
        RegWriteMask(PowerCtl, 0x08, 0x08);
    }

    public void GetXYZ(out Int16 x, out Int16 y, out Int16 z)
    {
        RegReads(DataX0, ref _xyz);
        x = (Int16)(((UInt16)_xyz[1] << 8) + (UInt16)_xyz[0]);
        y = (Int16)(((UInt16)_xyz[3] << 8) + (UInt16)_xyz[2]);
        z = (Int16)(((UInt16)_xyz[5] << 8) + (UInt16)_xyz[4]);
    }

    private void SetRange4G()
    {
        // 4Gまで測定するには第2引数を 0x01
        // 2G・・・0x00, 4G・・・0x01, 8G・・・0x10
        RegWrite(DataFormat, 0x01);     
    }

    private void ToWakeup()
    {
        RegWriteMask(PowerCtl, 0x00, 0x04);     // 通常モードに入る(スリープモードではない)
    }
}

 


アプリケーションのコード例

ADXL345 を使うアプリケーションのコード例は以下の通り。

1秒間隔で加速度データを読み出しています。

.NET っぽくイベントハンドラーを使いたいなどは、(ここでもやはり)GrFamilyLibrary を参照してください。

public class Program
{
    private const ushort Adxl345Address = 0x1d;     // ADXL345のアドレス
    private const int DefaultClockRateKhz = 100;    // 転送速度 (KHz)
    private const int Timeout = 1000;               // 送受信ごとのタイムアウト

    public static void Main()
    {
        var prog = new Program();
        prog.Run();
    }

    private void Run()
    {
        var accel = new Adxl345(Adxl345Address, DefaultClockRateKhz, Timeout);

        while (true)
        {
            Int16 x;
            Int16 y;
            Int16 z;

            accel.Measure();
            accel.GetXYZ(out x, out y, out z);
            Debug.Print("X = " + x + ", Y = " + y + ", Z = " + z);

            Thread.Sleep(1000);
        }
    }
}

 


念のため、結線について

I2C は本当に結線が簡単ですが、念のため紹介しておくと以下のようにします。
※PinKit のセンサーボードをお持ちの方は、もちろん GR-PEACH に載せるだけ。

2016-01-16-18-23-51WP_20151103_004

NETMF : I2C の実装方法と I2C 接続キャラクターディスプレイの例

I2C (=Inter-Integrated Circuit) について、.NET Micro Framework で実装する方法を紹介します。
※「I2C とは何?」について本稿では概要を紹介します。詳しい話は検索結果にお任せします。

GPIO だとマイコンボードからキャラクターディスプレイを利用するのに 4ピン(+電源系 2ピン)の接続が必要です。
(8ビットモードだと 8ピン必要)
I2C だと SCL、SDA の 2本の信号線接続(+電源)で済みます。複数の I2C モジュールを接続したい場合でも2本の信号線を共用できます。ピンの消費が少ないのがメリットです。


I2C の概要

I2C は2本の信号線で、マスター(マイコンボード)とスレーブ(モジュール)とが双方向の通信ができます。
通信はすべてマスター主導で行われます。
各スレーブはアドレスを持っているため、マスターからのデータ送受信要求に対して、特定のスレーブだけがその後のデータ送受信を継続します。これによって各スレーブは信号線を共用できるわけです。2本の信号線はプルアップ抵抗を介して VDD (電源) に接続する必要があります。

アドレス、プルアップ抵抗をモジュールに内蔵しているかどうかなどは、各モジュールのデータシートに記載されています。
※スレーブのアドレスは製造元が “独自に” 決めます。アドレスの衝突があり得るため、ジャンパーなどでアドレス切り替えできるモジュールもあります。


.NET Micro Framework の I2C 対応

.NET Micro Framework は組込み用途での利用が一般的なので、ライブラリ内に I2C の機能を持っています。

  • 初期化
    I2C 利用に必要なモジュールのアドレスは、.NET Micro Framework では I2CDevice クラスのインスタンス化のタイミングで指定します。var _i2C = new I2CDevice(new I2CDevice.Configuration(i2CAddress, clockRateKhz));

    Configuration クラスのコンストラクタの i2cAddress 引数がモジュールのアドレス、clockRateKhz が転送速度です。
    (転送速度は 100KHz または 400KHz、さらに速い規格もありますが、評価用途のマイコンボード、モジュールでは 100KHz でも困らないはず)
  • データ送受信
    .NET Micro Framework では送受信したいデータを CreateWriteTransaction または CreateReadTransaction で作成し、これらを I2CTransaction 型の配列に必要なだけ入れます。
    実際の送受信は Execute メソッドで行います。
    データの組み立ては自前で行う必要があるため、ヘルパークラスを用意するとラクにコードが書けます。これについては後述。

I2C  通信(に限りませんが)本当はモジュールのタイミングチャートを見てタイミングを図る必要があるのですが、I2CDevice クラスがそのあたりの面倒を隠ぺいしてくれています。おかげで非常に簡単にデータ送受信ができます。


I2C 接続キャラクターディスプレイのコード例

I2C の例として、キャラクターディスプレイを使ってみます。

GPIO の使い方の例としてもキャラクターディスプレイを使いましたが、I2C 接続の場合でも GPIO 接続ディスプレイの知識がそのまま使えます。
というのは、I2C 接続であっても多くの場合は HD44780 互換のディスプレイを積んでいるからです。

GPIO ではビットごとにピンを割り当てていたものを、I2C ではバイトの形にまとめて送受信するという違いだけです。(つまり、モジュール側でデータを分解してディスプレイの各ピンの制御を行っているということです)


I2CDevice の継承クラス

I2CDevice クラスはデータ送受信そのものの詳細を隠ぺいしてくれますが、データを作るところは対応していません。そこでこんなクラスを用意してみます。

public class I2CDeviceEx
{
    private readonly I2CDevice _i2C;
    private readonly int _timeout;

    private readonly byte[] _adata = new byte[1];   // アドレス指定用のバイトデータ領域
    private readonly byte[] _rdata = new byte[1];   // データ受信用のバイトデータ領域
    private readonly byte[] _wdata = new byte[2];   // データ送信用のバイトデータ領域

    private I2CDevice.I2CTransaction[] _trRegRead;      // データ受信用のトランザクションデータ
    private I2CDevice.I2CTransaction[] _trRegWrite;     // データ送信用のトランザクションデータ

    public I2CDeviceEx(ushort i2CAddress, int clockRateKhz = 100, int timeout = 1000)
    {
        _i2C = new I2CDevice(new I2CDevice.Configuration(i2CAddress, clockRateKhz));
        _timeout = timeout;

    }

    protected byte RegRead(byte reg)
    {
        _adata[0] = reg;
        _trRegRead = new I2CDevice.I2CTransaction[] {
            I2CDevice.CreateWriteTransaction(_adata),
            I2CDevice.CreateReadTransaction(_rdata) };
        _i2C.Execute(_trRegRead, _timeout);
        return _rdata[0];
    }

    protected void RegReads(byte reg, ref byte[] data)
    {
        _adata[0] = reg;
        _trRegRead = new I2CDevice.I2CTransaction[] {
            I2CDevice.CreateWriteTransaction(_adata),
            I2CDevice.CreateReadTransaction(data) };
        _i2C.Execute(_trRegRead, _timeout);
    }

    protected void RegWrite(byte reg, byte val)
    {
        _wdata[0] = reg;
        _wdata[1] = val;
        _trRegWrite = new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(_wdata) };
        _i2C.Execute(_trRegWrite, _timeout);
    }

    protected void RegWriteMask(byte reg, byte val, byte mask)
    {
        var tmp = RegRead(reg);
        _wdata[0] = reg;
        _wdata[1] = (byte)(tmp & ~(int)mask | ((int)val & (int)mask));
        _trRegWrite = new I2CDevice.I2CTransaction[] { I2CDevice.CreateWriteTransaction(_wdata) };
        _i2C.Execute(_trRegWrite, _timeout);
    }
}

キャラクターディスプレイのクラス

I2CDeviceEx クラスでは、I2CDevice 自体を使いやすくするメソッドを実装しました。

各モジュールに対して、今回の例で言うと、キャラクターディスプレイモジュールに対しては、I2CDeviceEx を継承するクラスを用意するとコードの見通しがよくなります。
例えば、
RegWrite((byte)0x80, data);
よりは
WriteCharactor(data);
のほうが読みやすいですね。(”(byte)0x80” ってなんだよという話です)

public class I2CLiquidCrystal : I2CDeviceEx
{
    private bool _displayOn = true;         // ディスプレイをオンにするか
    private bool _cursorOn = false;         // カーソルを表示するかどうか
    private bool _blinkOn = false;          // カーソル位置でブリンクするか

    private readonly int _commandWait = 1;  // コマンド実行後のウェイト

    public I2CLiquidCrystal(ushort acm1602N1Address, int defaultClockRateKhz, int timeout) :
        base(acm1602N1Address, defaultClockRateKhz, timeout)
    {
        Thread.Sleep(1000);

        WriteCommand(0x30, 5);      // 8ビットモードにセット + 5msウェイト
        WriteCommand(0x30);         // 8ビットモードにセット
        WriteCommand(0x30);         // 8ビットモードにセット
        WriteCommand(0x38);         // 行数とフォントの設定
        WriteCommand(0x80);         // 表示オフ

        WriteCommand(0x01);         // 表示クリア
        WriteCommand(0x06);         // カーソルと表示のシフト設定
        WriteCommand(0x0c);         // カーソルとブリンクの表示をオフ

        Thread.Sleep(100);
    }

    public void Print(string msg)
    {
        for (var i = 0; i < msg.Length; i++)
        {
            WriteCharactor((byte)msg[i]);
        }
    }

    public void Clear()
    {
        WriteCommand(0x01, 5);      // Clear Displayはウェイトが必要
    }

    public void Home()
    {
        WriteCommand(0x02, 5);      // Return Homeはウェイトが必要
    }

    public void DisplayOn(bool displayOn)
    {
        ControlDisplay(displayOn, _cursorOn, _blinkOn);
        _displayOn = displayOn;
    }

    public void CursorOn(bool cursorOn)
    {
        ControlDisplay(_displayOn, cursorOn, _blinkOn);
        _cursorOn = cursorOn;
    }

    public void BlinkOn(bool blinkOn)
    {
        ControlDisplay(_displayOn, _cursorOn, blinkOn);
        _blinkOn = blinkOn;
    }

    public void SetCursor(int row, int col)
    {
        var addr = (byte)(((byte)row) << 6) + (byte)col;
        WriteCommand((byte)(0x80 | addr));
    }

    private void ControlDisplay(bool displayOn, bool cursorOn, bool blinkOn)
    {
        var cmd = (byte)0x08;
        if (displayOn)
            cmd |= 0x04;
        if (cursorOn)
            cmd |= 0x02;
        if (blinkOn)
            cmd |= 0x01;

        WriteCommand(cmd, _commandWait);
    }

    public void WriteCharactor(byte data)
    {
        RegWrite((byte)0x80, data);
    }

    public void WriteCommand(byte cmd, int wait = 1)
    {
        var reg = (byte)0x00;
        RegWrite(reg, cmd);
        Thread.Sleep(wait);
    }
}

キャラクターディスプレイはマイコンボードからモジュールへのデータ送信しかありません。出力デバイスなので当然ですね。
制御コマンドの場合は対応する 1バイトのみをトランザクションに入れて Execute を呼び出します。
文字出力の場合は、制御コマンド 0x80 に続いて出力したい文字データをトランザクションに入れます。

モジュールによっては HD44780 拡張コマンドを使用するものがあります。この場合は上記のコードでは動作しません。特に初期化コマンドに差があります。拡張コマンドについては GR ファミリー用のクラスライブラリのソースコードを参考にしてください。


アプリケーションのコード例

キャラクターディスプレイの使用方法の例を兼ねて、アプリケーションのコードも紹介します。

public class Program
{
    private const ushort Acm1602N1Address = 0x50;   // ACM1602N1-FLW-FBWのアドレス
    private const int DefaultClockRateKhz = 100;    // 転送速度 (KHz)
    private const int Timeout = 1000;               // 送受信ごとのタイムアウト

    public static void Main()
    {
        var lcd = new I2CLiquidCrystal(Acm1602N1Address, DefaultClockRateKhz, Timeout);

        lcd.Clear();                        // 画面クリア (クリア後は 1行 1列にカーソル)
        Thread.Sleep(1000);
        lcd.Print("Hello, I2C CLCD!");      // 文字列表示
        lcd.SetCursor(1, 4);                // カーソル移動
        lcd.Print("I2C Demo");
        Thread.Sleep(3000);

        while (true)
        {
            lcd.BlinkOn(true);              // 次の文字出力位置で点滅
            Thread.Sleep(3000);
            lcd.BlinkOn(false);
            lcd.CursorOn(true);
            Thread.Sleep(3000);
            lcd.CursorOn(false);            // カーソル表示
            Thread.Sleep(3000);

            for (var i = 0; i < 3; i++)
            {
                lcd.DisplayOn(false);       // 画面非表示
                Thread.Sleep(1000);
                lcd.DisplayOn(true);        // 画面表示
                Thread.Sleep(1000);
            }

            Thread.Sleep(2000);
        }
    }
}

 


今回のコードは、秋月電子の ACM1602NI-FLW-FBW で動作確認しました。
実際に自分でも動かしてみたい方はパーツを購入した上で以下を参考に回路を組んでみてください。

WP_20151231_13_12_52

回路図はこんな感じ。

2015-12-31 14-37-012015-12-31 14-49-06

 

今回のコードで I2C 接続のキャラクターディスプレイを利用できるようになりますが、毎回似たようなコードを書くのも面倒です。
より手軽に呼び出すために GR ファミリー用のクラスライブラリの利用もアリです。

NETMF の実装方法の紹介まで、かなり間が開きました。
GR-PEACH + I2C モジュールの紹介をしようとしたところで、回路図が書けず(Fritzing で書こうにも当然 GR-PEACH のパーツがない)、そうこうしているうちにハンズオン準備などがあり、先送りしてしまっていました。
Fritzing 用のパーツを作ったことでようやく I2C について書けました。

GR-PEACH の Fritzing 用パーツを公開しました

GR-PEACH の Fritzing パーツを公開しました。

マイコンボードを使った回路を保存したり公開したりしたいと思った場合、当然ですが、まずは回路図を書く必要があります。
その時に便利なのが Fritzing ですね。無償ですし、直感的に使えます。
(なお、本稿では Fritzing 自体の使い方は紹介しません。ネット上にたくさん情報があるので、そちらを参照してください)

GR-PEACH を買った方、IoT ALGYAN のハンズオンなどで GR-PEACH を手に入れた方は、ぜひ活用してたくさんのデバイスを開発して、情報を公開してください。


パーツを Fritzing にインポートする手順

GR-PEACH パーツを Fritzing で使用するにはインポートする必要があります。
その手順については紹介しておきます。

  1. パーツをダウンロード
  2. Fritzing を起動(ダウンロードは こちらのサイト から)
  3. My Parts を開く
    2015-12-28 9-54-17
  4. My Parts のメニューまたは右クリックで [Import …] を選択
    2015-12-28 9-54-49
  5. ダイアログでダウンロードしたパーツを選択
    2015-12-28 9-55-332015-12-28 9-56-35
  6. My Parts のメニューまたは右クリックで [Save Bin] を選択
    2015-12-28 9-56-46

以上です。
これで GR-PEACH が Fritzing で使えるようになります。

2015-12-28 10-03-432015-12-28 10-03-55

 


制限事項

公開したパーツは以下の制限事項があります。

  • Arduino 互換ピン(外側の1列のピン)のみ定義しています。GR-PEACH はピンが3列並んでいますが、これらについては実装していません。
  • 3.3V ピンに配線すると、回路図ビューで GR-PEACH のピンの途中から結線されることがあります。パーツの svg を修正すればいいのですが、回路図上は問題ないのでそのまま残しています。

ダウンロードしたパーツの fzpz ファイルの拡張子を zip に変更すると svg ファイルが見えます。気になる方は修正していただければ。

 


■  (おまけ)パーツ作成の経緯

GR-PEACH ありの回路図を Fritzing で作る場合、Arduino Uno R3 で代替することが考えられます。デバイスのインターフェイスが GPIO の場合には問題ありません。

ところが I2C 対応のデバイスを使うと困ってしまいます。Arduino の I2C ポートは A4, A5 ピンと共用なんですよね。
GR-PEACH は違います。専用のピンを持っています。
ハンダジャンパーで A4, A5 ピン共用に変更できるので、Arduino のシールドを使いたいなどの理由があればハンダジャンパーで A4, A5 ピン共用にできますが、それ以外では意味はないでしょう。

2015-12-28 10-04-46

ということで、自分が GR-PEACH で I2C な回路を組んで回路図がほしくなったために、Fritzing 用パーツを作ったのでした。

いざ作り始めると Fritzing の パーツの仕様、Parts Editor のクセとか SVG エディタ(Inkscape を使いました)の使い方やらで、結構苦労しました。
ようやく公開にこぎ着けたという感じです。