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 http://www.0x0000ff.com/src/main.c 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 by cassiope●November 20, 20082008-11-20
On Nov 20, 10:03=A0am, Simon <goo...@gornall.net> wrote:
> 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=3D1, 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 athttp://www.0x0000ff.com/src/main.cif
> 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=3DADC7, ch2=3DADC6,
> ch3=3DADC5 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=3DADC-1, ch2=3DADC-2, ch3=3DADC-3 and I saw the pot's val=
ue
> 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,
> =A0 Simon
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 by BlueDragon●November 20, 20082008-11-20
Simon wrote:
> 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 http://www.0x0000ff.com/src/main.c if
> you're interested...
Are you sure the ADC can handle this sampling rate? Check your settings
against ADC clock limits and conversion period.
> So, am I being unreasonable, asking the ADC to switch at 32kHz ? Am I
> just doing it wrong ? Any ideas very welcome :)
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 by Simon●November 20, 20082008-11-20
On Nov 20, 11:22=A0am, BlueDragon <easy2f...@the.net> wrote:
> Simon wrote:
> > 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=3D1, 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 athttp://www.0x0000ff.com/src/main.cif
> > you're interested...
>
> Are you sure the ADC can handle this sampling rate? Check your settings
> against ADC clock limits and conversion period.
>
> > So, am I being unreasonable, asking the ADC to switch at 32kHz ? Am I
> > just doing it wrong ? Any ideas very welcome :)
>
> 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.
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.
>
> 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.
Yeah, I've tried a few variations on the theme before posting. Agreed
on the wait. The next plan was to use the ADC-result-ready ISR to
just (1) store the value in the correct buffer, (2) switch the MUX,
(3) start a new conversion. I didn't think that would solve my MUX
issue though, so thought I'd post the code...
On Nov 20, 10:52 am, cassiope <f...@u.washington.edu> wrote:
>
> You don't say which uC you are using...
It's a Mega644
> 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.
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 by David Brown●November 20, 20082008-11-20
Simon wrote:
> On Nov 20, 11:22 am, BlueDragon <easy2f...@the.net> wrote:
>> Simon wrote:
>>> 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 athttp://www.0x0000ff.com/src/main.cif
>>> you're interested...
>> Are you sure the ADC can handle this sampling rate? Check your settings
>> against ADC clock limits and conversion period.
>>
>>> So, am I being unreasonable, asking the ADC to switch at 32kHz ? Am I
>>> just doing it wrong ? Any ideas very welcome :)
>> 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.
>
> 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.
>
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 by Vladimir Vassilevsky●November 20, 20082008-11-20
Simon wrote:
> 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 http://www.0x0000ff.com/src/main.c if
> you're interested...
[...]
> So, am I being unreasonable, asking the ADC to switch at 32kHz ? Am I
> just doing it wrong ? Any ideas very welcome :)
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
http://www.abvolt.com
Reply by Simon●November 21, 20082008-11-21
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 by ●November 21, 20082008-11-21
On 20 Nov, 18:03, Simon <goo...@gornall.net> wrote:
> 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 athttp://www.0x0000ff.com/src/main.cif
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 by Simon●November 21, 20082008-11-21
On Nov 21, 8:54=A0am, iai...@googlemail.com wrote:
> On 20 Nov, 18:03, Simon <goo...@gornall.net> wrote:
>
> > 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=3D1, 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 http://www.0x0000ff.com/src/main.cif
>
> I have used a number of AVRs and you seem to be using free running
> mode.
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.
> 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 =A0change 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....!
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...
> 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.
Now that's just frustrating :)
Cheers,
Simon.
Reply by Bob●November 21, 20082008-11-21
Simon wrote:
> 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
>
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 http://www.teranews.com **
Signal Processing Engineer Seeking a DSP Engineer to tackle complex technical challenges. Requires expertise in DSP algorithms, EW, anti-jam, and datalink vulnerability. Qualifications: Bachelor's degree, Secret Clearance, and proficiency in waveform modulation, LPD waveforms, signal detection, MATLAB, algorithm development, RF, data links, and EW systems. The position is on-site in Huntsville, AL and can support candidates at 3+ or 10+ years of experience.