[C#/WinIoT/I2C] ラズパイ＋WindowsIoTCore＋C# で9軸センサ(MPU-9150)の値をとる

**もくじ**
→https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと
Windows Iot Coreを使って、I2Cに接続するデバイスと通信したい。
たまたま手元にある掲題の9軸センサで、I2Cで通信する練習をしたい。

# 作成作業
下記の機材とソフトを使用する。

- ラズベリーパイ3
- 9軸センサーモジュール(共立のKP-9150／InvenSenseのMPU-9150センサを搭載)
- VisauStudio2019(UWP/C#)


また、下記のようなイメージで作業を進める。

- 回路作成。ラズパイ3とセンサをつなげる回路を作成
- センサとのやり取りの流れ確認。何をどう読み書きすればよいか確認
- プログラム作成。センサとI2Cで通信するWinIoT上で動くUWPプログラムを作成

## 回路作成
下記のような回路を作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/6c21bd3d-3ebd-7ec3-797c-b3c0291b09bc.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/a25bd800-fa36-7361-a61c-a5642997601d.png)


## センサとのやり取りの流れ確認
下記のような手順で、通信を行う。

- I2Cで通信するための準備
 - Windows IoT Extensions for UWPを参照に追加する
 - usingの追加
- イニシャライズ通信
 - アドレス0x75を指定して読み込み
 - アドレス0x6Bに0x00を書き込み
 - アドレス0x37に0x02を書き込み
- センサ値の取得
 - アドレス0x3Bを指定して読み込み

## プログラム作成

### Windows IoT Extensions for UWPを参照に追加する

- プロジェクトの参照を右クリックし、[プロパティ]をクリック
- 「Universal Windows」の「拡張」の中から、Windows IoT Extensions for UWP(今回は10.0.15063.0)を選択し追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/ed7c52e6-19fc-5698-6613-d18194caf2de.png)

### usingの追加
I2Cデバイスの制御に必要な、下記のusingを追加する。

- using Windows.UI.Xaml.Controls;
- using Windows.Devices.Enumeration;
- using Windows.Devices.I2c;

### イニシャライズ通信
上の[センサとのやり取りの流れ確認]のところで確認した流れを実装する。

```MainPage.xaml.cs
private async void InitI2CAccel()
{
    // すべてのI2Cデバイスを取得するためのセレクタ文字列を取得
    string aqs = I2cDevice.GetDeviceSelector("I2C1");

    // セレクタ文字列を使ってI2Cコントローラデバイスを取得
    var dis = await DeviceInformation.FindAllAsync(aqs); 
    
    if (dis.Count == 0)
    {
        Text_Status.Text = "I2Cコントローラデバイスが見つかりませんでした";
        return;
    }
    
    // I2Cアドレスを指定して、デフォルトのI2C設定を作成する
    // private const byte ACCEL_I2C_ADDR = 0x68;
    var settings = new I2cConnectionSettings(ACCEL_I2C_ADDR);

    // バス速度を設定(FastMode：400 kHz)(指定しないと、標準設定(StandardMode：100kHz)になる)
    settings.BusSpeed = I2cBusSpeed.FastMode;

    // 取得したI2Cデバイスと作成した設定で、I2cDeviceのインスタンスを作成
    I2CAccel = await I2cDevice.FromIdAsync(dis[0].Id, settings);

    if (I2CAccel == null)
    {
        Text_Status.Text = string.Format(
            "スレーブアドレス {0} の I2C コントローラー {1} はほかのアプリで使用されています。他のアプリで使用されていないか、確認してください。",
            settings.SlaveAddress, dis[0].Id);
        return;
    }
    
    // ジャイロセンサ(MPU-9150)の設定を行うための通信
    try
    {
        // 送信用バッファ
        byte[] WriteBuf;
        // 受信用バッファ
        byte[] ReadBuf;

        // アドレス取得(イニシャルに必須ではない。どこのサンプルにも書いてるので一応やっとく)
        WriteBuf = new byte[] { 0x75 };
        ReadBuf = new byte[1];
        I2CAccel.WriteRead(WriteBuf, ReadBuf);

        // センシング開始要求(レジスタ0x6Bに00を書き込み)
        WriteBuf = new byte[] { 0x6B, 0x00 };
        I2CAccel.Write(WriteBuf);

        // センシング開始要求(レジスタ0x37に02を書き込み)
        WriteBuf = new byte[] { 0x37, 0x02 };
        I2CAccel.Write(WriteBuf);
    }
    catch (Exception ex)
    {
        Text_Status.Text = "デバイスとの通信に失敗しました。: " + ex.Message;
        return;
    }

    // 初期化完了。100msごとにデータを採るためのコールバックを作成する
    periodicTimer = new Timer(this.TimerCallback, null, 0, 100);
}
```

これで、センサーに、通信開始を要求したことになる。

### イニシャライズ通信の時に読んでるAPIについて

**・I2CAccel.Write(WriteBuf);**
引数の1バイト目に書き込みたいレジスタのアドレス、2バイト目以降にそのレジスタ以降に書き込みたいデータをいれておくと、書き込みを行ってくれる。

