detachInterrupt() の解説と実装リファレンス

記事内には広告が含まれています。

detachInterrupt() の解説と実装リファレンス

【概要】

detachInterrupt() は、Arduino環境において特定のピンまたは割り込みベクトルに関連付けられた割り込みサービスルーチン(ISR)を解除する関数です。この関数を実行することで、ハードウェアから発生する特定のイベント(ボタン押下、センサー信号等)に対するコールバック処理の実行を停止させます。システムの動作中に、動的に割り込みの有効・無効を切り替える必要がある場合に用いられます。

【仕様】

  • 引数: volatile interrupt *func
  • 解除対象となるISR関数のポインタを指定します。この関数は、あらかじめ attachInterrupt() で登録された関数ポインタを識別するために使用されます。
  • 戻り値: void
  • 戻り値はありません。
  • データ型: 関数ポインタ
  • 初期状態: 割り込みは attachInterrupt() によって有効化されていることが前提となります。関数自体は、指定された関数ポインタと一致する割り込み設定を無効化します。

【技術的特徴】

  • ハードウェアレベルの動作:
    Arduino Uno(AVR)等のプラットフォームでは、この関数は特定の外部割り込み(INT0, INT1等)に関連するマスクレジスタを操作、あるいは特定の割り込みベクトルへの対応を解除します。ESP32やSAMD系のチップでは、抽象化レイヤーを介して割り込みコントローラ(Interrupt Controller)の設定を変更します。
  • noInterrupts() との比較:
    noInterrupts() はCPU全体の割り込みをグローバルに停止させるのに対し、detachInterrupt() は特定の割り込みソースのみを標的とします。特定の入力を一時的に無視しつつ、他の割り込み(タイマー割り込みやシリアル通信など)を継続させる場合に適しています。
  • ISRコンテキストの分離:
    detachInterrupt() 自体は通常メインループ(メインコンテキスト)から呼び出されます。ISR内部から自分自身を detachInterrupt() する行為は、プラットフォームによっては動作が不安定、あるいは禁止される場合があるため、注意が必要です。

【注意点】

  • アトミック性の確保:
    detachInterrupt() を呼び出す動作自体は高速ですが、割り込みの解除と、それに付随する状態変数の更新をアトミックに行う必要がある場合は、noInterrupts() と interrupts() を併用してクリティカルセクションを保護してください。
  • 状態の不整合:
    割り込みを解除する瞬間に、割り込み要求(Interrupt Request)がハードウェアレベルで保持されている場合、解除の直後に再度割り込みが発生する可能性があります。解除前にフラグをクリアする、あるいはレジスタの状態を確実に確認する実装が推奨されます。
  • プラットフォーム依存性:
    すべてのArduinoボードで同一の挙動を保証するものではありません。特にマルチコア構成のチップや複雑なDMA制御を伴うチップでは、割り込みの「論理的解除」と「ハードウェアレベルの停止」の間にレイテンシが生じる可能性があります。
  • 関数の再登録:
    detachInterrupt() を実行した後に attachInterrupt() を再実行する場合、ISR内で参照する共有変数が適切な初期状態にあるか確認してください。

【サンプルコード】

volatile bool buttonPressed = false;
volatile bool isInterruptActive = true;

// 割り込みサービスルーチン
void handlePress() {
  buttonPressed = true;
  // 条件によって割り込みを解除するロジックをシミュレート
  if (buttonPressed) {
    isInterruptActive = false;
  }
}

void setup() {
  Serial.begin(9600);
  pinMode(2, INPUT_PULLUP);

  // ピン2に割り込みを登録
  attachInterrupt(digitalPinToInterrupt(2), handlePress, FALLING);
  Serial.println("Interrupt attached.");
}

void loop() {
  if (buttonPressed) {
    Serial.println("Button pressed. Detaching interrupt...");

    // 割り込みを解除
    detachInterrupt(handlePress);

    buttonPressed = false;
    Serial.println("Interrupt successfully detached.");

    // 以降、ボタンを押してもhandlePressは実行されない
    while(1) { 
      // 停止または他の処理
    }
  }
}

【活用例】

  • 低消費電力設計: 特定のセンサー入力が必要な期間だけ割り込みを有効にし、待機期間中は detachInterrupt() を使用して電力消費を最小限に抑える。
  • 初期化シーケンス: 機器の起動時、システムが不安定な状態にある間は割り込みを無効化し、すべての初期化処理が完了した後に attachInterrupt() を呼び出す。あるいは、特定の処理中にのみ detachInterrupt() で入力を遮断する。
  • 動的なイベント制御: ユーザーインターフェースにおいて、特定のモード(例:設定変更中)ではボタン入力を無視するように、動的に割り込みを解除・再開するアプリケーション。

コメント

タイトルとURLをコピーしました