Go Down

Topic: pulseIn timeout not working (Read 1 time) previous topic - next topic

ohnoezitasploded

Hello!

I'm trying to use the pulseIn() function to read from a parallax ping() sensor.  It reads the pulse length from the sensor great, but the optional timeout part of it is not working.

My ping outputs a pulse length of about 9000 when I point it at empty space.  I'd like to set pulseIn to give up at around 2600, which would give me a speed boost because I wouldn't have to wait the extra 6.4mS for the ping to time out.

Here is the test code:
Code: [Select]


long pulselength = 0;
//float inches = 0;
int pulsepin = 2;


void setup() {
 

Serial.begin(57600);
 
 
} //end setup()


void loop() {
 
pinMode(pulsepin, OUTPUT);
digitalWrite(pulsepin, LOW);
delayMicroseconds(2);
digitalWrite(pulsepin, HIGH);  
delayMicroseconds(5);
digitalWrite(pulsepin, LOW);
 
pinMode(pulsepin, INPUT);
pulselength = pulseIn(pulsepin, HIGH, 2600);

Serial.println(pulselength);

} //end loop



Note: even if you don't have a ping, you can load this up and verify that it is returning higher numbers than 9000, instead of the expected 0.


retrolefty

To test out your code without a ping wired in you might try wiring a pull down resistor to keep the digital pin from 'floating', as noise my be the cause of not detecting the time-out period.


Lefty


ohnoezitasploded

I tried replacing the ping with a resistor, and sure enough it started returning 0s.  I'm not sure why-- you mentioned 'noise' but I don't see how this would affect the operation of the timer.  If the pin was bouncing down to LOW, I would be seeing erroneously short pulses, but how could it affect the timeout variable?

I guess that I can just hack something together with a pair of while() loops and the micros() function, but it would be nice if the timeout worked, other than that pulseIn is very accurate and useful.

ohnoezitasploded

Here's the code from wiring_pulse.c
Code: [Select]
/* 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)
           width++;

     // convert the reading to microseconds. The loop has been determined
     // to be 10 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 * 10 + 16);
}


I took a look at this, and then realized that I probably hadn't read the doc carefully enough, because it will return 0 if a pulse does not start before the timeout, but will not return 0 if the pulse does not end before the timeout.  Sure enough, from:
http://www.arduino.cc/en/Reference/PulseIn
"Gives up and returns 0 if no pulse starts within a specified time out."

So that's all cleared up.

Now, adding the ability to return 0 if the pulse does not end before the timeout is easy enough:
Code: [Select]

     // wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
           if (numloops++ == maxloops)
                 return 0;
               width++;


but how do I adjust the width-to-microseconds conversion so that it will be accurate with the new while loop time?  I understand you can look at the assembly code and then look up how many clock cycles each command takes, but I've never done it and I need some hand holding, if you don't mind.

thanks,
Max

hellsinki

#4
Nov 06, 2010, 04:03 pm Last Edit: Nov 06, 2010, 04:03 pm by hellsinki Reason: 1
This might come a bit late, but did you finally figure out how to do that?
I have precisely the same problem: I want to limit the amount of time PING))) is waiting with timeout. How can I rewrite the pulseIn() function so that  the sensor gives up waiting for an echo after timeout's duration in microseconds?

jumpjack

#5
Dec 07, 2010, 10:04 pm Last Edit: Dec 07, 2010, 10:05 pm by jumpjack Reason: 1
As far as I can understand:
- locate wiring_pulse.c file in your Arduino folder
- locate these lines:

Code: [Select]
     // wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
           width++;

- change them in this way:

Code: [Select]
     // wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
           if (numloops++ == maxloops)
                 return 0;
               width++;



....leave your sketch unchanged, upload it, and it should work (not tested).

robtillaart

think you need some brackets too otherwise width will only be incremented once :)

Code: [Select]
// wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
       {
           if (numloops++ == maxloops)
                 return 0;
               width++;
       }
Rob Tillaart

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

jumpjack

That's right.

ohnoezitasploded, what the heck are you writing?!?   ;D   ;)

jumpjack

Some additional slight changes allow identifying invalid values returned by PulseIn function:

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

becomes

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

and

     // 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)
//            width++;

// wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
       {
           if (numloops++ == maxloops)
                 return 0;
               width++;
       }



becomes:

     // wait for any previous pulse to end
     while ((*portInputRegister(port) & bit) == stateMask)
           if (numloops++ == maxloops)
                 return -1;
     
     // wait for the pulse to start
     while ((*portInputRegister(port) & bit) != stateMask)
           if (numloops++ == maxloops)
                 return -2;
     
     // wait for the pulse to stop
//      while ((*portInputRegister(port) & bit) == stateMask)
//            width++;

// wait for the pulse to stop
     while ((*portInputRegister(port) & bit) == stateMask)
       {
           if (numloops++ == maxloops)
                 return -3;
               width++;
       }



But you'll also need to change two more files: wiring.h and WProgram.h, both containing
unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout)

which must be changed to

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

This way, at first running, PulseIn will return -2;  then, if your pulse-sensor starts running, a -3 will be returned as soon as it stops, and -1 will keep be returning as long as it remains stopped.

This was tested on a wind sensor connected to GND and Pin 2: it's a just a switch which gets closed and quickly reopened for each turn of a wheel.

robtillaart

I like the modification but the max value of PulseIn is shortened by a factor 2 is quite costly. So undecided... :-/
Rob Tillaart

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

jumpjack

#10
Dec 07, 2010, 11:47 pm Last Edit: Dec 07, 2010, 11:50 pm by jumpjack Reason: 1
It's just an idea, you could also use 0xffff.ffff, 0xffff.fffe and 0xffff.fffd rather than -1, -2 and -3 .

edit:
using signed long, wouldn't 0xfff.ffff seconds be around 2.000.000.000 microseconds, i.e. 2000 seconds, i.e >0,5 hours?

robtillaart

#11
Dec 08, 2010, 09:25 am Last Edit: Dec 08, 2010, 09:29 am by robtillaart Reason: 1
That idea is better, all values starting with 0xFFFF.FFF- for error purpose => a simple & operation detects any errorflag.
Rob Tillaart

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

jumpjack

I guess we should use bit-indipendent codes (does it exist a name for this?!?  :-? ):
0xFFFF.FFF0
0xFFFF.FFF1
0xFFFF.FFF2
0xFFFF.FFF4
0xFFFF.FFF8

Anyway, from source comment:
Quote
Works on pulses from 2-3 microseconds to 3 minutes in length


3 minutes are 180.000.000 us, which is 0x0ABA.9500 , so we don't actually need an unsigned long, a signed one should be enough, and "-1", "-2" and "-3" are  quite more readable than 4294967284, 4294967288 and so on... (no need to convert to HEX for displaying).

Alternatively, we could keep using LONG type, and use first digit to represent if returned length is for high or low pulse, rather than specifying it in the second parameter.
E.G.:
500,000 us = 0x7A120 = 0x0007.A120
0x0007.A120 will mean "500,000 us up pulse",
0x1007.A120 will mean "500,000 us down pulse"

But it would be too complex for me to implement such a change...  :(

robtillaart

Quote
bit-indipendent codes (does it exist a name for this?!?

bit-flags?

Quote
But it would be too complex for me to implement such a change...  

You have almost finished the analysis of how you want it to behave including arguments, so you are closer to it than you think! Keep faith in yourself! :)
Rob Tillaart

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

Go Up