Arduino日本語リファレンス|UNO R3/R4で使用する関数の解説
Arduinoの関数を日本語で調べようとすると、たどり着くのは2010年前後で更新が止まったページか、英語の公式ドキュメントです。関数名と意味は分かっても、実際どれくらい時間がかかるのか、どちらを使えばいいのかまでは書かれていません。
この記事は、Arduinoの関数を分類から引ける一覧にしたうえで、UNO R3とUNO R4の実機で測った値を並べたものです。載せているのは、数字で確かめたものだけです。digitalWrite()は1回4.980マイクロ秒。millis()が1回で進むのは1.024ms。tone()を鳴らすと特定のピンのPWMが止まります。
- Arduinoの関数を分類から引ける(デジタルI/O、アナログI/O、時間、割り込み…)
analogWrite()が出しているのは電圧ではない(実測グラフつき)- 各関数が1回あたり何マイクロ秒かかるか(R3・R4の実測)
- UNO R3とR4で挙動が変わる関数と、その中身
目次
この記事の使い方
関数はArduino公式のLanguage Referenceと同じ分類で並べています。公式ドキュメントと行き来しても迷わないようにするためです(公式は英語です)。
そのうえで、他のリファレンスに無い情報を2つ足しました。
delayとmillisのように似た関数が並ぶところで、どちらを使うべきかを書いています。Arduinoで書いているコードがそもそも何の言語なのかは、Arduinoの言語は何?C言語・C++との違いを現役組み込みエンジニアが解説で説明しています。
デジタルI/O|ピンをHIGH/LOWで扱う
pinMode(pin, mode)- ピンを
OUTPUT/INPUT/INPUT_PULLUPのどれかに設定する。setup()で1回だけ。書き忘れると入出力が動かない digitalWrite(pin, value)R3 4.980 µs / R4 0.669 µs- 出力に設定したピンを
HIGH(5V)かLOW(0V)にする。LEDやリレーのオンオフ。速さが要るならレジスタを直接書く(下の実測) digitalRead(pin)R3 3.324 µs / R4 1.127 µs- 入力ピンが
HIGHかLOWかを読む。スイッチやセンサの出力を読む
pinMode()を書かないと、ピンは入力のままです。digitalWrite()を呼んでもLEDは光らず、コンパイルは通るのでエラーも出ません。配線を疑う前にここを見てください。pinMode(8, OUTPUT);
unsigned long start = micros();
for (uint16_t i = 0; i < 1000; i++) {
digitalWrite(8, HIGH);
}
Serial.println((micros() - start) / 1000.0, 3);
/* 同じ動作をレジスタ直接で。D8=PORTBの0ビット目 */
/* AVR(UNO R3)だけ。R4では動かない */
start = micros();
for (uint16_t i = 0; i < 1000; i++) {
PORTB |= _BV(PB0);
}
Serial.println((micros() - start) / 1000.0, 3);
digitalWrite が「遅い」と言われる理由|実測で89倍
digitalWrite()は、渡されたピン番号から毎回テーブルを引いて、どのレジスタのどのビットかを調べ、割り込みを止めてから書き込みます。安全な代わりに手間がかかります。
レジスタを直接書けば同じ動作が終わります。UNO R3で両方を1,000回ずつ呼んで測りました。
| 書き方 | 1回あたり(UNO R3) |
|---|---|
digitalWrite(8, HIGH) |
4.980 µs |
PORTB |= _BV(PB0)(同じピンを直接操作) |
0.056 µs |
1回では5マイクロ秒の違いですが、1万回のループなら約50ミリ秒変わります。逆に言えば、数回のオンオフに
digitalWrite()を使うことは何の問題もありません。ループの中で何万回も叩くときだけ、この差が時間に表れます。レジスタを直接触る書き方はArduino Lチカのやり方|digitalWriteからレジスタ操作まで練習問題9問で、同じLチカを両方の書き方で試せます。
INPUT_PULLUP は内蔵の抵抗を使う設定
スイッチを読むときINPUTのままだと、スイッチが開いている間のピンの電圧が定まらず、読むたびに値が変わります。INPUT_PULLUPにすると内部の抵抗でピンが5V側へ引き上げられ、スイッチを押したときだけLOWになります。外付けの抵抗が要りません。
この内蔵抵抗の値を、外付けの10kΩとの分圧から逆算して測りました。UNO R3で約37〜44kΩ(5回の測定)。ATmega328Pのデータシートは20〜50kΩと幅のある書き方をしていますが、その範囲に収まっています。同じ個体でも測るたびに数kΩ動くので、設計で当てにする値ではありません。
アナログI/O|電圧を読む・PWMを出す
analogRead(pin)R3 111.792 µs / R4 21.424 µs- アナログピンの電圧を数値で読む。R3は0〜1023(10bit)。可変抵抗・光センサ・温度センサ。他の関数より2桁遅いので、ループで連発すると時間を取られるR3は10bit固定。R4は
analogReadResolution()で最大14bitに変えられる analogWrite(pin, value)R3 8.268 µs / R4 25.121 µs- 指定した比率でHIGHとLOWを繰り返す(PWM)。電圧を出す関数ではない。LEDの明るさ、モータの速さ。R3で使えるのは3・5・6・9・10・11番ピンR3は0〜255の8bit固定。R4は
analogWriteResolution()で12bitまで上げられる。R4のほうが3倍おそい analogReference(type)analogRead()の基準電圧を変える。小さい電圧を細かく読みたいとき選べる値がR3とR4で違う
long sum = 0;
int lo = 1023;
int hi = 0;
for (uint16_t i = 0; i < 200; i++) {
int v = analogRead(A0);
sum += v;
if (v < lo) {
lo = v;
}
if (v > hi) {
hi = v;
}
}
/* 平均電圧 */
double volt = (sum / 200.0) * 5.0 / 1023.0;
/* 振れ幅(LSB)。大きいならノイズを疑う */
Serial.println(hi - lo);
/* PWMを出す。128 でデューティ約50% */
analogWrite(3, 128);
analogWrite は電圧を出す関数ではない
出ているのはHIGHとLOWの繰り返しです。名前がanalogWriteなので「指定した電圧が出る」と読めますが、そうではありません。analogWrite(pin, 128)なら、およそ半分の時間だけ5Vになる波形が出続けます。
テスタで測ると中間の電圧に見えるのは、テスタが平均を表示しているからです。確かめるために、PWMの出力を抵抗とコンデンサで平滑して、平均電圧を実際に測りました。

