На борту ESP32 находится два 12-bit АЦП-а, поддерживающих суммарно 18 каналов аналоговых измерений. Классно ведь или нет?
Будем разбираться по порядку.
АЦП драйвера поддерживает ADC1 (8 каналов — GPIOs 32 — 39), и ADC2 (10 каналов — GPIOs 0, 2, 4, 12 — 15 и 25 — 27). Однако ADC2 имеет определенные ограничения по использованию:
- ADC2 используется Wi-Fi драйвером, поэтому приложение может использовать ADC2 только когда он не используется.
- Некоторые пины ADC2 используются также для других целей (GPIO 0, 2, 4, 15) поэтому есть ограничения по их использованию.
У меня плата ESP-WROOM-32 DevKit v1. Выводы ADC1 которые можно использовать без каких-либо ограничений отмечены на схеме фиолетовым цветом (3 колонка с права) - не так уж и много.
Необходимо также учитывать, что в ESP32 есть внутренний датчик Холла, данные с которого можно получить с ADC1, вызвав функцию hall_sensor_read(). Хотя датчик Холла внутренний, в ESP32, чтение данных с него задействует каналы 0 и 3 ADC1 (GPIO 36 и 39). Не нужно использовать эти контакты и изменять их конфигурацию в противном случае это может сказаться на измерении сигналов с низким уровнем напряжения, получаемых с датчика (это в том случае, если он конечно вообще нуж0н в конкретном проекте).
Точность измерений определяется в первую очередь, уровнем (Vref) и стабильностью источника опорного напряжения.
Для ESP32 Vref = 1.1V Естественно, что все платы ESP32 имеют некоторый разброс точного значения этого напряжения - однако все платформы ESP32 выпущенные после 2018 года откалиброваны прямо на заводе (значение было измерено и прошито во фьюзы). В папке с примерами к этой статье вы найдете скетч 01_VREF_Fuse, в котором это можно проверить.
Загрузите его в вашу ESP32, откройте монитор порта на скорости 115200 и клацните кнопку перезагрузки:
ADC number: 1
ADC attenuation: ADC_ATTEN_DB_11. The input voltage will be reduced to about 1/3.6
ADC bit width: ADC_WIDTH_BIT_12. ADC capture width is 12Bit
ADC coeff_a: 52126
ADC coeff_b: 142
ADC VRef: 1086
Characterized using eFuse Vref
"Characterized using eFuse Vref" говорит о том, что плата уже откалибрована, и для всех аналоговых измерений будет использоваться именно значение 1086мВ.
Из среды Aduino IDE, можно прокинуть напряжение опорного источника на некоторые выводы (например 27) и померять его вольтметром.
Подключив к 27 ноге высокоточный (ага, Китайский, ага не поверенный и ага не Ц-шку) вольтметр видим:
что значения совпадают, а посему всю "копипсату" из Интернет про калибровку Vref можно выкинуть ф корзину - производитель уже все померял и сделал за нас.
Все кто собирал вольтметры на АЦП знают, что максимальное измеряемого напряжения необходимо привести к опорному с помощью делителя. В ESP32 все необходимое уже есть на борту и подключается программно с помощью функции:
analogSetAttenuation(attenuation);
- ADC_0db
- ADC_2_5db
- ADC_6db
- ADC_11db
Диапазоны измеряемых напряжений при этом будут составлять:
- 0dB ослабление (ADC_ATTEN_DB_0) дает диапазон до 1.1V.
- 2.5dB ослабление (ADC_ATTEN_DB_2_5) дает диапазон до 1.5V.
- 6dB ослабление (ADC_ATTEN_DB_6) дает диапазон до 2.2V.
- 11dB ослабление (ADC_ATTEN_DB_11) дает диапазон до 3.3V.
Однако из-за характеристики АЦП наиболее точные результаты измерений получаются в более узком диапазоне, о чем заявлено в документации. На начальном и конечном участке более или менее линейная характеристика АЦП изгибается и становится более пологой, поэтому измерения очень неточные. В документации указаны рекомендуемые диапазоны измерений:
Таким образом первая причина для приобретения ГЗМ - измерять напряжения менее 100mV и более 3200mV ESP32 не умеет в принципе.
Вторая причина - точность измерений при разных уровнях аттенюации.
При измерении напряжений от 0.1 до 3.2V приемлемая точность будет до 0,01V. (ага 12 битный говорили они....)
Третья причина приобретения ГЗМ при работе с ESP32 - АЦП не линейный:
Из графика понятно, что пересчитать значение полученное с АЦП в вольты по формуле voltage_value = (ADC_VALUE * 3.3 ) / (4095); не получится.
С нелинейностью есть народные методы борьбы. Оперативки в ESP32 пруд пруди. Берем регулируемый источник напряжения (например встроенный в ESP32 ЦАП), подаем с этого источника напряжение с шагом 3,3/4095 и на каждом шагу записываем в массив чему равно значение с АЦП при определенном напряжении. На GitHub есть даже библиотека на эту тему:
после создания набора
значение АЦП <-> значение напряжения на входе АЦП
подгружаем этот набор в оперативную память на этапе загрузки скетча и пользуемся им как эталоном. Главный минус тут - эту процедуру придется делать для каждой новой платы ESP32.
Однако, все не так плохо - для получения значения напряжения в среде Arduino IDE производителем предусмотрена специальная функция:
uint32_t analogReadMilliVolts (uint8_t pin);
её исходный текст можно посмотреть тут (очень многа букаф):
Пример работы можно найти в примерах:
Для тестирования соберём аналог Китайского вольтметра с ТОЧНОСТЬЮ диапазона 0....20V (0.01V). На вход будем подавать напряжение от 0....3.3V с самодельного ЛБП.
Напряжение подаётся через резистор 10кОм. На входе керамика 0.1мкф.
Если у вас нет дисплея - в папке с примерами к этой статье будет пример для передачи показаний в монитор порта.
Каждые 50мс производится чтение напряжения на ножке 36 и их суммирование. Каждые 0.5с производится усреднение всех накопленных значений измерений.
Осталось вывести результаты на дисплей и самодельный аналог Китайского вольтметра готов.
Резюме: АЦП в ESP32 полная шляпа имеет некоторые особенности, которые необходимо учитывать при работе с ним. Для передачи в сеть показаний о состоянии батарей, цепей питания или других грубых измерений - подойдет.
Для работы с аналоговыми датчиками, либо придется докупить ГЗМ и довольствоваться некими пороговыми значениями, либо прикрутить внешний АЦП.
ГЗМ можно купить тут:
Работа с внешним АЦП будет разобрана в одной из следующих статей.
Ссылка на официальную документацию тут.
Примеры кода из статьи тут:
Оглавление канала доступно тут:
Всем удачи!