Author Topic: Accurate VCC for ADC Measurements  (Read 2283 times)

mwolter

  • NewMember
  • *
  • Posts: 17
Accurate VCC for ADC Measurements
« on: October 01, 2018, 12:54:19 PM »
Hello,
I am trying to get a more accurate voltage reading from a current sensor (Pololu ACS711EX https://www.pololu.com/product/2452) but the values are off due to an inaccurrate VCC.  Does anyone have a trick for establishing an accurate VCC for the M0? I found examples for AVR based boards that use 1.1v vref to calculate the VCC but nothing for SAMD. Looking for something software based if available.

Below is an example of establishing an accurate VCC for an AVR MCU posted by @TomWS. Hopefully, it's possible to do something like this with the SAMD chips.

Code: [Select]
unsigned int readVcc() {
  unsigned long result;
  byte saveADMUX;
 
  saveADMUX = ADMUX;
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284P__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif

  #if defined(__AVR_ATmega2560__)
    /****
    it took me a while to figure-out the problem, but on MEGA 2560, immediately after analogRead(A8), ADCL started returning zero.
    So every attempt to read from A8-A16 on Arduino MEGA will damage the functionality of readVcc().
    I've resolved the problem by adding:
    ADCSRB = 0;
    just before
    delay(20);
    **********************************/

    ADCSRB = 0;
  #endif

  delay(20); // Wait for Vref to settle (this can probably be shortened a bit)
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH
  uint8_t high = ADCH; // unlocks both

  result = (high<<8) | low;
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000 (because...= 3300*1023/3 since 1.1 is exactly 1/3 of 3.3V)

  ADMUX = saveADMUX;  // restore the MUX to previous setting

  return result; // Vcc in millivolts
}

Felix

  • Administrator
  • Hero Member
  • *****
  • Posts: 6866
  • Country: us
    • LowPowerLab
Re: Accurate VCC for ADC Measurements
« Reply #1 on: October 01, 2018, 05:32:07 PM »
The internal 1.1v reference on the AVRs is not quite super accurate either. It's good enough in most cases like displaying a battery voltage.
To get accuracy you need some external dedicated reference.
The SAMD21 also has some internal references, I have not spent too much time with these but I expect the accuracy to be similar to the AVR or perhaps better.
These are the REFSEL options for the ADC reference:
0x0 INT1V 1.0V voltage reference
0x1 INTVCC0 1/1.48 VDDANA
0x2 INTVCC1 1/2 VDDANA (only for VDDANA > 2.0V)
0x3 VREFA External reference
0x4 VREFB External reference