Offline
Newbie
Karma: 0
Posts: 17
In a relationship with Arduino :P
|
 |
« on: October 09, 2011, 02:43:11 am » |
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: 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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #1 on: October 09, 2011, 04:26:51 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #2 on: October 09, 2011, 04:33:27 am » |
|
|
|
|
|
Logged
|
|
|
|
|
Seattle, WA USA
Offline
Brattain Member
Karma: 314
Posts: 35507
Seattle, WA USA
|
 |
« Reply #3 on: October 09, 2011, 08:33:50 am » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #4 on: October 09, 2011, 10:36:30 am » |
OK, forgot about the headerfile and the optional parameter  But stil, the default value for the parameter = 1.000000L which is 5x bigger that the max value the OP got. ... 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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 17
In a relationship with Arduino :P
|
 |
« Reply #5 on: October 09, 2011, 01:27:29 pm » |
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
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #6 on: October 09, 2011, 03:59:42 pm » |
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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #7 on: October 09, 2011, 04:07:40 pm » |
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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 17
In a relationship with Arduino :P
|
 |
« Reply #8 on: October 10, 2011, 06:10:47 am » |
So, what you are suggesting is that I should replace the following code: return clockCyclesToMicroseconds(width * 21 + 16); }
With this ? return ((width*21+16) / clockCyclesPerMicrosecond() );
Where did this come from: #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.
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #9 on: October 10, 2011, 12:49:59 pm » |
#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
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #10 on: October 10, 2011, 01:23:51 pm » |
Back again, my testcode 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 /* 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
|
|
|
|
|
Offline
Newbie
Karma: 0
Posts: 17
In a relationship with Arduino :P
|
 |
« Reply #11 on: November 12, 2011, 08:06:23 am » |
The code seems to work fine after the correction  Also check out the latest version of Arduino 0023.
|
|
|
|
|
Logged
|
|
|
|
|
Netherlands
Offline
Tesla Member
Karma: 90
Posts: 9401
In theory there is no difference between theory and practice, however in practice there are many...
|
 |
« Reply #12 on: November 13, 2011, 03:44:45 am » |
(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 #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 #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
|
|
|
|
|
0
Offline
Newbie
Karma: 0
Posts: 14
Moving things with Arduino
|
 |
« Reply #13 on: January 14, 2012, 04:13:29 pm » |
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 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
Melbourne, Australia
Offline
Shannon Member
Karma: 218
Posts: 13897
Lua rocks!
|
 |
« Reply #14 on: January 14, 2012, 04:50:12 pm » |
Don't use pulsein, use interrupts. This sketch: 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
|
|
|
|
|
|