Deep Insider の Tutor コーナー
>>  Deep Insider は本サイトからスピンオフした姉妹サイトです。よろしく! 
.NET対応組み込みデバイス「Netduino」入門(5)

.NET対応組み込みデバイス「Netduino」入門(5)

Netduinoシリアル通信(I2C)でLCD表示

2015年5月15日

シリアル通信(I2C)をさらに学ぼう。I2C通信で液晶ディスプレイ(LCD)に文字を出力するサンプルを作成する。

Microsoft MVP for Windows Platform Development 初音 玲
  • このエントリーをはてなブックマークに追加

 前回は、温度センサーからI2C通信で温度を取得する方法を取り上げた。今回は、I2C通信で液晶ディスプレイ(LCD)に文字を出力する方法を取り上げる。前回と組み合わせれば、温度センサーの値をLCDに表示することも可能になる。

AQM0802Aモジュールについて

 今回のサンプルでは、I2C対応機器としてAQM0802A-RN-GBW液晶+ブレッドボード用ピッチ変換基盤というLCDモジュール(図1)を使用する。

AQM0802Aモジュール
図1 LCDモジュール「AQM0802A」

 このAQM0802Aモジュールは、LCDとピッチ変換基盤の間で9本の足のはんだ付けが必要で、さらに、ピッチ変換基盤に5本足のはんだ付けが必要だ。

 はんだ付けを少しでも減らしたいのであれば、AQM0802Aとピッチ変換基盤の間がはんだ付け済みで、あとは5本足のはんだ付けだけでよいLCDモジュールというのもある。今回の記事とはピン配置が若干違うが、同じプログラムで動作するので、費用と手間とのバランスでどちらかを選択するのがいいだろう。

Netduino+AQM0802Aモジュールのハード構成

AQM0802Aモジュールの物理仕様

 AQM0802Aモジュールには5つの足と2つのジャンパーがある(図2)。

 ジャンパーをそれぞれはんだ付けすることで、SCLやSDAのプルアップ抵抗を有効にできるが、別途、ブレッドボード上にプルアップ抵抗を設置するのではんだ付けは行わない。

AQM0802Aモジュール回路図
図2 LCDモジュール「AQM0802A」の回路図(AQM0802データシートPDFから引用して改変)

 今回使用したピッチ変換基盤は、ブレッドボードのピン間隔に合わせるだけではなく、+5VとGND間に挟むパスコン、VOUT、CAP1N、CAP1Pに必要なコンデンサーなども基板上に組み込んである。よって、Netduinoとの間に新たにコンデンサーを挟み込む必要はない。

AQM0802Aモジュール接続回路図

図3 AQM0802Aモジュール接続回路図

 I2Cはシリアル通信なので、流れるデータはOnとOffのビットデータとなる。Onのときには5V、Offのときには0Vになる。Netduinoで使用するときはプルアップ抵抗を入れることで、OnとOffの電圧値がより安定する。

 AQM0802Aモジュール自体でもプルアップ抵抗を有効にできるが、ジャンパーをはんだ付けしてしまうとプルアップ抵抗が不要なときに対処できないので、ADT7410モジュールの外側で明示的にプルアップ抵抗を回路に組み込むことにした。

 プルアップ抵抗値は10kΩを使用する。AQM0802Aの3番ピン(=SCL)と4番ピン(=SDA)を、Netduinoを接続するラインから分岐するように抵抗を挟んで+5Vラインに接続する。

ブレッドボード実装

 回路図(図3)を基にしてブレッドボードに部品を配置する(図4)。

図4 AQM0802Aモジュールをブレッドボード上に配置

 Netduinoからの5Vラインは赤いラインを通ってブレッドボードの一番下のプラス電源ラインGNDからの線も同様に青いラインを通って下から二番目のGNDラインに接続する。

 A11E11は内部的に接続されているので、A11プラス電源ラインに接続することで、AQM0802AモジュールのVDDに+5Vを供給する。同様にAQM0802AモジュールのRESETも+5Vに接続する。GNDGNDラインからA15、そしてE15への内部結線を経由してAQM0802AモジュールのGNDに接続する。

 I2C特有のSCLSDAの2つの信号線は、SCLがB13、SDAがC14でNetduinoに接続している。また、プルアップ用の10KΩ抵抗がA13A14からプラス電源ラインの間を接続している。

 実際に接続すると、ブレッドボード上のAQM0802Aモジュールは次のようになる。

図5 AQM0802Aモジュール実装写真

Netduino+AQM0802Aモジュールのプログラミング

 回路が完成したら、アプリのプログラミングを始める。

 最初に行う手順は、Visual Studioの[新しいプロジェクト]ダイアログで[Micro Framework]カテゴリの[Netduino Plus 2 Application]テンプレートを選択して、新規にプロジェクトを作成することだ(本稿の例では、プロジェクト名はVB.NET用は「AQM0802LcdVB」、C#用は「AQM0802LcdCS」とした)。

送信データの理解

 AQM0802Aモジュールは、液晶表示のコントローラーとしてST7032iというLCDコントロールICを使用している。

 ST7032iでは、ST7032i側への書き込みはできるが読み出しができず、ビジーフラグの確認もできない。従って書き込み後は、ST7032データシートPDF(英語)に記載された待ち時間以上の時間が経過するのを待って、次の書き込みを行う必要がある。

 送信データのフォーマットは、データシートに全て記載があるが、かなり読み解きづらい。

 まずI2Cスレーブアドレスが「0x7c(2進数で1111 1100)」とあるが、これはRWビットという内部的に使われる1bitを先頭に含んでいるので、プログラムとして指定するアドレスは残り7bitの「0x3c(2進数で111 1110)」となる(余談となるが筆者は、このアドレス指定の差にはまって、画面に表示できるまでかなり時間がかかってしまった)。

 ST7032iではコマンドデータという概念があり、その識別はRSビットにより区別する。また書き込みしかできないのにRWビットという常に「0」を指定するRead(読み込み)/Write(書き込み)区別ビットがある。データシートには、「この2bit(=RSビットとRWビット)+8bit」でさまざまなコマンドの説明がされている(図6)。

図6 DISPLAY INSTRUCTION TABLE(AQM0802データシートPDFから引用して抜粋)

 そのため「10bitデータが基本か」と思ったら、「1byte(=データ連続送信用のCOビット+RSビット+RWビットを含んだ制御バイト)+1byte(=8bit)」の2bytesデータが基本となっているので、注意してほしい。

 実際の送信では、この2bytesに先立ってスレーブアドレスを指定することになるが、このあたりは.NET Micro FrameworkのI2Cライブラリが適切に処理してくれるので、プログラムを組みときにはあまり意識しなくてよい。その他にもデータシートにあるSTARTビットSTOPビットACK受信などについても、プログラムコードとしては登場しない要素なので、どのように処理するかを気にしないでよい。

1. コマンド送信

 コマンド送信は、Coビット=0、RSビット=0、RWビット=0の制御バイト(1byte)とコマンド(1byte)からなる。つまり「0x00+コマンドバイト」だ(図7)。

図7 コマンド送信

 コマンド送信が必要な局面の大半は初期化コマンドだ。必要な初期化コマンドについてもデータシートに記載がある。固定で次のような初期化コマンドを送るといいだろう。

  • 0x38: 画面サイズ指定
  • 0x39: 拡張コマンド設定
  • 0x14: Internal OSC Frequency
  • 0x70:コントラスト
  • 0x52: 5V指定
  • 0x6c: Followerコントロール
  • 0x38: 拡張コマンド設定完了
  • 0x0c: 表示オン
  • 0x06: Entry mode set
  • 0x01: 画面クリア
2. データ送信(継続あり)

 コマンドではなく文字コードをデータとして連続して送信したいときは1文字ずつに分解して、1文字ごとに制御バイトを付けた2bytesデータ(図8)にして送信する。このとき、最終文字以外についてはCoビットを「1」とする。RSビットも「1」となるので、データフォーマットは「0xC0+表示文字コード」になる。NetduinoのI2Cライブラリ的には、1つのI2CWriteTransactionに対して連続して2bytesデータを送信する流れになる。

図8 データ送信(継続あり)
3. データ送信(継続なし)

 1文字しか送信しない場合、もしくは文字列の最後の1文字を送信するときはCoビットを「0」に設定する。RSビットは「1」となるので、データフォーマットは「0x40+表示文字コード」になる。

図9 データ送信(継続なし)

表示文字コード

 表示文字コードもデータシートに記載がある。

図10 表示文字コード(AQM0802データシートPDFから引用して抜粋)

 0x20~0x7dに割り当てられた英数字はUTF-8コードと同じである。よって、英数字およびほとんどの記号はUTF-8のバイト文字として送信できる。その他については変換テーブルなどを作って対応する。

 それでは以上を踏まえてプログラムを作成していこう。

I2C用クラス作成

 前回作成したI2C用クラスファイル(Visual Basicなら「I2CLib.vb」ファイル、C#なら「I2CLib.cs」ファイル)を、今回のプロジェクトに取り込む。

 ソリューションエクスプローラーでプロジェクト名を右クリックして(表示されるコンテキストメニューの)[追加]-[既存の項目]でダイアログを表示して「I2CLib」クラスファイルをプロジェクトに追加する。

AQM0802用クラス作成

 I2CLibクラスを継承してAQM0802Libクラスを作ってAQM0802A特有の部分を隠ぺいしてから使う。AQM0802Libクラスは次のようなコードになる。

Visual Basic
Private Class AQM0802Lib
  Inherits I2CLib
  Private Const LCD_CLEARDISPLAY As Byte = &H1
    ' ……中略……
  Private Encoding As System.Text.Encoding = System.Text.Encoding.UTF8  ' ……1

  Public Sub New()
    MyBase.New(&H3E, 100, 500)  ' ……2
    Thread.Sleep(40)            ' 40ms待つ

    ' 画面サイズ指定
    Me.WriteCommand(LCD_FUNCTIONSET Or LCD_8BITMODE Or LCD_2LINE)          ' ……3
    ' 拡張コマンド設定開始
    Me.WriteCommand(LCD_FUNCTIONSET Or LCD_8BITMODE Or LCD_2LINE Or ISBit) ' ……4
    ' AQM0802用固定設定
    Me.WriteCommand(&H14)  ' Internal OSC Frequency
    Me.WriteCommand(&H70)  ' Contrast set
    Me.WriteCommand(&H52)  ' Power/ICON/Contrast control(5V用)             ' ……5
    Me.WriteCommand(&H6C)  ' Follower control
    ' 拡張コマンド設定終了
    Thread.Sleep(200)           ' ……6
    Me.WriteCommand(LCD_FUNCTIONSET Or LCD_8BITMODE Or LCD_2LINE)
    '
    Me.WriteCommand(LCD_DISPLAYCONTROL Or LCD_DISPLAYON)  ' DISPLAY ON
    Me.WriteCommand(&H6)      ' Entry mode set
    Me.ClearDisplay()
  End Sub

  Private Sub WriteCommand(value As Byte)
    Me.WriteToRegister(LCD_Command, value)
    Thread.Sleep(1)             ' ……7
  End Sub

  Public Sub ClearDisplay()
    Me.WriteCommand(LCD_CLEARDISPLAY)   ' DISPLAY CLRAR
    Thread.Sleep(2)                     ' ……8
  End Sub

  Public Sub Locate(x As Byte, y As Byte)
    Me.WriteCommand(LCD_SETDDRAMADDR Or CType(CursorOffset, Byte) * y + x)  ' ……9
  End Sub

  Public Sub WriteMessage(msg As String)
    Dim byteArray() As Byte = Encoding.GetBytes(msg)             ' ……10

    For index As Integer = 0 To msg.Length - 2
      Me.WriteToRegister(LCD_DataContinue, byteArray(index))     ' ……11
    Next
    Me.WriteToRegister(LCD_DataLast, byteArray(msg.Length - 1))  ' ……12

    Thread.Sleep(1)
  End Sub
End Class
C#
private class AQM0802Lib : I2CLib
{
  private const byte LCD_CLEARDISPLAY = 0x1;
    // ……中略……
  private System.Text.Encoding Encoding = System.Text.Encoding.UTF8;  // ……1

  public AQM0802Lib()
    : base(0x3e, 100, 500)   // ……2
  {
    Thread.Sleep(40);        // 40ms待つ
    // 画面サイズ指定
    this.WriteCommand(LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE);         // ……3
    // 拡張コマンド設定開始
    this.WriteCommand(LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE | ISBit); // ……4
    // AQM0802用固定設定
    this.WriteCommand(0x14); // Internal OSC Frequency
    this.WriteCommand(0x70); // Contrast set
    this.WriteCommand(0x52); // Power/ICON/Contrast control(5V用)          // ……5
    this.WriteCommand(0x6C); // Follower control
    // 拡張コマンド設定終了
    Thread.Sleep(200);       // ……6
    this.WriteCommand(LCD_FUNCTIONSET | LCD_8BITMODE | LCD_2LINE);
    //
    this.WriteCommand(LCD_DISPLAYCONTROL | LCD_DISPLAYON);  // DISPLAY ON
    this.WriteCommand(0x6);      // Entry mode set
    this.ClearDisplay();
  }

  private void WriteCommand(byte value)
  {
    this.WriteToRegister(LCD_Command, value);
    Thread.Sleep(1);                        // ……7
  }

  public void ClearDisplay()
  {
    this.WriteCommand(LCD_CLEARDISPLAY);    // DISPLAY CLRAR
    Thread.Sleep(2);                        // ……8
  }

  public void Locate(byte x, byte y)
  {
    this.WriteCommand((byte)(LCD_SETDDRAMADDR + CursorOffset * y + x)); // ……9
  }

  public void WriteMessage(string msg)
  {
    byte[] byteArray = Encoding.GetBytes(msg);                      // ……10

    for (int index = 0; index <= msg.Length - 2; index++)
    {
      this.WriteToRegister(LCD_DataContinue, byteArray[index]);     // ……11
    }
    this.WriteToRegister(LCD_DataLast, byteArray[msg.Length - 1]);  // ……12

    Thread.Sleep(1);
  }
}
リスト1 LCD接続のためのサンプルコード(上:Module1.vb、下:Program.cs)

Visual BasicはModule1モジュール内に、C#はProgramクラス内に、上記のクラスを追記する。

  • 1文字列をバイト配列に変換する。
  • 2I2Cスレーブアドレスとして「0x3e」を指定。
  • 32行表示指定(=LCD_2LINE)で標準モード(=LCD_FUNCTIONSETLCD_8BITMODE)を設定。これは「0x38(2進数で0011 1000)」となり、「画面サイズ指定」コマンドを意味する。図6で示した表の「Function Set」という行を読めば意味が分かる。
  • 4標準モード+IS(Instruction Select: 拡張コマンド選択)ビット(=ISBit)Onで、拡張モード指定の開始を指示(=「0x39(2進数で0011 1001)」となり、「拡張コマンド設定」コマンドを意味する)。なお、IS(拡張コマンド選択)で指定できる拡張コマンドについては、ST7032データシートPDF(英語)の「IS : normal/extension instruction select」という節の中を参照してほしい。
  • 5データシートでは3.3V用に「0x56」になっているが、「0x04」(=ブースター回路On/Off)を「0」(off)にすることで、入力電圧5Vをそのまま使用する。
  • 6Follower Controlの設定には「200ms」以上待つ。
  • 7コマンドの設定には26.3μs以上必要だが、.NET Micro Frameworkの待ち合わせ時間指定は1ms単位なので切り上げて「1ms」待つ。
  • 8画面クリアの処理時間として「2ms」待ち。
  • 9画面上の文字の表示位置を先頭に指定。
  • 10文字列をバイト配列に変換。
  • 11文字列の最後の2文字までを継続データ用の制御データ(=LCD_DataContinue)を付与して1文字ずつ送信。
  • 12文字列の最後の1文字は、最終データ用の制御データ(=LCD_DataLast)を付与して送信。

 あとは、Mainメソッドで上記のAQM0802Libクラスのインスタンスを生成して、Locateメソッドで文字の表示位置を指定して、WriteMessageメソッドを呼び出して文字列でメッセージを書き込めばよい(本稿のサンプルでは、1行目に「Hello」、2行目に「現在の時間」を書き込んでいる)。その実装方法は本論ではないので、サンプルコードを参照してほしい。

アプリ実行

 アプリを実行すると1秒ごとに起動してからの時間を更新表示する。

【動画】Netduino plus 2+LCD

まとめ

 NetduinoからLCDが使えるようになると、Debug.Printメソッドを使ってVisual Studioの[出力]ウィンドウで動作を確認しなくてもよくなる。NetduinoはVisual Studioと接続しなくても動作するので、動作状況や測定結果などをLCDに表示できるようにすると、PCと接続せずに動作できるようになる。

 次回は、前回の温度センサーと今回のLCDを同時に使用して。複数の機器をI2Cで接続する方法を取り上げる。

※以下では、本稿の前後を合わせて5回分(第3回~第7回)のみ表示しています。
 連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。

.NET対応組み込みデバイス「Netduino」入門(5)
3. Netduinoアナログ入力の基本(温度センサー活用)

Lチカができたら、アナログ入力を使ってみよう。温度センサーの値をNetduinoで取得するサンプルを作成し、アナログ入力の基礎を説明する。

.NET対応組み込みデバイス「Netduino」入門(5)
4. Netduinoシリアル通信(I2C)の基本

アナログ入力の次はシリアル通信(I2C)の基礎を習得しよう。I2C対応の温度センサーの値をNetduinoで取得するサンプルを作成する。

.NET対応組み込みデバイス「Netduino」入門(5)
5. 【現在、表示中】≫ Netduinoシリアル通信(I2C)でLCD表示

シリアル通信(I2C)をさらに学ぼう。I2C通信で液晶ディスプレイ(LCD)に文字を出力するサンプルを作成する。

.NET対応組み込みデバイス「Netduino」入門(5)
6. Netduinoシリアル通信(I2C)で複数機器接続

シリアル通信(I2C)で2つ以上の機器を同時に使用するサンプルを作成する。温度センサーから室温を取得して液晶ディスプレイ(LCD)にリアルタイム表示してみよう。

.NET対応組み込みデバイス「Netduino」入門(5)
7. Netduinoの計測結果をクラウド集計

Netduinoで計測した室温データをクラウドに送信してWebから見えるようにしてみよう。

サイトからのお知らせ

Twitterでつぶやこう!