ADC design advice?

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi all,

 

I am working on my ADC automatic nightlight learning thing (see code below), and I am running into a problem with timing.  Basically, I multiplex ADC0, 1, 2 to get the hysterisis level, the setpoint level, and the current value of the ambient light respectively; currently each of those is attached to a pot voltage divider for testing input.  I use an interrupt to signal when the ADC is finished.  I also write out the value for each ADC input to the LCD.  I do all this every 100 ms, all the ADC readings in a row (see below around line #133, "if ( tick % 10 == 0 ) {  ").  However, ... the output to the LCD is all screwy, as if each pot is going to the wrong spot on the LCD, like cells 5 through 9 instead of 0 through 4, etc.  

 

I am sure it is a timing problem in which the thread of execution  moves on to reading ADC1 before the ADC is finished , then we wait for the LCD somewhat non-deterministically, then the ISR fires, and it is a big mess.  (It is kind of obvious in retrospect.)  But with respect to the timing problem, I have some more specific questions and possible solutions for which I would love feedback:

 

  1. I thought I would change a few things: 
    1. store the values from each ADC input then assemble the LCD output only once to reduce lag from the LCD . 
    2. Do each multiplexed ADC input at a different time according to the heartbeat, like modulo 2 for the hyst input, modulo 4 for the setpoint, etc.
    3. Use follower buffers on the inputs.
    4. Slow everything down generally
  2. I have a scope, how would I test the timing in this case?  Set a pin high with the ADC ISR?
  3. Would it help to use an op amp follower on the pots to get better input impedance to the ADCs?  I know it would theoretically, but practically?
  4. Is there a way to do this deterministically?  Just spreading out the readings and slowly them down will probably work, but it won't be guaranteed and I would like that if possible. 

 

I am hoping for comments from the collective on the above or other suggestions.

 

Thanks in advance for everyone's good advice!

 

/*
    lcd_adc.c
    Synopsis:  Using a 10ms tick, handle long and short button inputs
        and do something with ADC input
    Created: 2018-03-20 7:38:09 PM
    Author : wsprague

    HARDWARE DESCRIPTION (lab notes might be more up to date)
        PD2      (4) button input
        PC5     (28) power on LED
        PC2     (26) multiplex ADC hyst pot
        PC1     (25) multiplex ADC setpoint pot
        PC0     (24) multiplex ADC input
        PC4     (27)    output LED
        PBx      (*) lcd control pins   

    Notes:

        2018-04-19 wws:  modifying with ADC; pot input for hyst and setpoint; function for LCD display at x, y, buffer; 

            I don't know why I did this earlier, deleting:  PORTC |= ( 1 << PC5);  // pull up 

            TODO make sure button working and counting up.  I am going to use it for something....

        2018-03-20 wws:  remember && for logical AND  

 */

#define F_CPU 1000000UL  // delay.h supplies reasonable default, but this fixes warning

#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <avr/eeprom.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <util/atomic.h>

// include header file for lcd library
#include "lcd.h"

// prototypes
void wws_write_lcd (int x, int y, int w, char* str);

// define event names and numbers
#define _0NULL     0
#define _1TICK     1

// define state names and numbers
#define _0NULL     0

// useful constants
#define RADIX        10    // radix for lcd numeric output, want hex but decimal much easier still
#define HOLD_TICKS 1000    // how many ticks to wait for hold before reset
#define TICK_TOP    155    // how many ticks before heartbeat/ cld increment. 155 with 64 prescale gives about  10.0 msec, 100 Hz

// globals
volatile uint32_t  tick       = 0;        // ticks (~10ms) since reset, incremented each wake up.  Overflows on 1.36 years
volatile uint16_t  adc_val    = 0;        // ADC value set by ISR at capture
uint16_t EEMEM eeprom_pcount  = 0;        // eeprom memory allocation 

// progmem something with name of project
const char pname[] PROGMEM = "exp#15";

// define short nop macro
#define _NOP() do { __asm__ __volatile__ ("nop"); } while (0)

//
int main () {
    char buffer[32];       // to store lcd stuff
    uint8_t ind;           // to store PD input
    uint8_t holdt   = 0;   // number of held down ticks
    uint8_t p1      = 0;   // boolean for whether or not button has been pushed and not yet released
    uint16_t pcount = 0;   // count of button pushes,
    uint16_t hbeat  = 0;   // 1 sec heartbeat counter
    uint16_t setp   = 0;   // setpoint 10 bit like ADC
    uint16_t hyst   = 0;   // hyst value 10 bit like ADC
    uint16_t lumen  = 0;   // light value 10 bit like ADC

    // initialize lcd, put pname in lower right corner
    lcd_init(LCD_DISP_ON);
    lcd_clrscr();
    // lcd_gotoxy(10,1);     // x,y zero indexed
    // lcd_puts_p(pname);

    // update lcd with pcount and progmem info on program.  Relies on consistent EEPROM behavior after being programmed
    pcount = eeprom_read_word (& eeprom_pcount );
    if (pcount == 0xFFFF) {
        pcount = 0;
        eeprom_update_word (& eeprom_pcount, pcount );
    }
    // lcd_gotoxy(0,1);       // x,y zero indexed
    // itoa(pcount, buffer, RADIX);
    // lcd_puts(buffer);

    // enable output power-on-indicator light on PC5 (28), nightlight LED PC4.
    DDRC  |= ( 1 << PC5) | ( 1 << PC4) ;
    PORTC |= ( 1 << PC5);

    // input on PD2 (button, pin 4)
    DDRD  &= ~(1 << PD2 );    // ensure input pin
    PORTD = 0xFF;             // set pull up on all pins for well defined behavior, assumes all zero and input pins
    holdt = 0; // initialize for how long the button pin is held

    // config TC0 ticking at 10ms CTC.
    TCCR0A |= (1 << WGM01);                // mode 2 CTC
    OCR0A   = TICK_TOP;                    // turn over when hit OCROA,
    TCCR0B |= (1 << CS01) | (1 << CS00);   // turn on, 64 prescale
    TIMSK0 |= (1 << OCIE0A);               // fire off interrupt on match

    // config ADC
    ADMUX |= (1 << REFS0);  // simply use VCC of chip as reference
    ADCSRA |= (1 << ADEN) | (1 << ADIE) | (1<<ADPS1) | (1 << ADPS0);  // ADC enable, ISR enable, 8 prescale (1M / 8 ) == 125k

    // go to sleep and wait for 10ms ISR or other interrupt
    set_sleep_mode(SLEEP_MODE_IDLE);
    sei();
    while (1) {
        // sleep, waking on interrupts
        sleep_enable();
        sleep_cpu();
        sleep_disable();

        // read input pin, deal with later
        ind = PIND;  

        // handle tick things.
        // .. 100  msec
        if ( tick % 10 == 0 ) {   

            // ..update heartbeat thing, hope modulus not too slow
            // ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
            //     hbeat++;
            // }
            // itoa(hbeat, buffer, RADIX);
            // lcd_gotoxy(0,0);         // x,y zero indexed
            // lcd_puts("        ");
            // lcd_gotoxy(0,0);
            // lcd_puts(buffer);
            // PINC |= (1 << PC3); 

            //         PC2     (26) multiplex ADC hyst pot
            //         PC1     (25) multiplex ADC setpoint pot
            //         PC0     (24) multiplex ADC input

            // get hyst.  Displaying ok      Could make capture 3 lines a function -- clear anyway, set, do capture
            ADMUX &= ~((1<<MUX0) | (1<<MUX1) | (1<<MUX2) | (1<<MUX3)); // clear mux bits.
            ADMUX |= (1 << MUX1);   // ADC2 multiplex 0010
            ADCSRA |= (1 << ADSC);  // start capture
            ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {                        // grab adc_val into better variable
                hyst = adc_val;
            }
            itoa(hyst, buffer, RADIX);
            wws_write_lcd(10, 0, 5, buffer);  // write value to LCD

            // get setpoint
            ADMUX &= ~((1<<MUX0) | (1<<MUX1) | (1<<MUX2) | (1<<MUX3)); // clear mux bits
            ADMUX |= (1 << MUX0);   // ADC1  multiplex 0001
            ADCSRA |= (1 << ADSC);  // start capture
            ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {                        // grab adc_val into better variable
                setp = adc_val;
            }
            itoa(setp, buffer, RADIX);
            wws_write_lcd(5, 0, 5, buffer);

            // get LDR
            ADMUX &= ~((1<<MUX0) | (1<<MUX1) | (1<<MUX2) | (1<<MUX3)); // clear mux bits, zero = ADC0, P0, pin 23
            // leave as 0000 for ADC0  multiplex 0000
            ADCSRA |= (1 << ADSC);                                     // start capture
            ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {                        // grab adc_val into better variable
                lumen = adc_val;                                       // should calculate later R_ldr = 500 / Lumens
            }
            itoa(lumen, buffer, RADIX);
            wws_write_lcd(0, 0, 5, buffer);

            // (un)light LED depending on set point and hyst values use PCn pin config'ed  above
            //  should be atomic for math?
            if  (lumen >= 500) {                                              // (lumen >= setp + hyst) {
                PORTC  &= ~( 1 << PC4);        // clear LED
            } else if (lumen < 500) {                                         // (lumen <= setp - hyst){
                PORTC  |= ( 1 << PC4);         // light LED
            } else {
                // no op just keep current state at PC4
            }
        } else if (tick % 10 == 2 ) {

        }   

        // handle button input
        // ..  count number of ticks with button held/ pushed.  (ind & (1<<PD2))  syntax to check single bit at PD2.  .
        if (!(ind & (1<<PD2)) && p1 == 0){                // bit low and not started a press,  so start incrementing
            holdt ++;
            p1 = 1;
        } else if (!(ind & (1<<PD2)) && p1 == 1 && holdt <= 254) {
            holdt ++;
        } else if ((ind & (1<<PD2)) && (p1 == 1) ) {     // bit high for first time so set to zero
            holdt = 0;
            p1 = 0;
        } else {                                         // bit continuing low, do nothing or write zero de
            // holdt = 0;
        }

        // ..  do stuff based on how long button down
        switch (holdt) {
            // for each push, debounced at 50 ms, increment counter
            case 5 :
                ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
                    pcount = pcount+1;
                }
                eeprom_update_word (& eeprom_pcount, pcount );
                itoa(pcount, buffer, RADIX);
                // wws_write_lcd(0, 1, 5, buffer);

                break;

            // at 1 sec, zero counter and write to eeprom and lcd
            case 100:
                ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
                    pcount = 0;
                }
                eeprom_update_word (& eeprom_pcount, pcount );
                itoa(pcount, buffer, RADIX);
                // wws_write_lcd(0, 1, 5, buffer);

                break;

            // default:    // XXX dont want always running
            //     break;

        }  // holdt case

    }      // while loop
}          // main()

