Changing ADC channels in free-running mode

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

I just ran out of time* on a school project, and am wondering about something. I implemented a free-running ADC loop that changed channels between each conversion. Having used AVR Freaks extensively as a reference (excellent tutorials, btw!), I have read a few comments about having to wait a few cycles for transient responses to settle after changing the ADC MUX, but the only reference to this I found in the datasheets, that for the ATmega32(L), in my case, was for differential voltage measurements.

Atmel, on pg. 207, wrote:
Special care should be taken when changing differential channels. Once a differential channel has been selected, the gain stage may take as much as 125 µs to stabilize to the new value. Thus conversions should not be started within the first 125 µs after selecting a new differential channel. Alternatively, conversion results obtained within this period should be discarded. The same settling time should be observed for the first differential conversion after changing ADC reference (by changing the REFS1:0 bits in ADMUX).

Does someone have experience with getting bad measurements in single-ended conversions, as I have implemented it?

uint8_t *delay_adc_4chan(uint8_t *chan, uint8_t *amux, uint8_t *data, uint8_t pre, uint16_t t)
{
        PORTC = amux[0];	// set external (analog) MUX selects
        while(!(TIFR & (1<<OCF1A))) {};	// wait for timer1A
        TIFR |= (1<<OCF1A);
        ADCSRA |= (1<<ADSC);	// start 1st conversion
        
        uint8_t i=0;
        do {
                _delay_loop_1(1.5*pre/3);
                ADMUX = chan[i];
                while(!(ADCSRA & (1<<ADIF))) {};
                PORTD |= amux[i];	// change analog mux selects
                data[i] = ADCH;
        } while (++i<4);
        
        OCR1A = t;
        return data;
}

There's also a bit of code where I change which signal to let through an external analog multiplexer - this chip has ns-response and settle time, so I don't even take its error addition into account.
And yeah, I know, returning a pointer that was an argument is redundant. :P
*ie: it was not completed, so I wasn't able to test this out for myself.

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

Quote:
but the only reference to this I found in the datasheets, that for the ATmega32(L), in my case, was for differential voltage measurements.

But this specifically says "differential channels". I don't think that it applies to single-ended conversions.

When changing channels in free-running mode, you will always be one channel off since the conversion has already started before you change channels. Also, your _delay_loop_1 does nothing for you, again because the conversion has already started. In fact, it could hurt since if there is a settling time, you have just given the channel less time to settle.

        while(!(TIFR & (1<<OCF1A))) {};   // wait for timer1A

And when was the timer started?

        PORTC = amux[0];   // set external (analog) MUX 
...
                PORTD |= amux[i];   // change analog mux selects 

So how many analog channels do you have?

Regards,
Steve A.

The Board helps those that help themselves.

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

If you are going to change channels you might want to review again whether you really want to use free running anyway - what does it actually "buy you"? When not just:

set ADMUX
set ADSC
wait ADSC=0
read
loop

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

Koshchi wrote:
When changing channels in free-running mode, you will always be one channel off since the conversion has already started before you change channels. Also, your _delay_loop_1 does nothing for you, again because the conversion has already started.
Yes, I know about the channel lock during a conversion - actually took advantage of it by waiting for the lock to take place, 1.5 ADC clock cycles into the conversion (after sample & hold), then setting the next one. _delay_loop_1 waits for these 1.5 ADC clock ticks. I have already set the 1st channel during ADC initialization.
Quote:

        while(!(TIFR & (1<<OCF1A))) {};   // wait for timer1A

And when was the timer started?

Less than 100ms before the function call, by main(). I was trying to capture a specific part of a transient signal. It was the shadow of a small particle falling across the face of an array of photodiodes (quad photodiode).
Quote:

        PORTC = amux[0];   // set external (analog) MUX 
...
                PORTD |= amux[i];   // change analog mux selects 

So how many analog channels do you have?

Whoops! Messed up which ports to use. There were 28 signals. This specific function would only cycle through 4, though.
clawson wrote:
If you are going to change channels you might want to review again whether you really want to use free running anyway - what does it actually "buy you"?
You're absolutely right - it doesn't buy me much time at all. I was trying to be able to tell exactly how many clock cycles this function call would take, and, since each free-running loop is 14 ADC ticks, I didn't have to time it, as well. These kind of shenanigans are an invitation for hidden transients and difficult input/output verification. I was wondering if someone had run actually done the ip/op verification and found that they could not read from different channels between each free-running sample, reliably.