pulsein() -- pulsewidth measurement problem

Hi,

My issue has to do with the pulsein() function, which I am using in a project. I am using arduino 0022 with a Duemilanove Arduino board (atmega 328).


In the documentation on the Arduino website (http://www.arduino.cc/en/Reference/PulseIn), it says that pulsein() will read pulse widths between 10 microseconds and 3 minutes. This should be perfect for my project, but with my setup at least, it will only report accurately up to 31 Hertz (pulsein reports this as a 16022 us high pulse, or, about 16.0 miliseconds).

I have determined this through testing with 50% duty cycle square waves. Feeding it a 30 Hertz square or slower results in completely incorrect results (e.g. 246us or 0.2ms at 30 hz). 31 Hertz or faster are fine.

I am reading pulsein() into an unsigned long variable, so it shouldn’t be a rollover problem (this is a 32 bit variable and should be able to count microseconds up to 4,294,967,295 according to the documentation).


Does anyone have any thoughts? Thanks in advance for your help!

(I’m new to the forum – I did run a search, but if this topic has been addressed elsewhere, just let me know and I’ll move on…)

Welcome!

Show us your test Sketch.

Here is the operative part from void loop():

//----------------------------------------------------------- //

duration = pulseIn(buttonPin, LOW);

// gives pulsewidth in microseconds. duration is a globally declared unsigned long. // buttonPin is a global int used with pinMode in void setup() to read a button press or // test signal on the digital port of the Arduino board.

if (duration != 0) { printout(); }

// this works fine - the printout() function sends nonzero results to Serial.println(duration, DEC)

//----------------------------------------------------------- //

I can include the rest, but there is almost nothing left - i have pared it down to isolate the problem. I use tone() to generate the waveforms for test (turned on once), and I don't #include any libraries. It works perfectly down to 31 Hz.

I use tone() to generate the waveforms for test

There was some discussion a while back (on the old forum, I think) that, if I remember correctly, came to the conclusion that the way tone is written it can not generate accurate output below 31 Hz. Perhaps this is the root of your problem.

duration is an int?

duration is an int?

Without seeing more code (hint) this one gets my vote.

duration is an int?

duration is a globally declared unsigned long.

The comments are correct, but what we think something is and what they actually are can be quite different things. That's why you need to have someone else look at all the code.


Rob

Thanks for the replies everyone!

PaulS:

I use tone() to generate the waveforms for test

There was some discussion a while back (on the old forum, I think) that, if I remember correctly, came to the conclusion that the way tone is written it can not generate accurate output below 31 Hz. Perhaps this is the root of your problem.

If this is true - then this would be a major problem, and this limitation should be added to the documentation at http://www.arduino.cc/en/Reference/Tone. Do you know how I can read the code for tone()?

I have also tested this with both un-debounced and hardware debounced switches however, and the results are way off unless the presses are extremely short. This is why I was doing the testing with the waveform.

I had been using the tone() hack to test this in order to avoid having to use a waveform generator or an O scope, as I have neither. I guess at some point I'll have to make some investments..

Graynomad:

duration is an int?

duration is a globally declared unsigned long.

The comments are correct, but what we think something is and what they actually are can be quite different things. That's why you need to have someone else look at all the code.


Rob

As to the first comment, I'm fairly sure that unsigned long is the way to go. This is a quote from the documentation for pulsein():

"Returns the length of the pulse (in microseconds) or 0 if no pulse started before the timeout (unsigned long)"

Since an unsigned long is an integer data type, this makes sense. As for the second point, here is the rest of the code. I tried unsigned int and int as well, as you will see in the comments:

/*


designed to run on the "duemilanove" prototype board equipped with an ATmega328 microprocessor


*/



// global constants
// set pin numbers:
const int buttonPin = 7;   // the number of the pushbutton pin (I tried an alternate pin to make sure it wasn't a faulty input problem)
const int wavePin = 4;     // the number of the test waveform pin


// global variables
int buttonState = 0;         // variable for reading the pushbutton status
int controlTone = 0;         // used to control the tone call for waveform testing


unsigned long duration; // shouldn't be the problem b/c this data type ranges from 0 to 4,294,967,295 (2^32 - 1). 
//unsigned int duration; //
//int duration; //




//======================================================================
//======================================================================

void setup() {
     
  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);     
  
  // initialize a pin for outputting a waveform to test the input
  pinMode(wavePin, OUTPUT);
  
  // -----------------------------------
  
      Serial.begin(9600);                       // Start serial communication at 9600 bps
  
  // -----------------------------------
  

}

