概要
delayMicroseconds()は、指定した値(マイクロ秒秒単位)だけ待つ関数です。空ループを使ってビジーウエイトします。
delayMicroseconds()のリファレンスはこちらをご覧ください。
ソースコード
delayMicroseconds()は、hardware/arduino/avr/cores/arduino/wiring.c に定義されています。以下に全ソースコードを示します。実際には#if によりさまざまなチップに対応しています。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/* Delay for the given number of microseconds. Assumes a 1, 8, 12, 16, 20 or 24 MHz clock. */
void delayMicroseconds(unsigned int us)
{
// call = 4 cycles + 2 to 4 cycles to init us(2 for constant delay, 4 for variable)
// calling avrlib's delay_us() function with low values (e.g. 1 or
// 2 microseconds) gives delays longer than desired.
//delay_us(us);
// for the 16 MHz clock on most Arduino boards
// for a one-microsecond delay, simply return. the overhead
// of the function call takes 14 (16) cycles, which is 1us
if (us <= 1) return; // = 3 cycles, (4 when true)
// the following loop takes 1/4 of a microsecond (4 cycles)
// per iteration, so execute it four times for each microsecond of
// delay requested.
us <<= 2; // x4 us, = 4 cycles
// account for the time taken in the preceding commands.
// we just burned 19 (21) cycles above, remove 5, (5*4=20)
// us is at least 8 so we can subtract 5
us -= 5; // = 2 cycles,
// busy wait
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t" // 2 cycles
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
);
// return = 4 cycles
}
|
入力はunsinged int型の変数です。戻り値はありません。
引数が1以下であれば、すでに1マイクロ秒経過しているので、何もしないでリターンします。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* Delay for the given number of microseconds. Assumes a 1, 8, 12, 16, 20 or 24 MHz clock. */
void delayMicroseconds(unsigned int us)
{
// call = 4 cycles + 2 to 4 cycles to init us(2 for constant delay, 4 for variable)
// calling avrlib's delay_us() function with low values (e.g. 1 or
// 2 microseconds) gives delays longer than desired.
//delay_us(us);
// for the 16 MHz clock on most Arduino boards
// for a one-microsecond delay, simply return. the overhead
// of the function call takes 14 (16) cycles, which is 1us
if (us <= 1) return; // = 3 cycles, (4 when true)
|
次に入力された引数を4倍(左に2ビットシフト)し、5を引きます。
16
17
18
19
20
21
22
23
24
|
// the following loop takes 1/4 of a microsecond (4 cycles)
// per iteration, so execute it four times for each microsecond of
// delay requested.
us <<= 2; // x4 us, = 4 cycles
// account for the time taken in the preceding commands.
// we just burned 19 (21) cycles above, remove 5, (5*4=20)
// us is at least 8 so we can subtract 5
us -= 5; // = 2 cycles,
|
引数を4倍するのは、ビジーウエイトするループ1回が0.25マイクロ秒かかるためです。このため、この時点で引数(us)は、マイクロ秒ではなく、ループ数になります。
余談ですが、delayMicroseconds()のリファレンスによると、delayMicroseconds()で指定できるのは16383までと書いてあります。これは、引数がunsinged intなので16ビット。delayMicroseconds()内で、引数を左に2ビットシフトしているため、実質14ビットが有効なビット数となるからです。また、引数のエラー処理をしていないことにも注意してください。
また、今までの処理に要した時間として、5ループ分=20クロック=1.25マイクロ秒減算します。
これまでの処理をアセンブラで見てみると、以下のようになりました。
1
2
3
4
5
6
7
8
|
cpi r24,2
cpc r25,__zero_reg__
brlo .L1
lsl r24
rol r25
lsl r24
rol r25
sbiw r24,5
|
最初の3行が if ( us <= 1) return;、次の4行が us «= 2;、最後の1行が us -= 5; に相当します。
sbiwが2クロック、その他は1クロックで、合計9クロックです。
また、関数呼び出しのオーバーヘッドが6から8クロックと書いてありますが、少し計算が合わない気がします。私がコンパイルオプションを間違えているのかもしれませんが。
最後に、ビジーウエイトします。
26
27
28
29
30
31
32
|
// busy wait
__asm__ __volatile__ (
"1: sbiw %0,1" "\n\t" // 2 cycles
"brne 1b" : "=w" (us) : "0" (us) // 2 cycles
);
// return = 4 cycles
}
|
sbiwは引き算、brneは比較を行うアセンブラ命令です。
sbiw、brneとも2クロックを要する命令で、全体で4クロックかかります。
Arduino Unoでは、クロック数は16MHzなので、1クロックに要する時間は、1/16000000秒=0.0000000625秒=0.0000625ミリ秒=0.0625マイクロ秒です。1回のループに要する時間は、4クロック分なので、0.0625*4=0.25マイクロ秒です。
バージョン
Arduino AVR Boards 1.8.6
最終更新日
March 21, 2023