ADC Accuracy Issue

Hey Everyone,

Somewhat new to AVR programming. Recently I was customizing an analog read function to speed up my main loop for my midi controller. I’m using Arduino and the included analogRead() function is does not use interrupts. Once you request a reading on a pin it will wait until the A/D conversion is complete before it returns a value. Unfortunately it is slowing down my loop too much, even just 1 read per main loop cycle is too slow. So based on the Arduino code, I basically just split it up so that while the conversion is happening my loop continues until a bit changes in the ADSRA register which I use as a flag to execute certain blocks of code. My main loop is 50% faster, however now certain voltage readings are less accurate and vary more than they should. Does anyone know why?

Here is the ADC function together, which works fine (just slower):

#if defined(ADCSRB) && defined(MUX5)

// the MUX5 bit of ADCSRB selects whether we’re reading from channels
// 0 to 7 (MUX5 low) or 8 to 15 (MUX5 high).

ADCSRB = (ADCSRB & ~(1 << MUX5)) | (((analogPin >> 3) & 0x01) << MUX5);
#endif

// set the analog reference (high two bits of ADMUX) and select the
// channel (low 4 bits). this also sets ADLAR (left-adjust result)
// to 0 (the default).

#if defined(ADMUX)
ADMUX = (DEFAULT << 6) | (analogPin & 0x07);
#endif

// start the conversion

sbi(ADCSRA, ADSC);

while(bit_is_set(ADCSRA, ADSC) {
}

low2 = ADCL;
high2 = ADCH;
//value gets put into a variable
value = (high2 << 8) | low2;

However if I split it up like below, my voltage readings vary more than they should for a few seemingly arbitrary pots on my unit (like -+10 units, as opposed to like 2 which is usually the max they deviate when you read analog pins). This is what I did to split up the process and free up my loop:

if (readBlock) {
//this function is just the code before the while loop in the previous example nestled into a function
getValue1(channel, whichAnalogPin);
//blocks the function from being triggered again during conversion
readBlock = 0;
}

if (readBlock 0 && (bit_is_set(ADCSRA, ADSC)) 0) {

low = ADCL;
high = ADCH;
value = (high2 << 8) | low2;

//opens up the previous loop while closing this one

readBlock = 1;
}

Sorry for the bad formatting there, i’ll clean it up after dinner.

I don’t fully understand your code reorganization, so assume it is correct - for example I assume that ADC registers are not messed up with while a conversion is already taking place.

Even if your code is correct:

If pins are toggled on/off during the conversion (explicitly or by your code or through one of the built-in peripherals), this might inject noise - either through capacitive coupling of adjacent traces, or by causing fluctuations of the power supply line powering the MCU / the ADC reference or ground currents (for example if LEDs blink while the conversion happens… baaad!), or just internally in the AVR.

If you want to keep using this unblocking approach, you’ll need a couple of changes on the software side to deal with the increase in ADC noise - the first one would be to low pass filter the ADC readings.

If you want to get back to a blocking model, it might still be possible to get a speed boost by increasing the speed of the ADC clock - but it’ll cost you accuracy too.

Thank you so much pichenettes This was in fact the problem! You are so helpful!

Toggling pins during the conversion process was in fact the cause of the fluctuations. I just disabled the digital/read writes during the conversion process, but the loop still runs, so now everything is working great and running much faster.

Curious though, if toggling digital pins injects noise then why is serial in/out not affecting it too? Isn’t that also just a digital pin being toggled? I am running lots of midi data through the arduino while the ADC is taking place and it is perfectly stable, yet toggling any other pins than the RX/TX causes issues. Why is that?

> Isn’t that also just a digital pin being toggled?

Yes, though a property of MIDI is that it doesn’t tax the processor that much in terms of current (short bursts of 5mA if your circuitry is correctly designed). It could also be that the MIDI transmission code uses some buffering behind the scenes - causing the data to be sent at times the ADC conversion is finished. Or it could be that the switching noise created by the toggling of the serial output pin has a very high frequency and that the ADC is only sensitive to low frequency noise - or the opposite. Or a board layout thing - when you toggle a pin, you cause something to happen (a big change of current consumption that is not damped by bypass capacitors; strong returns current flowing to the MCU) in an area of the board in which the pots are located.

> yet toggling any other pins than the RX/TX causes issues. Why is that?

What are the pins you are toggling connected to? For example if they are directly hooked to LEDs and cause big changes in the current consumption of the MCU, this could be the explanation…

If we want to know more… then we’d need the schematics of the atmega :slight_smile:

Very informative answer. Those are all important things to keep in mind. Thanks again!