こんにちは。koraです。
今回は、新型マウスのSPI通信を設定します。
CubeMXの設定
今回製作したマイクロマウスは、STM32F732マイコンに搭載されている3つのSPIのうち2つを使用します。左右のエンコーダがSPI1に、ジャイロセンサがSPI2に接続してあります。
Pinoutの設定
ピンの設定画面でSPI1とSPI2の設定を行います。
- MISO (Master In Slave Out)
- MOSI (Master Out Slave In)
- SCK (Serial Clock)
SPI通信では、マスタが通信相手を切り替えるためのCSピン (Chip Select あるいは SS: Slave Select) が必要なので、GPIOピンを割り当てます。同時に、System Core → GPIO → Configurationを開いて、GPIOにわかりやすいラベルを付けます。
- CS_ENCR (右側のエンコーダ)
- CS_ENCL (左側のエンコーダ)
- CS_GYRO (ジャイロセンサ)
このときGPIO output level (GPIOピンの初期状態) はHighにしておきます。CSピンはアイドル状態がHigh、通信中がLowとなるためです。
SPI Modeの設定
ピンの設定が済んだら、SPI1とSPI2のModeを設定します。
- Mode: Full-Duplex Master (受信と送信を同時に行えるモード)
- Hardware NSS Signal: Disable (ソフト的にCSピンを使う)
SPI1 Configurationの設定
エンコーダMA702のデータシートを読むと、次のような仕様になっています。
- Data order: MSB first
- Clock rate: 最大25MHz
- SPI mode: Mode0とMode3をサポート※
そこで、SPI1のConfigurationを次のように設定します。
- First Bit: MSB first
- Baud Rate: 13.5 MBits/s
- Clock Polarity: High
- Clock Phase: 2 Edge
SPI2 Configurationの設定
ジャイロセンサICM20648の仕様に合わせます。データシートによると、
- Data order: MSB first
- Clock rate: 最大7MHz
- Clock Phase: クロックの立ち上がりでデータ受信
となっていますので、次のように設定します。
- First Bit: MSB first
- Baud Rate: 6.75 MBits/s
- Clock Polarity: High
- Clock Phase: 2 Edge
※SPI通信は、クロックの極性 (クロックのアイドル状態) とクロックの位相 (データ受信のタイミング) について4つのモードが定義されています。
参考:ANALOG DEVICES AN-1248 アプリケーション・ノート
クロックの極性 | クロックの位相 | |
Mode 0 | アイドル状態がLOW | クロックの立ち上がり(1番目のエッジ)にデータ受信 |
Mode 1 | アイドル状態がLOW | クロックの立ち下がり(2番目のエッジ)にデータ受信 |
Mode 2 | アイドル状態がHIGH | クロックの立ち下がり(1番目のエッジ)にデータ受信 |
Mode 3 | アイドル状態がHIGH | クロックの立ち上がり(2番目のエッジ)にデータ受信 |
ジャイロセンサの動作確認
SPI通信用の関数
CubeMXのGENERATE CODEボタンを押すと、今回設定したSPI関連のコードが生成されます。
SPI通信でマイコンからデバイスへ送信するには、HAL_SPI_Transmit()関数を使います。第一引数がSPIハンドラ、第二引数がデータバッファ、第三引数がデータバッファのサイズ(バイト)、第四引数がタイムアウトの時間(ms)です。反対にデバイスからデータを受信するとき、HAL_SPI_Receive()関数を使用します。引数はさきほどと同様です。
なお、通信を開始する前にHAL_GPIO_WritePin()関数で、通信したいデバイスのCSピンをLowにして、通信を終えた後Highに戻します。
ジャイロセンサ用のモジュール
これを踏まえたうえで、ジャイロセンサのモジュールを作ります。まず、Srcフォルダに以下のようなgyro.cというファイルを作ります。
#include "gyro.h" #include "spi.h" #include "usart.h" #include <stdio.h> int16_t gyro_raw; float gyro_ang_vel; static uint8_t read_byte( uint8_t reg ) { uint8_t val = 0x00; reg = reg | 0x80; // read bit 1--- ---- HAL_GPIO_WritePin(CS_GYRO_GPIO_Port, CS_GYRO_Pin, GPIO_PIN_RESET); // CS = 0 HAL_SPI_Transmit(&hspi2, &reg, 1, 100); HAL_SPI_Receive(&hspi2, &val, 1, 100); HAL_GPIO_WritePin(CS_GYRO_GPIO_Port, CS_GYRO_Pin, GPIO_PIN_SET); // CS = 1 return val; } static void write_byte( uint8_t reg, uint8_t val) { reg = reg & 0x7F; // write bit 0--- ---- HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET); // CS = 0 HAL_SPI_Transmit(&hspi2, &reg, 1, 100); HAL_SPI_Transmit(&hspi2, &val, 1, 100); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET); // CS = 1 } void gyro_init(void) { gyro_raw = 0; // who am i uint8_t who_am_i; who_am_i = read_byte(0x00); printf("WHO_AM_I = 0x%02x\n\r", who_am_i); // ICM-20648 settings write_byte(0x06, 0x81); // 1000 0001 wake from sleep HAL_Delay(50); write_byte(0x06, 0x01); // 0000 0001 turn off low power mode HAL_Delay(50); write_byte(0x7f, 0x20); // 0010 0000 User Bank2に変更 HAL_Delay(50); write_byte(0x01, 0x07); // 0000 0111 GYRO_FS_SEL=3(Range 2000dps)に変更 HAL_Delay(50); // Digital Low Pass Filterを有効化 // このときgyro_sensitivityは 32768/2000=16.4 LSB/dps write_byte(0x7f, 0x00); // 0000 0000 User Bank0に変更 HAL_Delay(50); write_byte(0x06, 0x21); // 0010 0001 turn on low power mode HAL_Delay(50); } void gyro_update(void) { uint8_t zout_h, zout_l; zout_h = read_byte(0x37); zout_l = read_byte(0x38); gyro_raw = ((zout_h << 8) & 0xff00) | (zout_l & 0x00ff); gyro_ang_vel = (float)gyro_raw / 16.4; }
gyro_init()関数を呼び出せばジャイロセンサの初期設定がまとめて行われ、gyro_update()関数を呼び出せばジャイロの角速度情報をまとめて読み込めます。
次に、Incフォルダにgyro.hというファイル名でヘッダーファイルを作ります。他のソースファイルでこのヘッダーファイルをインクルードすれば、gyro_init()関数とgyro_upate関数を呼び出せるようになります。
#ifndef GYRO_H_ #define GYRO_H_ #include <inttypes.h> extern int16_t gyro_raw; extern float gyro_ang_vel; void gyro_init(void); void gyro_update(void); #endif /* GYRO_H_ */
最後に、main.cに次のコードを追加します。
/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "gyro.h" #include <inttypes.h> #include <stdio.h> /* USER CODE END Includes */
/* USER CODE BEGIN 2 */ uint8_t state; printf("hello world!\r\n"); HAL_Delay(100); gyro_init(); /* USER CODE END 2 */
/* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // Gyro gyro_update(); printf("GYRO = %d, %d\n\r", gyro_raw, (int)(gyro_ang_vel*100)); HAL_Delay(5); } /* USER CODE END 3 */
ジャイロセンサ動作確認プログラムの実行
ビルドしてCubeProgrammerでマウスに書き込みます。
TeraTermに次のような角速度が表示されると成功です。
次回
ジャイロの値は取れたので、次回はエンコーダを試してみたいと思います。モータマウント・タイヤ・磁石などを取り付けて、エンコーダの動作を確認します。