Arduino PulseIn:How to Measure input signal periods using an Arduino. There are two Pulse-In functions. Which one you should use for best accuracy? Find out why there two functions, why interrupts must be on for one and off for the other, and why your measurement might be inaccurate.


Arduino pulseIn:

  • Pulsein Measures high or low periods of a signal in microseconds.

  • Why is your pulseIn measurement inaccurate - Find out here.

  • Why does one function need interrupts On, while the other one needs them Off?

  • There are two functions pulseIn and pulseInLong...

  • Find out the difference between them here.

  • Find out why pulseInLong is uses the word 'Long' - hint: It's not a type definition.
arduino pulsein - how to do it easily with code examples

You might be wondering why your Arduino pulseIn measurement is slightly inaccurate, or even why there are two versions of the function pulseIn and pulseInLong! You can find out all this and more on this page.

The pulseIn function measures the time period of a high or low pulse input signal. A typical use is to measure the output from an ultrasonic distance module (HC-SR04) which outputs a signal period proportional to the round trip sound reflection time from which you can calculate distance.

The simple way to use these functions for measuring a high pulse is simply to insert them into the code.

    pulseIn( <pin>, HIGH);
    or

    pulseInLong( <pin>, HIGH);

Note: PulseInLong requires interrupts to be running.

The question now is WHY are there 2 functions? Each of which returns the same result!

You can find out in this page, which explores:
  • Why there are two functions.
  • How each function works.
  • Why each function may or may not be accurate.
  • Compares the accuracy of each function.
Note: The HIGH/LOW parameter lets you to measure the high or low period of the signal. You can only perform a HIGH or LOW measurement not both at the same time.

Measuring Frequency using pulseIn

You can also use pulseIn to measure frequency; two measurements are needed one to measure the low period and one to measure the high period (if the M:S ratio is not 1:1). The project in this page link uses pulseIn to measure the TCS320 frequency outputs.

The pulseIn function can measure pulses from 2-3us to 3minutes. It provides a simple way to measure signal periods on any pin. There are in fact two versions of the function explored below (pulseIn and pulseInLong) one uses interrupts and one is coded in assembler. 

Note: PulseIn measures the high or low time period while a signal continuously repeats, and the input signal must continuously repeat for these functions to work correctly.

The function also has a timeout - if this expires then the function returns zero (The default timeout is 1 second).

For a different timeout use an unsigned long value (in microseconds) for the last parameter:

noInterrupts();
pulseIn( <pin>, HIGH, 10000000UL);  // 10 second timeout.
interrupts();

Note: You can use pulseIn with interrupts active, but results are less accurate.

Using Arduino PulseIn

The following code expects a signal on pin 7 and uses the pulseIn function to measure it, and then reports the results to the serial interface.

The timeout value is set to 3 seconds and the code also reports when that timeout fires indicating that no signal is present.

// pulseIn Example

const byte INPIN = 7;

void setup() {
   Serial.begin(9600);
   pinMode(INPIN, INPUT);
}

void loop() {
unsigned long res;

  Serial.print("Time: ");
  Serial.println(millis());
  Serial.println("Measuring...");
  Serial.flush(); // Needed since serial requires interrupts to operate.

  noInterrupts();
  res = pulseIn(INPIN, HIGH, 3000000UL); // 3 second timeout
  interrupts();

  if (res) {
     Serial.print("Time high (us): ");
     Serial.println(res);
  } else
     Serial.println("No signal present");

}

Here's the output with a 1kHz signal input to pin7:

Measuring...
Time high (us): 499
Time: 11817
Measuring...
Time high (us): 501
Time: 11869
Measuring...
Time high (us): 500
Time: 11919
Measuring...
Time high (us): 500
Time: 11970
Measuring...
Time high (us): 500
Time: 12020

Note: pulseIn can take up to 1.5 periods of input signal to determine a result.

The above results show that pulseIn is returning a result period of (500+500)us i.e. a frequency of 1kHz.

Warning: Turning off interrupts means millis returns the wrong time.

In the above results each measurement returns 500us as the high period of the input signal. Since interrupts are turned off during this period millis() will be out by
n * 500us after each loop (for n loops).

This is still using pulseIn but the reason that millis appears to work is that the pulseIn function is not timing out, it is taking a maximum of 1.5ms to return a result.

This shows you the principle reason that you may want to use pulseInLong() - millis() will not work unless interrupts are active i.e you can still do regular coding using the millis() function if and-only-if you use pulseInLong() - because interrupts are active.

Change the above code to use pulseInLong() to check (not forgetting to comment out the interrupt control functions).

Arduino PulseIn Problem


The problem with pulseIn is that you really want to use it with interrupts turned off for maximum accuracy. It is written in fixed assembler code that works by path timing measurement i.e. it works by assuming a 16MHz clock speed and measuring the number of assembler instructions used - resulting in a measured time.

Here's the output of the program with no signal input:

Time: 0
Measuring...
No signal present
Time: 24
Measuring...
No signal present
Time: 70
Measuring...
No signal present
Time: 116

Do you see what's wrong? - No interrupts so millis() can not work in fact the value should change by at least 3000ms between readings since the pulseIn timeout time is 3000ms.

Conclusion - it's easy to forget that you stopped interrupts to get a more accurate pulseIn measurement - all your timings will be out if you use pulseIn and millis(). The last reading above are not true time since the interrupts are off for ~1.5ms while pulseIn completes.

Difference Between PulseIn and PulseIn Long

PulseIn or PulseInLong?

There are two versions of pulseIn:

  • pulseIn
  • pulseInLong

They both have the same specification and return the same result i.e. measure pulse from 2-3us to 3 minutes but the first one uses an assembler routine to make the measurement while the second one uses the Timer0 interrupt to calculate the result.

The first one: pulseIn, can be used if interrupts are turned off (and if they are off will return a more accurate result - since it won't be interrupted while measuring), whereas the second one can not be used unless interrupts are turned on!

TIP: Turn off interrupts for a more accurate pulseIn result.

Arduino pulseIn vs pulseInLong Table

pulseIn vs pulseInLong
Function
Usage
Use with Interrupts
Range
pulseIn()
Best for short pulses. [*]
Must be off [**]
2-3us to 3min
pulseInLong() Best for long pulses. Must be on.
2-3us to 3min
*   long pulse measurements will have greater error as the calibration is empirical.
** See experiments below showing effect of interrupts on pulseIn accuracy.

It would appear that pulseInLong is the better choice, since it relies on a hardware interrupt. However if other interrupts fire, then this routine would have to wait for them to complete e.g. a serial port interrupt etc. so you may get better results with interrupts off and using pulseIn().

However, pulseIn has been calibrated empirically (by observation) so its output is an estimate but it seems to be quite a good one for lower pulse widths. The problem with long pulse widths is, as the errors build up, so the error in the reported pulse width will increase.

TIP: pulseInLong() is a better choice for measurement of long pusles as errors do not build up over time (true of pulseIn).

How does Arduino PulseIn work

Note: pulseIn and pulseInLong use the same algorithm to measure a pulse, so the description here applies to both functions

Lets assume that we are going to measure the high portion of a signal.

To measure a pulse, the function has to wait for any existing pulse to disappear so that it can start a measurement from the beginning of a complete pulse. So the first thing that the function does is to check the current state of the input.

1. If the input is HIGH then wait for the signal to go LOW. i.e wait until the input is inactive.

2. Next, wait while the input is LOW (waiting for the active state HIGH).

3. When a HIGH input signal is found then start the count.

4. Wait for the signal to go inactive again (LOW).

5. Stop the count.

The count now represents the time period of the HIGH pulse period.

For a LOW pulse, just swap HIGH and LOW in the above description.

arduino pulse operation showing signal input and algorithm operation


Arduino PulseIn Delay

You can see from the way that pulseIn works (see above) that it is dependent on the input signal. If you start the function while the input is active the function will have to wait. There's no way to know when a signal is starting since the signal is an external asynchronous signal.

Note: This is why there is a timeout parameter - so that if a signal is missing the function will not stay forever monitoring the input pin.

The function then waits for the entire time of the inactive pulse.

So for an input signal of frequency f (with equal mark to space ratio) you could be waiting for a period of time from between 1.5T to 0.5T (where T = 1/f) before you get a result.

PulseIn Arduino Source Code

You can find the source code for pulseIn in the following directory:

C:/Program Files/Arduino/hardware/arduino/avr/cores/arduino/wiring_pulse.c

For the assembler version you can find the code in

C:/Program Files/Arduino/hardware/arduino/avr/cores/arduino/wiring_pulse.S

For a 32bit machine the location will start with:

    C:/Program Files (x86)/Arduino/

The following is the source code that is used to generate the auto assembler output:

/*
 * The following routine was generated by avr-gcc 4.8.3 with the following parameters
 * -gstabs -Wa,-ahlmsd=output.lst -dp -fverbose-asm -O2
 * on the original C function
 *
 * unsigned long pulseInSimpl(volatile uint8_t *port, uint8_t bit, uint8_t stateMask, unsigned long maxloops)
 * {
 *     unsigned long width = 0;
 *     // wait for any previous pulse to end
 *     while ((*port & bit) == stateMask)
 *         if (--maxloops == 0)
 *             return 0;
 *
 *     // wait for the pulse to start
 *     while ((*port & bit) != stateMask)
 *         if (--maxloops == 0)
 *             return 0;
 *
 *     // wait for the pulse to stop
 *     while ((*port & bit) == stateMask) {
 *         if (++width == maxloops)
 *             return 0;
 *     }
 *     return width;
 * }
 *
 * some compiler outputs were removed but the rest of the code is untouched
 */ 

After the above comment the assembler output is presented. The assembler output (not shown) is generated from the original C code shown in the comment, so to understand what the assembler output is doing just read the above c code.

Note: Generating assembler in this way means it is not compiled by the C compiler and therefore means that it can not change when the C compiler is "improved". So timings will be consistent even if the C code or compiler changes things. The problem is that it will only operate on the avr so that is why the directory structure shows avr - other processors will require different equivalent code.

The code measures the pulse by incrementing the variable 'width' within the last while loop. The previous two while loops are there to prepare for measurement.

It is quite simple. if you think of a continuous square wave pulse, and lets say you want to find the period of the high part of the signal then you would set the variable 'state' (in the C code below) high which would set the corresponding bit in statemask high.

The algorithm first finds an instance of the signal being high (if present but since you don't know if you found it in the middle of a high period you can not tell if you are at the start of the pulse. The algorithm then waits for low to ensure that the next high input is the start of a pulse.

So that is what the 1st two while loops are doing preparing to find the start of the signal, and the third one performs the measurement while waiting for the pulse to go low again.

  1. While valid wait for invalid pulse polarity - Wait for end of previous pulse.
  2. While invalid wait for valid pulse polarity - Wait for pulse to start.
  3. While valid polarity increment width pulse - Wait for pulse to stop.

The operation is shown in the diagram below

arduino pulse operation showing signal input and algorithm operation

The function generated by the code is:

countPulseASM

From the C part of the code we have the following prototype (or function definition):

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

...in which you specify the pin to use as input and the pulse state required i.e. whether you are looking for an active high or active low pulse, and a timeout parameter in microseconds - the maximum time to wait for a pulse to arrive (in microseconds).

The full code is shown below and is used as a wrapper routine around the assembler code and defines bit, port and statemask as fixed quantities (so that the assembler code does not have to use digitalWrite etc. and is therefore fast).

A fudge factor of divide-by-16 takes care of converting from the timeout in microseconds to cycles through one loop through the assembler code - the comments say that the assembler routine takes approximately 16 cycles per iteration.

Warning: The timeout fudge factor (/16) will only allow this assembler version of pulseIn to operate correctly at 16MHz. This is probably why there is an interrupt driven version (pulseInLong) - since that version uses Timer0 it wont use a fudge factor.

At the end of the code, output from the assembler is cleaned up:in the event of a timeout in the assembler code t return zero width.

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

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

unsigned long width = countPulseASM(portInputRegister(port), bit, stateMask, maxloops);

// prevent clockCyclesToMicroseconds to return bogus values if countPulseASM timed out
if (width)
return clockCyclesToMicroseconds(width * 16 + 16);
else
return 0;
} 

You can see that the measurement is empirical where each loop around the function countPulseASM (the assembler code) is measured as 16 instruction cycles - approximately - since there are condition actions in that assembler that can cause slight timing variations (depending on the input signal state).

PulseInLong Arduino Source code

The function pulseInLong follows the same algorithm as the assembler code above working in a similar way to find the start of a valid pulse (see above description). The only difference is that the timing mechanism uses the value from micros() function. This function returns a value calculated from Timer0 and so pulseInLong requires that interrupts are active.

The timing mechanism uses the standard technique of storing time in an unsigned long and subtracting this from micros to get the elapsed time.

unsigned long pulseInLong(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 startMicros = micros();

// wait for any previous pulse to end
while ((*portInputRegister(port) & bit) == stateMask) {
if (micros() - startMicros > timeout)
return 0;
}

// wait for the pulse to start
while ((*portInputRegister(port) & bit) != stateMask) {
if (micros() - startMicros > timeout)
return 0;
}

unsigned long start = micros();
// wait for the pulse to stop
while ((*portInputRegister(port) & bit) == stateMask) {
if (micros() - startMicros > timeout)
return 0;
}
return micros() - start;
} 

Pulse Accuracy

The Arduino reference states that both functions are work from 10us to 3minutes while the comments in the code state they are accurate from 2-3us to 3minutes.

Arduino PulseIn Accuracy

You can increase the accuracy of pulseIn if you turn off interrupts during a measurement otherwise the code will get interrupted by any running interrupt e.g. Timer0, changing the pulse measurement time and consequent output. Note this is only for pulseIn and not for pulseInLong (pulseInLong relies on interrupts).

The accuracy of the algorithm is hard to say as there is a fudge factor of divide-by-16. The best you can say is that the code will return consistent results for the same signal. However from real world measurements it provides useful accuracy.

The pulseIn function is more useful for short pulse measurement.

Arduino PulseinLong Accuracy

The algorithm depends only on the function micros() which is an interrupt driven timer. Its accuracy is the same as the accuracy of the timer used in the micros() function i.e. it will have the accuracy of the main system clock.

This is better suited to measuring long pulses as it does not suffer from empirical accumulated error.

Arduino PulseIn Testing Sketch

The following sketch shows how to use pulseIn and allows you to compare the operation for different function versions.

The following sketch measures the high and low periods of a signal attached to pin 4 - you could use any digital pin. The code takes the average of 10 readings for the high period, and 10 readings for the low period, and averages each set of results. It then adds them and uses floating point to calculate the frequency of the input signal. Note the signal source is not a very stable one - just an RC oscillator at 1kHz output frequency.

//
// PulseIn Test program.
//
// Measures 10 high and 10 low periods
// and calculates each average. Then calculates
// The freuqnecy of the signal.
//
// Two test controls are used:
// noPulseInterrupts when high stops interrupts during
//    the measurements.
// usePulseInLong when high switches to pulseInLong function.
//
// Input is a 0~5V 1kHz squarewave to INPUTPIN.
//
// Copyright John Main - Free for non commercial use.
//
void setup() {
   Serial.begin(115200);
}

#define INPUTPIN 4

// Controls
#define noPulseInterrupts 1
#define usePulseInLong    0

// Must have interrupts for pulseInLong
#if usePulseInLong
  #define noPulseInterrupts 0
#endif

void loop() {
uint8_t i;
uint16_t avg1,avg2;
static unsigned int stop=0;
unsigned long val;
float f;

  avg1=0;
  if (stop==0) {
    for(i=0;i<10;i++) {

        if(noPulseInterrupts) noInterrupts();
           if (usePulseInLong)
              val = pulseInLong(INPUTPIN,HIGH,10000);
           else
              val = pulseIn(INPUTPIN,HIGH,10000);
        interrupts();

        Serial.print(i);Serial.print(" ");
        Serial.println(val);
        avg1+=val;
    }

    avg1/=10;

    Serial.print("Avg high:");Serial.print(avg1);
    Serial.println("\n-------");

    avg2=0;
    for(i=0;i<10;i++) {

        if(noPulseInterrupts) noInterrupts();
           if (usePulseInLong)
              val = pulseInLong(INPUTPIN,LOW,10000);
           else
              val = pulseIn(INPUTPIN,LOW,10000);
        interrupts();

        Serial.print(i);Serial.print(" ");
        Serial.println(val);
        avg2+=val;
    }

    avg2/=10;

    Serial.print("Avg  low:");Serial.println(avg2);
    Serial.println("\n-------");

    f =1.0 / ( (avg1 + avg2)*1e-6 );
    Serial.print("Frequency: ");
    Serial.println(f);

  }
  stop=1;

  if (Serial.read()=='1') stop = 0;

}

Arduino PulseIn Sketch output

The following results are for pulseIn with interrupts on.

0 497
1 497
2 497
3 497
4 491
5 492
6 491
7 491
8 491
9 491
Avg high:493
-------
0 449
1 447
2 446
3 469
4 493
5 493
6 493
7 493
8 487
9 487
Avg  low:475

-------
Frequency: 1033.06

The above results are for using pulseIn when interrupts are running i.e. inaccurate results occur.

The following set of results shows the difference between interrupts on and off (for pulseIn) and results for pulseInLong (which requires interrupts active). The input signal is 1013 to 1014 Hz measured on the PIC frequency counter. It drifted up a little as the source is not high stability.

The most consistent results are for the assembler version (pulsein) with interrupts off. When interrupts are on pulseIn results vary a lot. Interestingly, pulseInLong results are consistently stable and compare very well to pulseIn (ints off).

If you need to have interrupts active then pulseInLong will give acceptable results.

pulseIn no ints
Frequency: 1012.15
Frequency: 1012.15
Frequency: 1013.17
Frequency: 1013.17
Frequency: 1012.15

pulseIn Ints
Frequency: 1037.34
Frequency: 1040.58
Frequency: 1028.81
Frequency: 1037.34
Frequency: 1030.93

pulseInLong
Frequency: 1015.23
Frequency: 1014.20
Frequency: 1018.33
Frequency: 1016.26
Frequency: 1016.26

Conculusion


The function pulseIn() uses empirical measurement; the longer the pulse the more error accumulates so use it for short pulses.

Use pulseInLong() for longer pulses (the opposite argument to the statement above). This is why the function uses the word 'Long' - it is because the function is more accurate for longer duration pulses.

To make pulseIn more accurate turn off interrupts when using it, but remember the millis and micros timers will also be stopped!


Comments

Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.




Privacy Policy | Contact | About Me

Site Map | Terms of Use