前回、TDK InvenSense社ICM-20602と通信でき、回路の正常動作が確認ができました。次はセンサデータの取得です。
参考サイトです。


ICM-20602データシート
温度
前回出てきたConfigurationの資料を再掲します。

画像引用:ICM-20602データシート
ここには、ジャイロのローパスフィルタ(3-dB BW)周波数やノイズ帯域幅(Noise BW)、センサ値取得周期(Rate)、温度のローパスフィルタ(3-dB BW)周波数が設定できます。マイクロマウスだとジャイロセンサは1kHzの制御周期で使うので、デフォルトでは250Hzなので遅いです。こちらも後ほど適切に設定する必要がありますが、初期値の0x80のままにしておきます。
温度Configurationの資料です。データシートに書いてあるアドレスは0x41と0x42。

画像引用:ICM-20602データシート
計算式が書いてあります。
TEMP_degC = (TEMP_OUT/Temp_Sensivity) + RoomTemp_Offset = (TEMP_OUT/326.8) + 25[℃]
というのをプログラムに組み込めば良さそうです。
ジャイロ
設定を見てみます。アドレスは0x1B、初期値は0x00。

画像引用:ICM-20602データシート
デフォルトのままだと、計測範囲は±250[dps]のようです。マイクロマウスだと±2000[dps]が必要なので、のちほど変更すると思いますが一旦このままで行きます。
次、計測データの資料を見ます。

画像引用:ICM-20602データシート
- ジャイロX軸:Hバイト側0x43、Lバイト側0x44
- ジャイロY軸:Hバイト側0x45、Lバイト側0x46
- ジャイロZ軸:Hバイト側0x47、Lバイト側0x48
計算式は、
GYRO_OUT= Gyro_Sensivity * angular_rate = 131 * angular_rate
というのをプログラムに組み込めば良さそうです。

画像引用:ICM-20602データシート
Gyro_Sensivityは、計測範囲±250[dps]だと131と書かれています。計測範囲±2000[dps]にする場合は、16.4に書き換えることになりそうです。
加速度
設定を見てみます。2つありますね。アドレスは0x1Cとx1D。

画像引用:ICM-20602データシート
1つ目は計測範囲の設定のようです。デフォルトの±2Gのままで行きます。2つ目は内部フィルタをいろいろ設定できるようです。こちらものちのち設定すると思いますが、そのまま初期値でいきます。
次、計測データの資料を見ます。XYZ軸それぞれで、Hバイト側とLバイト側で別れています。

画像引用:ICM-20602データシート
- 加速度X軸:Hバイト側0x3B、Lバイト側0x3C
- 加速度Y軸:Hバイト側0x3D、Lバイト側0x3E
- 加速度Z軸:Hバイト側0x3F、Lバイト側0x40
計算式が書かれていませんが、おそらくジャイロと同じようにSencivityをかけてあげれば良いのだと思います。それに当たる資料を見てみます。
Sensivityは、計測範囲±2[G]では16384と書かれています。

プログラム
前回のプログラムだと、CSでHighとLowをいちいち切り替える記述や1Byteデータの送受信がありました。そこは使いませるよう関数化してしまいます。
/**
* @brief Write 1byte to ICM-20602
* @param address
* @param data
* @retval None
*/
static void IMU_Write1byte(uint8_t addr, uint8_t data)
{
uint8_t address = addr & 0x7f;
HAL_GPIO_WritePin(SPI2_SSEL_GPIO_Port, SPI2_SSEL_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, &address, 1, 1);
HAL_SPI_Transmit(&hspi2, &data, 1, 1);
HAL_GPIO_WritePin(SPI2_SSEL_GPIO_Port, SPI2_SSEL_Pin, GPIO_PIN_SET);
}
/**
* @brief Read 1byte to ICM-20602
* @param address
* @retval Receive data
*/
static uint8_t IMU_Read1byte(uint8_t addr)
{
uint8_t address = addr | 0x80;
uint8_t data;
HAL_GPIO_WritePin(SPI2_SSEL_GPIO_Port, SPI2_SSEL_Pin, GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi2, &address, 1, 1);
HAL_SPI_Receive(&hspi2, &data, 1, 1);
HAL_GPIO_WritePin(SPI2_SSEL_GPIO_Port, SPI2_SSEL_Pin, GPIO_PIN_SET);
return data;
}
設定の部分はイニシャライズ関数にまとめてしまいます。なぜかダミーデータを一度送らないと、データ取得できず不安定になりました。参考サイトにも注意事項として記述があり、お作法があるようです。
/**
* @brief Initialize ICM-20602
* @param None
* @retval None
*/
static void IMU_Initialize(void)
{
// Dummy WHO AM I send
uint8_t who_am_i = IMU_Read1byte(0x75);
// POWER MANAGEMENT 1
IMU_Write1byte(0x6B, 0b00000000); // Wake up & Internal 20MHz oscillator
HAL_Delay(50);
// WHO AM I
who_am_i = IMU_Read1byte(0x75);
printf("who am i = %x\n\r", who_am_i);
// CONFIGURATION
IMU_Write1byte(0x1A, 0x80);
HAL_Delay(50);
// ACCELEROMETER CONFIGURATION
IMU_Write1byte(0x1C, 0b00000000); // Accel Full Scale Select ±2g
HAL_Delay(20);
// GYROSCOPE CONFIGURATION
IMU_Write1byte(0x1B, 0b00000000); // Gyro Full Scale Select ±250 dps
HAL_Delay(50);
}
main関数で用意する変数とイニシャライズ関数の実行です。
/* USER CODE BEGIN 2 */
setbuf(stdout, NULL);
setbuf(stdin, NULL );
printf("Started!\n\r");
int16_t accel_x_value;
int16_t accel_y_value;
int16_t accel_z_value;
float accel_x;
float accel_y;
float accel_z;
int16_t gyro_x_value;
int16_t gyro_y_value;
int16_t gyro_z_value;
float gyro_x_ang_vel;
float gyro_y_ang_vel;
float gyro_z_ang_vel;
int16_t temp_value;
float temp;
IMU_Initialize();
/* USER CODE END 2 */
計測と表示部分です。
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// ACCELEROMETER MEASUREMENT ACCEL_XOUT_H & ACCEL_XOUT_L
accel_x_value = ( ((int16_t)IMU_Read1byte(0x3B)<<8) | ((int16_t)IMU_Read1byte(0x3C)&0x00ff) );
accel_x = (float)accel_x_value / 16384;
printf("ACCEL_XOUT = %6d[Raw] = %6.3lf[g]\n\r", accel_x_value, accel_x);
// ACCELEROMETER MEASUREMENT ACCEL_YOUT_H & ACCEL_YOUT_L
accel_y_value = ( ((int16_t)IMU_Read1byte(0x3D)<<8) | ((int16_t)IMU_Read1byte(0x3E)&0x00ff) );
accel_y = (float)accel_y_value / 16384;
printf("ACCEL_YOUT = %6d[Raw] = %6.3lf[g]\n\r", accel_y_value, accel_y);
// ACCELEROMETER MEASUREMENT ACCEL_ZOUT_H & ACCEL_ZOUT_L
accel_z_value = ( ((int16_t)IMU_Read1byte(0x3F)<<8) | ((int16_t)IMU_Read1byte(0x40)&0x00ff) );
accel_z = (float)accel_z_value / 16384;
printf("ACCEL_ZOUT = %6d[Raw] = %6.3lf[g]\n\r", accel_z_value, accel_z);
// GYROSCOPE MEASUREMENT GYRO_XOUT_H & GYRO_XOUT_L
gyro_x_value = ( ((int16_t)IMU_Read1byte(0x43)<<8) | ((int16_t)IMU_Read1byte(0x44)&0x00ff) );
gyro_x_ang_vel = (float)gyro_x_value / 131;
printf(" GYRO_XOUT = %5d[Raw] = %8.3lf[deg/s]\n\r", gyro_x_value, gyro_x_ang_vel);
// GYROSCOPE MEASUREMENT GYRO_YOUT_H & GYRO_YOUT_L
gyro_y_value = ( ((int16_t)IMU_Read1byte(0x45)<<8) | ((int16_t)IMU_Read1byte(0x46)&0x00ff) );
gyro_y_ang_vel = (float)gyro_y_value / 131;
printf(" GYRO_YOUT = %5d[Raw] = %8.3lf[deg/s]\n\r", gyro_y_value, gyro_y_ang_vel);
// GYROSCOPE MEASUREMENT GYRO_ZOUT_H & GYRO_ZOUT_L
gyro_z_value = ( ((int16_t)IMU_Read1byte(0x47)<<8) | ((int16_t)IMU_Read1byte(0x48)&0x00ff) );
gyro_z_ang_vel = (float)gyro_z_value / 131;
printf(" GYRO_ZOUT = %5d[Raw] = %8.3lf[deg/s]\n\r", gyro_z_value, gyro_z_ang_vel);
// TEMPERATURE MEASUREMENT GYRO_ZOUT_H & GYRO_ZOUT_L
temp_value = ( ((int16_t)IMU_Read1byte(0x41)<<8) | ((int16_t)IMU_Read1byte(0x42)&0x00ff) );
temp = (float)temp_value / 326.8 + 25.0;
printf(" TEMP_OUT = %5d[Raw] = %3.1lf[℃]\n\r", temp_value, temp);
HAL_Delay(10);
// 画面クリア&カーソル初期化
printf("\033[2J");
printf("\033[%d;%dH" ,0,0);
/* USER CODE END WHILE */
Hバイト側とLバイト側の1バイトずつ取得したデータを2バイトに統合して用意した変数に格納します。
その生データを、それぞれ計算式(Sensivityの値とかをかける)で分かりやすい単位に変換します。
実行
出力してみます。静止時のセンサ生データと単位変換したデータです。

動画です。
静止時のデータなので、Z軸加速は正常に1Gを表示しています。なぜか、Y軸加速度だけ-2Gに張り付いてしまいました。アドレスだけしか変えてないので、原因がわかりません。機体を変えても同様なので、故障ではなさそうです。
Who Am I ダミーデータの送信以外で、何らかのお作法があるのでしょうか?後ほどオシロスコープで調べることになりそうです。
次回は、定期割り込み処理を行います。
