Go Down

Topic: measure time <1ms (Read 5478 times) previous topic - next topic


I'd like to measure speed with two light barriers. The objects are ~1.5m/s fast but I have only <500mm space. So 1ms is just ok but if possible, I'd like to be more exact (and thus save space).

Is this possible and how?



Jun 04, 2007, 09:17 pm Last Edit: Jun 04, 2007, 09:19 pm by jims Reason: 1
You can use the pulseIn() function if you can arrange for your signal to arrive as a pulse. If this isn't possible because it involves two pins or someother reason then you must look outside the normal API.

If you look at the hardware level this becomes possible. Timer0 is an 8 bit counter that increments every 4 microseconds with a 16MHz Arduino in the standard configuration. You can read its value with TCNT0. You'll have to watch for when it rolls over and do the right thing.

You can read about the timers in the Atmel chip manual for your processor.


Jun 04, 2007, 10:42 pm Last Edit: Jun 04, 2007, 10:51 pm by wandrson Reason: 1
It sounds like you are trying to use two photogates to measure the speed of an object -- or something similar.  In this case TMR0 could allow you to measure up to about 1 ms between the events  with a 4uS resolution as it is configured in the arduino environment.  Custom timer configurations could allow uS resolution with indefinite time intervals.

However, given your stated speed of 1.5 m/s and a 0.5m spacing you need to time an interval of approximately 333 ms, something like below should do for this situation.  This setup should even work down to at least a 100mm spacing at 1.5m/s (abt 67ms)

Code: [Select]

  while (digitalRead(input1)  == HIGH) { }
  time1 = millis();
  while (digitalRead(input2) == HIGH) { }
  time2 = millis();
  speed = 0.5 / ((time2-time1)/1000)  // this should calculate speed in m/s given a separation distance of 0.5m


Thanks for the answers.

jims: pulseIn sounds good but as I understand it the processor is blocked during the measurement. That unfortunatly must not happen as I have two objects to measure at the same time.
Is the roll over of Timer0 what counts up the value returned by millis()? If so then I could add millis() plus Timer0 to get a better result.

Where do I find that manual? There seems to be much more to the programming than the reference shows.

wandrson: yes, you are right. So there is another timer TMR0 which I can use and it is probably what I need. Where do I find documentation about all these nice, hidden things?


The TCNT0 register from timer0 isn't really a part of the Arduino environment, it is the underlying hardware.

I wouldn't try to correlate TCNT0 rollovers and millis().  Millis is derived from the rollover count, but sometimes it jumps 1 and sometimes millis() jumps 2 because the timer doesn't rollover on a millisecond interval, plus doing long integer arithmetic really eats up the clock cycles and bytes so I avoid it unless I really need it.

If your time interval is less than 1024 microseconds, and I suspect if you move the gates close enough you can be certain of that, then you have either zero or one rollover in any measurement and when you subtract the times you know. If you got a negative time interval add 1024 microseconds. Be careful about when you convert from unsigned to signed.

The atmega8 and atmega168 manuals are from Atmel.  http:// http://www.atmel.com/dyn/products/devices.asp?family_id=607 will get you into the neighborhood.


Ok, I think I found the documentation and a description of TCNT0 and TCNT1. But I have to admit that I didn't understand much of it especially how that information relates to the Arduino.

I did some testing with this code:
Code: [Select]

int ledPin = 13;
int t1,t2,diff;

