AVR ADC multiplexer

Hi all,

I'm trying to sample the AVR's ADC on 3 channels at 8kHz, 8kHz and

16kHz respectively. I've set up a 32kHz interrupt on an 8MHz clock using timer0 (prescale=1, count to 250), and within the timer interrupt handler, I have a state machine which samples ch-1, ch-2, ch-3, ch-2. The code is at
formatting link
if you're interested...

To test it, I put a variable resistor between 0 and AVCC and wired it up to ch-1 - it all worked fine, twiddling the potentiometer caused me to get values between 0x00 and 0xFF. The problem is that changing which pin I linked the potentiometer up to *still* gave values on ch-1. Any of the inputs I selected with the mux would always read the same value as the pot.

I verified it wasn't a hardware problem (linking ch1=ADC7, ch2=ADC6, ch3=ADC5 and putting the pot on ADC-1 showed no reading. Putting the pot on any of ADC-7, ADC-6, ADC-5 showed the reading on ADC-7. Then I switched so ch1=ADC-1, ch2=ADC-2, ch3=ADC-3 and I saw the pot's value on any of ADC-1, ADC-2, ADC-3).

So, am I being unreasonable, asking the ADC to switch at 32kHz ? Am I just doing it wrong ? Any ideas very welcome :)

Cheers, Simon

Reply to
Simon
Loading thread data ...

ue

You don't say which uC you are using... I think that most of the AVRs cannot support this acquisition rate. Usually they have a limit of 200kHz on the ADC clock, and a minimum of 13 clocks for each conversion.

Sorry, didn't look at your code enough to see why your multiplexer switching was broken.

-f

Reply to
cassiope

Are you sure the ADC can handle this sampling rate? Check your settings against ADC clock limits and conversion period.

Your problem might result from wrong definition of buffer access macros

- make sure you use "volatile" keywrd when necessary. Also, if you use AVR-GCC, examine the compiler output carefully (.lss file). You may encounter some surprises there - "overoptimized" code is quite common.

One basic idea about your program, not directly related to your problem: DO NOT put any waiting loop in the interrupt service routine. The timer interrupt routine should read and store the result of previous conversion, start the next one and exit. You will get the result of just-started conversion on the next interrupt.

Reply to
BlueDragon

I will look at the assembly... I thought the 'volatile' was only necessary when accessing variables both inside *and* outside the ISR, though. At the moment, I push the ADC value into a circular buffer from within the loop, which should just copy and increment a buffer pointer.

Yeah, I've tried a few variati>

It's a Mega644

I thought you could trade accuracy for sample-rate. I'm only using 8- bits of resolution here (the sampled audio doesn't have to sound great :), so I thought I could push the sample rate higher. I guess that's part of what I'm asking - is that true ? Or do I just have crap code ? :)

Cheers, Simon

Reply to
Simon

You haven't posted code for the cb* types and functions, making it difficult to check them. Typically, you'll want to make the buffer pointers volatile but not the buffer itself.

Reply to
David Brown

[...]

  1. The internal ADC of AVR can be set to work as you described, although the accuracy will be down to 8 bits or so.
  2. Your code exposes the great deal of inexperience. Apparently there are some silly bugs.

Vladimir Vassilevsky DSP and Mixed Signal Design Consultant

formatting link

Reply to
Vladimir Vassilevsky

Ok, so here's a followup - the code now looks like:

/ ****************************************************************************** \ |* ADC has finished a conversion, store it and trigger another \******************************************************************************/ ISR (ADC_vect) { static uint8_t channel = SAMPLE_LEFT;

switch (channel) { case SAMPLE_LEFT: __adc[SAMPLE_LEFT] = ADCH; PORTB = ADCH; channel = SAMPLE_TC1; ADMUX = _BV(ADLAR) | TIMECODE_CHANNEL; ADCSRA |= _BV(ADSC); break;

case SAMPLE_TC1: __adc[SAMPLE_TC1] = ADCH; channel = SAMPLE_RIGHT; ADMUX = _BV(ADLAR) | RIGHT_AUDIO_CHANNEL; ADCSRA |= _BV(ADSC); break;

case SAMPLE_RIGHT: __adc[SAMPLE_RIGHT] = ADCH; channel = SAMPLE_TC2; ADMUX = _BV(ADLAR) | TIMECODE_CHANNEL; ADCSRA |= _BV(ADSC); break;

default: case SAMPLE_TC2: __adc[SAMPLE_TC1] = ADCH; channel = SAMPLE_LEFT; ADMUX = _BV(ADLAR) | LEFT_AUDIO_CHANNEL; ADCSRA |= _BV(ADSC); break; } }

