STM32 + LL のSPI通信を使用してICM-20602と通信する

2020年6月11日

STM32マイコンのペリフェラル関連記事を一覧にまとめました。

今回はSTM32F401REのNUCLEOボードとSatoさんがスイッチサイエンスに委託販売をしているICM-20602モジュールでSPI通信をする方法について書いていこうと思います。

HALライブラリを使用してMPU6500と通信する記事は下記を見ていただければと思います。

 




リファレンスマニュアルを読む

SPI通信の方法を確認する

RM0090 リファレンスマニュアルより引用

 

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

SPIはマスターモードで使用するので、リファレンスマニュアルの手順を確認しておきます。後で、生成されたソースコードの確認をするときに必要になります。SPI通信の送受信を行う際には、TXEフラグ、RXNEフラグ、DRレジスタの3つをタイミングを合わせて使用していくことでできるということが読み取れます。

プログラムに使用するレジスタを確認する

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

RM0090 リファレンスマニュアルより引用

いっぱい引用させていただきました。SPI通信に関しては、先程取り上げた場所よりもビットの説明を見た方がわかりやすいと考えたためです。

SPI通信には8bitまたは16ビット通信があります。CPOL,CPHA、クロック周期などの設定は通信するデバイスのデータシートを確認してそれに合わせて設定をする必要があります。SRレジスタに関しては通信で使用するので確認しておきましょう。

 

ICM-20602のマニュアルを読む

SPI通信について確認する

 

DS-000176 ICM-20602 データシートより引用

ここからSPIの設定方法を読み取れたことは以下の通りです。

  • CSピンをLowにしてから通信をスタート
  • 10MHzが最大通信速度
  • CPOL=1, CPHA=1 すなわちMODE=3

といったところでしょうか。

ここででてきたCPOL,CPHAというのはSPIの設定で必要となるものです。

CORELISより引用

SPI通信の仕様のひとつでデータの通信タイミングを示したものになります。これを間違えると通信が上手くできていない可能性があります。

DS-000176 ICM-20602 データシートより引用

SPIのインターフェースについてです。MSBファーストで、最初に送信をするビットで読み込み/書き込みを指定するそうです。読み込みはビットを1,書き込みはビットを0に設定してあげればよいそうです。

レジスタの確認をする

今回のプログラムではジャイロ3軸、加速度3軸、温度のすべてを一定時間割り込みで表示したいと考えているため、設定を必要となるレジスタを上げると以下のものがあがりました。また、FIFOは使用する予定がないため確認は行わないものとします。

  • PWR_MGMT1
  • PWR_MGMT2
  • CONFIG,GYRO(ACCEL)_CONFIG
  • WHO_AM_I

いくつかのレジスタを確認してみたいと思います。

DS-000176 ICM-20602 データシートより引用

ローパスフィルタやFIFOMODEの設定等ができるそうです。詳しくは各自確認をお願いします。

DS-000176 ICM-20602 データシートより引用

ジャイロの取得dpsの設定、加速度の取得gの設定等ができます。このように、どのビットにどのような機能設定ができるのかということが書いてあることが分かったと思います。

他に使用するレジスタに関しては各自で確認あをしていただければと思います。

プログラムに必要な点を確認する

データシートを読んでいて取得した値を一定の定数で割ることで角速度や加速度に変換することが可能ということがわかりました。

参考までに、2000deg/secのときは16.4で割る、16gのときは2048でわればよいそうです。データシートから情報を探してみてください。

 

STM32CubeMXの設定

一定時間ごとの割り込みで文字列を表示したいと考えているためTIMの設定、USARTの設定も同時に行っていきます。

SPIの設定

TIM5の設定(NVICのチェックボックスにチェックを入れてあります)

USART2の設定

クロックの設定

コード生成における設定

その他

使用するライブラリをすべてLLに設定、プロジェクト名は各自で決めてください。私はプロジェクト名を[blogSPI]としました。

 

ソースコードを実装する

生成されたソースを確認する

SPIのPIN設定、プリスケーラの設定、モードの設定等はすべてやってくれていることがわかるので実装しなければならないことは送受信関数だけでOKということが読み取れます。

また、SPIを有効にされていないため使用する前にSPIの機能を有効にする必要性があるということも同時にわかりました。

 

