I have the following code for reading a battery voltage on the ADC on a microcontroller (Atmel SAM D21 to be precise.) The reference voltage is 3.3V and the ADC is reading at 12 bit resolution:
/**
* Union for Readings
*
*/
typedef union u_reading {
int16_t i;
uint8_t c[2];
} reading;
/**
* Read the main battery voltage.
*
*/
static void read_battery_level()
{
// Switch on the Control Pin
gpio_set_pin_level(ADC_CONTROL, 1);
// Battery Voltage
float batt_voltage = 0.0f;
// Array of Samples
reading batt_readings[BATTERY_READINGS_COUNT];
int x;
// Loop through ad Average the Readings
for (x = 0; x < BATTERY_READINGS_COUNT; x++)
{
// Read the ADC Channel
adc_sync_read_channel(&ADC_BATTERY, 0, batt_readings[x].c, 2);
delay_us(20);
}
// Counter for the Sum
uint32_t sum = 0;
// Loop through and Average the Readings
for (x = 0; x < BATTERY_READINGS_COUNT; x++)
{
// Add the Sum
sum += batt_readings[x].i;
}
// Calculate the Mean Reading
batt_voltage = (sum / (float)BATTERY_READINGS_COUNT) * 0.8;
// Set the Battery Level
battery_level.i = (uint16_t)batt_voltage;
// Switch off the Control Pin
gpio_set_pin_level(ADC_CONTROL, 0);
}
The code works and gives me a very accurate reading for battery voltage - I've tried it with a pretty accurate power source and multiple voltages and the reading is good every time. When I switch the reference voltage to 5V, it's no longer accurate, unless I remove the * 0.8
multiplier.
I'm still wrapping my head around how ADCs work, and I was wondering if someone could explain what's going on here.
Why does a multipler of 0.8 work for a 3.3V input and a multiplier of 1 work for 5V?