Сравнение АЦП (ADC) ADS1115 и ESP32

В предыдущей статье я подробно рассмотрел как работать с АЦП микроконтроллера ESP32. Протестируем какие данные дает 12-битный АЦП этого микроконтроллера и сравним с показаниями 16-ти битного АЦП ADS1115. При тестировании я буду использовать следующее оборудование:

  • Модуль АЦП CJ-ADS1115 — приобретен на Aliexpress. Цена порядка 1.5 USD. Использую плату вишневого цвета.
  • Генератор сигналов 4..20 мА — приобретен на Aliexpress. Подробное описание тестирования генератора — здесь.
  • Высокоточный цифровой мультиметр UNI-T UT61E. Приобретен на Aliexpress у этого продавца за 50 USD.
  • Цифровой мультиметр Victor VC890C+.
  • ESP32 DevKit — приобретен на Aliexpress.

Измерительное сопротивление для АЦП ESP32 и ADS1115

Тестирование точности измерений выполняю в рамках одного из проектов, где используются датчики тока. Поэтому тестирование АЦП выполняется таким сложным образом, с использованием генератора тока 4.20 мА, вместо того, чтобы просто подать на вход эталонное напряжение.

Схемотехнику снятия данных с датчиков 4.20 мА я подробно рассмотрел в предыдущей статье. Собственно, основной элемент — это измерительное сопротивление на котором падает напряжение, измеряемое АЦП.

У АЦП ADS1115 есть несколько параметров напряжений для различных режимов работы аттенюатора

  // ads.setGain(GAIN_TWOTHIRDS);  +/- 6.144V  1 bit = 0.1875mV (default)
  //ads.setGain(GAIN_ONE);         +/- 4.096V  1 bit = 0.125mV
  // ads.setGain(GAIN_TWO);        +/- 2.048V  1 bit = 0.0625mV
  // ads.setGain(GAIN_FOUR);       +/- 1.024V  1 bit = 0.03125mV
  // ads.setGain(GAIN_EIGHT);      +/- 0.512V  1 bit = 0.015625mV
  // ads.setGain(GAIN_SIXTEEN);    +/- 0.256V  1 bit = 0.0078125mV 

Для АЦП ESP32 значения несколько другие:

0dB attenuaton (ADC_ATTEN_DB_0) between 100 and 950mV
2.5dB attenuation (ADC_ATTEN_DB_2_5) between 100 and 1250mV
6dB attenuation (ADC_ATTEN_DB_6) between 150 to 1750mV
11dB attenuation (ADC_ATTEN_DB_11) between 150 to 2450mV

Примерно одинаковые варианты ослабления входного напряжения GAIN_FOUR, +/- 1.024V для ADS1115 и 0dB attenuaton (ADC_ATTEN_DB_0) от 100 до 950 mV для АЦП ESP32.

В пределе при токе 20 mA напряжение должно быть 950 mV. Соотвественно, по закону Ома сопротивление R = 0,95/0,002 = 47,5 Ом. Ближайшее из стандартных сопротивление как раз 47 Ом.

Измерения АЦП ESP32 и ADS1115

Данные по напряжению на измерительном сопротивлении снимались параллельно АЦП ESP32, ADS1115 и высокоточным Unit-T UT61E.

Менее точный VC809C+ работал в качестве измерителя тока, формируемого генератором сигнала 4..20 mA.