実装

今回使用するTIM割り込み、USARTのプログラムについては以前の記事を参照していただければと思います。そのため、省略をさせていただきます。

★過去の記事

これから、SPIの送受信関数の実装を行っていきたいと思います。先程のリファレンスマニュアルで確認した図の通り一つずつフラグを確認してDRレジスタに値を書き込み、読み込みを行っていくと次の流れで行えばいいことがわかります。

  1. SPIを有効化する
  2. CSピンを設定
  3. RXNEフラグが立っていないかチェック
  4. DRレジスタに送信データを書き込み
  5. TXEフラグが立つまでまつ
  6. RXNEフラグが立つまでまつ
  7. DRレジスタから値の読み込み
  8. これの繰り返し
  9. CSピンを設定

このように行うことでデータの送受信ができるということがわかります。

プログラムに落とし込むと次のようになりました。

void SPI_Start(SPI_TypeDef *SPIx)
{
  LL_SPI_Enable(SPIx);
}

void SPI_Communication(SPI_TypeDef *SPIx ,uint8_t *tx_data, uint8_t *rx_data, uint8_t length, GPIO_TypeDef *GPIOx, uint32_t CS_Pin)
{
  uint8_t count = length;

  LL_GPIO_ResetOutputPin(GPIOx, CS_Pin);

  if ( LL_SPI_IsActiveFlag_RXNE(SPIx) == SET ) LL_SPI_ReceiveData8(SPIx);
  if ( LL_SPI_IsEnabled(SPIx) == RESET ) LL_SPI_Enable(SPIx);

  while(count > 0){
    LL_SPI_TransmitData8(SPIx, *tx_data++);
    while( LL_SPI_IsActiveFlag_TXE(SPIx) == RESET );
    while( LL_SPI_IsActiveFlag_RXNE(SPIx) == RESET );
    *rx_data++ = LL_SPI_ReceiveData8(SPIx);
    count--;
  }

  LL_GPIO_SetOutputPin(GPIOx, CS_Pin);

}

 

ICM 20602用の書き込み関数、読み込み関数を作成する

SPI通信用の送受信用の関数を作ることができたので、使用する素子に合わせた通信用のプログラムを書いていきたいと思います。

void ICM20602_WriteByte(uint8_t reg, uint8_t data)
{
  uint8_t tx_data[2];
  uint8_t rx_data[2];

  tx_data[0] = reg & 0x7F;
  tx_data[1] = data;

  SPI_Communication(SPI3, tx_data,rx_data, 2, gyro_cs_GPIO_Port, gyro_cs_Pin);
}

uint8_t ICM20602_ReadByte(uint8_t reg)
{
  uint8_t tx_data[2];
  uint8_t rx_data[2];

  tx_data[0] = reg | 0x80;
  tx_data[1] = 0x00;

  SPI_Communication(SPI3, tx_data,rx_data, 2, gyro_cs_GPIO_Port, gyro_cs_Pin);

  return rx_data[1];
}

 

ICM 20602用の初期設定、値を読み込む関数等を作成する

ICM20602_WriteByte, ICM20602_ReadByte関数を適宜使用して先程確認したレジスタに値を設定、読み込みなどを行い設定ができているか、通信ができているかどうかを確認する初期化関数とジャイロのデータと加速度のデータなどをそれぞれ読み込みデータとして返す関数等をかけばOKです。

データは16bitで取得が可能です。これを取得するためにはビットシフトを上手く使用して取得が可能になります。

プログラムの参考例

int16_t ICM20602_getData(uint8_t H_reg, uint8_t L_reg)
{
  int16_t data = (int16_t)( (int16_t)( ICM20602_ReadByte(H_reg) << 8) | (int_16_t)( ICM20602_ReadByte(L_reg) ) );
  return data;
}

float convertPhysicalQuantity(int16_t data, flaot factor)
{
  float conv = data / factor;
  return conv;
}

 

さいごに

LLAPIを使用したSPI通信のやり方を書きました。次は、マイコンとPCを繋いでQtのシリアル通信を使用してジャイロの傾きを表示するプログラムを作成してみようかなと考えています。

 

参考文献

RM0090 リファレンスマニュアル

DS-000176 ICM-20602 データシート