/ ****************************************************************************** \ |* Timer-0 overflow. Either change the ADC MUX or sample a value \******************************************************************************/ ISR (TIMER0_COMPA_vect) { static uint8_t state = SAMPLE_LEFT; / ************************************************************************** \ |* State machine for setting up samples, and sampling \**************************************************************************/ switch (state) { case SAMPLE_LEFT: state = SAMPLE_TC1; cbWrite(&_left, __adc[SAMPLE_LEFT]); break;

case SAMPLE_TC1: state = SAMPLE_RIGHT; cbWrite(&_tc, __adc[SAMPLE_TC1]); break;

case SAMPLE_RIGHT: state = SAMPLE_TC2; cbWrite(&_right, __adc[SAMPLE_RIGHT]); break;

case SAMPLE_TC2: state = SAMPLE_LEFT; cbWrite(&_tc, __adc[SAMPLE_TC1]); break; } TCNT0 = 0; }

Note that PORTB is being set to the value of ADCH within the ISR, and that there's no loops waiting for anything any more - the ADC_vect is self-triggering once the first conversion has been requested.

In this situation, the LEDs light up for port-B no matter which input (TIMECODE_CHANNEL, LEFT_AUDIO_CHANNEL, RIGHT_AUDIO_CHANNEL) is connected to the potentiometer. I tried changing the ADC sample-rate (setting the ADC prescaler to 128 from 16) and the interrupt-sample- rate (setting timer0's prescaler to CLK/64 from CLK). No difference.

At this point, it looks as though the AVR is simply ignoring my request to change the MUX. I'm seriously thinking of getting 3 AT- Tiny's and making them do one channel each rather than try to mux it on the mega-AVR...

Simon

Reply to
Simon

I have used a number of AVRs and you seem to be using free running mode. Ypu will need to be careful about when you change the MUX. The MUX will only change after the current conversion completes. In free-running mdoe a conversion is in progress when you change the mux so the change won't happen until that conversion completes. If you use free-running mode you should use the ADC conversion complete interrupt and then change the mux but remember by the time you enter your ISR a new conversion will have started so you need to set the mux to the state you want for the conversion 2 after the one you are reading the result for....!

Either way I can confirm that you can get the ADC mux to work in AVRs you just have to read the datasheets carefully and understand what they are saying.

HTH

Iain

Reply to
iainjt

I'm not (well ADCSRA:ADATE is set to 0), I'm just re-triggering the ADC by setting ADCSRA:ADSC to 1 within the ADC_vect ISR. The ISR and timer functions are actually above.

Hmm - perhaps there's something in that, although I use an internal state machine in the ISR and set it to (state+1 % 4). The thing is that (without the auto-trigger firing), I think that ought to be correct. ADMUX is set first, then ADCSRA:ADSC is set to start the conversion.

I think it has to be an issue with the MUX, and nothing to do with any volatile variables because I'm setting PORTB (the leds) to the value of ADCH, and no matter which input I connect my potentiometer to (ADC5, ADC6, ADC7) , those leds light up as I rotate the pot.

It's a shame to dedicate an AVR as a 1-channel ADC, and it offends my sense of elegance, but they're so damn cheap that using 3 of them is probably the most efficient solution in terms of time...

Now that's just frustrating :)

Cheers, Simon.

Reply to
Simon

\******************************************************************************/

\******************************************************************************/

I don't understand why you use both the timer and the ADC interrupts: use the ADC complete if you want to "free run" at max rate _or_ use the timer for a controlled sample rate.

After that, try setting a breakpoint inside your ISR and step through the code. You may have to disable other interrupt sources to do it, but you should always step through all your code and test all execution paths. (that's _after_ it passes lint, of course).

One other thing: what do you connect to the other inputs while you move the pot around? You aren't leaving them floating, are you? Floating inputs are basically leaky capacitors and as you cycle it the MUX will happily charge all of them to the same voltage as your one low-impedance source.

Bob

** Posted from
formatting link
**
Reply to
Bob

Well, you have to understand that this is the Nth iteration of this particular code. When (N-2) fails, you try (N-1), then (N), ...

Also, I want 2 different sample rates (simplified somewhat by one being 2x the other) - I originally had everything happening in the timer ISR, at double the rate of the max sample frequency, so it would {(set the mux), (sample the value)} on alternating calls. I did it this way because I didn't want to wait 13 ADC clocks within the timer ISR...

Mmm. I'm on a Mac. There's not much in the way of debugging apart from printf() and LEDs...

[slaps head]. Gaaaah. Hopefully that's it. Yes, they're floating. Yes, that's stupid. I even had a hint: when I was dumping ADC6 to PORTB, but connected the pot to ADC7, I got slightly different results at the extremes of the pot value. I had tried using a multimeter to check there was no path between the pins on the STK500, but I'd not considered the capacitance.

Thanks :)

ATB, Simon

Reply to
Simon

I use the AVR JTAG ICE Mk II in my XP environment but I see reports on the web that people are using it on OSX w/ Win and a VM (e.g. VMWare Fusion). Compared to the $20k+ ICE of yore, a JTAG ICE for $300 is a steal. I'll try it on my macBook at home tonight. Bob

** Posted from
formatting link
**
Reply to
Bob

...

In C, 'volatile' is a shorthand for "Dear Compiler, please don't optimize away any read or write access to this variable. Its value might be changed behind your back, so you must perform _every_ read the programmer requested; its value might be used behind your back, so you must perform _every_ write."

The rule of thumb I used (and advised) when wearing my Compiler Vendor Technical Support hat was: Declare as 'volatile' any variable which is written-to inside an ISR and read-from outside that ISR, or read-from inside an ISR and written-to outside that ISR. Any variable accessed only inside a single ISR, or only outside all ISRs, will not benefit from 'volatile'.

If the ISR puts the value into the circular buffer, and not-that-ISR gets it from the buffer, then the buffer pointers _and_ the buffer itself need to be 'volatile'.

mlp

Reply to
Mark L Pappin

I think you're agreeing with me. At least, you're spelling out my understanding of 'volatile' too :)

