В предыдущей статье я подробно рассмотрел как работать с АЦП микроконтроллера 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С+, mA | UT61E, мV | ESP32, mV | ADS1115, mV | Ошибка ESP32 абс, mV | Ошибка ADS абс, mV | Ошибка ESP32, % | Ошибка ADS, % |
4,43 | 209,21 | 207 | 209,08 | 2,21 | 0,13 | 1,06% | 0,06% |
4,5 | 212,38 | 211 | 212,25 | 1,38 | 0,13 | 0,65% | 0,06% |
5 | 235,7 | 232 | 235,57 | 3,7 | 0,13 | 1,57% | 0,06% |
5,5 | 259,1 | 256 | 259,3 | 3,1 | -0,2 | 1,20% | -0,08% |
6 | 282,7 | 280 | 282,8 | 2,7 | -0,1 | 0,96% | -0,04% |
6,49 | 306 | 301 | 306,1 | 5 | -0,1 | 1,63% | -0,03% |
6,99 | 329,4 | 325 | 329,53 | 4,4 | -0,13 | 1,34% | -0,04% |
7,49 | 352,7 | 347 | 352,93 | 5,7 | -0,23 | 1,62% | -0,07% |
7,98 | 376 | 370 | 376,19 | 6 | -0,19 | 1,60% | -0,05% |
8,48 | 399,4 | 393 | 399,54 | 6,4 | -0,14 | 1,60% | -0,04% |
8,98 | 422,8 | 416 | 422,93 | 6,8 | -0,13 | 1,61% | -0,03% |
9,47 | 445,9 | 438 | 446,14 | 7,9 | -0,24 | 1,77% | -0,05% |
9,96 | 469,1 | 462 | 469,37 | 7,1 | -0,27 | 1,51% | -0,06% |
10,46 | 492,3 | 484 | 492,63 | 8,3 | -0,33 | 1,69% | -0,07% |
10,96 | 515,6 | 506 | 515,68 | 9,6 | -0,08 | 1,86% | -0,02% |
11,44 | 538,6 | 530 | 538,85 | 8,6 | -0,25 | 1,60% | -0,05% |
11,94 | 562 | 552 | 562,02 | 10 | -0,02 | 1,78% | 0,00% |
12,43 | 585,2 | 575 | 585,3 | 10,2 | -0,1 | 1,74% | -0,02% |
12,92 | 608,1 | 598 | 608,13 | 10,1 | -0,03 | 1,66% | 0,00% |
13,41 | 631,2 | 620 | 631,62 | 11,2 | -0,42 | 1,77% | -0,07% |
13,91 | 654,2 | 642 | 654,73 | 12,2 | -0,53 | 1,86% | -0,08% |
14,4 | 677,7 | 665 | 678,22 | 12,7 | -0,52 | 1,87% | -0,08% |
14,9 | 701,2 | 689 | 701,23 | 12,2 | -0,03 | 1,74% | 0,00% |
15,36 | 722,8 | 712 | 723,21 | 10,8 | -0,41 | 1,49% | -0,06% |
15,88 | 747 | 734 | 747,74 | 13 | -0,74 | 1,74% | -0,10% |
16,38 | 770,4 | 755 | 770,76 | 15,4 | -0,36 | 2,00% | -0,05% |
16,87 | 793,6 | 777 | 793,62 | 16,6 | -0,02 | 2,09% | 0,00% |
17,36 | 816,2 | 801 | 816,75 | 15,2 | -0,55 | 1,86% | -0,07% |
17,83 | 838,8 | 825 | 839,26 | 13,8 | -0,46 | 1,65% | -0,05% |
18,32 | 862 | 847 | 862,56 | 15 | -0,56 | 1,74% | -0,06% |
18,82 | 884,8 | 870 | 885,12 | 14,8 | -0,32 | 1,67% | -0,04% |
19,31 | 907,5 | 892 | 907,92 | 15,5 | -0,42 | 1,71% | -0,05% |
19,78 | 930,6 | 915 | 930,69 | 15,6 | -0,09 | 1,68% | -0,01% |
9,490606 | -0,2297 | 1,62% | -0,04% |

По результатам измерений видно, что 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; } }