Sorry for the confusing headline, but I'll try to explain better here.
In my code, I have used the commonly known code for reading the 5v input as described here: GitHub - Yveaux/Arduino_Vcc: Arduino library to read VCC supply level without external components with some small modifications. In addition I have added code for reading internal temperature as outlined in this post: Arduino Playground - InternalTemperatureSensor.
The strange thing that I cannot wrap my head (nor code) around, is that when I'm reading both V_in and Temperature, the Temperature reading returns a value of -186,89 (raw value from ADC = 88). V_in returns 5.033v which is close to actual input.
Reading only internal temperature (that is, commenting out the call read_vi_mv), the reading returns just fine, with a temperature of 27 degrees uncalibrated, which starts rising if I touch the chip.
The same result is happening if I try to read Temperature and any other analog input. The analog input reading go okay, but the Temperature return a constant of -186,89.
Any suggestions?
double temperature = 0.0;
double vIn_mv = 5000; // best guess. Is updated periodically in software
const long SlowUpdateInterval = 200; //[ms]
long LastSlowUpdate = 0;
double read_vin_mv(boolean init) {
static double vIn = 0.0;
// Read 1.1V reference against AVcc
// set the reference to Vcc and the measurement to the internal 1.1V reference
if (ADMUX != (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1)))
{
ADMUX = (_BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1));
// Bandgap reference start-up time: max 70us
// Wait for Vref to settle.
delayMicroseconds(350);
}
// Start conversion and wait for it to finish.
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1); // enable the ADC div64
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA, ADSC)) {};
// Result is now stored in ADC.
// read result
unsigned int adc;
adc = ADCW;
// data sheet 24.7 ADC Conversion Result
// ADC = vref * 1024 / vin
// notice that vin and vref have exchanged roles because we
// measure the reference voltage against vin
// ==> vin [V] = vref [V] * 1024 / adc
// ==> vin [mV] = 1.1 * 1000 * 1024 / adc
if (init) {
vIn = (Vref*1024.0) / (double)adc;
}
else {
vIn -= vIn / 10.0; //Recursive filter averaging over 10 samples
vIn += ((Vref*1024.0) / (double)adc)/10.0;
}
return vIn;
}
double GetTemp(boolean init)
{
static double temp;
unsigned int wADC;
if (ADMUX != (_BV(REFS1) | _BV(REFS0) | _BV(MUX3))) {
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
// Bandgap reference start-up time: max 70us
// Wait for Vref to settle.
delayMicroseconds(350);
}
// Start conversion and wait for it to finish.
ADCSRA = _BV(ADEN) | _BV(ADPS2) | _BV(ADPS1) ; // enable the ADC div64
ADCSRA |= _BV(ADSC);
while (bit_is_set(ADCSRA, ADSC)) {};
// The offset of 324.31 could be wrong. It is just an indication.
// The returned temperature is in degrees Celcius.
// Reading register "ADCW" takes care of how to read ADCL and ADCH.
wADC = ADCW;
if (init)
temp = (((double)wADC - 324.31) / 1.22);
else {
temp -= temp / 20.0; //Recursive filter averaging over 20 samples
temp += (((double)wADC - 324.31) / 1.22) / 20.0;
}
return (temp);
}
int ReadADC(int channel, int AverageNum) {
long adc = 0;
for (int i = 0; i<AverageNum; i++) {
//delay(1);
adc += analogRead(channel);
// wait for conversion to finish
while (bit_is_set(ADCSRA, ADSC)) {};
}
return (int)(adc / AverageNum);
}
void setup() {
// ***************************************************************************
// ******* Initialize Serial port for use with Meguino GUI *******************
// ***************************************************************************
Serial.begin(115200);
temperature = GetTemp(true);
vIn_mv = read_vin_mv(true);
}
void loop() {
if ((millis() - LastSlowUpdate) > SlowUpdateInterval)
{
LastSlowUpdate = millis();
//delay(20);
temperature = GetTemp(false);
temperature = GetTemp(false);
vIn_mv = read_vin_mv(false);
updatePlot();
}
}
Update:
Problem is solved. As it often is, the solution was pretty obvious.
Reading with analogRead() and read_vin_mv() connects the ADC to 5v reference (Vin). Reading the temperature connect Aref to 1.1v. Lookin at Aref with a scope, it takes approximately 4.5mS to drop from 5v to 1.2v. Rising from 1.2v to 5v again take 9uS.
Adding a delay in the correct place solved the issue.
double GetTemp(boolean init)
{
static double temp;
unsigned int wADC;
// Store current ADMUX in temporary
uint8_t tempADMUX = ADMUX;
if (ADMUX != (_BV(REFS1) | _BV(REFS0) | _BV(MUX3))) {
ADMUX = (_BV(REFS1) | _BV(REFS0) | _BV(MUX3));
// Bandgap reference start-up time: max 70us
// Wait for Vref to settle.
delayMicroseconds(350);
delay(6); //Must delay at least 4.5mS for Avdd to drop
}
// Start conversion and wait for it to finish.
ADCSRA = (_BV(ADEN) | _BV(ADPS2) | _BV(ADPS1)) ; // enable the ADC div64
ADCSRA |= (_BV(ADSC) | _BV(ADIF));
while (bit_is_set(ADCSRA, ADSC)) {};
// The offset of 324.31 could be wrong. It is just an indication.
// The returned temperature is in degrees Celcius.
// Reading register "ADCW" takes care of how to read ADCL and ADCH.
wADC = ADCW;
if (init)
temp = (((double)wADC - 324.31) / 1.22);
else {
temp -= temp / 20.0; //Recursive filter averaging over 20 samples
temp += (((double)wADC - 324.31) / 1.22) / 20.0;
}
ADMUX = tempADMUX;
return (temp);
}
