STM32 HALのGPIOで割り込みを使ってみる

  • 2020.07.03
  • HAL
STM32 HALのGPIOで割り込みを使ってみる

今回はGPIOの割り込みを使ってみます。

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

PC:Windows10 OS
IDE: STM32CubeIDE Version1.3.0
Configurator: STM32CubeMX Version5.5.0
Board: STM32Nucleo-F401RE

GPIOの割り込み概要

人間社会における割り込みは悪いイメージがありますが、マイコンを使ったシステムにおいて割り込みは重要な役割を果たします。
内容的にはどちらも一緒で、今やっていることを中断して割り込んだものを先に処理します。

GPIOの割り込みは入力の変化に使います。
あるポートの入力信号が H から L にかわったり、 L から H にかわることをいち早く検出したい場合に割り込みを使います。

例えば100msec周期のポーリングで入力の信号変化をチェックしているとします。
たまたまチェックし終えた直後に信号が変化したら、次のチェックまで約100msecあるのでそれだけ検出が遅れることになります。

そうかと言って 1msec周期のポーリングにすると検出は速くなりますが、その分忙しくなります。

そこでGPIOの割り込みを使います。
割り込みを使えば入力ポートの状態をサンプリングする必要がなくなり、信号が変化したことをすぐに検出することができます。

プロジェクトの概要

それではボードを使って実際に動かしてみましょう。

ボードには青いスイッチがついていて、GPIOの PC13 につながっています。
回路図を見て頂くとわかりますが、PC13は 4.7kΩの抵抗でプルアップされています。
そしてスイッチの反対側は GND につながっていますので、PC13は通常は H レベルでスイッチが押されると L レベルになります。

割り込みは PC13 の信号が H から L にかわったことを検出することにし、その都度 LED をつけたり消したりしてみます。

緑色のLEDが PA5 につながっているので、スイッチが押される度にこちらをパタパタさせてみることにします。

プロジェクトを作成する

IDEを起動し、File- New – STM32 Project を選択し、Target Selection ウィンドウが出たら Board Selector タブを選択し Boards List から NUCLEO-F401RE を選択し Next ボタンを押します。

Project 名に F401GPIoInt と入力し、Finishボタンを押します。
Initialize all peripherals with their default Mode ? と聞いてくるので Yesを押します。
This kind of project is associated with the STM32CubeMx perspective. Do you want to open this perspective now ? と聞いてくるので Yesを押します。

設定の確認と変更

Pinout & Configuration – Categories – System Core から GPIO を選択します。

GPIOタブが選択された状態で Pin Name の PA5 を選択します。

GPIO mode が Output Push Pull になっていることを確認します。

次に Pin Name の PC13-ANTI_TAMP を選択します。

GPIO mode が External Interruput Mode with Falling edge trigger detection になっていることを確認します。

H から L になることを検出するので Falling edge で良いですね。

続いて Pinout & Configuration – Categories – System Core から NVIC を選択します。

そして EXTI line[15:10] interrupts の Enabled にチェックを入れて割り込みを許可します。

コーディングする

今回の割り込みベクタは EXTI15-10の6本のラインの割り込みがまとめられています。

従ってもし複数のラインの検出を行いたい場合には、割り込み処理の中でどのラインの割り込みが入ったのか判定する必要があります。

Project Explorer から Core – Src – stm32f4xx_it.c をダブルクリックして開きます。

ファイルの最後に EXTI15_10_IRQHandler() という関数が書かれています。

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_GPIO_EXTI_IRQHandler(GPIO_PIN_13) の記述があります。

ここをダブルクリックして選択し、右クリックして Open Declaration を選びます。

この中に HAL_GPIO_EXTI_Callback(GPIO_Pin); という記述があります。

この関数は、そのすぐ下に以下のように書かれています。

__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属性がついていて、かつ「ここは触らないで」ということなので、main()の少し手前に以下の記述をしました。

(__weak属性のついている関数は他で定義することで、そちらの関数が優先的に使われます)

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}
/* USER CODE END 0 *

つぶやき

その1

あれ、そしたらPA13とPC13の両方のラインを割り込みで検出したい場合にはどうするの?

GUIでマイコンの両方の端子に GPIO_EXTI13を選択すると後から設定した方が有効になり、前に設定したピンのGPIO_EXTI13は緑色から灰色になりました。

つまりPA13とPC13の両方(PB13も)同時には使えませんよ ということでしょう。

その2

割り込みは以下の流れで関数が処理されていきます。

void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}

void HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin)
{
if(__HAL_GPIO_EXTI_GET_IT(GPIO_Pin) != RESET)
{
__HAL_GPIO_EXTI_CLEAR_IT(GPIO_Pin);
HAL_GPIO_EXTI_Callback(GPIO_Pin);
}
}

void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == GPIO_PIN_13)
{
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
}
}

HAL_GPIO_TogglePin()はGPIOポートの出力を反転する関数です。

この状態だと判定はいらないので、もうひとつ例えばPA10とかを割り込みで使うように設定してみるとコンフィグレーターは以下のコードを出力しました。

void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
}

もし6本全てを割り込みで使うならば

void EXTI15_10_IRQHandler(void)
{
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_10);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_11);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_13);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_14);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
}

ってコードを出力するのでしょうか。

おいおい「STさん、何とかしてくださいな」と感じるのは私だけでしょうか。

プログムを実行してみる

それではビルドして実行してみてください。

スイッチが押されるたびに割り込みが入って LED が点滅すれば成功です。

HALカテゴリの最新記事