Search code examples
atmegaadc

10 bit ADC Value to Voltage measurement


I am trying to do ADC using ATMEGA8 and receiving the ADC value from the potentiometer. As it is 10 Bit ADC the highest value i can receive is 1024. Now i want to convert this value to actual voltage and view it on terminal using serial. My reference voltage is 5V.

this is what i am doing

#define REF_ADC_Volt    5000
#define ADC_Div_Factor  1023

//init ADC
void Init_ADC()
{
    ADMUX  |= (1<<REFS0);                   //Reference voltage set at AREF pin
    ADCSRA |= (1 << ADEN);                //Enable ADC
    ADCSRA |= (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);    //set prescale by 128 div factor
}
//Read ADC
uint16_t Read_ADC(uint8_t ch)
{
    ch = ch & 0x07;
    ADMUX |= ch;                        //Setting ADC Channel
    ADCSRA |= (1<<ADSC);                //ADC start conversion 
    while (! (ADCSRA & (1<<ADIF)) );    //Wait till conversion is over
    ADCSRA |= (1<<ADIF);                //Clear ADC Flag    
    return(ADCW);                       //Return ADC value 10 bit
}

int main(void)
{
    _delay_ms(2000);
    Init_ADC();
    USART_Init(103);
    double ADC_Val,Res_ADC_Val;
    char *number_string="00000";

    USART_Transmit_String("ACS712 Current Sensor ADC Value: \r\n");
    while (1) 
    {
        ADC_Val = Read_ADC(0);

        Res_ADC_Val = ((REF_ADC_Volt / ADC_Div_Factor) * ADC_Val)/1000;
        dtostrf(Res_ADC_Val,1,2,number_string);
        USART_Transmit_String(number_string);
        itoa(ADC_Val,number_string,10);
        USART_Transmit(' ');
        USART_Transmit_String(number_string);
        USART_Transmit_String("\r\n");

        ClearBuffer(number_string);
        _delay_ms(1000);
    }
}

Now the problem is after conversion the highest voltage i am getting is 4.09V at ADC value of 1023. But it should be 5V right??

According to this calculation

Res_ADC_Val = ((REF_ADC_Volt / ADC_Div_Factor) * ADC_Val)/1000;

where

REF_ADC_Volt  = 5000mV
ADC_Div_Factor = 1023
ADC_Val = 1023

I am totally confused as when i use my calculator its 5V only but I'm getting 4.09. Why? and How to solve this?

Thanks in advance.


Solution

  • Both REF_ADC_Volt and ADC_Div_Factor are two integer literals.

    Hence, the first division produces an integer result (most likely 4).

    Then, you multiply the result of this division (4) by ADC_Val.

    This means 4 * 1023 = 4.092.

    You should promote your literals to floating point:

    #define REF_ADC_Volt    5000.0
    #define ADC_Div_Factor  1023.0
    

    or re-arrange the expression to allow implicit casting to work, eg:

    Res_ADC_Val = REF_ADC_Volt * ADC_Val / ADC_Div_Factor / 1000.0;
    

    EDIT #1:

    Optimization tip

    As pointed out in other answers, the implementation above is sub optimal. Optimization was not the topic of the answer, but it is always interesting to discuss such things.

    Please note that the solutions proposed in the other answers are not the most efficent either.

    In fact, there is no need to perform all those divisions, since they all involve constant values.

    You can define one constant as your scalar and perform just one multiplication each time:

    #define ADC_TO_VOLT 0.00488758553275 // (5000.0 / 1023.0) / 1000.0
    
    
    Res_ADC_Val = ADC_Val * ADC_TO_VOLT;
    

    Moreover, there may be no need to use double values. I believe single precision values (float) should suffice, but that depends on your application and it's hard to judge from your minimal example.