// ISR for ADC read.
ISR(ADC_vect){
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        adc_val = ADC;
    }
}

// ISR for TC0 CTC .
ISR(TIMER0_COMPA_vect){
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        tick++;
    }
}

// write a string to lcd, blanking out w wide characters
void wws_write_lcd (int x, int y, int w, char* str){
    char buffer[32];       // to store lcd stuff

    memset(buffer, ' ', w);
    buffer[w+1] = '\0';
    lcd_gotoxy(x, y);  // x,y zero index
    lcd_puts(buffer);  // blank screen
    lcd_gotoxy(x, y);  // x,y zero index
    lcd_puts(str);     // write values

}

 

Last Edited: Tue. May 1, 2018 - 04:28 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Forget about the extra mess of losing yourself in ADC IRQs...your program barely barely has anything to do...just select a channel and read the desired ADC when needed...you can certainly wait 1/15000th of a second.

You may barely even need any timers, other than maybe debouncing some switches.

Don't understand what you EEprom is for...hope you are not writing to it over & over.  Should generally write to it after you have the entry value, not while it's being adjusted ( so if you had an adj from 0 to 333, as you adjust up & down it's not writing 300 times. Only write when  the adjustment is no longer changing (like 1 second), or some type of enter key is pressed)

 

loop:

Read 3 ADC channels, get A, B, C

Do any of these values differ from previous A,B,C values by more than x

   IF YES:

          Display  A,B,C   ...display only update as needed to prevent flicker.

          Update Previous ABC with ABC

Use A,B,C to turn on/off light

read button

delay 10ms

end of loop 

 

 

 

  

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

Forget about the extra mess of losing yourself in ADC IRQs...your program barely barely has anything to do...just select a channel and read the desired ADC when needed...you can certainly wait 1/15000th of a second... <snip>

 

How do you wait until the ADC channel has finished capturing?  Busy-wait checking the register?  I also like to avoid busy-waiting, even for small applications, because I want to get practice with more scaleable and low power techniques, but maybe this is a place where it is an appropriate technique?

 

Your advice is probably great if I were just trying to get a nightlight to work, but I am really more interested in figuring out how to design a scaleable system that multiplexes a bunch of ADCs, with a toy nightlight application as a very small starting point.  I appreciate the response though.

 

So, does anyone have design advice for multiplexing a bunch of ADC inputs?  Given that the datasheet guarantees 13 ADC clock cycles for a capture, assuming low impedance, it seems to me that one should be able to easilly do a read on one of the inputs every 10 ms in a polling system.  How to know when the ADC is ready in that case?  Is that a reasonable approach?

 

Sorry if my question is vague, I will maybe post again with some more thought if this doesn't go anywhere..

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The main problem with using interrupts and tge adc is that there is the issue of synchronisation between the isr reading the adc and the other code using the values. If you really want to do it this way, then have the isr store the values in an array. Then write an accesor function to read an individual value from the array whilst having the interrupts disabled so you get an ‘atomic’ read of a multibyte value.

For many cases, you’re not pushed for time and/or performance so doing a wait loop is the simplest solution. That’s what most of my instrumentation apps do.

You’ve chosen to use asm, so writing ‘smarter’ code takes more effort on the programmer as compared with using C. Having an isr store the adc value in an array is a couple of lines of C vs a dozen of asm.

Last Edited: Tue. May 1, 2018 - 05:33 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 it seems to me that one should be able to easilly do a read on one of the inputs every 10 ms in a polling system.  How to know when the ADC is ready in that case?

Yes, you can read even 8 inputs every 10 ms. 

uint16_t adc_read(uint8_t channel)
{
   ADMUX = (1<<REFS0) | channel;  // set reference and channel
   ADCSRA |= (1<<ADSC);           // start conversion  
   while(ADCSRA & (1<<ADSC)){}    // wait for conversion complete  
   return ADC;
}

Note line  // wait for conversion complete.

So with folowing you can read adc with maximal speed 

   while(1)
   {
      adc_value0 = adc_read(0); // read ADC0 
      adc_value1 = adc_read(1); // read ADC1 
      adc_value2 = adc_read(2); // read ADC2 
      // etc...

 Is that a reasonable approach?

Probably not, if you want to display the values on Lcd.

I doubt you have so good eyes to read the display in 10 ms. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Visovian wrote:

 it seems to me that one should be able to easilly do a read on one of the inputs every 10 ms in a polling system.  How to know when the ADC is ready in that case?

Yes, you can read even 8 inputs every 10 ms.

 

Quick questions:  If my output impedance is below 10K, then am I *guaranteed* 13 clock cycles for a capture?   And are those the prescaled clock cycles for the ADC, not MCU cycles?

 

Thanks everyone!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 

forkandwait wrote:
Quick questions: If my output impedance is below 10K, then am I *guaranteed* 13 clock cycles for a capture? And are those the prescaled clock cycles for the ADC, not MCU cycles?

Let's back up a bit...

forkandwait wrote:
#define F_CPU 1000000UL // delay.h supplies reasonable default, but this fixes warning

--Which AVR model are you using?

-- How fast is your AVR running?  What is the clock speed; what is the clock source; how have you verified that your AVR is really running at that speed.  One just doesn't toss in an F_CPU line to get rid of the warning; the warning implies that you are using facilities that should not be lied to.

 

AVR8 models have a fairly comprehensive chapter on the ADC.  I suggest a careful reading and re-reading.  Many of your questions are answered there; many responses here will just be quoting the datasheet, or re-phrasing.

 

From you context, I think you use the term "capture" for the entire ADC conversion process.  Probably more common in ADC work is to use that word for when the sample-and-hold "captures" the level on the ADC pin.

 

Signal impedance has nothing to do with how long a conversion takes.  See the datasheet discussion on the conversion process.  If you run the ADC clock too fast then the converter doesn't have enough time to do a decent job.  You decide.  As the datasheet says,

the successive approximation circuitry requires an input clock frequency between 50kHz and
200kHz to get maximum resolution.
 

How long does a conversion take?  From the datasheet,

A normal conversion takes 13 ADC clock cycles. The first conversion after the ADC is switched on (ADEN in
ADCSRA is set) takes 25 ADC clock cycles in order to initialize the analog circuitry.
 

What is the ADC clock?  From the datasheet,

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How do you wait until the ADC channel has finished capturing?  Busy-wait checking the register?  I also like to avoid busy-waiting, even for small applications, because I want to get practice with more scaleable and low power techniques, but maybe this is a place where it is an appropriate technique?

You don't have to wait...you know that after 15 instructions, the adc will be finished.

Start an ADC conversion

set up registers to display results

get some values to avg with the reading

do something you want to do...

get the ADC value...it is now ready

start the next conversion

....

 

So as long as you do something that takes at least 15 instructions between readings, there will never be any waiting around.

Just writing a value to an LCD will take many more than that.

ex: Start an adc, call a subroutine to read/debounce some switches, when that returns get your adc value

 

If my output impedance is below 10K, then am I *guaranteed* 13 clock cycles for a capture?

Your question means nothing. Upon command to start a conversion, it will...whether the adc pin is connected to air or Hoover dam.   For most accurate results, use <10k 

If you put a big cap on the adc pin, then the settling time when switching channels is your responsibility...the adc  could care less.

 

 

 

  

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Tue. May 1, 2018 - 03:29 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In one of my projects where I am using 8 ADC channels, I separate the starting of the ADC from the reading of the ADC.

I have an array that holds the last read value from each ADC and then read from that array when data is required.

uint16_t adc_data_array[8];

#define AREF_CONFIG ((0 << REFS1) | (1 << REFS0))  // Select AVcc reference

// ...

void start_next_ADC(void) {
    static uint8_t current_adc = 0;
    while (ADCSRA & (1 << ADSC)) {       // Wait for conversion completion (should never have to wait)
		} 
    adc_data_array[current_adc] = ADC;   // Save previous result in adc_data_array
    current_adc++;                       // Setup for next channel
    current_adc &= 0x07;                 // Roll over to channel 0
    ADMUX = AREF_CONFIG | current_adc;   // Select the next channel to convert
    ADCSRA = (1<<ADSC) | ADC_SETUP;      // Start conversion
}

uint16_t get_ADC_data(uint8_t ch) {
	return(adc_data_array[ch]);          // Return data from adc_data_array
}

In my program, start_next_ADC() is run every 50ms (but not within an interrupt), and the get_ADC_data() for all channels is run typically twice each second, so the oldest data is always  less than half a second "old".

 

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Those are ADC clock cycles, hence "scaled". With a few exceptions, the 13 cycles happen, no matter what.

 

Don't know what "output impedance" has to do with it. Comments in the spec sheet about source impedance have to do with the charging of the sample/hold cap inside the input of the ADC. 

 

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:
You don't have to wait...you know that after 15 instructions, the adc will be finished.

???  Which 15 instructions would that be?  [see the picture above, and remember that at e.g. a /64 prescaler every ADC clock cycle is 64 "insturctions".  Instructions in quotes because some instructions are more than one CPU cycle]  Your statement is very misleading at best.

 

Yes, one can do other stuff while a conversion is in progress.  Yes, if you have e.g. a periodic timer tick of a few millisecond then your conversion result will be waiting for you.  It depends on the app.  Mostly I use interrupt-driven ADC, and round-robin channel conversion.  But some apps I'll use the "start a new conversion each timer "tick" after grabbing the result of the previous".  In some apps, I'll indeed sit in one place with the polled read_adc() or equivalent.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sometimes I simply put the ADC in "free running mode" when only one channel is needed.

Then you only have to read the ADC register to get the latest value.

 

As an alternative, you can write a function to first read the ADC value, and then re-start a new conversion (on possibly another channel) before returning.

You can add a small static array to buffer all channels so the ADC channel you want to read does not have to be from the latest conversion.

This is very similar to Frog_Jr #9, with the main difference that start_next_ADC() and get_ADC_data() are combined into a single function, but it also gets rid of the the global adc_data_array[] because you can make it static within your ADC() function.

 

And what is this?

forkandwait wrote:

// ISR for TC0 CTC .
ISR(TIMER0_COMPA_vect){
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        tick++;
    }
}

Interrupts are by default disabled in an interrupt, so an extra ATOMIC_BLOCK is not needed.

You also have an ATOMIC_BLOCK (commented out) around your "hbeat" variable, but hbeat is a local variable in main and it is also not declared as being volatile.

It seems that you are trying to use the ATOMIC_BLOCK in a weird way, or do not completely understand what it is / how to use it.

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Tue. May 1, 2018 - 04:12 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

While it can be overclocked, for 10 bit readings the ADC needs to be run as close to 200kHz as possible. Because it takes 13 clock to convert that means it is 200kHz / 13 = 15.3kHz for the sample rate. The reciprocal of that means 66us. So in theory the "best time" you can achieve for a 10 bit reading is 66us. Now it will depend on your F_CPU but at 1MHz that is obviously 66 cycles. At 8MHz it is 528 cycles. At 16MHz it is 1056 cycles and so on. Like Lee asked:

theusch wrote:
??? Which 15 instructions would that be?

First off "instruction != cycle" (some are 1, some are 2 and some are more) but even if you talked about single cycle instructions you have to "wait" (or rather "get on with other stuff") for an awful lot more than 15.

 

Of course it is stated that if you don't need all 10 bits you can run faster. I think evidence has shown that you get a reasonable 8 bits at about 1MHz for the ADC clock. At that speed a conversion is 1MHz / 13 = 76.9kHz and the time is 13us. So, yeah, if the F_CPU was also 1MHz I suppose you might then be in the realms of 15 cycles (well 13) but otherwise there are likely a lot more cycles/instructions between ADC readings being available.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So in theory the "best time" you can achieve for a 10 bit reading is 66us. Now it will depend on your F_CPU but at 1MHz that is obviously 66 cycles.

PEDANT: 'In theory', yes.  However, since the ADC clock can only be prescaled from the system clock, the closest match for a 1 MHz system clock is a 125kHz ADC clock (1MHz / 8).  13 cycles will be 104 us. /PEDANT

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do you know I was even thinking about that very thing (can you actually hit 200kHz for a given clock) as I typed that but I thought "best not complicate things". It's a curious fact of life that sometimes it pays to reduce F_CPU to some 200kHz binary multiple rather than increase it so the 200kHz can be achieved. Flies in the face of the theory of "must go as fast as possible to run ADC as fast as possible" that might be a natural thought.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
It's a curious fact of life that sometimes it pays to reduce F_CPU to some 200kHz binary multiple ...

Indeed there are apps where you want to run the ADC as fast as practical.  But in practice any convenient ADC speed is practical for my apps.  Most typical is probably magic crystal speeds of 3.68 or 7.37 MHz, and an ADC clock of 57600.  That gives about 250us with ISR overhead, or about 4ksps.  For fast ADC work an AVR8 isn't the right tool anyway, is it?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You don't have to wait...you know that after 15 instructions, the adc will be finished.

 

???  Which 15 instructions would that be?  [see the picture above, and remember that at e.g. a /64 prescaler every ADC clock cycle is 64 "insturctions".  Instructions in quotes because some instructions are more than one CPU cycle]  Your statement is very misleading at best.

My goodness, forgot about the prescaler...looks like it could often be roughly in the range of 1100-1300 single-cycle instructions (say 15/200kHz *16MHz).  Basically if you have something going on that takes more than, say 80us, you can probably use it as a
"spacer" between ADC reads, that was the idea I was trying to present. So begin a conversion, do the slow things, by then the conversion will always be ready.

 

 

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:
For fast ADC work an AVR8 isn't the right tool anyway, is it?
Depends on the AVR.

megaAVR 0-series have a faster ADC clock plus an ADC accumulator to reduce the CPU load.

XMEGA AU AVR have a pipeline ADC for speed plus DMA to reduce the CPU load.

 

http://www.microchip.com/promo/atmegaavr-family

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gchapman wrote:
Depends on the AVR.

That's why I said "AVR8", indicating "classic" models.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.