はじめに
I2C接続の3軸加速度センサー+3軸ジャイロセンサーをネットの検索結果やChatGPTとのやり取りでPicoMite(MMBasic)での利用できるようになったのでその方法をまとめた。
3軸加速度センサー+3軸ジャイロセンサー
加速度センサーは物体の直線的な動きである加速度を、ジャイロセンサーは物体の回転である角速度を検知できるセンサーです。
今回はAmazonで購入したREES52 MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュールを使用した。
このセンサの機能は次の通りです。
- 3軸MEMSジャイロスコープ(MPU-60X0)
- X・Y・Z 各軸独立の振動型コリオリ式ジャイロを搭載
- 回転によるコリオリ力で振動→静電容量検出→増幅・復調・フィルタリング→角速度に比例した電圧を生成
- 各軸ごとにオンチップ16ビットADCでデジタル化
- フルスケール範囲:±250/±500/±1,000/±2,000 °/s(プログラム選択)
- サンプリングレート:8,000 SPS~3.9 SPS(プログラム選択)
- 選択可能なローパスフィルタで多様なカットオフ周波数を設定可能
- 3軸MEMS加速度計(MPU-60X0)
- 各軸に独立したプルーフマス+差動静電容量センサーを配置
- 製造ばらつき・熱ドリフトに強いアーキテクチャ
- 水平設置時:X/Y軸=0 g、Z軸=+1 g を測定
- 工場校正済みのスケール係数(電源電圧に依存しない)
- 各軸ごとに専用シグマデルタ16ビットADCでデジタル出力
- フルスケール範囲:±2/±4/±8/±16 g(プログラム選択)
X、Y、Z軸の方向は次の写真のとおりです。
配線
次のように配線しました。MPU6050はRaspberry Pi Picoとは別のブレッドボードに載せてセンサだけを動かせるようにした。
MPU6050のデータ読み取りと表示プログラム
MPU6050からの読み取ったデータをOLEDに表示するプログラムを次の手順で作成した。
処理手順
- 定数、変数の定義
- MPU6050のジャイロスコープ、加速度計、電力モード、クロックソースなどを設定
- 無限ループ内でMPU6050から加速度、角速度、温度データを読み取り、16ビット符号付きの数値への変換
- 加速度、角速度、温度をOLEDへ出力
プログラム
Option EXPLICIT ' Forcing a variable to be defined
CLS
' Define constat values
Const MPUADDR = &H68 ' MPU6050 I2C address
Const REG_CONFIG = &H1A ' Configuration register
Const REG_GYRO_CONFIG = &H1B ' Gyroscope configuration register
Const REG_ACC_CONFIG = &H1C ' Accelerometer configuration
Const REG_PWR_MGMT1 = &H6B ' Power management 1 configuration register
Const REG_ACC_XOUT_H = &H3B ' Accelerometer X output High bit register
Const REG_TEMP_OUT_H = &H41 ' Temperature register
Const REG_GYR_XOUT_H = &H43 ' Gyroscope X output High bit register
Const SENS_A = 16384 ' Accelerometer LSB sensitive
Const SENS_G = 131 ' Gyroscope LSB sensitive
' Define variables
Dim acc_x, acc_y, acc_z ' store Accelemeter data
Dim gyr_x, gyr_y, gyr_z ' store Gyrometer data
Dim temp ' store Temperature data
' Define I2C pins
SetPin GP17, GP16, I2C
' Open I2C channel 1 100kHz, timeout 100msec
I2C OPEN 100, 100
' Send configuration to MPU6050
I2C WRITE MPUADDR, 0, 2, REG_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_GYRO_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_ACC_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_PWR_MGMT1, &H00
Do
' Get accelerometer X, Y, Z value
acc_x = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H) / SENS_A
acc_y = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H + 2) / SENS_A
acc_z = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H + 4) / SENS_A
' Get Temerature value
temp = ReadWordToSigned16(MPUADDR, REG_TEMP_OUT_H) / 340 + 36.53
' Get Gyroscope X, Y, Z value
gyr_x = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H) / SENS_G
gyr_y = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H + 2) / SENS_G
gyr_z = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H + 4) / SENS_G
' Draw values to OLED
Text 0, 0, "acc x = " + Str$(acc_x, -3, 3) + "g ",,7
Text 0, 8, " y = " + Str$(acc_y, -3, 3) + "g ",,7
Text 0, 16, " z = " + Str$(acc_z, -3, 3) + "g ",,7
Text 0, 24, "gyr x = " + Str$(gyr_x, -3, 3) + "/s",,7
Text 0, 32, " y = " + Str$(gyr_x, -3, 3) + "/s",,7
Text 0, 40, " z = " + Str$(gyr_z, -3, 3) + "/s",,7
Text 0, 48, "temp = " + Str$(temp, -3, 2) + "C",,7
Pause 500
Loop
' Define function, get data and calcurate 16 signed value
Function ReadWordToSigned16(ADDR As Integer, REG As Integer) As Integer
Local data%(2), tmp ' Define local variables
I2C WRITE MPUADDR, 0, 1, REG ' Send register #
I2C READ MPUADDR, 0, 2, data%() ' Read register value
tmp = data%(0) << 8 Or data%(1) ' make 16bit value
If tmp >= &H8000 Then tmp = tmp - &H10000 ' Check negative value, make signed value
ReadWordToSigned16 = tmp ' Return 16bit signed value
End Function
End
変数宣言の強制
OPTION EXPLICIT
MMBasicではプログラム冒頭でOPTION EXPLICIT
コマンドを記述することで変数宣言を強制できる。このコマンドにより宣言していない変数は使用できなくなる。
MPU6050のレジスタなどを定数として定義
Const MPUADDR = &H68
Const REG_CONFIG = &H1A
略
プログラム中で使用するI2Cアドレス、MPU6050の各種レジスタのアドレスなどの数値を定数として定義している。
定義した定数は次の表の通り。レジスタのアドレスは MPU6050レジスタマップに記されている。
定数 | 値(16進) | 説明 |
---|---|---|
MPUADD | &H68 | MPU6050のI2Cアドレス |
REG_CONFIG | &H1A | 設定レジスタ |
REG_GYRO_CONFIG | &H1B | ジャイロメータ設定レジスタ |
REG_ACC_CONFIG | &H1C | 加速度計設定レジスタ |
REG_PWM_MGMT1 | &H6B | 電力管理1設定レジスタ |
REG_ACC_XOUT_H | &H3B | 加速時計X軸データ上位8ビットの値格納レジスタ |
REG_TEMP_XOUT_H | &H41 | 温度X軸データ上位8ビットの値格納レジスタ |
REG_GYR_XOUT_H | &H43 | ジャイロスコープX軸データ上位8ビットの値格納レジスタ |
SENS_A | 16384 | 加速度計LSB感度値 |
SENS_G | 131 | ジャイロスコープLSB感度値 |
変数宣言
Dim acc_x, acc_y, acc_z
Dim gyr_x, gyr_y, gyr_z
Dim temp
宣言した変数は下記の7つで型指定の後置子を省略しているのですべて浮動小数点数型になる。
変数 | 格納する値 |
---|---|
acc_x | X軸方向の加速度 |
acc_y | Y軸方向の加速度 |
acc_z | Z軸方向の加速度 |
gyr_x | X軸方向の角速度 |
gyr_y | Y軸方向の角速度 |
gyr_z | Z軸方向の角速度 |
temp | 温度 |
I2CのオープンとMPU6050の設定
Raspberry Pi PicoとMPU6050との間のI2Cチャンネル1で接続するピンを配線図に従って指定し、クロック周波数100kHz、タイムアウト100ミリ秒で開いている。
SetPin GP17, GP16, I2C
OPEN 100, 100
MPU6050の設定
I2C WRITE MPUADDR, 0, 2, REG_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_GYRO_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_ACC_CONFIG, &H00
I2C WRITE MPUADDR, 0, 2, REG_PWR_MGMT1, &H00
I2C WRITE
コマンドでMPU6050の複数の設定用レジスタへ設定を設定値&H00
を書き込んでいる。設定値&H00はそれぞれの設定で次に示すの意味をもつ。
設定レジスタ(REG_CONFIG)
ジャイロスコープと加速度センサーの両方について、外部フレーム同期(FSYNC)ピンのサンプリングとデジタルローパスフィルタ(DLPF)の設定を行います。レジスタの構成は下記のとおり。
レジスタ | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|
1A | - | - | EXT_SYNC_SET[2:0] | DLPF_CFG[2:0] | ||||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
ジャイロスコープと加速度計は外部のフレーム同期(EXT_SYNC_SETを0)を使用せず、ディジタルローバスフィルタの設定で加速度計は帯域幅260Hz、遅延0msで、ジャイロスコープは帯域幅256Hz、遅延0.98ms、サンプリング周波数8kHzにする(DLPF_CFGを0)。3ビットのDLPF_CFGは下の表の8とおりの値の組み合わせを示す。
DLPF_CFG | 加速時計 | ジャイロスコープ | |||
---|---|---|---|---|---|
(Fs=1kHz) | 帯域幅(Hz) | 遅延(ms) | 帯域幅(Hz) | 遅延(ms) | Fs(kHz) |
0 | 260 | 0 | 256 | 0.98 | 8 |
1 | 184 | 2.0 | 188 | 1.9 | 1 |
2 | 94 | 3.0 | 98 | 2.8 | 1 |
3 | 44 | 4.9 | 42 | 4.8 | 1 |
4 | 21 | 8.5 | 20 | 8.3 | 1 |
5 | 10 | 13.8 | 10 | 13.4 | 1 |
6 | 5 | 19.0 | 5 | 18.6 | 1 |
7 | 予約済み | 予約済み | 8 |
ジャイロスコープ設定(REG_GYRO_CONFIG)
ジャイロスコープのセルフテストの有無とフルスケールレンジを設定するレジスタ。レジスタの構成は下記のとおり。
レジスタ | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|
1B | XG_ST | YG_ST | ZG_ST | FS_SEL[1:0] | - | - | - | |
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
ジャイロスコープのX、Y、Z軸のセルフテストは実行せず(Bit7〜Bit5を0)、フルスケールレンジを±250°/sにする(FS_SEL[1:0]を0)。LSB感度は下表から131LSB/°/sになる。
FS_SEL | フルスケールレンジ | LSB感度(SENS_G) |
---|---|---|
0 | ±250°/s | 131 LSB/°/s |
1 | ±500°/s | 65.5 LSB/°/s |
2 | ±1000°/s | 32.8 LSB/°/s |
3 | ±2000°/s | 16.4 LSB/°/s |
加速度計設定(REG_ACC_CONFIG)
加速度計のセルフテストの有無、フルスケール範囲を設定する。また、このレジスタはデジタルハイパスフィルタ(DHPF)も設定します。レジスタの構成は下記のとおり。
レジスタ | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|
1C | XA_ST | YA_ST | ZA_ST | AFS_SEL[1:0] | - | |||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
加速度計のX、Y、Z軸のセルフテストは実行せず(Bit7〜Bit5を0)、フルスケールレンジを±2gにする(AFS_SEL[1:0]を0)LSB感度は下表から16384 LSB/gになる。
AFS_SEL | フルスケールレンジ | LSB感度(SENS_A) |
---|---|---|
0 | ±2g | 16384 LSB/g |
1 | ±4g | 8192 LSB/g |
2 | ±8g | 4096 LSB/g |
3 | ±16g | 2048 LSB/g |
電力管理1の設定(REG_PWM_MGMT1)
ユーザーが電力モードとクロックソースを設定する。デバイス全体のリセット、温度センサーを無効にするためのビットも備えています。
レジスタ | Bit7 | Bit6 | Bit5 | Bit4 | Bit3 | Bit2 | Bit1 | Bit0 |
---|---|---|---|---|---|---|---|---|
6B | DEVICE_RESET | SLEEP | CYCLE | - | TEMP_DIS | CLKSEL[2:0] | ||
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
クロックに内蔵8MHz発振器(CLKSELを0)を使い、スリープをせず(SLEEPを0)、温度センターを有効にする(TEMP_DISを0)。
CLKSEL | クロックソース |
---|---|
0 | 内蔵8MHz発振器 |
1 | X軸ジャイロスコープリファレンス付きPLL |
2 | Y軸ジャイロスコープリファレンス付きPLL |
3 | Z軸ジャイロスコープリファレンス付きPLL |
4 | 外部32.768MHzリファレンス付きPLL |
5 | 外部19.2MHzリファレンス付きPLL |
6 | 予約済み |
7 | クロックを停止し、タイミングジェネレータをリセット状態に保つ |
MPU6050から読み取ったデータから加速度、角速度、温度を算出
acc_x = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H) / SENS_A
acc_y = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H + 2) / SENS_A
acc_z = ReadWordToSigned16(MPUADDR, REG_ACC_XOUT_H + 4) / SENS_A
temp = ReadWordToSigned16(MPUADDR, REG_TEMP_OUT_H) / 340 + 36.53
gyr_x = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H) / SENS_G
gyr_y = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H + 2) / SENS_G
gyr_z = ReadWordToSigned16(MPUADDR, REG_GYR_XOUT_H + 4) / SENS_G
後述するMPU6050から加速度、角速度、温度のデータを読み取る関数ReadWordToSigned16
を使用して取得したそれぞれのセンサーの16ビット符号付きの値を、それぞれの感度等の値で除して加速度、角速度、温度の値を算出しています。
MPU6050の加速度とジャイロスコープのX、Y、Z軸のデータを保持するレジスタは次の表(レジスタマップより)に示すように連続しているのでX軸の上位8ビットのレジスタの値に2および4を加算すればY軸、Z軸のレジスタにアクセスできる。
アドレス | 名前 | Bit7〜Bit0 | 格納されている値 |
---|---|---|---|
3B | ACCEL_XOUT_H | ACCEL_XOUT[15:8] | 加速度のX軸データの上位8ビット |
3C | ACCEL_XOUT_L | ACCEL_XOUT[7:0] | 加速度のX軸データの下位8ビット |
3D | ACCEL_YOUT_H | ACCEL_YOUT[15:8] | 加速度のY軸データの上位8ビット |
3E | ACCEL_YOUT_L | ACCEL_YOUT[7:0] | 加速度のY軸データの下位8ビット |
3F | ACCEL_ZOUT_H | ACCEL_ZOUT[15:8] | 加速度のZ軸データの上位8ビット |
40 | ACCEL_ZOUT_L | ACCEL_ZOUT[7:0] | 加速度のZ軸データの下位8ビット |
41 | TEMP_OUT_H | TEMP_OUT[15:8] | 温度データの上位8ビット |
42 | TEMP_OUT_L | TEMP_OUT[7:0] | 温度データの下位位8ビット |
43 | GYRO_XOUT_H | GYRO_XOUT[15:8] | ジャイロスコープのX軸データの上位8ビット |
44 | GYRO_XOUT_L | GYRO_XOUT[7:0] | ジャイロスコープのX軸データの下位8ビット |
45 | GYRO_YOUT_H | GYRO_YOUT[15:8] | ジャイロスコープのY軸データの上位8ビット |
46 | GYRO_YOUT_L | GYRO_YOUT[7:0] | ジャイロスコープのY軸データの下位8ビット |
47 | GYRO_ZOUT_H | GYRO_ZOUT[15:8] | ジャイロスコープのZ軸データの上位8ビット |
48 | GYRO_ZOUT_L | GYRO_ZOUT[7:0] | ジャイロスコープのZ軸データの下位8ビット |
OLEDへの出力
Text 0, 0, "acc x = " + Str$(acc_x, -3, 3) + "g ",,7
Text 0, 8, " y = " + Str$(acc_y, -3, 3) + "g ",,7
Text 0, 16, " z = " + Str$(acc_z, -3, 3) + "g ",,7
Text 0, 24, "gyr x = " + Str$(gyr_x, -3, 3) + "/s",,7
Text 0, 32, " y = " + Str$(gyr_x, -3, 3) + "/s",,7
Text 0, 40, " z = " + Str$(gyr_z, -3, 3) + "/s",,7
Text 0, 48, "temp = " + Str$(temp, -3, 2) + "C",,7
直前までに得られた加速度、角速度、温度をStr\$関数で文字列に変換し、TextコマンドでOLEDへ出力する。Str\$関数の2番目の引数は整数部の桁数を指定するが-をつけることで正負の符号を常に不可することになる。
測定される温度はチップの温度です。
関数ReadWordToSigned16
Function ReadWordToSigned16(ADDR As Integer, REG As Integer) As Integer
Local data%(2), tmp
I2C WRITE MPUADDR, 0, 1, REG
I2C READ MPUADDR, 0, 2, data%()
tmp = data%(0) << 8 Or data%(1)
If tmp >= &H8000 Then tmp = tmp - &H10000
ReadWordToSigned16 = tmp
End Function
関数ReadWordToSigned16
は次の処理をしています。
- 引数に与えられたデバイスのI2Cアドレス(ADDR)のレジスタ(REG)に格納されている2バイトのデータを配列data%()に格納します
- 配列に格納した2つの8ビットデータのうち、上位8ビットを左へ8ビットシフト下値と下位8ビットの値の論理和で結合して16ビットの値にする。
temp = data%(0) << 8 Or data%(1)
- 得られた16ビットデータは2の補数表現なので正数の&H0000〜&H7000(10進で0〜32767)の場合はその値を,負数の&H8000〜&HFFFF(10進の32768〜65535)の場合には&H10000(10進の65536)から値を減じて符号付きにした値を返している
MMBasicでは変数や関数の戻り値の型指定は後置子を使う以外に変数 As 型
という書式でも指定できます。戻り値は関数名への代入で指定します(ReadWordToSigned16 = tmp
)。
MPU6050からのデータ読み出しの別な方法
ChatGPTにコードを問い合わせて得られた別のデータ読み出し方法としてはMPU6050のレジスタACCEL_XOUT_H(&H3B)からGYRO_ZOUT_L(&H48)まで14このレジスタが連続しているので14バイト分を一気に読み込む方法がある。読み込みを始めるレジスタを書き込むときにストップコンディションを送信しないように指定すればよい。
Dim buf%(14)
I2C WRITE MPUADDR, 1, 1, REG_ACCEL_XOUT_H
I2C READ MPUADDR, 0, 14, buf%(0)
動作確認
プログラムを実行させ、MPU6050の向きを変えて加速度(重力加速度)を取得した結果を以下に示す。
センサ基盤を水平に置いたとき(Z軸方向への重力加速度)
基盤のシルク印刷面を上にしたとき。Z軸+方向で、+1になるはずだがわずかにずれている。
基盤のシルク印刷面を下向きにしたとき。Z軸ー方向で、–1になるはずだがわずかにずれている。
センサ基盤の短辺を上にしたとき(Y軸方向への重力加速度)
VCCなどのピンを左側にした状態で基盤の短辺を上にしたとき。Y軸+方向で、+1になるはずだがこれもわずかにずれている。
VCCなどのピンを右側にした状態で基盤の短辺を上にしたとき。Y軸ー方向で、–1になるはずだがこれもわずかにずれている。
センサ基盤の長辺を上にしたとき
基盤の長辺のピンを上にしたとき。X軸+方向で、+1になるはずだがこれもわずかにずれている。
基盤の長辺のピンを下にしたとき。X軸ー方向で、–1になるはずだがこれもわずかにずれている。
以上のようにRaspberry Pi PicoのMMBasicでもI2C接続の3軸加速度センサー+3軸ジャイロセンサーからデータを取得できることが確認できた。