From d11bcda2923d40159c6cc09fb99c7cb7339579ff Mon Sep 17 00:00:00 2001 From: code8buster Date: Sat, 13 May 2023 20:38:37 -0400 Subject: [PATCH] Implementing a calibrated ESP32 ADC reading --- src/Power.cpp | 98 ++++++++++++++++++++++++-------- src/power.h | 9 ++- variants/tlora_v2_1_16/variant.h | 10 +++- 3 files changed, 89 insertions(+), 28 deletions(-) diff --git a/src/Power.cpp b/src/Power.cpp index b129fbcd..1eb668f0 100644 --- a/src/Power.cpp +++ b/src/Power.cpp @@ -6,6 +6,7 @@ #include "main.h" #include "sleep.h" #include "utils.h" +static const char *TAG = "ADCmod"; #ifdef DEBUG_HEAP_MQTT #include "mqtt/MQTT.h" @@ -17,6 +18,22 @@ #define DELAY_FOREVER portMAX_DELAY #endif +#ifdef BATTERY_PIN + +#ifndef BAT_MEASURE_ADC_UNIT // ADC1 +static const adc1_channel_t adc_channel = ADC_CHANNEL; +#else // make adc1 default +static const adc2_channel_t adc_channel = ADC_CHANNEL; +RTC_NOINIT_ATTR uint64_t RTC_reg_b; +#include "soc/sens_reg.h" // needed for adc pin reset +#endif + +esp_adc_cal_characteristics_t *adc_characs = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t)); + +static const adc_atten_t atten = ADC_ATTEN_DB_11; +static const adc_unit_t unit = adc_unit_name; +#endif // BATTERY_PIN + #ifdef HAS_PMU #include "XPowersAXP192.tpp" #include "XPowersAXP2101.tpp" @@ -36,7 +53,7 @@ class HasBatteryLevel /** * The raw voltage of the battery or NAN if unknown */ - virtual uint16_t getBattVoltage() { return 0; } + virtual uint32_t getBattVoltage() { return 0; } /** * return true if there is a battery installed in this unit @@ -105,7 +122,7 @@ class AnalogBatteryLevel : public HasBatteryLevel /** * The raw voltage of the batteryin millivolts or NAN if unknown */ - virtual uint16_t getBattVoltage() override + virtual uint32_t getBattVoltage() override { #ifndef ADC_MULTIPLIER @@ -128,18 +145,27 @@ class AnalogBatteryLevel : public HasBatteryLevel // Set the number of samples, it has an effect of increasing sensitivity, especially in complex electromagnetic // environment. uint32_t raw = 0; - for (uint32_t i = 0; i < BATTERY_SENSE_SAMPLES; i++) { - raw += analogRead(BATTERY_PIN); +#ifndef BAT_MEASURE_ADC_UNIT // ADC1 + for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) { + raw += adc1_get_raw(adc_channel); } +#else // ADC2 + uint32_t adc_buf = 0; + for (int i = 0; i < BATTERY_SENSE_SAMPLES; i++) { + // ADC2 wifi bug workaround, see + // https://github.com/espressif/arduino-esp32/issues/102 + WRITE_PERI_REG(SENS_SAR_READ_CTRL2_REG, RTC_reg_b); + SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV); + adc2_get_raw(adc_channel, ADC_WIDTH_BIT_12, &adc_buf); + raw += adc_buf; + } +#endif // BAT_MEASURE_ADC_UNIT raw = raw / BATTERY_SENSE_SAMPLES; - float scaled; -#ifndef VBAT_RAW_TO_SCALED - scaled = 1000.0 * operativeAdcMultiplier * (AREF_VOLTAGE / 1024.0) * raw; -#else - scaled = VBAT_RAW_TO_SCALED(raw); // defined in variant.h -#endif + scaled = esp_adc_cal_raw_to_voltage(raw, adc_characs); + scaled *= operativeAdcMultiplier; // LOG_DEBUG("battery gpio %d raw val=%u scaled=%u\n", BATTERY_PIN, raw, (uint32_t)(scaled)); + last_read_value = scaled; return scaled; } else { @@ -147,7 +173,7 @@ class AnalogBatteryLevel : public HasBatteryLevel } #else return 0; -#endif +#endif // BATTERY_PIN } /** @@ -222,36 +248,62 @@ bool Power::analogInit() pinMode(EXT_PWR_DETECT, INPUT); #endif +#ifndef HAS_PMU + #ifdef BATTERY_PIN LOG_DEBUG("Using analog input %d for battery level\n", BATTERY_PIN); // disable any internal pullups - pinMode(BATTERY_PIN, INPUT); + pinMode(35, INPUT); +#endif // BATTERY PIN + +#ifndef BATTERY_SENSE_RESOLUTION_BITS +#define BATTERY_SENSE_RESOLUTION_BITS 12 +#endif #ifdef ARCH_ESP32 - // ESP32 needs special analog stuff - adcAttachPin(BATTERY_PIN); +// ESP32 needs special analog stuff +// configure ADC +#ifndef BAT_MEASURE_ADC_UNIT // ADC1 + adc1_config_width(width); + adc1_config_channel_atten(adc_channel, atten); +#else // ADC2 + adc2_config_channel_atten(adc_channel, atten); + // ADC2 wifi bug workaround, see + // https://github.com/espressif/arduino-esp32/issues/102 + RTC_reg_b = READ_PERI_REG(SENS_SAR_READ_CTRL2_REG); #endif + + LOG_DEBUG("ADC using channel %s\n", adc_channel_name); + // calibrate ADC + esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, width, DEFAULT_VREF, adc_characs); + // show ADC characterization base + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + ESP_LOGI(TAG, "ADC characterization based on Two Point values stored in eFuse"); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + ESP_LOGI(TAG, "ADC characterization based on reference voltage stored in eFuse"); + } else { + ESP_LOGI(TAG, "ADC characterization based on default reference voltage"); + } +#endif // ARCH_ESP32 + #ifdef ARCH_NRF52 #ifdef VBAT_AR_INTERNAL analogReference(VBAT_AR_INTERNAL); #else analogReference(AR_INTERNAL); // 3.6V #endif -#endif - -#ifndef BATTERY_SENSE_RESOLUTION_BITS -#define BATTERY_SENSE_RESOLUTION_BITS 10 -#endif - - // adcStart(BATTERY_PIN); + adcStart(BATTERY_PIN); analogReadResolution(BATTERY_SENSE_RESOLUTION_BITS); // Default of 12 is not very linear. Recommended to use 10 or 11 // depending on needed resolution. + +#endif // ARCH_NRF52 + batteryLevel = &analogLevel; return true; #else return false; -#endif +#endif // !HAS_PMU } bool Power::setup() @@ -302,7 +354,7 @@ void Power::readPowerStatus() { if (batteryLevel) { bool hasBattery = batteryLevel->isBatteryConnect(); - int batteryVoltageMv = 0; + uint32_t batteryVoltageMv = 0; int8_t batteryChargePercent = 0; if (hasBattery) { batteryVoltageMv = batteryLevel->getBattVoltage(); diff --git a/src/power.h b/src/power.h index 6b787d32..8c3cd92a 100644 --- a/src/power.h +++ b/src/power.h @@ -1,7 +1,8 @@ #pragma once #include "PowerStatus.h" #include "concurrency/OSThread.h" - +#include +#include /** * Per @spattinson * MIN_BAT_MILLIVOLTS seems high. Typical 18650 are different chemistry to LiPo, even for LiPos that chart seems a bit off, other @@ -14,6 +15,10 @@ #define BAT_MILLIVOLTS_FULL 4100 #define BAT_MILLIVOLTS_EMPTY 3500 +#ifdef BAT_MEASURE_ADC_UNIT +extern RTC_NOINIT_ATTR uint64_t RTC_reg_b; +#include "soc/sens_reg.h" // needed for adc pin reset +#endif class Power : private concurrency::OSThread { @@ -45,4 +50,4 @@ class Power : private concurrency::OSThread #endif }; -extern Power *power; +extern Power *power; \ No newline at end of file diff --git a/variants/tlora_v2_1_16/variant.h b/variants/tlora_v2_1_16/variant.h index 11c5921b..42f3a0d5 100644 --- a/variants/tlora_v2_1_16/variant.h +++ b/variants/tlora_v2_1_16/variant.h @@ -3,9 +3,13 @@ #define GPS_RX_PIN 15 // per @der_bear on the forum, 36 is incorrect for this board type and 15 is a better pick #define GPS_TX_PIN 13 -#define BATTERY_PIN 35 // A battery voltage measurement pin, voltage divider connected here to measure battery voltage +#define BATTERY_PIN 35 +#define ADC_CHANNEL ADC_GPIO35_CHANNEL +#define BATTERY_SENSE_SAMPLES 30 +#define DEFAULT_VREF 1100 + // ratio of voltage divider = 2.0 (R42=100k, R43=100k) -#define ADC_MULTIPLIER 2.11 // 2.0 + 10% for correction of display undervoltage. +#define ADC_MULTIPLIER 2 #define I2C_SDA 21 // I2C pins for this board #define I2C_SCL 22 @@ -19,4 +23,4 @@ #define LORA_DIO0 26 // a No connect on the SX1262 module #define LORA_RESET 23 #define LORA_DIO1 33 // https://www.thethingsnetwork.org/forum/t/big-esp32-sx127x-topic-part-3/18436 -#define LORA_DIO2 32 // Not really used +#define LORA_DIO2 32 // Not really used \ No newline at end of file