VC890С+, mAUT61E, мVESP32, mVADS1115, mVОшибка ESP32 абс, mVОшибка ADS абс, mVОшибка ESP32, %Ошибка ADS, %
4,43209,21207209,082,210,131,06%0,06%
4,5212,38211212,251,380,130,65%0,06%
5235,7232235,573,70,131,57%0,06%
5,5259,1256259,33,1-0,21,20%-0,08%
6282,7280282,82,7-0,10,96%-0,04%
6,49306301306,15-0,11,63%-0,03%
6,99329,4325329,534,4-0,131,34%-0,04%
7,49352,7347352,935,7-0,231,62%-0,07%
7,98376370376,196-0,191,60%-0,05%
8,48399,4393399,546,4-0,141,60%-0,04%
8,98422,8416422,936,8-0,131,61%-0,03%
9,47445,9438446,147,9-0,241,77%-0,05%
9,96469,1462469,377,1-0,271,51%-0,06%
10,46492,3484492,638,3-0,331,69%-0,07%
10,96515,6506515,689,6-0,081,86%-0,02%
11,44538,6530538,858,6-0,251,60%-0,05%
11,94562552562,0210-0,021,78%0,00%
12,43585,2575585,310,2-0,11,74%-0,02%
12,92608,1598608,1310,1-0,031,66%0,00%
13,41631,2620631,6211,2-0,421,77%-0,07%
13,91654,2642654,7312,2-0,531,86%-0,08%
14,4677,7665678,2212,7-0,521,87%-0,08%
14,9701,2689701,2312,2-0,031,74%0,00%
15,36722,8712723,2110,8-0,411,49%-0,06%
15,88747734747,7413-0,741,74%-0,10%
16,38770,4755770,7615,4-0,362,00%-0,05%
16,87793,6777793,6216,6-0,022,09%0,00%
17,36816,2801816,7515,2-0,551,86%-0,07%
17,83838,8825839,2613,8-0,461,65%-0,05%
18,32862847862,5615-0,561,74%-0,06%
18,82884,8870885,1214,8-0,321,67%-0,04%
19,31907,5892907,9215,5-0,421,71%-0,05%
19,78930,6915930,6915,6-0,091,68%-0,01%
9,490606-0,22971,62%-0,04%
Сравнение АЦП ESP32 и ADS1115
Сравнение АЦП ESP32 и ADS1115

По результатам измерений видно, что 16-ти битный АЦП ADS1115 значительно лучше встроенного 12-ти битного АЦП ESP32:

  • Средняя относительная ошибка измерения ESP32 — 1,62%
  • Средняя относительная ошибка измерения ADS1115 — 0,04%. График практически полностью совпадает с показаниями высокоточного вольтметра UT61E.
  • Относительная ошибка ADS1115 остается практически постоянной и минимальной на всем интервале измерений.
  • Ошибка у АЦП ESP32 меняется в зависимости от величины измеряемого тока. При низких значениях тока, а, соответственно, напряжения, на входе — ошибка значительная.

Исходный код

#include <driver/adc.h>
#include "esp_adc_cal.h"
#include <Wire.h>
#include <Adafruit_ADS1015.h>
 
Adafruit_ADS1115 ads(0x4A);
const float multiplier = 0.03125; //0.1875F;
//float ads_voltage = 0.0;

//#define channel ADC1_CHANNEL_7 
#define channel ADC1_CHANNEL_3 //SVN input

//#define pin ADC1_CHANNEL_7_GPIO_NUM
#define pin ADC1_CHANNEL_3_GPIO_NUM //SVN input

#define DEFAULT_VREF 1100

static esp_adc_cal_characteristics_t *adc_chars;
static const adc_atten_t atten = ADC_ATTEN_DB_0;
static const adc_unit_t unit = ADC_UNIT_1;

void check_efuse()
{
    //Check TP is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_TP) == ESP_OK) {
        Serial.println("eFuse Two Point: Supported");
    } else {
        Serial.println("eFuse Two Point: NOT supported");
    }

    //Check Vref is burned into eFuse
    if (esp_adc_cal_check_efuse(ESP_ADC_CAL_VAL_EFUSE_VREF) == ESP_OK) {
        Serial.println("eFuse Vref: Supported");
    } else {
        Serial.println("eFuse Vref: NOT supported");
    }
}

String print_char_val_type(esp_adc_cal_value_t val_type)
{
    switch (val_type)
    {
      case ESP_ADC_CAL_VAL_EFUSE_VREF:
        return "ESP_ADC_CAL_VAL_EFUSE_VREF. Characterization based on reference voltage stored in eFuse.";
      case ESP_ADC_CAL_VAL_EFUSE_TP:
        return "ESP_ADC_CAL_VAL_EFUSE_TP. Characterization based on Two Point values stored in eFuse.";
      case ESP_ADC_CAL_VAL_DEFAULT_VREF:  
        return "ESP_ADC_CAL_VAL_DEFAULT_VREF. Characterization based on default reference voltage.";
    };
    return "Unknown";
}

