記事

Raspberry PiにI2CインターフェースのLCDモジュールを接続する(4)I2cDeviceクラス

I2cDeviceクラスと非同期処理

 I2Cの通信のため クラスI2cDeviceと呼ばれるクラスが用意されています。このクラスを利用して、I2CインターフェースをもつLCDキャラクタ・ディスプレイ・モジュールに必要な事項を表するプログラムを作成します。利用するモジュールは、秋月電子通商で販売している「I2C接続キャラクタLCDモジュール」で、本Webの「ArduinoにLCDキャラクタ・ディスプレイ・モジュールを接続する(1)」で利用するものと同じものです。
    
 Arduino場合のプログラムとRaspberry PiのC#では、プログラムの書き方が少し異なります。しかし、LCDへの書き込み方法は同様ですので、そちらの記事も参考にしてください。

I2cDeviceクラスの使い方
 I2CDeviceクラスの使い方は、

  • Windows10 IoT Coreのサンプル・プログラムI2CAccelerometer
  • Microsoft Developerのサイトの「I2Cの温度センサーをWindows 10 IoT Core for Raspberry PI2で使う」

の記事を参考にさせてもらい、プログラムの一部を利用させていただきました。
  https://blogs.msdn.microsoft.com/hirosho/2015/05/31/i2cwindows-10-iot-core-for-raspberry-pi2/
 I2Cのデバイスは、I2cDevice classとしてクラス・ライブラリのかたちで提供されます。このクラスを利用するためにプログラムの最初に、

 using Windows.Device.I2c;


の記述を追加します。
 今回は、I2cDevice型のI2CTMP102の定義を、次の命令で行います。

   private I2cDevice I2CTMP102;


 このI2CTMP102のインスタンス作成は、次のようにして行います。

                string aqs = I2cDevice.GetDeviceSelector();
                var dis = await DeviceInformation.FindAllAsync(aqs);
                var settings = new I2cConnectionSettings(TMP102_I2C_ADDR);
                settings.BusSpeed = I2cBusSpeed.FastMode;
                I2CTMP102 = await I2cDevice.FromIdAsync(dis[0].Id, settings);


(1) 最初の命令で、GetDeviceSelector()メソッドでI2Cコントローラのオブジェクトを得るための文字列をaqsにセットします。
(2) 次の命令は、最初の命令で文字列aqsに得た文字列に一致するDeviceInformationオブジェクトを列挙します。列挙されたものはdis(Windows.Devices.Enumeration.DeviceInformationCollection型)にセットされます。
(3) 次に、I2Cのデバイスのアドレスやスピードなどの条件を設定する変数に、TMP102のアドレスを設定します。その設定はI2cConnectionSettingsメソッドで行い、この命令で設定のためのオブジェクトsettingsがインスタンス化され、スレーブのアドレスも設定されています。
(4) 次の命令で、設定のためのオブジェクトのsettingsのバスの速度がsettings.BusSpeedにFastModeとして設定されます。FastModeはクロックが400kHzとなります。
(5) 最後のこの命令で、TMP102とI2Cで通信を行うオブジェクトをインスタンス化します。デバイスID(dis[0].Id)で示されたデバイスをsettingsで設定されたオブジェクトとして取得し、I2CTMP102を生成します。


 これで、I2CTMP102でTMP102の読み書きができるようになります。

非同期処理のasyncとawait
 DeviceInformation.FindAllAsync(aqs)とI2cDevice.FromIdAsync(dis[0].Id, settings)は、非同期的に処理が行われます。そのために、それぞれの命令の前に非同期的な処理を指定するawaitが記述されています。また非同期処理を行う関数であることを示すために、関数名の前にasyncを記述します。

温度センサTMP102の読み書き
 TMP102からの温度読み取りの関数は、次のようになります。

  • 温度の読み取りを指定する0x00のコマンドを書き込む
  • その後2バイトのデータを読み取る

 書き込みデータの1バイトのバッファwriteBuf、読み取ったデータを格納する2バイトのバッファreadBufを用意して、WriteReadメソッドで、次のように温度の測定を行います。読み取った値の桁を調整し、加算し、測定データを得ます。得たデータに0.0625を乗算して、測定結果を得ます。

             private double get_temp()
             {
                byte[] writeBuf = new byte[] { 0x00 };
                byte[] readBuf = new byte[2];
                I2CTMP102.WriteRead(writeBuf, readBuf);
                int valh = ((int)readBuf[0]) << 4;
                int vall = ((int)readBuf[1] >> 4);
                int val = valh + vall;
                double reading = val * 0.0625;
                textBlock.Text = reading.ToString();
                return reading;
             }

 この関数の中で、測定データreadingを文字列にTostring()メソッドで変換し、表示画面のテキスト・ブロックに、次の命令で書き込んでいます。
      

                textBlock.Text = reading.ToString();


 前回のカウントアップの数値は書き込まれなくなりました。後でともに表示する方法を検討します。
 タイマで起動される関数Timer_Tick()に、温度測定の関数get_temp()を追加しました。

            private void Timer_Tick(object sender, object e)
            {
                var currentTemp = get_temp();
                cnt = cnt + 1;
            }

 少し未整備な部分もありますが、これで動作確認を行いました。
 デバッグを開始し、MainPage()最初のブレークポイントで停止しています。

 このときのRaspberry Piのモニタ画面は、次のようになっています。

 I2Cの初期化関数InitI2c()の関数内の命令を順番に処理していくと、次に示すようにウォッチ1のリストの内容が順番に埋まっていきます。

 タイマがスタートすると、測定データが表示されます。TMP102に手をのせると、体温で表示温度が変化します。

 今回テストしたプログラムのMainPage.xaml.csを次に示します。テスト中のプログラムなので今後変わっていきますが、動作確認に利用できます。

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace i2clcd01030
{
    public sealed partial class MainPage : Page
    {
        private SolidColorBrush redBrush = new SolidColorBrush(Windows.UI.Colors.Red);
        private int cnt = 0;
        private const byte TMP102_I2C_ADDR = 0x48;
        private I2cDevice I2CTMP102;

            public MainPage()
            {
                this.InitializeComponent();
                ledball.Fill = redBrush;
                textBlock.Text = "initialmsg";
                button.Content = "countup";
                DispatcherTimer timer = new DispatcherTimer();
                timer.Interval = TimeSpan.FromMilliseconds(500);
                timer.Tick += Timer_Tick;
                InitI2c();
                timer.Start();
            }
            private async void InitI2c()
            {
                string aqs = I2cDevice.GetDeviceSelector();
                var dis = await DeviceInformation.FindAllAsync(aqs);
                var settings = new I2cConnectionSettings(TMP102_I2C_ADDR);
                settings.BusSpeed = I2cBusSpeed.FastMode;
                I2CTMP102 = await I2cDevice.FromIdAsync(dis[0].Id, settings);
            }
            private double get_temp()
            {
                byte[] writeBuf = new byte[] { 0x00 };
                byte[] readBuf = new byte[2];
                I2CTMP102.WriteRead(writeBuf, readBuf);
                int valh = ((int)readBuf[0]) << 4;
                int vall = ((int)readBuf[1] >> 4);
                int val = valh + vall;
                double reading = val * 0.0625;
                textBlock.Text = reading.ToString();
                return reading;
            }
            private void Timer_Tick(object sender, object e)
            {
                var currentTemp = get_temp();
                cnt = cnt + 1;
            }
       }
}


(2016/10/3 V1.0)

<神崎康宏>

連載メニュー Raspberry PiにI2CインターフェースのLCDモジュールを接続する

(1) Raspberry Pi 3 model Bで新規のプロジェクトを作成しスタート画面を表示する

(2) モニタにメッセージを表示する

(3) モニタのデザインにタイマを追加

(4) I2cDeviceクラスと非同期処理

(5) I2cDeviceの作成

(6) バス・リピータを使用

(7) 温度の表示

(8) プログラムの配置

(9) バックグラウンド動作