I've been having trouble getting an audio project to work so I've resorted to an empirical testing mode.
I want to read 2 ADC data channels in free running mode, changing channel only after a buffer is full. Each channel has a buffer of 256 bins. I have an ISR that triggers on each conversion, writes the 8-bit ADC value (ADCH only, with ADLAR = 1) to the buffer and increments a counter. Once the counter hits the end, it resets to zero and the channel is changed. Once both channel buffers are full, a flag to output the buffers is set. The buffer output is handled in the main loop and currently dumps the contents to the serial console.
The problem I have is that the second channel is always garbage. I've tried swapping the physical inputs, reordering the channels and using different input channels. The result is always the same. Random values in the 2nd channel, regardless of which channel I started with.
I've tried moving the channel change out of the ISR and into the main loop but it has no effect. Neither does increasing the prescaler.
I've run out of ideas. I've not used interrupts before and similar projects just serial poll the ADCs but I've read through the datasheet and can't figure out why it doesn't work. I've tried clearing and enabling the ADSC bit prior to and after the channel change, clearing and setting ADEN, but no joy.
Anyone else tackle this problem before?
The vars are:
#define SAMPLES 256 // Number of FHT input bins per channel.
#define CHANNELS 2 // Number of audio channels.
volatile int8_t ADC_buffer[CHANNELS][SAMPLES]; // Ring buffers for ADC data.
volatile uint8_t ADC_channel; // Keeps track of channel.
volatile uint16_t ADC_sample; // Keeps track of samples.
volatile boolean ADC_buffer_full; // Flags buffer is full.
const uint8_t admux[CHANNELS] = { ADC0D, ADC1D }; // ADC MUX channels.
The setup code is:
void setup( void )
{
// ADCSRA.
ADCSRA = B10101011;
// ADEN - 1 // Enable ADC.
// ADSC - 0 // Disable conversions.
// ADATE - 1 // Enable ADC auto trigger.
// ADIF - 0 // Set by hardware
// ADIE - 1 // Enable interrupt.
// ADPS2 - 0 // }
// ADPS1 - 1 // }- ADC prescaler = 8.
// ADPS0 - 1 // }
// ADCSRB.
ADCSRB = B00000000;
// N/A //
// ACME - 0 // Disable Analogue Comparator Mux.
// N/A //
// N/A //
// N/A //
// ADTS2 - 0 // }
// ADTS1 - 0 // }- Free Running Mode.
// ADTS0 - 0 // }
// ADMUX.
ADMUX = B01100000;
// REFS1 - 0 // }- Use Vcc as reference.
// REFS0 - 1 // }
// ADLAR - 1 // Left adjust ADC results (8-bit mode).
// N/A //
// MUX3 - 0 // }
// MUX2 - 0 // }- ADC input 0.
// MUX1 - 0 // }
// MUX0 - 0 // }
// Turning off some functions can reduce power consumption and reduce noise.
//
// Timer0 is an 8-bit timer used for Arduino functions such as delay(),
// millis() and micros().
// DIDR0 and DIDR1 are digital input buffers that share circuitry with the
// ADC. Disabling them reduces noise.
// TIMSK0.
TIMSK0 - B00000000;
// N/A //
// N/A //
// N/A //
// N/A //
// N/A //
// OCIE0B - 0 // Disable timer output compare B interrupt.
// OCIE0A - 0 // Disable timer output compare A interrupt.
// TOIE0 - 0 // Disable timer overflow interrupt.
// DIDR0.
DIDR0 = B00111111;
// N/A // }- Pins ADC7D & ADC6D do not have input buffers.
// N/A // }
// ADC5D - 1 // Disable digital input ADC5.
// ADC4D - 1 // Disable digital input ADC4.
// ADC3D - 1 // Disable digital input ADC3.
// ADC2D - 1 // Disable digital input ADC2.
// ADC1D - 1 // Disable digital input ADC1.
// ADC0D - 1 // Disable digital input ADC0.
// DIDR1.
DIDR1 = B00000011;
// N/A //
// N/A //
// N/A //
// N/A //
// N/A //
// N/A //
// ACIS1 - 1 // Disable digital input AIN1D.
// ACIS0 - 1 // Disable digital input AIN0D.
// Clear buffer.
memset( (void *) ADC_buffer, 0, sizeof( ADC_buffer ));
// Set up counters and flags.
ADC_sample = 0;
ADC_channel = 0;
ADC_buffer_full = false;
ADC_change_channel=false;
cycle = 0;
Serial.begin( 115200 );
startADC(); // Start conversions.
}
and the ISR is:
ISR( ADC_vect )
{
// Fill next available buffer slot with ADC value.
ADC_buffer[ADC_channel][ADC_sample] = ADCH - 0x80; // Read ADC value.
// If the ADC buffer for the channel is full, switch channel.
if ( ++ADC_sample >= SAMPLES )
{
ADC_sample = 0;
if ( ++ADC_channel >= CHANNELS )
{
ADC_channel = 0;
ADC_buffer_full = true; // ADC sample buffer is full.
}
// Switch to next channel.
ADMUX = ( ADMUX & ~0x0f ) | ( admux[ADC_channel] & 0x0f );
}
}