Implementing a calibrated ESP32 ADC reading

raspi-portduino
code8buster 2023-05-13 20:38:37 -04:00 zatwierdzone przez Thomas Göttgens
rodzic 508cdf6060
commit d11bcda292
3 zmienionych plików z 89 dodań i 28 usunięć

Wyświetl plik

@ -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();

Wyświetl plik

@ -1,7 +1,8 @@
#pragma once
#include "PowerStatus.h"
#include "concurrency/OSThread.h"
#include <esp_adc_cal.h>
#include <soc/adc_channel.h>
/**
* 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;

Wyświetl plik

@ -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