analogWriteの値 |
実測した平均電圧 | 5V×値/255(理論値) |
|---|---|---|
| 0 | 0.000 V | 0.000 V |
| 64 | 1.266 V | 1.255 V |
| 128 | 2.507 V | 2.510 V |
| 192 | 3.763 V | 3.765 V |
| 255 | 4.995 V | 5.000 V |
理論値との差は最大0.9%でした。値と平均電圧はきれいに比例します。ただし出ているのはあくまで5Vと0Vの繰り返しで、LEDが暗く見えるのも、モータがゆっくり回るのも、平均の電力が下がるからです。
オーディオのように滑らかな電圧が必要なら、この記事で使ったように抵抗とコンデンサでローパスフィルタを作るか、DAC(デジタル・アナログ変換器)を積んだボードを使います。UNO R3にDACはありません。
PWMの周波数はピンによって違う(R3)
UNO R3でPWMを出せるのは3・5・6・9・10・11番ピン(基板に「〜」が付いています)。このピンは内部で複数のタイマに割り当てられていて、担当するタイマが違うと周波数も違います。同じanalogWrite(128)を出して測りました。
| ピン | UNO R3 | UNO R4 |
|---|---|---|
| D3 | 493.10 Hz | 491.16 Hz |
| D5 | 981.35 Hz | 491.16 Hz |
R3ではピンによって周波数が倍違います。モータが可聴域で唸る、LEDがちらついて見えるといった症状は、この違いで説明が付くことがあります。R4ではどちらも同じ491Hzで、この差はありません。
時間|待つ・測る
delay()は止まります。止めたくないならmillis()です。delay(ms)- 指定したミリ秒だけ待つ。待っている間に他のことをしないときだけ。その間ほかの処理は進まない(割り込みは動く)
delayMicroseconds(us)- マイクロ秒単位で待つ。センサの信号タイミングを作るとき。1µsの指定は効かない(下の実測)
millis()R3 2.008 µs / R4 0.249 µs- 起動からの経過ミリ秒を返す。止めずに周期処理を作る。
unsigned longで約49.7日で0に戻る1回で進む時間がR3は1.024ms、R4は約1.000ms(下の実測) micros()R3 3.928 µs / R4 2.041 µs- 起動からの経過マイクロ秒を返す。短い時間を測る。約71.6分で0に戻る。R3では4µs刻み
delay と millis の使い分け
待っている間に他のことをしないなら
delay()、するならmillis()。LEDを1秒ごとに点滅させるだけならdelay()で足ります。点滅させながらボタンも見たいならmillis()です。millis()で周期処理を書くときは、前回の時刻を覚えておいて引き算で判定します。
unsigned long last = 0;
void loop(void)
{
unsigned long now = millis();
if ((now - last) >= 1000) {
last = now;
/* ここに1秒ごとの処理 */
}
/* ここは止まらずに毎回まわる */
}
now >= last + 1000 ではなく (now - last) >= 1000 と書くのは、49.7日で0に戻ったときにも正しく動かすためです。引き算なら桁が一周しても差は正しく求まります。
周期処理そのものの練習はArduinoで一定周期の処理を作る|millisとタイマ割り込みの練習問題にまとめています。
millis() は1.000msずつ進まない(R3)
millis()の値が1つ進む実際の時間を測ったところ、UNO R3で1024.0マイクロ秒でした。1.000msではありません。
理由はArduinoのソースに書いてあります。AVR版のコア(wiring.c)では、時間を数えているタイマが一周するのが1024µsで、そのたびにmillis()を1進めています。
| コアの定義 | 値 | 意味 |
|---|---|---|
MICROSECONDS_PER_TIMER0_OVERFLOW |
1024 µs | タイマが一周する実時間 |
MILLIS_INC |
1 | 一周ごとに1進める |
FRACT_INC / FRACT_MAX |
3 / 125 | 端数を貯めて、約42回に1回だけ2進める |
つまり1歩は1.024msですが、42回に1回2進む補正が入るので、長い目で見た平均は1.000msになります。数秒の周期処理で困ることはありませんが、1歩を1msとして積算すると少しずつずれることは知っておいてください。UNO R4では999.7〜1000.0µsで、この補正は要りません。
delayMicroseconds(1) は何もしない
指定値と実際の待ち時間を、50回ずつ呼んで測りました。
| 指定 | UNO R3の実測 | UNO R4の実測 |
|---|---|---|
| 1 µs | 0.24 µs | 2.14 µs |
| 10 µs | 10.00 µs | 11.66 µs |
| 100 µs | 109.52 µs | 105.64 µs |
| 1000 µs | 1007.52 µs | 1045.68 µs |
R3で1µsの指定が待ち時間にならないのは仕様です。コアのソースにif (us <= 1) return;と書かれていて、1以下なら何もせずに返ります。コメントには「関数を呼ぶ手間だけで約1µs使うから」と理由も書かれています。
1µs前後の待ちが要る場面では、この関数を当てにしないのが正解です。なお100µsの指定だけ約1割長くなりましたが、原因は特定できていないため、実測値だけを載せます。
外部割り込み|待たずに拾う
loop()の順番を待たずに拾う仕組みです。attachInterrupt(num, fn, mode)- ピンの変化で指定した関数(ISR)を呼ぶ。短いパルスや、待っていられない入力を拾うR3は2番・3番ピンだけ。R4はD0〜D13の14本すべて(実測)
detachInterrupt(num)- 割り込みを外す。対で書く
digitalPinToInterrupt(pin)- ピン番号を割り込み番号に変換する。番号を直接書かないために使う。ボードが変わっても動く
interrupts()/noInterrupts()- 割り込み全体を許可・禁止する。禁止している間は
millis()も進まないので、必要な数行だけを挟む
volatile unsigned long isr_time_us = 0;
volatile bool isr_fired = false;
void on_rise(void)
{
/* 記録だけ。Serial.print は呼ばない */
isr_time_us = micros();
isr_fired = true;
}
void setup(void)
{
pinMode(2, INPUT);
attachInterrupt(digitalPinToInterrupt(2), on_rise, RISING);
}
割り込みは「すぐ」ではない
ピンが変化してから割り込み関数の中に入るまでの時間を測りました。UNO R3で11.20マイクロ秒(20回中20回成功)。この値には信号を出すためのdigitalWrite()(4.98µs)が含まれるので、割り込みそのものの応答は約6.2µsです。UNO R4は5.00µsでした。