void setup() {
 pinMode(ledPin, OUTPUT);  
 digitalWrite(ledPin, HIGH);  

void loop() {

 t1 = TCNT0;
 t2 = TCNT0;
 Serial.print("TCNT 1: ");  
 Serial.println(t1,DEC);           // print the value to the serial port
 Serial.print("TCNT 2: ");  
 Serial.println(t2,DEC);           // print the value to the serial port
 diff = t2 - t1;
 Serial.print("Diff: ");  
 Serial.println(diff,DEC);           // print the value to the serial port
 delay(900);                          // stop the program for some time

I get values from 12-20 for t1 and 37-47 for t2 but diff is always 25. Looks good, but I don't understand why I always get similar values back and not in the range from 0-255?

Next try: I read that TCNT1 is a 16bit counter so it should be able to measure up to 262ms (2^16 * 4microseconds). Now I get values in the range of 0-255 (diff is 25).

Could someone please explain this behaviour?


Jun 10, 2007, 01:46 am Last Edit: Jun 10, 2007, 01:51 am by jims Reason: 1
It is the delay(900) at the bottom. This waits for a counter that is incremented by TCNT0 rolling over so your code is always called not too long after the rollover.

About TCNT1, 16 bits is better, but... The way it is configured in the arduino I think it counts from 0 up to a limit based on your PWM output and then back down to 0. That would be difficult or impossible to interpret. You could change the counter mode, but you would lose the analog output.


Aha. Do you mean I would loose the PWM analog output or all analog outputs? I could live without PWM.

I read through the manual and as I understand it, I have to set the waveform generation mode bits (WGM13:0) to 0 to get into 'normal mode'. These can be found in the Timer/Counter Control registers A and B (TCCR1A and TCCR1B).

How do I write to those registers?


All of the outputs on the Arduino are PWM, that is just the way it works. You would only use the ones driven by the counter you mess with, and you could probably choose a mode where they would work, just not in "phase correct pwm" which  is the mode that causes downward counting.

Writing to a register, like TCCR1A is just like assigning a variable.


TCCR1A = 0;

will yield a 16 Bit counter TCNT1 which increases every 4 microseconds -> max. measurable time is ~260ms.

Analog out still works but is probably not phase correct anymore (can't measure that).

There also bit 0 (TOV1) in TIFR1 register which signals an overflow. Unfortunatley it looks like that is never reset. Can/must I do that myself?


I find the following useful - I modified it from http://www.arduino.cc/playground/Code/InfraredReceivers

Code: [Select]
 TCCR1A = 0x00; // Control Register 1A
                // COM1A1=0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1
                // WGM11(PWM11)=0,WGM10(PWM10)=0 => PWM Operation disabled
 TCCR1B = 0x03; // Control Register 1B
                // ICNC1=0 => Capture Noise Canceler disabled
                // ICES1=0 => Input Capture Edge Select (not used)
                // WGM12(CTC1)=0 => Clear Timer/Counter 1 on Compare/Match
                // CS12=0 CS11=1 CS10=1 => Set prescaler to clock/64
                   // 16MHz clock with prescaler means TCNT1 increments every 4uS
 TIMSK1 = 0x00; // Interrupt Mask Register
                // ICIE1=0 => Timer/Counter 1, Input Capture Interrupt Enable
                // OCIE1A=0 => Output Compare A Match Interrupt Enable
                // OCIE1B=0 => Output Compare B Match Interrupt Enable
                // TOIE1=0 => Timer 1 Overflow Interrupt Enable

The range of clock premultipliers that may be relevant are:
TCCR1B = 0x01; - clock/1 (No Prescaling) = increments every 0.05us - overflows in 3.28mS
TCCR1B = 0x02; - clock/8                        = increments every 0.4us  - overflows in 26.2mS
TCCR1B = 0x03; - clock/64                      = increments every 3.2us  - overflows in 209.7mS

Remember, you can reset the clock with "TCNT1 = 0;" just before you start using it to hopefully prevent overflows.



I think your calculations for the effect of the prescalers is incorrect.

By my understanding with the 16,000,000Hz clock of the arduino the prescalers would have the following result.

clock/1 = 1/(16,000,000/1) = counter increments every 0.063Us
clock/8 = 1/(16,000,000/8) = counter increments every 0.5us
clock/64 = 1/(16,000,000/64) = counter increments 4us

Overflows will depend upon the initial value of the counter. With an initial value of 0 they are

When set to 0 the overflows would be
clock/1 => 65536*0.063us = 4.096ms
clock/8 => 65536*0.5us = 32.768ms
clock/64 => 65536*4us = 262.144ms

The overflow time can be adjusted by replacing the 65536 with (65536 - init counter value) in the formula above.


True - I was basing my calculations on the ATMega168 being 20MHz - not 16MHz. The datasheet I have says 20MHz, so I am kind of confused why 16MHz keeps cropping up. I guess it is eaitehr the speed of the previous ATMega chip, or I am understanding something wrong.


20 MHz is the maximum speed you can use with the ATmega168.  The actual speed is determined by the crystal you connect it to, which on the Arduino board is 16 MHz.

Go Up