Measuring Pulse Width

@Michael_X

You meant that you were able to count as fast as less than 1 us? is it so?

you were able to count as fast as less than 1 us? is it so?

No. (Not yet. I found in playground Arduino Playground - Timer1, but I'm not sure how to set a prescale of 8 or even 1) Sounds fascinating, though.

I'm a newbie and all I know is:
One can measure in 4 µs units using the micros() method and I noticed that a dummy loop() usually takes 12 µs, which one can verify like this:

long count=0;
loop()
{
   if (++count == 1000000)
   {
      double speed = (double)millis() / 1000. ;  // about 11 millis per 1000 loops
      Serial.Write (speed);
      Serial.WriteLine(" microseconds per loop().");
   
   }
}

This message appears after a couple of seconds (11) .

You should use directport manipulation - Arduino Reference - Arduino Reference -

This code waits until PIN 3 goes HIGH, and then starts counting

//
//    FILE: PulseWidthMeter.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-mar-20
//
// PUPROSE: 
//

unsigned long count = 0;
void setup()
{
  Serial.begin(9600);
  Serial.println("pulse width meter 0.1");

  pinMode(3, INPUT);  
}

void loop()
{
  count = 0;
  while ((PIND & B00001000) == B00000000); // wait for HIGH
  unsigned long start = micros();                 // this line influences the reading...
  while ((PIND & B00001000) == B00001000) count++; // start counting until LOW
  unsigned long stop = micros();  

  Serial.print("CNT: ");
  Serial.println(count, DEC);
  Serial.print(stop-start, DEC);
  Serial.println(" microseconds ");
  Serial.print((1.0*count)/(stop-start), 3);
  Serial.println(" count per microseconds ");

  delay(1000);
}

The output:

CNT: 9447
5348 microseconds 
1.766 count per microseconds 
CNT: 9852
5584 microseconds 
1.764 count per microseconds 
CNT: 13755
7784 microseconds 
1.767 count per microseconds 
CNT: 3182753
1801088 microseconds 
1.767 count per microseconds 
CNT: 5843119
3306556 microseconds 
1.767 count per microseconds 
CNT: 905900
512644 microseconds 
1.767 count per microseconds 
CNT: 7752
4388 microseconds 
1.767 count per microseconds 
CNT: 16149
9144 microseconds 
1.766 count per microseconds 
CNT: 5033
2852 microseconds 
1.765 count per microseconds 
CNT: 4023
2276 microseconds 
1.768 count per microseconds

Note from the code that the setting of start=micros() influences the measurement!! as this is in the measurement loop

As one microsecond = 16 instructions one can see that this count++ loop with direct port manipulation uses 9/16 usec per count. (16/9 = 1.778 which is about the constant found)

So this way you can measure pulsewidth's to almost 0.5 uSec accurate ...

As the unsigned long will overflow @ 4 billion++ so it will overflow after 2415 seconds ~ 40 minutes.

michael_x:

  • What's the maximum timer speed for a 16MHz Arduino ? ( 1s/16,000,000 is obviously too fast )

Look at this data sheet: http://www.atmel.com/Images/doc8025.pdf
Chapter 16 talks about Timer1, which can count up to 65535. Doing that at a frequency of main clock is possible (clock source == 1)
Note that you don't actually generate interrupts on this timer, only from the rising/falling edges of your pulse. (This is in the context of the original question -- your use case may be different)
Also pay attention to the note about disabling interrupts while reading/writing 16-bit registers from the main code. (Interrupts are already disabled inside an interrupt handler)

  • How many commands can a interrupt routine handle at that speed ?

Not many. There are several microseconds just to enter and leave the interrupt handler. You typically just want to assign a global variable or two (as I said in the description)

  • What happens if another interrupt arrives before the routine has finished ?

It gets queued. If you are too slow to keep up over time, you will miss some interrupts.

  • Can loop() detect an overload ?

No, because if you're overloaded with interrupts, loop() doesn't execute at all. You will need the watchdog timer to detect this.

Note that the time through your main loop() is not very critical, as long as pulses arrive less often than the time of the loop, and/or as long as it's OK to miss measuring certain pulses. (Again, within the context of the original question)

Variation on previous code, now with an unsigned int as counter iso unsigned long.

//
//    FILE: PulseWidthMeter.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-mar-20
//
//    LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//

unsigned int count = 0;
void setup()
{
  Serial.begin(9600);
  Serial.println("pulse width meter 0.1");

  pinMode(3, INPUT);  
}

void loop()
{
  count = 0;
  while ((PIND & B00001000) == B00000000); // wait for HIGH
  unsigned long start = micros();
  while ((PIND & B00001000) == B00001000) count++; // start counting until LOW
  unsigned long stop = micros();  

  Serial.print("CNT: ");
  Serial.println(count, DEC);
  Serial.print(stop-start, DEC);
  Serial.println(" microseconds ");
  Serial.print((1.0*count)/(stop-start), 3);
  Serial.println(" count per microseconds ");

  delay(1000);
}

Output:

CNT: 40466
4416148 microseconds 
0.009 count per microseconds 
CNT: 11842
2575768 microseconds 
0.005 count per microseconds 
CNT: 29149
233520 microseconds 
0.125 count per microseconds 
CNT: 28208
10648 microseconds 
2.649 count per microseconds 
CNT: 26950
10168 microseconds 
2.650 count per microseconds 
CNT: 23250
8768 microseconds 
2.652 count per microseconds 
CNT: 20794
7852 microseconds 
2.648 count per microseconds 
CNT: 18987
7168 microseconds 
2.649 count per microseconds

As you can see the int counter overflows much faster but had a greater accuracy => 16/2.650 ~= 6 clock cylces (where the long has 9)
So one can measure pulsewidth in steps of 6/16 usec = 0.375 usec.

Overflow is at 65536 => 24.575,625 usec -> about 24.5 millis()

Advanced:
ALthough the counter overflowed we can derive from the microseconds how often it overflowed!

Example from output above:

CNT: 29149
233520 microseconds
0.125 count per microseconds

233520 / 24575,625 ~= 9.50... => 9 overflows

So the total counts become = 9 x 65536 + 29149 = 618973 counts

618973 x 6/16 usec = 232114,875 usec

Note that in practice the drift of the clock can be 10 ppm (or higher) so not all digits in the number above are significant.
Assuming 10ppm the number would be:

232114,875 usec ==> 232114 +-3 usec

@Robtillaart

Thank you so much for helping me out here but actually your code is giving me similar results as I was getting before the problem is that I have to measure the pulse widths between 10 to 15 microseconds accurately but your code as well as my code gives 8 or 12 microsecond for a 10 microsecond width input pulse and similarly it gives either 12 or 16 for pulse widths between 12 and 16 so that is the problem which I have been facing. The counter takes about 4 microsecond so is there any way to improve from this?

Thanks again

Hi,
I keep meaning to look into it myself and never get the time, but isn't the input capture feature of hardware timer1 exactly what you need to measure these small intervals ?

Duane B

rcarduino.blogspot.com

yes I need to measure that and it will in turn give me pulse width

Hi,
Just to be clear, I am referring to the input capture functionality of the timer, this is a timer configuration which will copy the exact timer count into a dedicated register when the pin state changes, you can then read this captured hardware timer value from your code. As this is using the hardware timer directly, its not reliant on or effected by any code that may be running.

Not sure if thats what you understood.

Duane B

rcarduino.blogspot.com

Thank you so much for helping me out here but actually your code is giving me similar results as I was getting before the problem is that I have to measure the pulse widths between 10 to 15 microseconds accurately but your code as well as my code gives 8 or 12 microsecond

The micros() was just a reference timing, find below a stripped pulsewidth meter for periods smaller than 24576 micros based on a tight software loop. One iteration takes 6/16 microsecond so we can 'measure" the following steps between 10 and 15 usec. So approx 15 steps.

26	9,750
27	10,125
28	10,500
29	10,875
30	11,250
31	11,625
32	12,000
33	12,375
34	12,750
35	13,125
36	13,500
37	13,875
38	14,250
39	14,625
40	15,000
41	15,375
//
//    FILE: PulseWidthMeter.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-mar-20
//
//    LINK: http://arduino.cc/forum/index.php?action=post;topic=96971.0
//

unsigned int count = 0;
void setup()
{
  Serial.begin(9600);
  Serial.println("pulse width meter 0.2");

  pinMode(3, INPUT);  
}

void loop()
{
  count = 0;
  while ((PIND & B00001000) == B00000000); // wait for HIGH
  while ((PIND & B00001000) == B00001000) count++; // start counting until LOW

  float usec = 1.0 * count * 6/16;
 
  Serial.print("CNT: ");
  Serial.println(count, DEC);
  
  Serial.print(" equals ");
  Serial.print(usec, 2);
  Serial.println(" microseconds.");

  delay(1000);
}

With the hardware timer more accuracy should be possible.

Thanks Robtillaart

I just want to ask that the values which you showed are the ones which you got after applying the pulse to arduino and the changing its width from 10 15 microseconds? is it so? actually I can't check this at this moment because I don't have arduino at this time but I am realy anxious to know this

Thanks again

I just want to ask that the values which you showed are the ones which you got after applying the pulse to arduino and the changing its width from 10 15 microseconds? is it so? actually I can't check this at this moment because I don't have arduino at this time but I am realy anxious to know this

The table was made by a spreadsheet the first column is the value of the counter and the second one is the number of microseconds the pulse was. As the units are in 6/16 of a micros this is an indication for the precision achievable this way (approx 1/3 micros)

So you said you need to measure pulse of 10-15 micros. How precise do you want to measure? Is a whole value OK, [10,11,12,13,14,15] or do you need a decimal digit?

ahaa that's very nice and yes the whole value is absolutely fine, means that if the input pulse width is 10.35 or so then even the value 10 would be perfect.

@Robtillaart

one more thing I want to ask is can i get serial print messages directly in an excel file simultaneously while the time of printing?

one more thing I want to ask is can i get serial print messages directly in an excel file simultaneously while the time of printing?

Yes, google for gobetwino

@Robtillaart

Thanks and today I have checked the code output by applying different pulses from 10 microseconds to 15 microseconds so for 10 microsecond input pulse the output comes to be 7.13 or 7.50 and for 12 microsecond input pulse the output comes to be either 9.00 or 8.13 and so on. I don't know whether we could improve it or not so what do you think?

Thanks again

what do you think?

The ratio seems to be correct!

7.5 : 9 == 10 : 12
7.13 : 8.13 ~~ 10 : 12

Which version of the code do you use? - the one with the unsigned int?
Can you post the actual output of the script?
How did you connect the signal? Have you connected the GND's?
Did you try a pull down/up resistor?
Do you apply one pulse or a constant stream?

There might be a problem with the synchronization at the start.

So there is a lot to think about,

I don't know whether we could improve

Hi,
Sorry for the persistence, but why doesn't anyone ever use timer1 input capture for this type of thing ?

I have not tried it yet myself and so if there is some fundamental reason why it wont work I would appreciate if someone could point me to it ?

Thanks

Duane B

rcarduino.blogspot.com

@Robtillaart yes I have used the code with unsigned int the output of the code is given below, I have applied constant pulse stream at 1Hz frequency and changed the input from 10 to 13 and then 15 microsecond. I have connected the ground as well but did not use pulldown/up resistor.

DuaneB:
Sorry for the persistence, but why doesn't anyone ever use timer1 input capture for this type of thing ?

My suggestion a few posts back was exactly that!