Rounding ADC values properly, ATmega328p-PU

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Dear all,

 

I came across to a code snippet recently, which is not quite clear to me. The snippet sources from a program written for ATmega family, and utilizes the onboard ADC module for some measurements.

Starting direct after the loop_until_bit_is_clear(ADCSRA, ADSC); what's  the purpose of the lines below. I am guessing that they do some re-scaling and rounding, but if someone could explain it to me in very simple way, what's the intention and why it is done in this way and not other way would help a lot. Sorry for basic question btw. 

 

while (1) {

    ADCSRA |= (1 << ADSC);                                /* start ADC */
    loop_until_bit_is_clear(ADCSRA, ADSC);

    voltage = ADC * vcc + vcc / 2;      
    voltage = voltage >> 10;              
    volts = voltage / 10;
    tenths = voltage % 10;
    transmitByte('0' + volts);

thanks in advance

 

work in progress...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On the AVR8s, if Vin <= Vref then
ADC = Vin * 1024 / Vref
rearranging gives
Vin = ADC * Vref / 1024


If 'your Vin' is > Vref then you need to scale it down (eg via a resistive divider on the input) to avoid damage and also to not saturate the ADC count, therefore
'your Vin' = k * ADC * Vref / 1024
(You need to see the circuit on that analogue input to properly determine the scaling factor k)


Assuming that voltage is a 16-bit integer variable then
voltage = ADC * vcc + vcc / 2; will produce a 5-digit decimal value of the form abcde
voltage = voltage >> 10; is voltage / 1024 which produces a 2 digit decimal value ab
volts = voltage / 10; extracts the decimal digit a
tenths = voltage % 10; extracts the decimal digit b
transmitByte('0' + volts); produces a human-readable character of the decimal digit a

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

mikech wrote:
Assuming that voltage is a 16-bit integer variable then
voltage = ADC * vcc + vcc / 2; will produce a 5-digit decimal value of the form abcde

Surely we need to know the type and value of the mysterious "vcc".

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


 

Thanks for the clarification. Although this expression is still odd to me. Does this source from math. or its a C programming trick. Is this used often while coding those kind of stuff. 

voltage = ADC * vcc + vcc / 2; will produce a 5-digit decimal value of the form abcde

 

Coming back to the question of the mysterious Vcc and its value let's see the small picture, I ripped this from a book.

 

As book author states,  his Vcc level is 5.08 Volts (I assume measured at AVcc ? ....),

Why does he needs to multiply it with x 10? and get 50.8 Volts?. The Vcc is not higher than Logic voltage in this case, to use a voltage divider and its constant ' k' as stated by poster " mikech "

int main(void) {
  uint16_t voltage;
  uint8_t volts;
  uint8_t tenths;
  uint8_t vcc = 51;                                          

  clock_prescale_set(clock_div_1);                                       
  initTimer0();
  initTimer2();
  sei();                                                       
  initADC();
  initUSART();

  

  while (1) {

    ADCSRA |= (1 << ADSC);                                           
    loop_until_bit_is_clear(ADCSRA, ADSC);

    voltage = ADC * vcc + vcc / 2;                
    voltage = voltage >> 10;                         

  
                          

  }                                                                  
  return 0;
}

 

work in progress...

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

All the calculations are done with integers, therefore the x10 scaling of Vref is a way to get the tenths-of-volts.
eg.
with a Vref of 5.08, an input voltage of 3.969 V produces an ADC count of 800
- if the variable vcc (Vref) is 51 then voltage = ADC * vcc + vcc / 2; produces 40825, which after the divide-by-1024 gives 39
- if vcc = 5 we get 4002, which after the divide-by-1024 produces 3
The volts = and tenths = expressions are, in-effect, a divide-by-ten to 'reverse' the x10 Vref.



The + vcc / 2 term in voltage = ADC * vcc + vcc / 2; is a DC offset for the analogue input.
Why ???. Does the author say what signal they are measuring (or trying to do) ?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Actually the program measures the voltage and then outputs it via voice message, Author refers to code as voltmeter, so I assume its for both DC voltages and analog signal like potentiomer.  So there are two arrays declared in PROGMEM, which in fact are voice samples from (0-9). That's why I believe two calculations are done, one to yield a integer part  "Volts" variable,  and then the fractional part as "tenths" variable. After these are done the timer outputs voice telling the current reading. In Book Author suggests measuring an analog voltage, like from analog potti or any other voltage of similar kind.

work in progress...