Skip to content

割り込み(Interrupt)

割り込みとは

割り込み(Interrupt) は、外部からの信号を検出したとき(例:GPIOピンの状態変化)、実行中のプログラムを一時停止して、あらかじめ登録した関数を実行する仕組みです。

リアルタイム性が必要な場合に最適です。


割り込みの登録

ESP32では attachInterrupt() 関数を使って、割り込みを登録できます。

// 基本的な使い方
attachInterrupt(GPIO_PIN, handlerFunction, triggerMode);
// 基本的に起動時に登録
void setup(void) {
  // GPIO4の立ち上がりエッジで onPPSPulse() を実行
  attachInterrupt(GPIO_NUM_4, onPPSPulse, RISING);
}

パラメーター

パラメーター 説明
GPIO_PIN 監視するGPIOピン番号 GPIO_NUM_4
handlerFunction イベント時に実行する関数 onPPSPulse
triggerMode トリガーする条件 RISING(立ち上がり)

トリガーモード

モード 説明
RISING 信号がLOW→HIGHに変わった時 1PPSパルス(立ち上がり)
FALLING 信号がHIGH→LOWに変わった時 ボタン押下(立ち下がり)
CHANGE 信号が変わった時(両方) 変化を全て捕捉したい場合
HIGH 信号がHIGHの間ずっと レベルトリガー
LOW 信号がLOWの間ずっと レベルトリガー

割り込みハンドラー

割り込みハンドラーは、 割り込みが発生したときに自動実行される関数です。

// 基本的な形
void IRAM_ATTR handlerFunctionName() {
  // 割り込み発生時に実行されるコード
}

IRAM_ATTRは、この関数をESP32の高速RAM(IRAM)に配置するマクロです。 割り込みハンドラーが高速に実行されるというメリットがあります。 ただし、IRAM容量には限界があるため、複雑な処理は避け、短い処理に限定すべきです。

ハンドラー定義のポイント

項目 説明
短い 複雑な処理は避ける。グローバル変数に値を記録して終わりが基本
高速 ハンドラーが長く実行されると、メインプログラムが停止してしまう
グローバル変数を使う ハンドラー内で値を記録し、メインループで処理する設計が標準
volatile修飾 複数の場所から参照する場合は volatile を使って最新値取得を保証

割り込みハンドラーのサンプル

// main.cpp の先頭
/// Timestamp captured from interrupt.
static uint64_t g_interrupt_value = 0;

/// Flag set by interrupt handler.
volatile bool g_interrupt_flag = false;

// 割り込みハンドラー(IRAM内で高速実行)
void IRAM_ATTR onInterruptEvent() {
  g_interrupt_value = micros();
  g_interrupt_flag = true;
}

// setup()で割り込みを登録
void setup(void) {
  attachInterrupt(GPIO_NUM_4, onInterruptEvent, RISING);
}

// メインループで処理
void loop() {
  if (g_interrupt_flag) {
    uint64_t value = g_interrupt_value;
    g_interrupt_flag = false;  // Clear flag first

    // 複雑な処理をここで実行
    Serial.printf("Interrupt at: %llu us\n", value);
  }
}

kurikintonsではGNSSモジュールから取得した時刻を1PPSパルスで補正するために、 割り込みを使用しています。


まとめ

項目 説明
割り込み GPIOピン変化時に即座にハンドラーを実行する仕組み
登録 attachInterrupt(GPIO_PIN, handler, RISING/FALLING/CHANGE)
ハンドラー void IRAM_ATTR handler() {...} の形式
設計 ハンドラーは最小限にして、メインループで処理

参考資料