要約まとめ
- digitalWrite() / pinMode() は便利だが激遅 ― 毎呼び出しで数µsをロス。大型プロジェクトでは致命的。
- レジスタ直叩きで約30〜50倍高速化。フラッシュサイズも縮小し、処理落ちリスクを一掃。
- avr_pinMode / avr_digitalWrite をドロップインで置換できるサンプルコードを公開。
- 比較表と FAQ で疑問を一掃。安全に導入するための落とし穴と対策も網羅。
- ATmega328P 以外の AVR でも応用可。ポートの対応表を用意すれば OK。
なぜ digitalWrite() は遅いのか?
Arduino 標準関数は “どのボードでも動く” ことを最優先に実装されている。裏では digitalPinToPort()
や割り込み保護処理を毎回呼び出し、条件分岐の嵐。16 MHz の ATmega328P では LED を 1 回点滅させるだけで 約 3 µs を消費する。
内部処理をざっくり図解
関数呼び出し → ピン番号をポート番号にマッピング → ビットマスク生成 → 割り込み禁止 → レジスタ書き込み → 割り込み復帰――と最低でも 7 ステップ。高速化には“汎用性を捨てて目的に特化”するのが最短ルートだ。
レジスタ直叩きで 50 倍速へ ― avr_* マクロ
以下の 2 関数を pinMode()
/digitalWrite()
と同じ引数でラップ。既存コードをほぼ書き換えず高速化できる。
#include <Arduino.h>
void avr_pinMode(uint8_t pin, uint8_t mode) {
uint8_t bit = digitalPinToBitMask(pin);
volatile uint8_t* reg = portModeRegister(digitalPinToPort(pin));
if (mode == OUTPUT) *reg |= bit; // 出力
else *reg &= ~bit; // 入力
}
void avr_digitalWrite(uint8_t pin, uint8_t val) {
uint8_t bit = digitalPinToBitMask(pin);
volatile uint8_t* out = portOutputRegister(digitalPinToPort(pin));
if (val == HIGH) *out |= bit; // ON
else *out &= ~bit; // OFF
}
使い方はシンプル:
void setup() { avr_pinMode(13, OUTPUT); }
void loop() {
avr_digitalWrite(13, HIGH);
delay(1000);
avr_digitalWrite(13, LOW);
delay(1000);
}
実測ベンチマーク
オシロで 1 kHz トグルを測定(ATmega328P + 16 MHz)。個体差やコンパイラ最適化で変動するため数値は 目安。本番前に 〔要実測〕 で確認しよう。
手法 | 1 トグル時間 | 最大周波数 | フラッシュ増加 |
---|---|---|---|
digitalWrite() | ≈3.5 µs | ≈286 kHz | +1.6 kB |
ポート直書き | ≈0.07 µs | ≈8.3 MHz | <10 B |
結果は 約50倍高速、コードサイズも 1/160。ブロッキング処理や PWM 疑似生成で真価を発揮する。
Arduino Uno のピン ↔ ポート対応表
Arduino Pin | Port | Bit |
---|---|---|
0 | PORTD | PD0 |
1 | PORTD | PD1 |
2 | PORTD | PD2 |
13 | PORTB | PB5 |
他 MCU はデータシートを参照しよう。
用語ミニ解説
- digitalPinToBitMask():ピン番号からビットマスクを返すマクロ。
- digitalPinToPort():ピンが属するポートレジスタを取得。
- portModeRegister():DDR(Data Direction Register)のポインタ。
- DDR:ピンを入力/出力に設定するレジスタ。
- AVR:Arduino Uno が搭載する 8-bit マイコンファミリ。
落とし穴とチェックリスト
- ライブラリが
digitalWrite()
を内部で呼ぶ場合は高速化の恩恵なし。 - 割り込み内で直接ポート操作する際は競合に注意。
- ポートを間違えると一発で LED が消灯しない。まずは テスター or LED で確認。
- L チカが速すぎて見えない場合は
delayMicroseconds()
で調整。
FAQ
Q. digitalWrite を速くしても体感できる?
A. 複数ピンを高速トグルする場面では劇的。I2C ビットバンギングやソフト PWM に効果大。
Q. Arduino Mega でも使える?
A. はい。digitalPinTo*
系マクロは Mega 系でも定義済み。ピン番号が増えるだけで API は共通。
Q. インラインアセンブラのほうがさらに速い?
A. レジスタ直書きと実行速度は同等。可読性と保守性を考えると C マクロのほうが無難。
Q. ライブラリを壊さずに全プロジェクトを高速化する方法は?
A. #define digitalWrite avr_digitalWrite
のトリックで一括置換できるが、依存の深い外部ライブラリでは事前検証必須。
Q. 破壊的な副作用は?
A. 割り込み競合とポート競合に注意すれば問題なし。タイマー/ADC 設定を弄らない限り安定動作する。
――詳しい内部実装や別 MCU での応用例は 関連記事:Arduinoをインラインアセンブラで爆速Lチカ! を参照。
コメント