Arduinoで遊ぶページ

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

pulseIn()

概要

pulseIn()は、ピンに入力されるパルスの時間を計測します。

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

ソースコード

pulseIn()は、hardware/arduino/cores/arduino/wiring_pulse.c に定義されています。以下に全ソースコードを示します。

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{
        // cache the port and bit of the pin in order to speed up the
        // pulse width measuring loop and achieve finer resolution.  calling
        // digitalRead() instead yields much coarser resolution.
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);
        uint8_t stateMask = (state ? bit : 0);
        unsigned long width = 0; // keep initialization out of time critical area
        
        // convert the timeout from microseconds to a number of times through
        // the initial loop; it takes 16 clock cycles per iteration.
        unsigned long numloops = 0;
        unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;
        
        // wait for any previous pulse to end
        while ((*portInputRegister(port) & bit) == stateMask)
                if (numloops++ == maxloops)
                        return 0;
        
        // wait for the pulse to start
        while ((*portInputRegister(port) & bit) != stateMask)
                if (numloops++ == maxloops)
                        return 0;
        
        // wait for the pulse to stop
        while ((*portInputRegister(port) & bit) == stateMask) {
                if (numloops++ == maxloops)
                        return 0;
                width++;
        }

        // convert the reading to microseconds. The loop has been determined
        // to be 20 clock cycles long and have about 16 clocks between the edge
        // and the start of the loop. There will be some error introduced by
        // the interrupt handlers.
        return clockCyclesToMicroseconds(width * 21 + 16); 
}

入力はpin、state、timeoutで、それぞれ、uint8_t、uint8_t、unsigned longです。戻り値はunsigned longです。

unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)
{

digitalPinToPort()digitalPinToBitMask()を使って、pinに対応するポートとビットマスクを求めます。digitalRead()の説明も参考にしてください。

        // cache the port and bit of the pin in order to speed up the
        // pulse width measuring loop and achieve finer resolution.  calling
        // digitalRead() instead yields much coarser resolution.
        uint8_t bit = digitalPinToBitMask(pin);
        uint8_t port = digitalPinToPort(pin);

stateに従って、stateMaskを設定します。またwidthとnumloopsを0に、maxloopsをmicrosecondsToClockCycles(timeout)/16で初期化します。

        uint8_t stateMask = (state ? bit : 0);
        unsigned long width = 0; // keep initialization out of time critical area

        // convert the timeout from microseconds to a number of times through
        // the initial loop; it takes 16 clock cycles per iteration.
        unsigned long numloops = 0;
        unsigned long maxloops = microsecondsToClockCycles(timeout) / 16;

stateMaskは、入力で指定したデジタルピン(pin)に対応するビットが0か1かを調べるための変数です。stateがLOWの場合は0を設定し、LOW以外の場合はbitを設定します。widthは、パルスの時間を測定するための変数です。numloopsは、この後のループ処理を何回実施したかをカウントするための変数です。maxloopsは、ループ処理を最大何回実施するかを表す変数です。

maxloopsは、入力パラメータで指定したtimeout(マイクロ秒単位)をmicrosecondsToClockCycles()でクロック数に変換し、1回のループでのクロック数の16で割ることで、最大何回ループするかを設定しています。ただ、ソースコードをコンパイルしアセンブラ命令を出力して確認したところ、1回のループが16クロックであるという確証は得られませんでした。私の間違いの可能性も高いですが。特に、3番目のループはwidthをインクリメントするという処理が入っているため、1番目・2番目のループよりもクロック数は多くなるはずです。

指定したポートのビットがstateMaskと等しい間はnumloopsをインクリメントしながら空ループを回します。portInputRegister()は、ポートに対応する入力レジスタを返すマクロです。numloopsがmaxloopsになったらタイムアウトなので、0を返します。

        // wait for any previous pulse to end
        while ((*portInputRegister(port) & bit) == stateMask)
                if (numloops++ == maxloops)
                        return 0;

このループは入力で指定したポートが既にstateと同じ場合に反転するのを待つためのループです。

次に、指定したポートのビットがstateMaskと等しくない間、numloopsをインクリメントしながら空ループを回します。numloopsがmaxloopsになったらタイムアウトなので、0を返します。

        // wait for the pulse to start
        while ((*portInputRegister(port) & bit) != stateMask)
                if (numloops++ == maxloops)
                        return 0;

このループは入力で指定したポートがstateになるのを待つためのループです。

最後に、指定したポートのビットがstateMaskと等しい間、numloopsとwidthをインクリメントしながら空ループを回します。numloopsがmaxloopsになったらタイムアウトなので、0を返します。

        // wait for the pulse to stop
        while ((*portInputRegister(port) & bit) == stateMask) {
                if (numloops++ == maxloops)
                        return 0;
                width++;
        }

このループは入力で指定したポートがstateと同じ状態である時間を測定するためのループです。

最後に、widthをマイクロ秒に変換してリターンします。

        // convert the reading to microseconds. The loop has been determined
        // to be 20 clock cycles long and have about 16 clocks between the edge
        // and the start of the loop. There will be some error introduced by
        // the interrupt handlers.
        return clockCyclesToMicroseconds(width * 21 + 16); 
}

clockCyclesToMicroseconds()は、クロック数をマイクロ秒に変換するマクロです。ここでは、最後のループは20クロックかかる前提で時刻を計算しているようです。

バージョン

Arduino 1.0.5



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

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