4
\$\begingroup\$

I'm making a small level meter using an ATtiny microcontroller. It uses a linear 5K potentiometer as a voltage divider coupled to an ADC pin. So far it's working except the digital values don't seem to correlate well with the measured voltage.

The schematic:

schematic

simulate this circuit – Schematic created using CircuitLab

The relevant code:

#define F_CPU 1000000
#include <avr/io.h>
#include <util/delay.h>

int adc_value;

void ADC_init()
{
    // Enable ADC
    ADCSRA |= (1 << ADEN);

    // Set ADC reference to AVCC
    ADMUX  |= (1 << REFS0);

    // Prescaler /8, 125kHz ADC sample rate @ 1MHz
    ADCSRA |= (0 << ADPS2) | ( 1 << ADPS1) | (1 << ADPS0);

    // Auto trigger enable
    ADCSRA |= (1 << ADATE);

    // Use input channel ADC2 (PB4)
    ADMUX |= (1 << MUX1) | (0 << MUX0);

    // Left shift ADC result
    ADMUX  |= (1 << ADLAR);

    // Start converting
    ADCSRA |= (1 << ADSC);
}

int main(void)
{
    DDRB = 0x07;
    PORTB = 0x00;

    ADC_init();

    while(1)
    {
        adc_value = ADCH;

        if (adc_value < 64)
            // output A
        else if (adc_value < 128)
            // output B
        else if (adc_value < 192)
            // output C
        else
            // output D
    }
}

I measured the voltage at PB4, and noted the points at which the output changed:

  • Minimum: 196 mV
  • 64: ~235 mV
  • 128: ~500 mV
  • 192: ~770 mV
  • Maximum: 2.849 V

I expected approximately 1.5 V to be where a digital value of 128 is obtained, not 500 mV.

If I understand correctly, the ATtiny13A has a 10-bit ADC, the values from which are stored in two registers, ADCH and ADCL. With ADLAR (the left adjust) enabled, the value is shifted into the ADCH register and effectively two bits are discarded.

The datasheet indicates:

If the result is left adjusted and no more than 8-bit precision is required, it is sufficient to read ADCH.

I'm confused about why I am getting a nonlinear conversion on the ADC. Can you explain what I may be overlooking?

\$\endgroup\$
11
\$\begingroup\$

You state that at 128, you measure about 500mV. This means at full scale (256) you measure about 1V.

According to the datasheet (14.12) with:

// Set ADC reference to AVCC
ADMUX  |= (1 << REFS0);

You actually do the following:

This bit selects the voltage reference for the ADC, as shown in Table 14-2. If this bit is changed during a conversion, the change will not go in effect until this conversion is complete (ADIF in ADCSRA is set).

Table 14-2.

Voltage Reference Selections for ADC
REFS0   Voltage Reference Selection
0       VCC used as analog reference.
1       Internal Voltage Reference.

Paragraph 14.3 states:

The ADC converts an analog input voltage to a 10-bit digital value through successive approximation. The minimum value represents GND and the maximum value represents the voltage on VCC or an internal 1.1V reference voltage.

So in conclusion, you are setting the ADC reference voltage to 1.1V and with that knowledge your findings are pretty accurate.

\$\endgroup\$
  • \$\begingroup\$ This is exactly it. Thanks for elaborating not only on what setting is wrong, but what it's doing which explains the measurements. \$\endgroup\$ – JYelton Oct 22 '13 at 6:40
4
\$\begingroup\$

The comment doesn't match the code.

// Set ADC reference to AVCC
ADMUX  |= (1 << REFS0);

But datasheet says:

enter image description here

\$\endgroup\$
  • \$\begingroup\$ This appears to be the problem. The code is from avrfreaks but applies to an ATmega128. I didn't think to check whether the voltage reference selection was different. \$\endgroup\$ – JYelton Oct 22 '13 at 6:39

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.