//======================================================================
//======================================================================
// the program main loop initializes the testing tone generator
// and then listens for a pulse on pin 7 (buttonPin)
//

void loop(){

// this if statement is used to turn on the tone generator.
// this is used only to generate a test waveform 


  if (controlTone == 0) {    // here, we're making sure it gets turned on only once
    
    Serial.println("about to turn on the tone generator");
    
    controlTone = 1;
    
    //tone(wavePin, 1000);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 100);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 10);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 50);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 40);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    tone(wavePin, 30);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 35);  // pin, frequency in hertz, duration in ms (optional) - leaving it running
    //tone(wavePin, 34);  // 
    //tone(wavePin, 33);  //
    //tone(wavePin, 32);  //
    //tone(wavePin, 31);  //
    
                
    Serial.println("tone generator should be running on pin 4 at this point");
    //Serial.println("1000 hertz 50% duty cycle"); // -- works -- reads as 493 or 486 (~0.5 miliseconds)
    //Serial.println("100 hertz 50% duty cycle"); // -- works -- reads as 4954 or 4960 (~5.0 miliseconds)
    //Serial.println("10 hertz 50% duty cycle"); // -- fails -- reads as 818 (.818 miliseconds - should be 50)
    //Serial.println("50 hertz 50% duty cycle"); // -- works -- reads as 9922 (9.92 miliseconds)
    //Serial.println("40 hertz 50% duty cycle"); // -- works -- reads as 12398, '396 or '400 (12.4 miliseconds)
    Serial.println("30 hertz 50% duty cycle"); // -- fails -- reads as 246 or 249 (~0.20 miliseconds)
    //Serial.println("35 hertz 50% duty cycle"); // -- works -- reads as 14178 or ' 9 (14.2 miliseconds)
    //Serial.println("34 hertz 50% duty cycle"); // -- works -- reads as 14557 or '6 (14.6 miliseconds) 
    //Serial.println("33 hertz 50% duty cycle"); // -- works -- reads as 15010 or '12 (15.0 miliseconds)
    //Serial.println("32 hertz 50% duty cycle"); // -- works -- reads as 15516 or '7 (15.5 miliseconds)
    //Serial.println("31 hertz 50% duty cycle"); // -- works -- reads as 16022, 29 or 27 (16.0 miliseconds)

    
  }

 //----------------------------------------------------------- //
 //------------  This is the main program loop --------------- // 


  duration = pulseIn(buttonPin, LOW);  //  gives pulsewidth in microseconds

  if (duration != 0) { printout(); }
  
  
 //------------  This is the main program loop --------------- // 
 //----------------------------------------------------------- //

    
}


//======================================================================
//======================================================================
// the printout subroutine prints ascii to the serial line


void printout(){
  
    // here we report the raw data, which is in microseconds:
    Serial.println(" ");
    Serial.println(duration, DEC);    

    Serial.println("------");
    
    return;
  
}


// END OF PROGRAM
//======================================================================
//======================================================================

Do you know how I can read the code for tone()?

You have the source - just open, and read.

As to the first comment, I'm fairly sure that unsigned long is the way to go

You wouldn't be the first noob to have a local and global variable of the same name.

If you don't post the code early, we just had to play "20 questions" until you did.

Sorry to have caused any confusion - I was trying not to swamp the board with too much code.. I hadn't discovered the /code tag (obv. I'm not much of a forum poster).

I really do appreciate everyone's time and comments (and patience). Please let me know if you spot any problems in the code or if anyone has further thoughts on low frequency pulsein() behavior.

PaulS:

I use tone() to generate the waveforms for test

There was some discussion a while back (on the old forum, I think) that, if I remember correctly, came to the conclusion that the way tone is written it can not generate accurate output below 31 Hz. Perhaps this is the root of your problem.

I did more searching based on this recollection and it turns out you are right - here are the links if anyone is interested:

http://arduino.cc/forum/index.php/topic,48727.msg348565.html#msg348565

http://arduino.cc/forum/index.php/topic,6940.0.html

I still have to figure out how to read long pulses, but I can scratch this off of the list of problems now. Thanks!

I still have to figure out how to read long pulses

Have you encountered a problem reading long pulses?