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.
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 ....
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.
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.
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.
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 ...
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
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 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;*/
}
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).
To print "counting" (followed by carriage-return, linefeed) at 9600 baud will take 1/960 seconds times 10. That is 0.0104 seconds. The inverse of that is 96 (that is 1/96 of a second) so you can hardly measure 1/250 of a second shutter speed if your message takes 1/96 of a second to display (inside your counting loop).