0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Arduino】Pin Change Interrupt を用いた割り込み

Posted at

fiord Advent Calendar 2025 4 日目の内容となります。

はじめに

一般に Arduino で外部割込みをするには attachInterrupt() という関数を利用し、Arduino UNO ではデジタルピン 2(INT0) と 3(INT1) の 2 本だけが対応しています。

しかし、他の方法で割り込み自体は可能です。Pin Change Interrupt(PCINT) について簡単に説明します。なお、下記では Arduino UNO であることを前提とします。他のボードの場合、それぞれのマイコンの機能を利用することになりますので、それぞれのデータシートを参照することをオススメします。

attachInterrupt と PCINT の違い

attachInterrupt() PCINT
対応ピン D2(INT0), D3(INT1) のみ デジタルピン 0-13 の全て
トリガー RISING/FALLING/CHANGE/LOW CHANGE のみ
種類 INT0, INT1 で個別 ポート単位で計 3 つ(PCINT0/1/2)
どのピンが変化したか 個別なので自明 自分で調べる必要がある
割り込み発生タイミング 個別なのでそのピン 同じポート内の有効化したピンのどれかが変化した際

ポートの概念について、これから補足していきます。

Arduino UNO のポートについて

アナログピン/デジタルピンには、裏で「ポート」という区分に分かれており、PB0-5(Port B)、PC0-6(Port C)、PD0-7(Port D) に分かれています。

Arduino のポート図

※上記画像は arduino.cc より引用しています。

例えばデジタルピン 1 を指す際に Arduino IDE 上で「PD1」という記述をしてもOKです。

ポート毎の割り込み

ポート毎に割り込みに利用する割り込みベクタが存在します。

| 割り込みベクタ | レジスタ | ピン |
| PCINT0_vect | PORTB | 8-13 |
| PCINT1_vect | PORTC | A0-A5 |
| PCINT2_vect | PORTD | 0-7 |

例えば PORTB の変更に対して割り込みを行う場合、下記の記述をします。

ISR(PCINT0_vect) {
  // 処理内容
}

実際の使用例を見てみましょう。

実践

下記の回路を作成します。

  • スイッチ 4 つと LED 4 つが Arduino UNO に接続されている(スイッチは全て Arduino UNO の内部プルアップ抵抗を利用する)
  • 各スイッチを押すと、対応する LED の ON/OFF が切り替わる
  • メインループ内で各スイッチの面倒を見ればよいが、割り込みで処理することにする

通常の外部割り込みではスイッチの数が足りません。下記ようなスケッチが有効です。

const uint8_t BUTTON_PINS[] = {8, 9, 10, 11}; // スイッチのピン
const uint8_t LED_PINS[]    = {2, 3, 4, 5};   // LED の出力ピン
volatile uint8_t led_state = 0x00;  // led の初期状態(全部 OFF)

volatile uint8_t button_state = 0xFF;    // 現在の状態(プルアップなので初期HIGH)
volatile uint8_t prev_state = 0xFF;

ISR(PCINT0_vect) {
  button_state = PINB;                   // PORTB の現在値を一気に読む

  uint8_t changed = (button_state ^ prev_state) & 0b00001111; // 排他的論理和で変更されたビットのみを取り出す
  uint8_t current  = button_state & 0b00001111;

  for (uint8_t i = 0; i < 4; i++) { // i 番目のスイッチについて、状態が変わったかをチェック
    if (changed & (1 << i)) { // 変わったかの判断
      // 押し下げ(HIGH→LOW)を検知したらトグル
      if (!(current & (1 << i))) {         // 今がLOW(押されている)
        // ON/OFF の切替
        bool led = (led_state >> i) & 1;
        digitalWrite(LED_PINS[i], !led);
        led_state ^= (1 << i);
      }
    }
  }
  prev_state = current;
}

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

  // LEDピンを出力に
  for (uint8_t i = 0; i < 4; i++) {
    pinMode(LED_PINS[i], OUTPUT);
  }

  // ボタンピン(8-11)を入力プルアップ
  for (uint8_t i = 0; i < 4; i++) {
    pinMode(BUTTON_PINS[i], INPUT_PULLUP);
  }

  // PCINT0(ピン8-13)の割り込みを有効化
  PCICR  |= (1 << PCIE0);                    // Pin Change Interrupt 0 を許可
  PCMSK0 |= (1<<PCINT0) | (1<<PCINT1) |       // 8,9,10,11 のみ監視
            (1<<PCINT2) | (1<<PCINT3);

  prev_state = PINB & 0b00001111;            // 下位4ビットの初期値
}

void loop() {
}

loop() 内では何もしていませんが、ボタンを押すとそれに対応した LED の ON/OFF が切り替わるようになりました!
(チャタリングの対策はしていないので、必要に応じて組み込む必要があります)

81b49fd4-554c-4b38-8425-a944c125440f1.jpg

まとめ

Arduino UNO では「デジタルピン 2/3 でしか割り込みが出来ない」のではなく、実は理論上全てのピンで割り込み処理が出来る、ということを理解して欲しい記事でした。
もし困っていたら、面倒ではありますが利用を検討してみてください。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?