ADC conversion vs.  Interrupts

Today I noticed that my timer IRQ driven ADC polling is about 2% of the mark. That is it runs ~2-4% slower than I would expect it. The issue is not due the crystal frequency because I already checked timing if all IRQs are off, I get exactly what I expect.

Then I started analyzing and now I have some theory about what happens. I have a timer IRQ at 10 kHz (the standard times is off). Then inside the IRQ I call analogRead().

Inside the standard libraries I found out that this will block for some time due to the following loop:

// ADSC is cleared when the conversion finishes
while (bit_is_set(ADCSRA, ADSC));

And in wiring.c I found

// set a2d prescale factor to 128
// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
// XXX: this will not work properly for other clock speeds, and
// this code should use F_CPU to determine the prescale factor.
sbi(ADCSRA, ADPS2);
sbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);

According to the datasheet conversions take 13 cycles. Now 13 cycles @ 125 kHz is about 104 microseconds. So I assume that in 4% of the timer interrupts the conversion is still blocking while another interrupt is triggered. This would explain the effect perfectly well.

The obvious solution to decrease the sample frequency is not exactly what I would like to to.

Now here come my two questions:

  1. Is my assesment correct?
  2. What would be the highest sampling frequency that is guaranteed to give proper timings? 9kHz? Or is this to agressive?

Cheers, Udo

The problem is the wait for ADC conversion completed loop. We want to be able to do other processing while the ADC conversion is taking place.

I think a usable strategy is to have a timer interrupt that does the following:

TimerInterruptHandler:
Check, Read and store result from previous ADC conversion
/* That is analogConversionRead() /
Start next ADC conversion
/
That is analogConversionStart(chn) */
end of TimerInterruptHandler

So the ADC conversions are done between the TimerInterrupts, not within the timer interrupts.

Or you might place an interrupt on the ASDC conversion completed event.

All of this requires some low level coding, the first is easier: just take the analogRead code, reverse the read value and start conversion and place this in the TimerInterruptHandler.

I shifted the ADC out of the timer ISR. Still I had 2% deviation. Then I figured that maybe the idea of reloading the timer in the ISR causes the deviations. I shifted to CTC mode as described here: http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1216085233

Now the results are much better. I now have a very different issue: how to figure out how accurate it actually is. I think it will run overnight against a stopwatch :slight_smile:

Finally success. After 10 hours it deviated about 40 ppm from the stopwatch --> this is within the tolerance of the crystal. Finally success :slight_smile: