Four microseconds precision chronometer

Hello,

I've made a four microseconds precision chronometer for a futur RC5 remote control decoder.

This chronometer is based on 16bits timer-1 on ATmega168. The big feature of this code is to generate an interrupt only for 100ms !

Here the code :

/* 4us precision chronometer
 * -------------------------
 *
 * Chronometer with a four microsecond of precision based on
 * 16bits internal timer-1 of ATmega168.
 * Only one interrupt by 100ms.
 *
 *
 * Created by Vincent Le Gouic in 27th January 2009
 * Copyleft 2009
 * France
 *
 */


// Variables definition
uint16_t microseconds = 0, milliseconds = 0;
uint8_t seconds = 0, minutes = 0, hours = 0;


// Interrupt service routines
ISR(TIMER1_COMPA_vect)  // Timer-1 OC1A Match interrupt handler
{
  milliseconds += 100;
  if (milliseconds == 1000)
  {
    milliseconds = 0;
    seconds++;
  }
  if (seconds == 60)
  {
    seconds = 0;
    minutes++;
  }
  if (minutes == 60)
  {
    minutes = 0;
    hours++;
  }
}


// Reset chronometer
void reset_chrono(void)
{
  unsigned char sreg;
  sreg = SREG;  // Save global interrupts flag
  cli();  // Disable interrupts
  TCNT1 = 0;  // Reset timer-1 counter
  SREG = sreg;  // Restore global interrupts flag
  milliseconds = 0;
  seconds = 0;
  minutes = 0;
  hours = 0;
}


// Read microseconds counter
uint32_t read_microseconds(void)
{
  uint32_t microseconds;
  unsigned char sreg;
  sreg = SREG;  // Save global interrupts flag
  cli();  // Disable interrupts
  microseconds = TCNT1*4;  // one timer-1 count for 4us
  SREG = sreg;  // Restore global interrupts flag
  return microseconds;
}


// Setup
void setup(void)
{
  Serial.begin(9600);  // Open serial port with 9600bps
  
  // Set timer-1 parameters
  TCCR1A = 1<<WGM10 | 1<<WGM11; // Fast PWM mode enabled with 16bits registers
  TCCR1B = 1<<CS10 | 1<<CS11 | 1<<WGM12 | 1<<WGM13;  // Fixe clock for timer1 F_CPU=16MHz/64 (prescaler)
  OCR1A = 24999;  // Set the top limit counter to 24999
  TIMSK1 = 1<<OCIE1A;  // Enable interrupt on 0C1A match
  sei();  // Enable interrupts
}


// Main loop
void loop(void)
{
  char char_readed = 0; 
  char_readed = Serial.read();  // Read serial port character
  
  if (char_readed == 0x31)  // Type "1" to reset chronometer
  {
    reset_chrono();
    Serial.println("Reset chronometer");
  }
  
  if (char_readed == 0x32)  // Type "2" to print chronometer time
  {
    uint32_t microseconds_count = 0;
    uint16_t milliseconds_display = 0, microseconds_display = 0, milliseconds_rest = 0;
    microseconds_count = read_microseconds();
    milliseconds_rest = (uint16_t)(microseconds_count/1000);  // Take the two most important digits (decimal value) of microseconds counter
    milliseconds_display = milliseconds + milliseconds_rest;  // Add rest to milliseconds hundred
    microseconds_display = (uint16_t)(microseconds_count-(uint32_t)(milliseconds_rest*1000));  // Conserve microseconds
    Serial.print(hours, DEC);
    Serial.print("h ");
    Serial.print(minutes, DEC);
    Serial.print("m ");
    Serial.print(seconds, DEC);
    Serial.print("s ");
    Serial.print(milliseconds_display, DEC);
    Serial.print("ms ");
    Serial.print(microseconds_display, DEC);
    Serial.print("us ");
    Serial.println();
  }
}

I think that can be useful for people because I don't find a clear code for microsecond timer an internet.

Thanks at all who are guide me to solution with their code. :wink:

Bye.

Nice project, thanks for sharing.
I wonder what the actual accuracy is.
It looks like there will be a small error every 100ms because (24999 * 4) is not quite 1000ms. I would guess this would cause the clock to lose a second per day. There will also be some drift due to the crystal, its my experience that the arduino crystal is accurate to around a few seconds per day. So bear in mind that the actual accuracy will be less than 4us. over a long period of minutes.

I have tested precision with 249 and one interrupt by millisecond and this choice is guided by timer implementation.

Apparently TCNT1 is cleared at the next front clock when TCNT1 is equal to OCR1A register, so it is equivalent to count 250.

I had found this bug when I had set 250, the chronometer took delay, with 249 the chronometer appears to be ok.

In interpolating, 24999 should be the good value and justified by datasheet of ATmega168 but if someone can justify it ?

why not fix clock for timer1 F_CPU=16MHz/8 (prescaler)
then 0.5 microseconds precision?