loop()の途中に割り込み、ISRを実行して続きへ戻る。ピンの変化からISRに入るまでが11.20µs(UNO R3の実測)割り込みの中でやってはいけない3つ
delay()を呼ばない。割り込みの中では時間を数えるタイマが進まず、戻ってこなくなりますSerial.print()を呼ばない。理由は下の実測を見てください- 共有する変数に
volatileを付ける。付けないと、コンパイラが「変わらない」と判断して古い値を使い続けることがあります
「ISRは短く」と言われても、どれくらいから問題になるのかは書かれていないことが多いので、測りました。2kHz(周期500µs)のパルスを200ms入れて、期待される400回のうち何回拾えるかを、ISRの中に入れる処理の長さを変えて調べています。
| ISRの長さ | 数えられた回数(期待400回) | 取りこぼし |
|---|---|---|
| 0 µs(数えるだけ) | 401 回 | 0.2% |
| 100 µs | 401 回 | 0.2% |
| 300 µs | 401 回 | 0.2% |
| 600 µs | 268 回 | 33.3% |
| 900 µs | 201 回 | 50.0% |
周期500µsに対し、ISRが500µsを超えたところから落ち始めます。900µsでは1回おきに落として50%です。そして
Serial.print()で10文字送るコストは850µs(次の節の実測)。ISRの中で1回呼ぶだけで、この割り込みの3〜5割を取りこぼす計算になります。割り込みを使った周期処理はタイマ割り込みの練習問題、消費電力と絡めた使い方はArduinoの低消費電力化ドリルで扱っています。
拡張I/O|音を出す・パルスを測る
tone()と、パルスの長さを測るpulseIn()。どちらもタイマを使うので、PWMと取り合いになります。tone(pin, freq)- 指定した周波数の矩形波を出す(圧電ブザーなど)。音を鳴らす。同時に鳴らせるのは1ピンだけR3では鳴らしている間、D3のPWMが止まる(下の実測)。R4では止まらない
noTone(pin)tone()を止める。止めるまで鳴り続けるので対で書くpulseIn(pin, value)- パルスの長さをマイクロ秒で返す。超音波センサの距離測定など。この記事のPWM周波数もこれで測っている
shiftOut()/shiftIn()- 1ビットずつ送る・受ける。シフトレジスタでピンを増やすとき
pinMode(2, INPUT);
analogWrite(3, 128);
delay(50);
unsigned long high_us = pulseIn(2, HIGH, 100000UL);
unsigned long low_us = pulseIn(2, LOW, 100000UL);
/* D3 なら 493.10 Hz */
double freq = 1000000.0 / (double)(high_us + low_us);
Serial.println(freq, 2);
analogWrite(3, 0);
/* 止めるまで鳴り続ける。noTone と対で */
tone(8, 440);
delay(500);
noTone(8);
tone() の周波数は高いほどずれる
| 指定 | UNO R3の実測 | UNO R4の実測 |
|---|---|---|
| 440 Hz | 445.6 Hz(+1.28%) | 442.7 Hz(+0.61%) |
| 1000 Hz | 1019.4 Hz(+1.94%) | 1010.1 Hz(+1.01%) |
| 4000 Hz | 4219〜4329 Hz(+5.5〜8.2%) | 4132.2 Hz(+3.31%) |
どちらのボードも高い周波数ほど誤差が大きくなります。タイマの分周が整数なので、周波数が上がるほど刻みが粗くなるためです。4000HzのR3だけ幅で書いたのは、測るたびに+5.5%と+8.2%の間で変わったからです。R4は3回とも4132.2Hzで動きませんでした。音階を正確に出したい用途では、この誤差を見込んでおく必要があります。
tone() を鳴らすとPWMが止まる(R3)
UNO R3のtone()は内部でタイマを1つ占有します。そのタイマはanalogWrite()のPWMにも使われているため、音を鳴らしている間だけ特定のピンのPWMが止まります。
D3とD5にanalogWrite(128)を出したまま、別のピン(D8)でtone()を鳴らして測りました。
| ピン | tone前 | tone中 | 結果 |
|---|---|---|---|
| D3 | 493.1 Hz | 出力が止まった | 同じタイマを使っている |
| D5 | 982.3 Hz | 996.0 Hz | 影響なし |
出力そのものが止まります。周波数が変わるのではありません。「音を鳴らしながらLEDの明るさを変えたら、そのLEDだけ消えた」という現象は、これで説明が付きます。音と一緒にPWMを使うなら、D3以外のピンを選んでください。UNO R4では両方とも影響を受けませんでした。
通信|Serialとその仲間
| 項目 | 何をするか | 使いどころ |
|---|---|---|
Serial |
PCとの文字のやり取り | 変数の中身を見る、動作の記録を残す |
Wire(I2C) |
2本の線で複数のセンサとつなぐ | 温湿度センサ、液晶など |
SPI |
高速な同期通信 | SDカード、ディスプレイなど |
Serial.begin()で速度を決め、シリアルモニタ側も同じ速度にします。ここが食い違うと文字化けします。
Serial.begin(115200);
/* R4はUSB直結。開くまで待つ */
while (!Serial) {
;
}
/* F() で文字列をフラッシュへ置く */
Serial.println(F("start"));
Serial.print は850µsかかる
115200bpsで10文字を送るコストを測ったところ、UNO R3で850.0µs、UNO R4で868.5µsでした。10文字=100ビットぶんの送信時間(約868µs)とほぼ一致します。通信の速さそのものが上限になっているということです。
1回850µsは、前の節で測った「ISRが600µsを超えると33%取りこぼす」の外側です。デバッグのつもりで割り込みの中に
Serial.print()を1行入れると、拾いたかった信号を3〜5割落とします。ISRではフラグを立てるだけにして、表示はloop()側でやるのが定石です。シリアル通信の使い方そのものはArduinoのシリアル通信入門|Serial.printとデータ受信の練習問題にまとめています。
数学・ビット・文字・乱数
map()がR3で39µsかかることのほうが実務では問題になります。| 分類 | 関数 | ひとこと |
|---|---|---|
| 数学 | abs() constrain() map() max() min() pow() sq() sqrt() |
map()は範囲の変換。R3では39.460µsかかる(整数の割り算を含むため)。R4は0.657µsで60倍速い |
| ビットとバイト | bit() bitRead() bitWrite() bitSet() bitClear() highByte() lowByte() |
レジスタを触り始めると毎回使う |
| 文字 | isDigit() isAlpha() isSpace() ほか |
受信した文字の判定に使う |
| 乱数 | random() randomSeed() |
そのままだと毎回同じ並びになる。種を変える必要がある |
map()がR3で40µs近くかかるのは意外に思われるかもしれませんが、8bitのマイコンには割り算の回路がありません。センサ値を毎回map()で変換するループでは、これが積み上がります。
ビット操作の考え方はC言語のビット演算|マスク・シフトの練習問題で扱っています。
/* 0〜1023 を 0〜100 へ直す */
int raw = analogRead(A0);
int pct = map(raw, 0, 1023, 0, 100);
/* 範囲外を丸める */
pct = constrain(pct, 0, 100);
/* 0ビット目を1にして、読み返す */
uint8_t flags = 0;
bitSet(flags, 0);
bool on = bitRead(flags, 0);
型・定数・修飾子
intの大きさがボードで変わります。割り込みと共有する変数にはvolatileが要ります。| 項目 | 内容 | 注意 |
|---|---|---|
| 定数 | HIGH / LOW、INPUT / OUTPUT / INPUT_PULLUP、LED_BUILTIN |
LED_BUILTINは基板上のLED(UNOでは13番) |
| 整数 | int long byte unsigned int ほか |
intの大きさがR3とR4で違う(下の節) | |
| 小数 | float double |
R3ではdoubleもfloatと同じ4バイト |
| 修飾子 | const static volatile |
volatileは割り込みと変数を共有するときに必須 |
| ユーティリティ | sizeof() PROGMEM |
PROGMEMはR3向け(RAMが2KBしかないため) |
配列・構造体・ポインタといったC言語側の話はC言語の配列、構造体、組み込みでのポインタで扱っています。
/* R3: 2 / R4: 4 */
Serial.println((int)sizeof(int));
/* R3: 4 / R4: 4 */
Serial.println((int)sizeof(long));
/* R3: 4 / R4: 8 */
Serial.println((int)sizeof(double));
/* R3: 2 / R4: 4 */
Serial.println((int)sizeof(void *));
UNO R3とUNO R4で違うところ
UNO R3のマイコンはAVRのATmega328P(16MHz)、UNO R4はルネサス RA4M1(48MHz)です。互換性を保つよう作られていますが、中身は別物なので変わるところがあります。
速さ
| 関数 | UNO R3 | UNO R4 | 差 |
|---|---|---|---|
digitalWrite() |
4.980 µs | 0.669 µs | 7.4倍速い |
digitalRead() |
3.324 µs | 1.127 µs | 2.9倍速い |
analogRead() |
111.792 µs | 21.424 µs | 5.2倍速い |
analogWrite() |
8.268 µs | 25.121 µs | 3倍おそい |
map() |
39.460 µs | 0.657 µs | 60倍速い |
| 割り込みの応答 | 11.20 µs | 5.00 µs | 2.2倍速い |
おおむねR4が速いのですが、analogWrite()だけはR4が3倍おそいという結果でした。PWMの設定を書き換えるたびに呼ぶ処理では、ここで時間を取られる可能性があります。
挙動
| 観点 | UNO R3 | UNO R4 |
|---|---|---|
| PWMの周波数 | D3=493 Hz/D5=981 Hz(ピンで違う) | どちらも491 Hz |
tone()中のPWM |
D3の出力が止まる | 止まらない |
| 割り込みに使えるピン | D2・D3の2本 | D0〜D13の14本 |
millis()の1歩 |
1.024 ms(42回に1回2進む補正) | 約1.000 ms |
delayMicroseconds(1) |
何もしない | 約2 µs待つ |
analogReadの分解能 |
10bit固定 | analogReadResolution()で最大14bit |
analogWriteの分解能 |
8bit固定 | analogWriteResolution()で12bitまで |
型の大きさ(移植するときに問題になる)
| 型 | UNO R3 | UNO R4 |
|---|---|---|
char |
1 バイト | 1 バイト |
int |
2 バイト | 4 バイト |
long |
4 バイト | 4 バイト |
float |
4 バイト | 4 バイト |
double |
4 バイト(floatと同じ) | 8 バイト |
void* |
2 バイト | 4 バイト |
intに32768以上を入れるコードはR3では壊れ、R4では動きます。逆にintのサイズを2バイト前提で書いたコードは、R4で意図しない動きになります。移植するならintではなくint16_tやint32_tで書くのが安全です。
R4だけコンパイルが通らないとき
この記事の計測スケッチを書いたときに、R3では通るのにR4だけ落ちたものが2つありました。
PIN_ANALOGという変数名が使えない。R4のコアが同じ名前をマクロとして持っているためで、expected ')' before numeric constantというエラーになります。ピン番号を入れる変数には自分用の接頭辞を付けてくださいNOT_AN_INTERRUPTが定義されていない(AVRのコアにはあります)。使うなら#ifndefで自分で定義しておきます
R3ではRAMが先に尽きる
UNO R3のRAMは2KBしかありません。AVRでは文字列リテラルがRAMに置かれるため、Serial.print("...")を並べるだけで足りなくなります。この記事の計測スケッチも、最初はRAMを3,542バイト(172%)使ってコンパイルが通りませんでした。
最大2048バイトのRAMのうち、グローバル変数が3542バイト(172%)を使っていて、
ローカル変数で-1494バイト使うことができます。
data section exceeds available space in board
直し方はF()で文字列をフラッシュ側へ移すことです。
/* RAMに置かれる */
Serial.println("digitalWrite の実測");
/* フラッシュに置かれる */
Serial.println(F("digitalWrite の実測"));
この置き換えだけで3,542バイト→254バイト(12.4%)まで下がりました。UNO R4はRAMが32KBあるので、この対処は要りません。R3向けのコードにPROGMEMやF()が出てくるのは、この2KBという制約のためです。
この数字の測り方(同じことを手元で再現する)
測定は1つのスケッチにまとめてあります。使うのはジャンパ3本(D3とD2、D5とD4、D8とD7)と、ブレッドボードに組む3つの回路(分圧、RCローパス、プルアップ測定)です。あとはArduino IDEで書き込み、シリアルモニタを115200bpsで開くだけです。
| 測ったもの | やり方 |
|---|---|
| 関数1回あたりの時間 | 1,000回呼んでmicros()で挟み、空ループぶんを引く |
| PWM周波数 | 出力を別のピンで受けてpulseIn()でHIGHとLOWの長さを測る |
| 平均電圧 | 1kΩと10µFで平滑してanalogRead()で読む |
| 割り込みの応答 | 出力をHIGHにする直前のmicros()と、ISRの中のmicros()の差 |
| 取りこぼし | tone()で2kHzを入れ、ISRの中にdelayMicroseconds()で負荷を入れて数える |
「ISRでSerialを使うと危ない」を確かめたくなりますが、AVRでは送信バッファが満杯のとき、割り込みを禁止したまま空くのを待ち続けてボードが固まります。この記事では代わりに
delayMicroseconds()で同じだけ時間を使い、原因である「ISRが長いこと」だけを再現しています。確認
LEDを1秒ごとに点滅させながら、ボタンが押されたかどうかも見たい。delay()とmillis()のどちらを使いますか。
解答・解説を見る
millis()を使います。
解説:delay(1000)を書くと、その1秒の間はボタンを読むコードが動きません。前回の時刻を覚えておき、(now - last) >= 1000 で判定すれば、待っている間もループが回り続けます。
よくあるつまずき
loop()側に反映されません。Serial.print1回(850µs)で3〜5割落とします。F()でフラッシュへ移します。R4では起きません。まとめ
Arduinoの関数は、分類さえ分かれば公式ドキュメントからでも引けます。この記事で足したのは実際に測った数字です。
digitalWrite()は1回4.980µs、レジスタ直接なら0.056µsで89倍の差があります。analogRead()は111.792µsで、他の関数より2桁遅い。map()はR3で39.460µsかかります。どれも1回なら気にする値ではありませんが、ループの中で何万回も回ると時間に表れます。
analogWrite()が出しているのは電圧ではなく、HIGHの時間の割合です。平滑して測ると値に比例した電圧になりますが、ピンによって周波数が倍違い(R3のD3とD5)、tone()を鳴らすとD3の出力は止まります。
割り込みは11.20µsで応答しますが、ISRが割り込みの周期を超えると取りこぼし始めます。Serial.print()は10文字で850µsかかります。ISRの中で1回呼べば3〜5割落とすという関係です。
そしてUNO R4は、ほとんどの関数でR3より速い一方、analogWrite()だけ3倍おそく、intは2バイトから4バイトに変わり、割り込みは2本から14本のピンで使えます。R3で当たり前だったF()やPWM周波数の差は、R4では出てきません。
数字が必要になったらこのページに戻ってきてください。測り方も載せてあるので、手元のボードで確かめ直すこともできます。