1mS Timer

Hello,

I am trying to sample analog data at exactly 1k Hz and send it to the serial port. I have this timer set up, but for some reason I am getting a sample rate of 994-998Hz.

When I just serial print the execution time I get 1004uS. Can anyone help explain why its not exact? I Used an NI Elvis to see how accurate the clock is, and it seems fine.

Im using a Leonardo at 16MHz. Thanks for any help!

/* 
 * Arduino 101: timer and interrupts
 * 2: Timer1 overflow interrupt example 
 * more infos: http://www.letmakerobots.com/node/28278
 * created by RobotFreak 
 */

#define ledPin 13

unsigned long t1, t2;

void setup()
{
  Serial.begin(115200);
  pinMode(ledPin, OUTPUT);
  t1 = 0; t2 = 0;
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;

  TCNT1 = 63535;            // preload timer 65536-16MHz/256/2Hz
  TCCR1B |= (1 << CS11);    // 256 prescaler 
  TIMSK1 |= (1 << TOIE1);   // enable timer overflow interrupt
  interrupts();             // enable all interrupts
}
// interrupt service routine that wraps a user defined function 
// supplied by attachInterrupt
ISR(TIMER1_OVF_vect)        
{
  TCNT1 = 63535;            // preload timer
  //digitalWrite(ledPin, digitalRead(ledPin) ^ 1);
  t2 = micros();
  Serial.println(t2 - t1);
  t1 = t2;
}

void loop()
{
  // your program here...
}

Have you done the math to ensure what you want to do is even possible?

  TCNT1 = 63535;            // preload timer 65536-16MHz/256/2Hz

So... how did you get 63535? I get 16e6/256 = 62500, so for a 1s timer expiration, you'd want TCNT to interrupt on a 62500 count. Or preload with 3036 if you're counting up and interrupting on overflow. Though I'd think a zero to 62500 with automatic reset would be preferable (CTC mode.)

westfw:
Though I'd think a zero to 62500 with automatic reset would be preferable (CTC mode.)

That would eliminate the race condition.

I did not change the commenting on the code (ooops). It was set to a prescaler of 8. That is why the timer preload was not making sense.

1000/(16000000/8) = 2000000

1000/2000000 = .0005uS (time per tick)

1mS/.0005 = 2000

2000-65535 = 63535

I changed the code to set the timer in CTC Mode, but still get 1004uS every once in a while (it seems to be a lot better!).

I am verifying this by setting up another arduino with the blink sketch and measuring a one second period. The blink sketch arduino was verified to be accurate using an o-scope, but when I import the data into excell I get data anywhere from 1010 to 991. Any other ideas?

{
  Serial.begin(115200);
  pinMode(A6,INPUT);
  
  // initialize timer1 
  noInterrupts();           // disable all interrupts
  TCCR1A = 0;
  TCCR1B = 0;
  TCNT1  = 0;

  OCR1A = 16000;            // compare match register 16MHz/1000
  TCCR1B |= (1 << WGM12);   // CTC mode
  TCCR1B |= (1 << CS10);    // No prescaler 
  TIMSK1 |= (1 << OCIE1A);  // enable timer compare interrupt
  interrupts();             // enable all interrupts
}

ISR(TIMER1_COMPA_vect)          // timer compare interrupt service routine
{
  t2 = micros();
  sensorVal= analogRead(A6);
  Serial.print(t2 - t1);
  Serial.print(",");
  Serial.println(sensorVal);
  t1 = t2;
}

void loop()
{
  

  
  // your program here...
}

Thanks for the help!

I changed the code to set the timer in CTC Mode, but still get 1004uS every once in a while

Every once in a while, your timer will expire while the AVR is off in un-interruptable code (notably the timer0 interrupt or a serial interrupt), leading to some delay before the ISR is serviced.

Note that doing serial output in an ISR is "strongly not recommended" - if you ever hit a condition where the serial output buffer is full, your code will hang waiting for the uart TX interrupt to empty the buffer and make room. But since you're in an ISR, the TX interrupt will never be serviced. (There may be other problems as well.) You might get away with it as long as the average data rate is much lower than the serial bitrate, and nothing else does serial output...