STM32F103C8T6マイコンにおける外部割り込みの設定手順

STM32マイコンにおける割り込みの概要

STM32マイコンにおいて、割り込みは実行中のメインプログラムを一時停止し、特定のイベントに応じた処理を実行する仕組みです。割り込みにはいくつかのカテゴリがあります。

  • 外部割り込み (EXTI): GPIOピンの状態変化により発生します。
  • 内部割り込み: タイマー、USARTなどの周辺機能に起因します。
  • システム割り込み: SysTickなどのコア機能に関連します。

割り込み処理には、NVIC(Nested Vectored Interrupt Controller)が優先度や有効化を制御します。

ハードウェア環境とソフトウェア準備

この実践では以下の環境を使用します。

  • マイコンボード: STM32F103C8T6 最小システム基板
  • 周辺部品: LED(PB0ピンに接続)、タクトスイッチ(PA0ピンに接続)
  • 開発環境: Keil MDK (バージョン5.36)
  • ファームウェアライブラリ: STM32 Standard Peripheral Library 3.5.0

目的は、PA0ピンのスイッチ押下をトリガーとしてPB0のLED状態を反転させることです。

外部割り込みの設定フロー

標準ライブラリを使用した外部割り込みの設定は、以下の手順で行います。

1. 関連周辺回路のクロックを有効化

GPIOポート、AFIO(Alternate Function I/O)のクロックを有効にします。AFIOは外部割り込みのピン割り当てに必須です。

// クロック有効化
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

2. GPIOピンの動作モード設定

PA0ピンを入力(内部プルアップ)、PB0ピンを出力(プッシュプル)に設定します。

GPIO_InitTypeDef gpioConfig;

// PA0設定: 入力(プルアップ)
gpioConfig.GPIO_Pin = GPIO_Pin_0;
gpioConfig.GPIO_Mode = GPIO_Mode_IPU;
gpioConfig.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &gpioConfig);

// PB0設定: 出力(プッシュプル)
gpioConfig.GPIO_Pin = GPIO_Pin_0;
gpioConfig.GPIO_Mode = GPIO_Mode_Out_PP;
gpioConfig.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &gpioConfig);

// LED初期状態: 消灯
GPIO_SetBits(GPIOB, GPIO_Pin_0);

3. AFIOを用いた外部割り込みラインの割り当て

GPIOAのピン0を外部割り込みライン0に接続します。

GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource0);

4. EXTIの動作パラメータ設定

割り込みトリガー条件(下降縁)を指定します。

EXTI_InitTypeDef extiConfig;
extiConfig.EXTI_Line = EXTI_Line0;
extiConfig.EXTI_Mode = EXTI_Mode_Interrupt;
extiConfig.EXTI_Trigger = EXTI_Trigger_Falling;
extiConfig.EXTI_LineCmd = ENABLE;
EXTI_Init(&extiConfig);

5. NVICによる割り込み優先度の設定

割り込みチャネルの有効化と優先度を指定します。優先度グループは一度だけ設定します。

NVIC_InitTypeDef nvicConfig;

// 優先度グループ設定 (全システムで1回のみ)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

// EXTI0割り込み設定
nvicConfig.NVIC_IRQChannel = EXTI0_IRQn;
nvicConfig.NVIC_IRQChannelPreemptionPriority =:
nvicConfig.NVIC_IRQChannelSubPriority = 1;
nvicConfig.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvicConfig);

6. 割り込みサービスルーチンの実装

割り込み発生時に呼ばれる関数です。割り込みフラグのクリアを必ず実行してください。

void EXTI0_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
        // チャタリング対策 (オプション)
        // Delay_ms(10);

        // LED状態反転
        if (GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_0)) {
            GPIO_ResetBits(GPIOB, GPIO_Pin_0); // LED点灯
        } else {
            GPIO_SetBits(GPIOB, GPIO_Pin_0); // LED消灯
        }

        // 割り込みフラグクリア
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

7. メイン関数での統合

上記設定をメイン関数で統合します。メインループでは特に処理を行わず、割り込みを待機します。

#include "stm32f10x.h"

int main(void) {
    SystemInit();

    // 上記1〜5の設定をここに記述

    while(1) {
        // バックグラウンドタスクがあればここに記述
        // 割り込みは割り込みハンドラで処理される
    }
}

実装時の注意点

  • AFIOクロックの有効化: 外部割り込みを使用する場合はAFIOクロックを必ず有効にします。
  • 割り込みフラグのクリア: 割り込みハンドラ内でフラグをクリアしないと、連続して割り込みが発生します。
  • GPIO入力モードの選択: 回路に応じて適切な入力モード(プルアップ/プルダウン)を選択します。
  • 優先度グループの設定: NVIC_PriorityGroupConfigはプログラム全体で一度だけ呼び出します。

応用例: 回転エンコーダの読み取り

2相式エンコーダのカウント例を示します。PB0とPB1を入力として使用します。

// エンコーダカウント変数
int16_t encoderCount = 0;

void Encoder_Init(void) {
    // GPIO, AFIOクロック有効化
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);

    // PB0, PB1を入力(プルアップ)に設定
    GPIO_InitTypeDef gpioConfig;
    gpioConfig.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    gpioConfig.GPIO_Mode = GPIO_Mode_IPU;
    gpioConfig.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &gpioConfig);

    // 外部割り込みライン割り当て
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);

    // EXTI設定 (両ラインを下降縁トリガーに)
    EXTI_InitTypeDef extiConfig;
    extiConfig.EXTI_Line = EXTI_Line0 | EXTI_Line1;
    extiConfig.EXTI_Mode = EXTI_Mode_Interrupt;
    extiConfig.EXTI_Trigger = EXTI_Trigger_Falling;
    extiConfig.EXTI_LineCmd = ENABLE;
    EXTI_Init(&extiConfig);

    // NVIC設定 (EXTI0, EXTI1)
    NVIC_InitTypeDef nvicConfig;
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    nvicConfig.NVIC_IRQChannel = EXTI0_IRQn;
    nvicConfig.NVIC_IRQChannelPreemptionPriority = 1;
    nvicConfig.NVIC_IRQChannelSubPriority = 1;
    nvicConfig.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&nvicConfig);

    nvicConfig.NVIC_IRQChannel = EXTI1_IRQn;
    nvicConfig.NVIC_IRQChannelPreemptionPriority = 1;
    nvicConfig.NVIC_IRQChannelSubPriority = 2;
    NVIC_Init(&nvicConfig);
}

// EXTI0割り込みハンドラ (PB0)
void EXTI0_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line0) == SET) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
            // PB0の下降縁でPB1の状態を確認し回転方向を判定
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
                encoderCount--; // 反時計回り
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

// EXTI1割り込みハンドラ (PB1)
void EXTI1_IRQHandler(void) {
    if (EXTI_GetITStatus(EXTI_Line1) == SET) {
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
            // PB1の下降縁でPB0の状態を確認
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) {
                encoderCount++; // 時計回り
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line1);
    }
}

タグ: STM32F103C8T6 外部割り込み NVIC EXTI GPIO

9月27日 15:43 投稿