They are, but that didn't affect matters - as I mentioned above, just immediately pushing the adc-sampled value onto the 8 leds as a byte showed the exact same issue, and that's totally internal to the ISR. I think Bob nailed it with the stray capacitance, but I'll check when I get home...

Cheers. Simon

Reply to
Simon

t

Hmm, if that works (I don't see why it wouldn't), I might splash the $35 on

formatting link
which claims to do the same thing...

"Program and debug your Atmel AVR Microcontroller project with our great new JTAG interface. This unit plugs directly into the target board for controlling and programming your AVR device. Works with the free AVR Studio IDE and allows for single stepping through your program, viewing register values and also for programming the microcontroller. Find program errors quickly and easily, with this easy to use debugging tool."

Reply to
Simon

I don't know anything about the futurlec box, but I can report that WniXP in Parallels runs AVR Studio and connects to the AVR JTAG ICE MkII with no problems at all.

Gee, for $35 I might get one of those ET boxes myself for home.

Bob

** Posted from
formatting link
**
Reply to
Bob

OTOH: buyer beware:

formatting link
these guys get pretty awful ratings. Bottom line seems to be: don't expect your stuff to be shipped for a while. Bob

** Posted from
formatting link
**
Reply to
Bob

gh

om

n
I

Hmm - I've not run into that myself. I've ordered from them before and it took just under 2 weeks to get here (CA). YMMV of course... I don't work for them, but I have bought from them before...

Simon

Reply to
Simon

For what it's worth, and to close the loop, it was indeed the stray capacitance that was causing me grief. Looks like the code worked just fine :)

I connected up 3 potentiometers (one on each of the ADC channels I'm sampling from), and the LEDs only react to one of the POT's being turned.

Thanks for the help :)

Simon

Reply to
Simon

And to close the other loop, the Futurlec device turned up after 12 days...

Simon

Reply to
Simon

ElectronDepot website is not affiliated with any of the manufacturers or service providers discussed here. All logos and trade names are the property of their respective owners.