delayMicroseconds()

# 概要

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 }``````

 `````` 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) ``````

 ``````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, ``````

また、今までの処理に要した時間として、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``````

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