String NumToAtten(int atten)
{
  switch (atten)
  {
    case 0:
      return "ADC_ATTEN_DB_0. No chages for the input voltage";
    case 1:
      return "ADC_ATTEN_DB_2_5. The input voltage will be reduce to about 1/1.34.";
    case 2:
      return "ADC_ATTEN_DB_6. The input voltage will be reduced to about 1/2";
    case 3:
      return "ADC_ATTEN_DB_11. The input voltage will be reduced to about 1/3.6";  
  }
  return "Unknown attenuation.";
}

String NumToWidth(int width)
{
  switch (width)
  {
    case 0:
      return "ADC_WIDTH_BIT_9. ADC capture width is 9Bit";
    case 1:
      return "ADC_WIDTH_BIT_10. ADC capture width is 10Bit";
    case 2:
      return "ADC_WIDTH_BIT_11. ADC capture width is 11Bit";
    case 3:
      return "ADC_WIDTH_BIT_12. ADC capture width is 12Bit";  
  }
  return "Unknown width.";
}

void setup() {
    Serial.begin(9600);
    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_config_channel_atten(channel, ADC_ATTEN_DB_0);

    check_efuse();

    //Characterize ADC at particular atten
    adc_chars = (esp_adc_cal_characteristics_t *)calloc(1, sizeof(esp_adc_cal_characteristics_t));
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);

    Serial.println("ADC number:\t" + String(adc_chars->adc_num));
    Serial.println("ADC attenuation:\t" + NumToAtten(adc_chars->atten));
    Serial.println("ADC bit width:\t" + NumToWidth(adc_chars->bit_width));
    Serial.println("ADC coeff_a:\t" + String(adc_chars->coeff_a));
    Serial.println("ADC coeff_b:\t" + String(adc_chars->coeff_b));
    Serial.println("ADC VRef:\t" + String(adc_chars->vref));

    //Check type of calibration value used to characterize ADC
    Serial.println(print_char_val_type(val_type));

    //ADS1115
    // ads.setGain(GAIN_TWOTHIRDS);  +/- 6.144V  1 bit = 0.1875mV (default)
    //ads.setGain(GAIN_ONE); //       +/- 4.096V  1 bit = 0.125mV
    // ads.setGain(GAIN_TWO);        +/- 2.048V  1 bit = 0.0625mV
     ads.setGain(GAIN_FOUR);       //+/- 1.024V  1 bit = 0.03125mV
    // ads.setGain(GAIN_EIGHT);      +/- 0.512V  1 bit = 0.015625mV
    // ads.setGain(GAIN_SIXTEEN);    +/- 0.256V  1 bit = 0.0078125mV 
    ads.begin();
}

//char buff[10];
int lastMillis = 0;
int lastMeasurementsMillis = 0;
float AverageESP = 0;
float AverageADS = 0;
int MeasurementsToAverage = 0;

void loop() {
  int currentMillis = millis();
  if (currentMillis - lastMeasurementsMillis > 50)
  {
    float adcr = adc1_get_raw(channel);

    int16_t  adc0 = ads.readADC_SingleEnded(0); //16 bits ADC read of input A0
    
    AverageESP += adcr;
    AverageADS += adc0;
    MeasurementsToAverage++;
    lastMeasurementsMillis = currentMillis;
  }

  if (currentMillis - lastMillis > 1000)
  {
    int adcr = adc1_get_raw(channel);

    //ADS1115 read
    int16_t  adc0 = ads.readADC_SingleEnded(0); //16 bits ADC read of input A0
    float ads_voltage = (adc0 * multiplier); ///1000
    //--------
    
    AverageESP /= MeasurementsToAverage;
    AverageADS /= MeasurementsToAverage;
    AverageADS *= multiplier;
    AverageESP = esp_adc_cal_raw_to_voltage(AverageESP, adc_chars);

    float voltage = esp_adc_cal_raw_to_voltage(adcr, adc_chars);
    Serial.println("Read ADC pin [" + String(pin) + "]: " + String(adcr) + "\tVoltage: " + String(voltage) + " [" + String(ads_voltage) + "]" + "\tAverage: " + String(AverageESP) + " [" + String(AverageADS) + "]");
    lastMillis = currentMillis;
    AverageESP = 0;
    AverageADS = 0;
    MeasurementsToAverage = 0;
  }
}

Полезные ссылки

Spread the love
Запись опубликована в рубрике IT рецепты с метками , , , , , . Добавьте в закладки постоянную ссылку.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *