pulseIn Bug

I've found a pretty bad bug with pulseIn, it made an appearance sometime between 0018 and 1.0. With 0018 pulseIn ends as soon as the pulse being monitored ends, in 1.0 there appears to be about a 40-50 uS "dead time" where pulseIn waits, after the pulse being monitored has ended. This causes the sketch to miss subsequent pulses if they start during this "dead time."

I found the problem when modifying a project that had been written using 0018 to run on a MEGA2560, which required a newer IDE.

A bigger problem is that I can't find any mention in the changelog of work being done on pulseIn that may have affected the function in this way.

There is no obvious cause for a 40-50 microsecond delay. It doesn't look like the code has been touched since 2007. Perhaps something in your code between calls to pulseIn() is introducing a delay.

wiring_pulse.c:

#include "wiring_private.h"
#include "pins_arduino.h"

/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH
 * or LOW, the type of pulse to measure.  Works on pulses from 2-3 microseconds
 * to 3 minutes in length, but must be called at least a few dozen microseconds
 * before the start of the pulse. */
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); 
}

This is why I'm confused, as I've checked the code myself and can't figure out what it is.

See this topic I started for more information: http://arduino.cc/forum/index.php/topic,86039.0.html

All I can tell you is if I use Arduino 1.0 to program my Arduino I get this error. If I use Arduino 0018 I do not get this error. Results are quite consistent. I urge you (or anyone else, really) to try this out and confirm (or refute) that this is a bug, because I'm pretty well stumped.

For extra frustration, I don't think my Arduino MEGA2560 is supported by 0018, which means I need to use an older Duemilanove which has a few issues when I try to program it.

A quick update.

I just started marching backwards through the Arduino IDEs, starting at 1.0. I noticed this error in everything newer than 0021. However, 0021, with a quick test, appears to work as intended.

What is the length of the pulse you are trying to measure?

I'm asking this as few months I reported a bug in pulseIn() regarding overflow in the math under some conditions. See - http://code.google.com/p/arduino/issues/detail?id=675 - for the details.

COuld you please post the code - or better a minimized version that still produces this side-effect aka bug ?

robtillaart: What is the length of the pulse you are trying to measure?

They're between 40 - 150 uS. You can see in the plot I've posted, though I apologize it's not great resolution, I just posted it to illustrate working results.

robtillaart: COuld you please post the code - or better a minimized version that still produces this side-effect aka bug ?

If you look in the link I posted above, I have another thread I started before I realized this appears to be a bug. Here's what's running to capture that plot above.

  noInterrupts();
  digitalWrite(Y, HIGH);
  P = pulseIn(X, LOW, usLong);
  digitalWrite(Y, LOW);
  a = pulseIn(X, LOW, usLong);
  digitalWrite(Y, HIGH);
  b = pulseIn(X, LOW, usLong);
  digitalWrite(Y, LOW);
  c = pulseIn(X, LOW, usLong);
  digitalWrite(Y, HIGH);
  d = pulseIn(X, LOW, usLong);
  digitalWrite(Y, LOW);

  interrupts();

The BLUE trace is X, the RED trace is Y. I was using Y as a simple method to show when exactly pulseIn starts and stops.

From the picture (more than good enough) I see the following: correct me if I'm wrong

  • The blue signal looks like it really takes time to get up to its max => looks like a "big capacitor effect". Are you using long/thick cables?
  • The blue signal is 3V. IIRC this is in the undefined range of TTL levels. It just could be that the HIGH level is not seen as HIGH

robtillaart: From the picture (more than good enough) I see the following: correct me if I'm wrong

  • The blue signal looks like it really takes time to get up to its max => looks like a "big capacitor effect". Are you using long/thick cables?
  • The blue signal is 3V. IIRC this is in the undefined range of TTL levels. It just could be that the HIGH level is not seen as HIGH

1) You can ignore the ramp up effects...that's a hardware issue in my custom shield that I'm aware of.

2) Yep, I'm playing with a "black box" that uses 3V logic. I've checked the datasheet for the MEGA2560 (Page 267), and anything over .6*Vcc (3V) is HIGH, so it should be fine, though it is pretty close to the edge. I've got a hardware fix I'm working on to do proper level shifting (would also fix issue in #1), but I haven't bothered with that yet because I've seen this hardware work.

So yes, you are correct on both, but neither actually matter. I've proven, to myself at least, that going from Arduino 1.0 to 0021 fixes the problem, at least at a quick glance. I encourage you to try a similar setup, at 5V. I'd like to try that, but I don't have time to do that right now.

Does that trace show working or non-working code?

The behavior shown there looks correct to me.


Rob

Graynomad:
Does that trace show working or non-working code?

The behavior shown there looks correct to me.

That image shows it working, when using Arduino 0021. If you clicked on the link above, you would see the original post (before I thought this was a bug) that shows the following image of it not working.

The code is the same. The only difference is what compiler I’m using. The bug does not appear to be in the pulseIn function itself, though there are some slight modifications between versions. I suspect the problem lies in interrupt handlers, perhaps for Serial communications, but I don’t have the time, or experience, to dig in and find the solution myself.

I hope that the developers responsible for the Arduino IDE see this (or, tell me where their bug tracker is, because I couldn’t find one linked on this website), because this seems like a pretty clear bug to me. Behavior should not change depending on the compiler used, especially without giant warnings to let users know of possible issues.

I suspect the problem lies in interrupt handlers,

An interrupt at the wrong moment can indeed "ruin" measurements, but is your code receiveing bytes while measuring? If no then there are no interrupts...

robtillaart:

I suspect the problem lies in interrupt handlers,

An interrupt at the wrong moment can indeed "ruin" measurements, but is your code receiveing bytes while measuring? If no then there are no interrupts...

Nothing else is happening when I am trying to take these measurements, but there is an open serial port (using the Serial commands).

For all intents and purposes, this appears to be resolved on my part by moving to an older Arduino IDE. I do apologize for being a "this isn't my problem" type of person, but I simply don't have time to troubleshoot this. All I can say is I've found what appears to be a regression bug, and I want to make sure the right people know.

iwoloschin: For all intents and purposes, this appears to be resolved on my part by moving to an older Arduino IDE. I do apologize for being a "this isn't my problem" type of person, but I simply don't have time to troubleshoot this. All I can say is I've found what appears to be a regression bug, and I want to make sure the right people know.

But until someone else can reproduce the issue you have reported, nothing will happen.

Iain

sixeyes:

iwoloschin: For all intents and purposes, this appears to be resolved on my part by moving to an older Arduino IDE. I do apologize for being a "this isn't my problem" type of person, but I simply don't have time to troubleshoot this. All I can say is I've found what appears to be a regression bug, and I want to make sure the right people know.

But until someone else can reproduce the issue you have reported, nothing will happen.

Iain

I've encouraged others to do so. I believe it should be easy enough to do, though you may need two Arduinos (or a function generator) since you won't be able to read a pulse while triggering another one, though you might be able to do something with interrupts, but that might have unintended results. Voltage shouldn't matter, so long as your LOW is under 0.1Vcc (500 mV) and your HIGH is over 0.6Vcc (3 V). I suspect it won't take long to do a quick test, but it would take some effort to properly regression test this. I've already done my "quick" testing, and I don't have the time, or knowledge of the Arduino IDE's guts, to do proper regression testing.

iwoloschin: I just started marching backwards through the Arduino IDEs, starting at 1.0. I noticed this error in everything newer than 0021.

From the Release Notes: ARDUINO 0022 - 2010.12.24 * Applying the timeout parameter of pulseIn() during measurement of the pulse, not just while waiting for it.

What value is your timeout (usLong)? Perhaps you are encountering timeout errors.

I thought that might have something to do with it too, but I don’t know why that would matter. My timeout (usLong) is set to 100 mS, or 10,000 uS. According to the reference page*, it needs to be between 10 uS and 3 minutes (1.8 x 108 uS), and it also says the following:

Reads a pulse (either HIGH or LOW) on a pin. For example, if value is HIGH, pulseIn() waits for the pin to go HIGH, starts timing, then waits for the pin to go LOW and stops timing. Returns the length of the pulse in microseconds. Gives up and returns 0 if no pulse starts within a specified time out.

I don’t see why that change should be affecting this, as the pulses I’m measuring are 2 orders of magnitude below my timeout period.

*http://arduino.cc/en/Reference/pulseIn

My bugreport - http://code.google.com/p/arduino/issues/detail?id=675 - included math problems due to macro expansion with timeout too ... But the effect was that "long" timeouts went off too early due to overflow, not that it gave too large values....

robtillaart: My bugreport - http://code.google.com/p/arduino/issues/detail?id=675 - included math problems due to macro expansion with timeout too ... But the effect was that "long" timeouts went off too early due to overflow, not that it gave too large values....

It looks like pulseIn is reporting the correct pulse length, just not returning right away which causes it to miss the next (closely, but not too closely) spaced pulse.

That could be due to unoptimized math happening at the end, I haven't really looked into that though.

When I look at your scope captures, I do not see a bug in pulsein() at all. It sure looks to me like pulsein() is working exactly as it is supposed to in both the "working" picture and the "non working" pictures. (the problem appears to be elsewhere)

From what I can tell it looks like the Red wave is being set high just before pulsein() is called and lowered just after pulsein() returns and the blue one is the signal being timed with pulsein().

Note: Given that there a different number of red waves in the two pictures, and the timing in between red pulses (calls to pulsein() ), is substantially different, it leads me to question whether the two pictures were actually taken with identical sketches. Ignoring that for a moment.......

You can see that in the "non working" picture, the red wave rises later - much later than in the "working" picture. So that in the "non working" case, the 2nd pulsein() appears to start when the wave to be timed is low and since it has been told to time a LOW pulse, it must now wait for it to go high and then low again. And if you look at when the RED wave drops, I don't not see a very long amount of time after the waves goes high after the timing of the low pulse ends. It looks to be about the overhead of digitalWrite(). And the amount of time seems to be consistent with the first pulsein().

BTW. For any serious type of timing you don't want to use digitalWrite() as it is WAY too slow. Use direct port i/o. The overhead will reduce from close to 6us to 125ns to strobe a pulse.

But overall, it looks to me like pulsein() is working just fine.

The question is what is the code doing after the first pulsein() returned which must happen in order to set the red wave low and the setting of the red wave high which is done just before the second call to pulsein()? This is code that is outside pulsein().

From what I see in the pictures, it appears that the code used to create the waves in the two pictures is not the same, either that or something kicks in after the first pulse and is hammering the processor with interrupts and slowing it down.

--- bill

I think you are correct bperrybap, it appears that the digitalWite() is taking too long and the next pulseIn() occurs in a LOW and therefore has to wait.

Has digitalWrite() changed between versions?

Using direct port writes should prove that one way or another.


Rob