1

I'm reading input from a camera and trying to reach a fast frame rate. Problem is my ATtiny85 has a pretty slow conversion time (65 - 260 µs Conversion Time). Is there any way to make this faster? I heard that you can set-up a prescaler for the analogRead() method by doing the following:

#define FASTADC 1

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

void setup() 
{
  #if FASTADC
   // set prescale to 16
   sbi(ADCSRA,ADPS2) ;
   cbi(ADCSRA,ADPS1) ;
   cbi(ADCSRA,ADPS0) ;
  #endif

  /*Rest of the code goes here.*/
}

I can't really make sense of the code and I found it on a less then reliable website so I was wondering if any of the gurus on here could let me know if what I'm attempting is possible and if this is the proper way of achieving my goal.

Also, would this also work for the Arduino Mega? I'm currently using it to test and debug my project since the ATtiny84 doesn't have serial output.

Datasheet: http://www.atmel.com/Images/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf

11
  • How many bits of precision do you need? You can speed up the conversion at the cost of loosing precision in the lower bits down to 1us/conversion.
    – bigjosh
    Commented Jul 7, 2016 at 20:21
  • Can you explain what bits of precision is? This is my first Arduino Project and first time working with a camera so I'm a little confused on the lingo.
    – sgmm
    Commented Jul 7, 2016 at 20:25
  • The ATTINY ADC normally gives 10 bits of precision- so you get a value between 0 and 2^10 (1024) and each number is different, so (say) 787 (1100010011) is always bigger than 786 (1100010010) even though 787 and 786 only differ by the lowest bit. You can make the ADC go faster, but the bottom bits start to not matter so you can only care about the topmost bits - so in our case if you only had 9 bits of precision then 786 and 787 would be the same (both would be (110001001x where the x means don't know or care). Make sense?
    – bigjosh
    Commented Jul 7, 2016 at 20:30
  • Makes a lot of sense. So when you set a prescaler you are reducing the number of bits used we can get the ADC to be faster but we will have less accuracy.
    – sgmm
    Commented Jul 7, 2016 at 20:33
  • The prescaler just divides the clock that goes into the ADC. So if you start with a 1MHz clock and a prescaler of /2, then the lock that goes into the ADC will be 500Khz. The ADC clock (after the prescaller) needs to be 50-200Khz to get all 10 bits of precision, but you can run it up to 1mhz but more of the bottom bits will start being wrong the faster you go. How much precision do you need and how fast you do need samples?
    – bigjosh
    Commented Jul 7, 2016 at 20:45

1 Answer 1

2

The ADCs in the ATtiny85 and ATmega2560 (the chip powering the Arduino Mega 2560) are pretty similar, except for the Mega having more inputs. In particular, the way you set the clock prescaler is the same, namely the bits ADPS2:0 in the register ADCSRA. You could use the same code to set the prescaler on both chips, but you probably don't want to unless your ATtiny is clocked at 16 MHz, like your Mega. I would normally just set the control register to the value I want, rather than touching only a few bits, but that is a matter of personal preference. So, to set the ADC clock of the Mega to 1 MHz, I would

ADCSRA = _BV(ADEN)    // enable the ADC
       | _BV(ADPS2);  // clock at F_CPU / 16 = 1 MHz

The same code would do the same thing on an ATtiny clocked at 16 MHz. If the ATtiny runs at 8 MHz, I would instead

ADCSRA = _BV(ADEN)    // enable the ADC
       | _BV(ADPS1)   // clock at
       | _BV(ADPS0);  //     F_CPU / 8 = 1 MHz

If you want to be generic:

#if F_CPU >= 12000000
# define ADPS_SETTING _BV(ADPS2)                 // F_CPU / 16
#else
# define ADPS_SETTING (_BV(ADPS1) | _BV(ADPS0))  // F_CPU / 8
#endif

ADCSRA = _BV(ADEN)      // enable the ADC
       | ADPS_SETTING;  // clock somewhere near 1 MHz

As bigjosh explained in comments, the prescaler setting is a trade-off between speed and accuracy. I recommend you read the article on the Arduino ADC by Nick Gammon. He did some tests at various speeds which showed that you can get decent results with clock speeds up to 1 MHz, then it becomes pretty bad at 2 MHz and mostly useless beyond. You may want to run the same tests with your particular setup.

Except for the very first conversion, which takes longer, an ADC conversion takes 13 cycles of the ADC clock. If you run that clock at 1 MHz, that is 13 µs per reading. If you get the readings with analogRead(), you won't get one reading every 13 µs, because the CPU needs extra time to execute the code you have between successive calls of analogRead(). You can save time by having the CPU and the ADC work in parallel, maybe along these lines:

ADCSRA |= ADSC;     // start the first conversion
for (int i = 0; i < NB_READINGS; i++) {
    loop_until_bit_is_clear(ADCSRA, ADSC);  // wait for the ADC
    uint16_t reading = ADC;     // get the reading
    ADCSRA |= ADSC;             // start the next conversion
    process(reading);           // process the reading we have
}

This way you process one reading while the ADC is taking the next one. Or you can set the ADC to “free running mode” (where it takes one reading after another without ever stopping) and get the readings in an interrupt service routine. Whichever method works best depends on your particular application: the amount of works your CPU has to do while reading the ADC and whether you can afford to do the whole series of readings with interrupts disabled.

6
  • If that's how you make the ADC faster then what does the code I posted do?
    – sgmm
    Commented Jul 8, 2016 at 14:53
  • It sets the prescaler to 16, just like my first code snippet. Commented Jul 8, 2016 at 15:31
  • Okay, then I might be understanding the concept behind a prescaler. I thought prescalers made things slower because they divided the speed at which things happen. Meaning if my ADC is operating at a 1MHz speed and I set a prescaler of 2 then it would run at half the speed.
    – sgmm
    Commented Jul 8, 2016 at 15:35
  • @CrystalPritzker: No. The prescaler sits between the CPU clock and the ADC clock. C.f. section 17.5 of the datasheet. If your CPU runs at 16 MHz (which is the case on the Mega), setting the prescaler to 2 would clock your ADC at 8 MHz, way too fast. The Arduino core sets it to 128, which clocks the ADC at 125 kHz, which is fine but too slow for your needs. I suggest you clock your ADC at 1 MHz, which means setting the prescaler to 16 on the Mega (I do not know F_CPU on your Tiny). Commented Jul 8, 2016 at 15:52
  • I see, but what happens if I just don't set a prescaler? Is their a default speed at which the ADC runs?
    – sgmm
    Commented Jul 8, 2016 at 15:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.