Pages: [1] 2   Go Down
Author Topic: Why doesn't the pulseIn display duration more than 170ms???  (Read 2915 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
Newbie
*
Karma: 0
Posts: 17
In a relationship with Arduino :P
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hey,

I am trying to measure the duration of an active LOW signal using pulseIn function.
The results are pretty confusing!
The controller only displays results of pulses that are fast ( i.e. less than 170ms) and returns "0" if the duration of the pulse exceeds more than 170. The max. value I could capture was 169250 usec.
I suspect it has something to do with the the overflow or the time out function.

Any help would be appreciated

Thanks!

Here's the code:
Code:
unsigned long duration;
int counter = 0;
void setup()
{
pinMode (5, INPUT);
Serial.begin(9600);
}
void loop()
{
  duration = pulseIn(5, LOW);
{
  Serial.println (duration);
   
}
}

« Last Edit: October 09, 2011, 02:47:11 am by Mrxnoxious991 » Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


In wiring_pulse.c  (its in the Arduino distribution ) the pulsein function is defined. It returns 0 if there is a timeout, so that is not the case as you get nr > 0.

The signature is :   unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)

you call it with 2 parameters so where does the 3rd param (timeout)  comes from.... I even wondered that it compiled ....

Can you explicitly pass a timeout value?


Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

see also - http://arduino.cc/forum/index.php/topic,74642.0.html -
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
you call it with 2 parameters so where does the 3rd param (timeout)  comes from.... I even wondered that it compiled ....
You need to look at the header file for the function, too. The timeout argument is defined with a default value, making it optional.

The pulseIn function is designed to measure events that occur within a reasonably short period of time. 169250 microseconds is not what pulseIn was designed to measure. For servo PPM pulses, for example, that time-frame would indicate that the pulse sender was dead in the water.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


OK, forgot about the headerfile and the optional parameter  smiley-red smiley-red smiley-red smiley

But stil, the default value for the parameter = 1.000000L  which is 5x bigger that the max value the OP got.

Code:
  ...
  return clockCyclesToMicroseconds(width * 21 + 16);
}

#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (F_CPU / 1000L) )

These two lines together cause an overflow...   Looking at the math it can be replaced with something simpler

return ((width*21+16) / clockCyclesPerMicrosecond() );


which overflows less fast as the factors 1000 (twice) is removed from the equation. This makes the PulseIn() function "behave better" in a larger range. Probably the division will be optimized to a shift so it will be a few cycles faster too.

Reported as bug/enhancement  in - http://code.google.com/p/arduino/issues/detail?id=675 -






Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 0
Posts: 17
In a relationship with Arduino :P
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
169250 microseconds is not what pulseIn was designed to measure

Then why does it say that the pulseIn function works on pulses b/w 10usec to 3min in length.

http://www.arduino.cc/en/Reference/PulseIn
Logged

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 473
Posts: 18695
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Confirmed. I am having trouble measuring pulses much higher than 1.8 mS (varying the timeout only leads to confusing results).

Anyway I am tempted to suggest you use a CHANGE interrupt rather than pulseIn.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset


Quote
Then why does it say that the pulseIn function works on pulses b/w 10usec to 3min in length.

That should be possible but the math prevents this. I expect the function has changed a bit over the different versions and that "this bug" creeped in ....

you may patch your instance of pulsein() with the above change - C:\Program Files (x86)\arduino-0022\hardware\arduino\cores\arduino\wiring_pulse.c - that should expand the working range of the function to at least 1 minute I estimate.

Rob
Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 0
Posts: 17
In a relationship with Arduino :P
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

So, what you are suggesting is that I should replace the following code:

Code:
return clockCyclesToMicroseconds(width * 21 + 16);
}
With this ?
Code:
return ((width*21+16) / clockCyclesPerMicrosecond() );
Where did this come from:
Code:
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (F_CPU / 1000L) )
The above code is not present in the header file of wiring_pulse

This is what I basically did but I didn't see any difference. smiley-sad
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (F_CPU / 1000L) )

Comes from - C:\Program Files (x86)\arduino-0022\hardware\arduino\cores\arduino\wiring.h - on my win 7 machine

Strange you did not see any difference .. I'm gonna search for a free arduino to test ...

Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Back again, my testcode
Code:
unsigned long duration;
int counter = 0;

unsigned long before = 0;
void setup()
{
  pinMode (5, INPUT);
  Serial.begin(9600);
}
void loop()
{
  duration = pulseIn(5, LOW, 10000000L); // yes 10.000.000
  {
    Serial.print("D: ");
    Serial.println (duration);
    Serial.print("M: ");
    Serial.println(millis()-before);
    before = millis();
  }
}

Got strange results indeed, ...think think think... review PulseIn() again, found it used the function  microsecondsToClockCycles()  also in the code to determine the timeout.

rewrote PulseIn() to get at least a better timeout

Code:
/* 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 = timeout * clockCyclesPerMicrosecond() / 16;     //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 (width * 21 + 16)/clockCyclesPerMicrosecond();    // return clockCyclesToMicroseconds(width * 21 + 16);
}

Seems to work a lot better!  If it works better I will report it as bug ...

Please give it a try...




Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Offline Offline
Newbie
*
Karma: 0
Posts: 17
In a relationship with Arduino :P
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The code seems to work fine after the correction smiley
Also check out the latest version of Arduino 0023.
Logged

Global Moderator
Netherlands
Offline Offline
Shannon Member
*****
Karma: 211
Posts: 13471
In theory there is no difference between theory and practice, however in practice there are many...
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

(rewritten this post a few times)

reread this thread after a few weeks, the error comes up in pulseIn() but its rootcause is in the macro's in wiring.h
=> C:\Program Files (x86)\arduino-0022\hardware\arduino\cores\arduino\wiring.h

Code:
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (F_CPU / 1000L) )
#define microsecondsToClockCycles(a) ( ((a) * (F_CPU / 1000L)) / 1000L )

The above macros should be rewitten to increase their working range preventing overflow for "relative small" values

Code:
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
#define microsecondsToClockCycles(a) ( ((a) * clockCyclesPerMicrosecond() )

Drawback is that CPU with frequencies that are not a whole multiple of 1Mhz get an error, but afaik there are only 8, 16 and 20 MHz versions of Arduino.

By changing the macros the original pulseIn() code would not need to be changed as the problem is solved at its root cause.

I'll update this in the bugreport - http://code.google.com/p/arduino/issues/detail?id=675 - too
« Last Edit: November 13, 2011, 04:15:45 am by robtillaart » Logged

Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

0
Offline Offline
Newbie
*
Karma: 0
Posts: 30
Expand your mind with creative play
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This had me really vexed!!!!

I found a example of a simple camera shutter speed tester and while I could manage to get usable readings down to about 1/30 sec below that was nothing. I also found it very hard to get the function to wait for a longer timeout so I tried making my own simple function that worked fine for lower speeds but seemed to fail above about 1/250 sec shutters, so I'm not sure what to try here.....

I have simple IR emitter/Receiver setup with the following code

Code:
const int readPin=12;

long pulseLength;
int pinStatus;
long speed=0;

void setup(){
  Serial.begin(9600);
}


void loop(){
  //Serial.print("Pin:");
  //Serial.println(digitalRead(readPin));
  speed=testShutter(readPin);
 
  //Serial.println(speed);
  //on Button go a wait for Read to go low and count how long
}

long testShutter(int rPin){
  unsigned long now;
  unsigned long done;

  do
{
  pinStatus=digitalRead(rPin);
  if (pinStatus==LOW){//wait for LOW
  Serial.println("counting");
    //LOW start counting
    now=micros();
      while (digitalRead(rPin)==LOW){
      //wait for High
      }
      done=micros();
     pulseLength=done-now;
     Serial.print ("Started:");
     Serial.println(now);
     Serial.print ("ended:");
     Serial.println(done);
     Serial.print("difference is pulse:");
     Serial.println(pulseLength);
     Serial.print("****");
     Serial.print(pulseLength/1000000ul);
     Serial.println("seconds");
     
  }
  return pulseLength;
 
  break;

} while (speed=0);

/*do {
  pulseLength=pulseIn(rPin,LOW);
}while(pulseLength==0);
return pulseLength;*/
}

Logged

-Bret Lanius

Global Moderator
Offline Offline
Brattain Member
*****
Karma: 473
Posts: 18695
Lua rocks!
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Don't use pulsein, use interrupts. This sketch:

Code:
volatile boolean started;
volatile unsigned long startTime;
volatile unsigned long endTime;

// interrupt service routine
void shutter ()
{
  if (started)  
    endTime = micros ();   // shutter close time
  else
    startTime = micros ();  // shutter open time

  started = !started;   // toggle flag
}  // end of shutter

void setup ()
{
  Serial.begin (115200);
  Serial.println ("Shutter test ...");
  attachInterrupt (0, shutter, CHANGE);
}  // end of setup

void loop ()
{

  if (endTime)
    {
    Serial.print ("Shutter open for ");
    Serial.print (endTime - startTime);
    Serial.println (" microseconds.");
    endTime = 0;
    }  

} // end of loop

You need to connect the shutter to D2 (one of the pins that takes a change interrupt). I am successfully measuring a pulse of 50 uS with that code (1/20000 of a second).

Just invert the result to get the shutter speed. eg.

For 50 microseconds take 1/ 0.000050 giving 20000 (shutter speed of 1/20000).
« Last Edit: January 14, 2012, 04:52:22 pm by Nick Gammon » Logged

Pages: [1] 2   Go Up
Jump to: