もくじ
→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プログラムを作成
回路作成
センサとのやり取りの流れ確認
下記のような手順で、通信を行う。
- 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)を選択し追加
usingの追加
I2Cデバイスの制御に必要な、下記のusingを追加する。
- using Windows.UI.Xaml.Controls;
- using Windows.Devices.Enumeration;
- using Windows.Devices.I2c;
イニシャライズ通信
上の[センサとのやり取りの流れ確認]のところで確認した流れを実装する。
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つに値を書き込むのか、忘れてしまった。(レジスタのデータシートを見ても、よくわからなかった)
一旦、MPU-9150ではお決まりの手順としてこのまま置いておく。
(後で調べる)
⇒19/08/26追記
0x6B
の方は「Sleep解除」(こちら参照)
0x37
の方は「I2Cで磁気センサ機能(AK8963)へアクセスできるようにする(BYPASS_EN=1)」(こちら参照)
センサ値の取得
上のperiodicTimer = new Timer(this.TimerCallback, null, 0, 100);
のところで、100msごとに呼ぶようにしたハンドラを実装する。
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:計測中";
});
}
とれたデータを、画面の各表示用テキストに書き込んでいる。
各値で計算しているのは、データシートに従い生データを意味の分かる値に計算するためのもの。
こちらを参照。
コード
参考
パイソンでの超わかりやすい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バイト目が書き込みたいデータ)(5.タイミングパラメータのところ)
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