NerdKits - electronics education for a digital generation

You are not logged in. [log in]

NEW: Learning electronics? Ask your questions on the new Electronics Questions & Answers site hosted by CircuitLab.

Microcontroller Programming » Why am I only geting a ADC: range of 0 to 500?

January 12, 2013
by Ralphxyz
Ralphxyz's Avatar

I have a joystick on my ADC pin so I am getting a full 0 to 5 volt range

but my code is on showing me 0 to 500 not 0 to 1024.

I know it is something I did as this is just expanded tempsensor code that stabilizes the ADC output thanks to Paul (Noter) and bretm (whom I really miss) here is a great discussion on ADC stabilization. Paul remember that discussion?

bretm and Paul got into some mathematicall analysis of my ADC signal which is still way over my head, but was really fascinating.

Ok here is my code:

    // tempsensor.c tempsensor-328
    // for NerdKits with ATmega168
    // mrobbins@mit.edu

    #define F_CPU 14745600

    #include <stdio.h>
    #include <math.h>

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h>
    #include <inttypes.h>

    #include "../libnerdkits/io_328p.h"
    #include "../libnerdkits/delay.h"
    #include "../libnerdkits/lcd.h"
    #include "../libnerdkits/uart.h"

    // PIN DEFINITIONS:
    //
    // PC0 -- temperature sensor analog input

    void adc_init() {
        // set analog to digital converter
        // for external reference (5v), single ended input ADC0
        ADMUX = 0;

        // set analog to digital converter
        // to be enabled, with a clock prescale of 1/128
        // so that the ADC clock runs at 115.2kHz.
        ADCSRA = (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

        // fire a conversion just to get the ADC warmed up
        ADCSRA |= (1<<ADSC);
    }

    uint16_t adc_read() {
        // read from ADC, waiting for conversion to finish
        // (assumes someone else asked for a conversion.)
        // wait for it to be cleared
        while(ADCSRA & (1<<ADSC)) {
            // do nothing... just hold your breath.
        }
        // bit is cleared, so we have a result.

        // read from the ADCL/ADCH registers, and combine the result
        // Note: ADCL must be read first (datasheet pp. 259)
        uint16_t result = ADCL;
        uint16_t temp = ADCH;
        result = result + (temp<<8);

        // set ADSC bit to get the *next* conversion started
        ADCSRA |= (1<<ADSC);

        return result;
    }
    //*
    double sampleToFahrenheit(uint16_t sample) {
        // conversion ratio in DEGREES/STEP:
        // (5000 mV / 1024 steps) * (1 degree / 10mV)
        //  ^^^^^^^^^^^      ^^^^^^^^^^
        //     from ADC       from LM34
        return sample * (5000.0 / 1024.0 / 10.0);  
    }
    //*/
    static double lastGoodAdc;          // last known good reading

    static int8_t goodReadingCount;     // count and sign of potential new good readings

    static uint16_t adc_read_smooth();  // use in place of adc_read     This is to stabilize the LCD reading

    static uint16_t adc_read_smooth()   // use in place of adc_read
    {
        uint16_t adc = adc_read();      // get a noisy reading

        if (adc == lastGoodAdc)         // if it hasn't changed
            return adc;                 // just return it again

        // check if it's crossing back and forth
        if (goodReadingCount != 0 && ((adc > lastGoodAdc) == (goodReadingCount < 0)))
            //if ((adc > lastGoodAdc) == (goodReadingCount < 0))
            {
            goodReadingCount = 0;      // it's noise, so reset the counter
            return lastGoodAdc;        // and return the last good reading
            }

        if (adc > lastGoodAdc)         // if it's great than before
            {
            //if (++goodReadingCount == 127) // and has been greater 127 times
            //if (++goodReadingCount == 25) 
            if (++goodReadingCount == 10) 
                {                              // without ever being less
                    lastGoodAdc = adc;     // then the new value is good
                    goodReadingCount = 0;  // and we can start over
                }
            }
        else                           // if it's less than before
            {
            //if (--goodReadingCount == -127) // and has been less 127 times
            //if (--goodReadingCount == -25)
            if (--goodReadingCount == -10)
                {                               // without ever being greater
                    lastGoodAdc = adc;     // then the new value is good
                    goodReadingCount = 0;  // and we can start over
                }
            }

        return lastGoodAdc;
    }

    int main() {
        // start up the LCD
        lcd_init();
        FILE lcd_stream = FDEV_SETUP_STREAM(lcd_putchar, 0, _FDEV_SETUP_WRITE);
        lcd_home();

        // start up the Analog to Digital Converter
        adc_init();  
        lastGoodAdc = adc_read();               
        goodReadingCount = 0;

        // start up the serial port
        uart_init();
        FILE uart_stream = FDEV_SETUP_STREAM(uart_putchar, uart_getchar, _FDEV_SETUP_RW);
        stdin = stdout = &uart_stream;

        // holder variables for temperature data
        uint16_t last_sample = 0;
        double this_temp;
        double temp_avg;
        uint8_t i;

        while(1) {
            // take 100 samples and average them!
            temp_avg = 0.0;

            /*
            for(i=0; i<100; i++) {
                last_sample = adc_read_smooth();
                this_temp = sampleToFahrenheit(last_sample);
                // add this contribution to the average
                temp_avg = temp_avg + this_temp/100.0;
            }
            //*/
            last_sample = adc_read_smooth();
            this_temp = sampleToFahrenheit(last_sample);
            //temp_avg = adc_read_smooth();

            // write message to LCD
            lcd_home();
            lcd_write_string(PSTR("ADC: "));
            lcd_write_int16(last_sample);
            lcd_write_string(PSTR(" of 1024   "));
            lcd_line_two();
            //fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), temp_avg);
            fprintf_P(&lcd_stream, PSTR("Temperature: %.2f"), this_temp);

            lcd_write_data(0xdf);
            lcd_write_string(PSTR("F "));

            // write message to serial port
            //printf_P(PSTR("%.2f degrees F\r\n"), temp_avg);
            printf_P(PSTR("%d\r\n"), adc_read());
        }

        return 0;
    }

This is really good code to stabilize the ADC reading if anyone is interested.

Ralph
January 12, 2013
by Noter
Noter's Avatar

Yes I remember that discussion. I still prefer oversampling to get stability in the 100'ths of a volt and usually do some averaging on top of that which usually is good down to a few milli-volts. To improve accuracy I've been using the internal 1.1v reference and a voltage divider on the input.

On you code I don't see anything obviously wrong so maybe you should drop back to the original temp sensor example and see if you are getting the full range or if something else is going on that is limiting you to 500.

Post a Reply

Please log in to post a reply.

Did you know that you can control multiple LEDs from one microcontroller output? Learn more...