SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
542 stars 140 forks source link

Proper Usage of ADC to Monitor Battery Voltage with Voltage Divider on IO #1120

Closed DigiMaxIO closed 2 days ago

DigiMaxIO commented 2 days ago

I am slightly confused about the documentation on ADC usage for battery monitoring. Based on the schematic below, using Pin PA4 with a voltage divider, would the code below be the proper usage for measuring the battery voltage? The code is based on the readTempVcc example sketch.

Note: VIN is battery voltage (a 3.7V LiPo), and VCC is regulated to 3.3V.

CleanShot 2024-07-02 at 12 01 55@2x

uint16_t readPinSupplyVoltage() //returns value in millivolts to avoid floating point
{ 
  analogReference(VDD);
  uint16_t reading = analogRead(PIN_PA4); //discarded reading
  reading = analogRead(PIN_PA4);
  Serial.println(reading);
  uint32_t intermediate = 1023UL * 1500;
  reading = intermediate / reading;
  return reading;
}

Any help much appreciated, thank you!

hmeijdam commented 2 days ago

With a 3.7V lipo battery you can skip the regulator and connect the Attiny directly to the lipo. You also do not need the voltage divider and the analog input pin.

Below sketch will return VDD in millivolts on a serial monitor . It works by measuring the internal 1.1V bandgap voltage and because that voltage is known (1.1V) it calculates the VDD from that.


void setup() {
Serial.begin(9600);
}

void loop() {
Serial.print("VCC: ");
Serial.println (readVDD());
delay(1000);
}

uint16_t readVDD(void){ //returns Vdd volts*100 (3.21v = return 321)
    //adc0 VREF = 1v1
    VREF_CTRLA = (VREF_CTRLA & ~VREF_ADC0REFSEL_gm) | VREF_ADC0REFSEL_1V1_gc;
    ADC0_CTRLB = ADC_SAMPNUM_ACC64_gc; //64 samples
    ADC0_CTRLC = ADC_REFSEL_VDDREF_gc | ADC_PRESC_DIV16_gc | ADC_SAMPCAP_bm;
    ADC0_CTRLD = ADC_INITDLY_DLY64_gc;
    ADC0_MUXPOS = ADC_MUXPOS_INTREF_gc;
    ADC0_CTRLA = 1; //enable adc
    ADC0_COMMAND = 1; //start
    while( ADC0_COMMAND ){} //wait
    //Vdd*100 = 1023*1.1*100*64/res (64 samples, so *64 also)
    return (1023*1.1*100ul*64) / ADC0_RES;
}
DigiMaxIO commented 2 days ago

@hmeijdam Thank you for the response!

However, my thinking was that this application requires a regulator because it accepts both USB power (for use while charging the battery) or battery power, as well as to maintain consistent neopixel brightness during the time it is on. Now, let's say I already have this board designed like it is in the above schematic.... how would you go about measuring the battery voltage using that pin?

hmeijdam commented 2 days ago

If it has to be that schematic, Ii would select the 2.5V internal reference analogReference(INTERNAL2V5); and then just do a analogRead Your lipo will be from 4.2 to 3.2 V , which via your divider becomes 2.1 to1.6 V. That fits nicely below the 2.5V reference. Then it's up to you to work either directly with the analogread values to protect your lipo or use a map() function to calculate it to presentable voltages.

DigiMaxIO commented 2 days ago

Thank you @hmeijdam for your quick and detailed responses, much appreciated!