Arduinoで遊ぶページ

Arduinoで遊んだ結果を残すページです。
garretlab
delayMicroseconds()

delayMicroseconds()

概要

delayMicroseconds()は、指定した値(マイクロ秒秒単位)だけ待つ関数です。空ループを使ってビジーウエイトします。

delayMicroseconds()のリファレンスはこちらをご覧ください。

ソースコード

delayMicroseconds()は、hardware/arduino/cores/arduino/wiring.c に定義されています。以下に全ソースコードを示します。実際には#if によりさまざまなチップに対応しています。

/* Delay for the given number of microseconds.  Assumes a 8 or 16 MHz clock. */
void delayMicroseconds(unsigned int us)
{
        // for a one-microsecond delay, simply return.  the overhead
        // of the function call yields a delay of approximately 1 1/8 us.
        if (--us == 0)
                return;

        // the following loop takes a quarter of a microsecond (4 cycles)
        // per iteration, so execute it four times for each microsecond of
        // delay requested.
        us <<= 2;

        // account for the time taken in the preceeding commands.
        us -= 2;

        // busy wait
        __asm__ __volatile__ (
                "1: sbiw %0,1" "\n\t" // 2 cycles
                "brne 1b" : "=w" (us) : "0" (us) // 2 cycles
        );
}

入力はunsinged int型の変数です。戻り値はありません。

入力された引数をデクリメントし0であれば、何もせずにリターンします。すでに1マイクロ秒経過しているからのようです。

        // for a one-microsecond delay, simply return.  the overhead
        // of the function call yields a delay of approximately 1 1/8 us.
        if (--us == 0)
                return;

関数呼び出し時のオーバヘッドを少し見てみましたが、コメントにあるような1 1/8マイクロ秒になる理由はよくわかりませんでした。call命令で4クロックかかるのは確実ですが、それ以外に引数を積んだりする処理があり、もう少し時間がかかりそうな気がするのですが…

次に入力された引数を4倍(左に2ビットシフト)し、2引きます。

        // the following loop takes a quarter of a microsecond (4 cycles)
        // per iteration, so execute it four times for each microsecond of
        // delay requested.
        us <<= 2;

        // account for the time taken in the preceeding commands.
        us -= 2;

引数を4倍するのは、ビジーウエイトするループ1回が1/4マイクロ秒かかるためです。このため、この時点で引数(us)は、マイクロ秒ではなく、ループ数になります。

余談ですが、delayMicroseconds()のリファレンスによると、delayMicroseconds()で指定できるのは16383までと書いてあります。これは、引数がunsinged intなので16ビット。delayMicroseconds()内で、引数を左に2ビットシフトしているため、実質14ビットが有効なビット数となるからです。実は、最初に1を引いているので、16384までが有効な気がします。

また、今までの処理に要した時間として、2ループ分=8クロック=0.5マイクロ秒減算します。これまでの処理をアセンブラで見てみると、以下のようになりました。

        sbiw r24,1
        breq .L1
        lsl r24
        rol r25
        lsl r24
        rol r25
        sbiw r24,2

sbiwが2クロック、その他は1クロックです。2個目のsbiw r24,2は、us-=2に対応する命令で、その分を含めると計9ロック、含めないと計7クロックのようです。

最後に、ビジーウエイトします。

        // busy wait
        __asm__ __volatile__ (
                "1: sbiw %0,1" "\n\t" // 2 cycles
                "brne 1b" : "=w" (us) : "0" (us) // 2 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 1.0.5



メニューを表示するためにJavaScriptを有効にしてください。

Arduinoで遊ぶページ
Copyright © 2012 garretlab all rights reserved.
inserted by FC2 system