STM32 nucleoを使う (3) EXTI割り込み


はじめに

SWの検出を割り込みで行うプログラムについて記述します。
前回のプログラムを改造していきます。
STM32にはGPIOの割り込みはEXTIを使います。EXTIとは拡張割り込み/イベントコントローラのことです。信号の立上り/立下りの検出を行い割り込みを発生させます。

EXTIの設定

iocファイルのSwAndLedTest.iocを開きます。

GPIOのコンフィグレーションを開きます。
GPIOモードをFalling edgeからRising/Falling Edgeに変更します。

NVICのコンフィグレーションを開き、EXTI Line[15..10] interruptsにチェックを入れます。
外部割込みを有効とします。

iocファイルを保存するとコードを生成するか確認が入るので、Yesを選択しコード生成をします。

割り込みハンドラ

割り込みの処理は割り込みハンドラに記述します。
stm32f3xx_it.cに各割り込みハンドラがありますので、EXTI line[15:10]の割り込みハンドラのEXTI15_10_IRQHandlerを探します。内容は以下のようになっています。
ここの/* USER CODE BEGIN EXTI15_10_IRQn 0 /と/ USER CODE END EXTI15_10_IRQn 0 */に割り込み処理を書いてもよいですが、もう少し内容を見てみます。

void EXTI15_10_IRQHandler(void)
{
  /* USER CODE BEGIN EXTI15_10_IRQn 0 */

  /* USER CODE END EXTI15_10_IRQn 0 */
  HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
  /* USER CODE BEGIN EXTI15_10_IRQn 1 */

  /* USER CODE END EXTI15_10_IRQn 1 */
}

HALのEXTIの割り込みハンドラHAL_GPIO_EXTI_IRQHandlerを呼んでいます。

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
  /* EXTI line interrupt detected */
  if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != 0x00u)
  {
    __HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
    HAL_GPIO_EXTI_Callback(GPIO_Pin);
  }
}

割り込みをクリアする関数とEXTIのコールバック関数HAL_GPIO_EXTI_Callbackを呼んでいます。
HAL_GPIO_EXTI_Callbackの内容は以下の通り。

/**
  * @brief  EXTI line detection callback.
  * @param  GPIO_Pin Specifies the port pin connected to corresponding EXTI line.
  * @retval None
  */
__weak void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(GPIO_Pin);

  /* NOTE: This function should not be modified, when the callback is needed,
           the HAL_GPIO_EXTI_Callback could be implemented in the user file
   */
}

このコールバック関数にはweakシンボルが付いています。他でHAL_GPIO_EXTI_Callbackが定義されていない場合はweakが付いた関数が使われますが、他で定義されている場合は使われません。
割り込み処理を記述するには、HAL_GPIO_EXTI_CallbackのコメントにあるようにユーザーファイルにHAL_GPIO_EXTI_Callbackを作成するのが良いでしょう。

HAL_GPIO_EXTI_Callbackの作成

main.cの/* USER CODE BEGIN 0 /と/ USER CODE END 0 */の間に記述します。
前と同じだと違いが判りませんので、押している間は消灯するように変更します。

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
  if (GPIO_Pin == B1_Pin)
  {
    if (HAL_GPIO_ReadPin(B1_GPIO_Port, B1_Pin) == GPIO_PIN_RESET)
    {
      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
    }
    else
    {
      HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_SET);
    }
  }
}
/* USER CODE END 0 */

pin15-10の割り込みが共通なので最初にB1による割り込みかどうかを判定しています。
立下り/立上りの割り込みとしたので、ピンの状態でLEDの点灯/消灯を決定しています。

mainルーチンからは処理を削除します。
何もしない無限ループとします。

  /* USER CODE BEGIN WHILE */
  while (1)
  {
    ;
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

実行

RUNボタンで実行します。
起動時は割り込みが発生していないのでLEDは消灯しています。
ボタンを押し離したときにLEDが点灯します。
その後は、押すと消灯し離すと点灯します。