Monday, January 19, 2015

Arduino ADC conversion rate

I know a lot of postings have been written about analogue to digital conversion rates in the 8 bit Arduino processors.  I decided to do a little poking around and performance timing to see for myself how well these little processors perform.  I will compare the performance of the 8 bit ATMega328 and ATMega2560 processors with the 32 bit Arduino Due processor.

The ADC clock is 16 MHz divided by a prescale factor.  The default setting is found in wiring.c:

        // 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);

        // enable a2d conversions
        sbi(ADCSRA, ADEN);

Using the default setting of 128 for the prescale factor gives a conversion clock of 125 kHz.  Since ADC conversion requires 13 ADC clocks the effective sample rate at best is approximately 125 kHz / 13 = 9.615 kHz.

Using a prescale of 16 would give an ADC clock of 1 MHz and a sample rate of 76.923 kHz.  Increasing the ADC clock can affect ADC accuracy however.  ATMel recommends that the maximum ADC clock frequency is limited by the internal DAC in the conversion circuitry and should not exceed 200 kHz.  However frequencies up to 1 MHz do not reduce the ADC resolution significantly.  Operation above 1 Mhz has not been characterized however.

So to do a quick test of the impact on performance I did a quick an dirty script to measure the time required to do 1000 analogRead operations before and after speeding up the ADC clock and see how much performance gain there is.

// useful defines for setting and clearing register bits
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

void setup() {
 int start;

 Serial.begin(115200) ;
 Serial.println("ADCTest at default 9.6 kHz sample rate") ;
 start = millis() ;
 for (int i = 0 ; i < 1000 ; i++)
   analogRead(0) ;
 Serial.print(millis() - start) ;
 Serial.println(" ms (1000 calls)") ;
 Serial.println();

 // set prescale to 16
 sbi(ADCSRA,ADPS2) ;
 cbi(ADCSRA,ADPS1) ;
 cbi(ADCSRA,ADPS0) ;

 Serial.println("ADCTest at 76.93 kHz sample rate") ;
 start = millis() ;
 for (i = 0 ; i < 1000 ; i++)
   analogRead(0) ;
 Serial.print(millis() - start) ;
 Serial.println(" ms (1000 calls)") ;
}

void loop()
{

}

The results are about as you would expect with nearly an order of magnitude improvement in ADC speed.

ADCTest at default 9.6 kHz sample rate
111 ms (1000 calls)

ADCTest at 76.93 kHz sample rate

18 ms (1000 calls)

Testing the Due with the following code shows the following:

ADCTest on Due 

3 ms (1000 calls)

Here is the code used:

void setup() 
{
 int start ;
 int i ;

 Serial.begin(115200) ;

 Serial.println("ADCTest on Due ") ;
 start = millis() ;
 for (i = 0 ; i < 1000 ; i++)
   analogRead(0);
 Serial.print(millis() - start) ;
 Serial.println(" ms (1000 calls)") ;
 Serial.println();

}

void loop() 
{

}

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete