STM32 I2Cの送信で割り込みを使う場合、使わない場合

  • 2021.04.02
  • HAL
STM32 I2Cの送信で割り込みを使う場合、使わない場合

以前、STM32 HALを使ってI2Cでメモリーにアクセスするの記事を紹介しました。

64バイトのデータを送る場合には、そこそこの時間がかかります。

そこで今回は割り込みを使ってデータ送信する場合とそうでない場合の処理時間を計測してみることにしました。

ベースとなるプロジェクト及びソースコードは上で紹介した記事を参考にして作成してください。

投稿時の開発環境を記しておきます。

PC:Windows10 OS
IDE: STM32CubeIDE Version1.6.0
Configurator: STM32CubeMX Version6.2.0
Board: STM32Nucleo-F401RE

割り込みを有効にする

Project Explorer で F401I2CHALMem.ioc をダブルクリックして設定を行います。

Pinout & Configuration – Categories – Connectivity – I2C1 を選択します。

Configuration – NVIC Settings で I2C1 event interrupt の Enabled にチェックを入れます。

ここで一度ビルドしておきます。

処理時間の計測

少し前に、STM32 タイマーを使って処理時間を計測するの記事を紹介しました。

こちらの DWT を使って処理時間を計測してみます。

この記事を参考にして Core-Src に DWT.c を追加し、Core-Inc に DWT.h を追加しておきます。

そして当然 DWT.h はインクルードしておく必要があります。

コーディングする

main.c のI2Cの初期化関数 MX_I2C1_Init() を以下のようにコーディングします。

static void MX_I2C1_Init(void)
{

  /* USER CODE BEGIN I2C1_Init 0 */

  /* USER CODE END I2C1_Init 0 */

  /* USER CODE BEGIN I2C1_Init 1 */

  /* USER CODE END I2C1_Init 1 */
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 100000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 0;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN I2C1_Init 2 */
  uint8_t sbuf[128] = {0};
  uint8_t rbuf[128] = {0};
  HAL_StatusTypeDef s;

#define DEVICE_ADDR 0xa0
#define BYTES_PER_PAGE 64

  for (int i = 0; i < BYTES_PER_PAGE; i++)
  {
    sbuf[i] = i;
  }

  initCycleCounter();
  resetCycleCounter();
  enableCycleCounter();

  HAL_I2C_Mem_Write(&hi2c1, DEVICE_ADDR, 0, I2C_MEMADD_SIZE_16BIT, sbuf, BYTES_PER_PAGE, 1000);

//  HAL_I2C_Mem_Write_IT(&hi2c1, DEVICE_ADDR, 0, I2C_MEMADD_SIZE_16BIT, sbuf, BYTES_PER_PAGE);

  uint32_t freqCount = getCycleCounter();

  float us = getTimeUs(freqCount);

  disableCycleCounter();

  HAL_Delay(10);

  s = HAL_I2C_Mem_Read(&hi2c1, DEVICE_ADDR, 0, I2C_MEMADD_SIZE_16BIT, rbuf, BYTES_PER_PAGE, 1000);

  /* USER CODE END I2C1_Init 2 */

}

HAL_I2C_Mem_Write()は割り込みを使わない送信用関数です。

(HAL_I2C_Mem_Write_IT()が割り込みを使う場合の送信用関数)

こちらで freqCount と us を測定しておき、次に送信関数のコメントを以下のように入れ替えて freqCount と us を測定します。

// HAL_I2C_Mem_Write(&hi2c1, DEVICE_ADDR, 0, I2C_MEMADD_SIZE_16BIT, sbuf, BYTES_PER_PAGE, 1000);

HAL_I2C_Mem_Write_IT(&hi2c1, DEVICE_ADDR, 0, I2C_MEMADD_SIZE_16BIT, sbuf, BYTES_PER_PAGE);

ビルドして測定する

ビルドして HAL_I2C_Mem_Read()あたりにブレークポイントを貼り停止させて、

HAL_I2C_Mem_Write(), HAL_I2C_Mem_Write_IT() それぞれの freqCount(カウント数) と us(処理時間) を記録します。

測定結果

関数 カウント数 処理時間[usec]
HAL_I2C_Mem_Write() 507359 6039.988
HAL_I2C_Mem_Write_IT() 218 2.595

こちらの計測では表の結果になりました。

割り込みを使わない関数を使う場合には、データ通信している間その関数から戻ってこないので、その間待たされることになります。

わずかなデータであれば割り込みを使う必要はないかも知れませんが、64バイトの送信を行うのであれば割り込みを考慮すべきでしょう。

約6040usec(6msec)は人間にとって一瞬ですが、高速のクロックで動作するマイコンにとっては非常に長い時間であり、その間待たされることになるからです。

割り込みを使わない場合の通信時間(関数から戻ってくるまでの待ち時間)を計算してみましょう。

64バイトのデータを送信しますが、データの前に、デバイスアドレスの1バイトとメモリーアドレスの2バイトを送るので合計67バイトのデータを送ることになります。

概算ですから、1バイトは8ビットのデータと1ビットのアクノリッジで9ビットとします。

I2Cのクロックは 100kHz ですから 67*9 * 1/100000 = 6030 usec となり、計測結果は妥当なところと言えそうです。

というわけでデータ転送数が多い場合には、ぜひ割り込みを使うことを検討してください。

いかがでしたか?

皆さんはうまく計測できましたか。

お疲れさまでした。

HALカテゴリの最新記事