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

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

【概要】

digitalPinToInterrupt() は、Arduinoフレームワークにおいて、ユーザーが指定する「デジタルピン番号」を、マイクロコントローラ(MCU)内部の「割り込み番号(IRQ番号)」に変換するための抽象化関数です。

多くのMCUでは、物理的なピン番号と割り込みコントローラ(NVICやPCICR等)が管理する割り込みソースのインデックスは一致しません。この関数は、ハードウェア固有のピン・マッピングをカプセル化し、開発者がハードウェアの詳細を意識せずにattachInterrupt()等を利用できるようにするための重要な橋渡しとして機能します。

【仕様】

  • 関数シグネチャ: int digitalPinToInterrupt(uint8_t pin);
  • 引数:
  • pin (uint8_t): 対象となるデジタルピン番号。
  • 戻り値:
  • int: 割り込み番号(正の整数)。
  • -1: 指定されたピンが割り込みに対応していない場合、または無効なピン番号の場合に返されます。
  • 初期状態: 実行時にメモリ上のルックアップテーブルまたはハードウェア固有の条件分岐によって動的に計算されます。

【技術的特徴】

  1. ハードウェア抽象化レイヤー (HAL) としての役割:
    この関数はArduinoコア(AVR, SAMD, ESP32等)ごとに実装が異なります。例えば、Arduino Uno (ATmega328P) では特定のアナログピンやデジタルピンが特定のIRQに紐付けられていますが、ESP32ではより複雑な割り込みマトリクスが存在します。この関数により、コードのポータビリティが維持されます。
  2. attachInterrupt() との関係:
    attachInterrupt() 関数は内部的に digitalPinToInterrupt() を呼び出しています。ユーザーが直接 digitalPinToInterrupt() を呼び出す場面は、低レベルのレジスタ操作と組み合わせて高度な割り込み制御を行う場合に限定されます。
  3. 非同期性の確保:
    この関数自体は同期的な変換処理ですが、ここで得られたIRQ番号は、後の非同期的な割り込み処理(ISR)のトリガーとして使用されます。

【注意点】

  • 対応外ピンの確認:
    すべてのデジタルピンが割り込みをサポートしているわけではありません。実装前に必ず digitalPinToInterrupt(pin) != -1 であることを確認することが推奨されます。
  • 割り込みの競合:
    関数が返すIRQ番号は、特定の割り込みソースを指します。同じIRQを複数の関数や処理で共有しようとすると、予期しない挙動を引き起こす可能性があります。
  • コンテキストの制約:
    この関数で取得したIRQ番号を基にレジスタを直接操作する場合、割り込み禁止フラグの設定や、アトミックな操作の考慮が必要になることがあります。
  • デバウンスの欠如:
    この関数および割り込み自体は、スイッチのチャタリング(物理的なノイズ)を除去しません。割り込み内でチャタリング処理を行うことはISRの実行時間を延ばすため、ハードウェア的なRCフィルタまたはソフトウェアでの時間制御を併用する必要があります。

【サンプルコード】

以下は、ボタン押下を検知する際に digitalPinToInterrupt() を利用する実装例です。

const uint8_t buttonPin = 2; 
volatile bool buttonPressed = false; 

void setup() {
  Serial.begin(9600);

  // ピンの割り込み対応を確認
  int irqNum = digitalPinToInterrupt(buttonPin);
  if (irqNum == -1) {
    Serial.println("Error: Pin does not support interrupts.");
    while(1); 
  }

  pinMode(buttonPin, INPUT_PULLUP);

  // 内部的には digitalPinToInterrupt が呼ばれている
  attachInterrupt(digitalPinToInterrupt(buttonPin), handleInterrupt, FALLING);
}

void loop() {
  if (buttonPressed) {
    Serial.println("Button Pressed!");
    buttonPressed = false;
  }
}

// ISR (Interrupt Service Routine)
void handleInterrupt() {
  buttonPressed = true; 
}

【活用例】

  • 高精度な回転数計測(タコメーター):
    エンコーダのパルスを digitalPinToInterrupt() で取得したIRQに紐付け、正確なパルス数をカウントする。
  • フローメータの流量計測:
    流体通過によるパルス信号を割り込みでキャッチし、リアルタイムな流量データを計算する。
  • リミットスイッチの検知:
    3DプリンターやCNCマシンにおいて、安全のためのエンドストップを高速に検知し、即座にモーター駆動を停止させる制御。
  • パルス幅変調(PWM)のカスタム実装:
    ハードウェアPWMが不足している場合、特定のピンの割り込みを利用して手動でPWM波形を生成する。

コメント

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