**I2CAccel.WriteRead(WriteBuf, ReadBuf)**
WriteBufに読み込みたいアドレス、ReadBufに呼んだデータのバッファを渡すと、WriteBufで指定したレジスタから読んだデータをReadBufに入れてくれる。
指定したレジスタアドレスから何バイト読むかは、バッファ(ReadBuf)のバイト数で決まる。
(var ReadBuf = new byte[14] としてたら、勝手に先頭から14バイト読んで入れてくれる))

**レジスタ0x6Bに00を書き込み、レジスタ0x37に02を書き込みしている部分について**
なぜこの2つに値を書き込むのか、忘れてしまった。([レジスタのデータシート](https://strawberry-linux.com/pub/RM-MPU-9150A-00.pdf)を見ても、よくわからなかった)
一旦、MPU-9150ではお決まりの手順としてこのまま置いておく。
(後で調べる)

⇒19/08/26追記
`0x6B`の方は「Sleep解除」([こちら](http://www.widesnow.com/entry/2015/09/10/061128)参照)
`0x37`の方は「I2Cで磁気センサ機能(AK8963)へアクセスできるようにする(BYPASS_EN=1)」([こちら](https://qiita.com/boyaki_machine/items/915f7730c737f2a5cc79)参照)

### センサ値の取得
上の`periodicTimer = new Timer(this.TimerCallback, null, 0, 100);`のところで、100msごとに呼ぶようにしたハンドラを実装する。

```cs
private void TimerCallback(object state)
{
    // 送信用バッファ
    byte[] WriteBuf;
    // 受信用バッファ
    byte[] ReadBuf;

    // アドレス取得
    WriteBuf = new byte[] { 0x3B };
    ReadBuf = new byte[14];
    I2CAccel.WriteRead(WriteBuf, ReadBuf);

    var task = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
    {
        Text_X_Axis.Text =      "加速度X：" + (ConvertSignedShortData2Decimal((short)ReadBuf[0] * 0x100 + (short)ReadBuf[1]) / 16384.0m).ToString("F1") + "";
        Text_Y_Axis.Text =      "加速度Y：" + (ConvertSignedShortData2Decimal((short)ReadBuf[2] * 0x100 + (short)ReadBuf[3]) / 16384.0m).ToString("F1") + "";
        Text_Z_Axis.Text =      "加速度Z：" + (ConvertSignedShortData2Decimal((short)ReadBuf[4] * 0x100 + (short)ReadBuf[5]) / 16384.0m).ToString("F1") + "";
        Text_Temperature.Text = "温度："    + (ConvertSignedShortData2Decimal((short)ReadBuf[6] * 0x100 + (short)ReadBuf[7]) / 340.0m + 35.0m).ToString("F1") + "℃";
        Text_X_Axis_Gyro.Text = "Gyro　X：" + (ConvertSignedShortData2Decimal((short)ReadBuf[8] * 0x100 + (short)ReadBuf[9]) / 131.0m).ToString("F1") + "";
        Text_Y_Axis_Gyro.Text = "Gyro　Y：" + (ConvertSignedShortData2Decimal((short)ReadBuf[10] * 0x100 + (short)ReadBuf[11]) / 131.0m).ToString("F1") + "";
        Text_Z_Axis_Gyro.Text = "Gyro　Z：" + (ConvertSignedShortData2Decimal((short)ReadBuf[12] * 0x100 + (short)ReadBuf[13]) / 131.0m).ToString("F1") + "";
        Text_Status.Text = "Status：計測中";
    });
}
```

とれたデータを、画面の各表示用テキストに書き込んでいる。
各値で計算しているのは、データシートに従い生データを意味の分かる値に計算するためのもの。
[こちら](http://www.widesnow.com/entry/2015/09/10/061128)を参照。

# コード
https://github.com/tera1707/PrivateRepos/tree/master/I2CAccelerometer/CS


# 参考
パイソンでの超わかりやすいMPU-9150制御プログラム
言語は違うが、制御の流れはほぼこちらのやり方で行けると思う。すごいわかりやすい
https://qiita.com/boyaki_machine/items/915f7730c737f2a5cc79

I2CDeviceクラス(MsDocs)
https://docs.microsoft.com/en-us/uwp/api/windows.devices.i2c.i2cdevice

Windows IoT Coreのサンプル
https://github.com/microsoft/Windows-iotcore-samples/blob/develop/Samples/Accelerometer/CS/

9軸センサモジュールのページ
http://www.kyohritsu.jp/eclib/PROD/MANUAL/kp9150.pdf

MPU-9150の通信開始手順の参考にしたもの
https://strawberry-linux.com/pub/mpu-9150-manual.pdf

MPU-9150レジスタマップ
https://strawberry-linux.com/pub/RM-MPU-9150A-00.pdf

データ送信/受信時のフォーマット(1バイト目がアドレス、2バイト目が書き込みたいデータ)(５．タイミングパラメータのところ)
https://monolizm.com/sab/pdf/%E7%AC%AC16%E5%9B%9E_%E3%83%97%E3%83%AC%E3%82%BC%E3%83%B3%E8%B3%87%E6%96%99(IC2%E9%80%9A%E4%BF%A1%E7%B7%A8).pdf

センサ値の計算式参考
http://www.widesnow.com/entry/2015/09/10/061128