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.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 が切り替わるようになりました!
(チャタリングの対策はしていないので、必要に応じて組み込む必要があります)
まとめ
Arduino UNO では「デジタルピン 2/3 でしか割り込みが出来ない」のではなく、実は理論上全てのピンで割り込み処理が出来る、ということを理解して欲しい記事でした。
もし困っていたら、面倒ではありますが利用を検討してみてください。

