Руководство · Начальный · 30 min

Чтение аналоговых напряжений: опорные напряжения и делители

analogRead возвращает отношение, а не напряжение. Выбор стабильного опорного напряжения, расчёт делителя и создание достоверного монитора аккумулятора.

Введение

analogRead() — самая удобная функция Arduino и одна из самых обманчивых. Она возвращает число от 0 до 1023, все считают это число напряжением, и обычно это ни к чему не приводит.

На роботе всё ломается, потому что у робота есть двигатели, а двигатели изменяют именно то, относительно чего измеряется это число.

analogRead возвращает отношение

ADC не измеряет вольты. Он сравнивает вход с опорным напряжением и сообщает отношение:

reading = 1023 × V_in / V_ref

На Arduino Uno V_ref по умолчанию равен AVCC — шине питания платы 5 V. Поэтому analogRead() на самом деле отвечает на вопрос «какую долю от 5 V составляет этот сигнал?», а перевод в вольты умножением на 5/1023 корректен только пока шина равна ровно 5 V.

Вы уже знаете, что это не так, как только работают двигатели.

Опорное напряжение — источник ошибки

Подключите аккумулятор через делитель к A0 и наблюдайте за числом при запуске двигателей.

Два наложенных графика. Верхний показывает отсчёты ADC в зависимости от напряжения аккумуляторного блока от 6 до 8.4 вольт для двух схем: схема с опорной шиной 5 вольт и делителем 20k/10k растёт примерно от 409 до 573 отсчётов, а схема с внутренним опорным напряжением 1.1 вольт и делителем 68k/10k — примерно от 715 до 1002. Нижний график показывает напряжение аккумулятора, которое каждая схема сообщает для постоянного блока 7.4 вольт, когда реальная шина 5 вольт меняется от 4.2 до 5.2 вольт: схема с опорой от шины падает с 8.8 вольт до 7.1 вольт, а схема с внутренней опорой остаётся на 7.4.
Снизу: аккумуляторный блок не меняется. Когда шина под нагрузкой двигателей опускается с 5.0 V до 4.2 V, код, доверяющий опоре 5 V, сообщает, что аккумулятор растёт с 7.4 V до 8.8 V. Внутренний источник опорного напряжения всё время показывает 7.40 V. Сверху: те же 10 бит распределены по 286 отсчётам вместо 164, потому что делитель рассчитан на заполнение опорного диапазона. Скачать SVG

Защита по низкому напряжению, построенная на первой схеме, делает противоположное своей задаче: чем сильнее работает робот, тем исправнее выглядит аккумулятор. И сбой тихий, потому что каждый отдельный отсчёт является совершенно допустимым отношением.

Исправление — стабильное опорное напряжение. В ATmega328P есть встроенный источник опорного напряжения на полосовом переходе 1.1 V:

analogReference(INTERNAL);   // 1.1 V on an Uno; INTERNAL1V1 on a Mega
analogRead(A0);              // discard — the reference needs a moment to settle

После переключения опоры всегда отбрасывайте первый отсчёт. Внутренней опоре и входному конденсатору нужно время, а первый замер выполняется до того, как оно пройдёт.

Расчёт делителя

Теперь опорное напряжение равно 1.1 V, а блок — 8.4 V, поэтому делитель должен привести верх диапазона чуть ниже опорного напряжения:

V_adc = V_pack × R2 / (R1 + R2)

При R1 = 68 kΩ и R2 = 10 kΩ отношение равно 0.128, поэтому полностью заряженный блок 8.4 V даёт 1.077 V — чуть ниже опоры, почти на всю шкалу. Три числа, которые стоит проверить в любом проектируемом делителе:

Проверка Этот делитель Почему
Верх диапазона и опора 1.077 V и 1.1 V При превышении опоры сигнал срезается
Ток покоя 108 µA За день расходует 2.6 mAh — пренебрежимо мало по сравнению с 2000 mAh
Выходное сопротивление (R1∥R2) 8.7 kΩ Держите ниже ~10 kΩ, см. ниже

Сравните с наивной схемой — делителем 20 kΩ/10 kΩ, считываемым относительно шины 5 V. Полный блок в нём даёт 2.80 V, то есть 573 отсчёта из возможных 1023, а на всём диапазоне аккумулятора 6.0–8.4 V используется 164 отсчёта против 286 у лучшей схемы. Почти вдвое больше разрешения при тех же двух резисторах и одной строке кода.

Разрешение не равно точности

Десять бит относительно опоры 5 V дают 4.88 mV на шаг, а относительно опоры 1.1 V — 1.07 mV. Ни одно из этих чисел не является точностью.

Что действительно вас ограничивает:

  • Допуск опорного напряжения. Источник опорного напряжения на полосовом переходе номинально равен 1.1 V и меняется на несколько процентов между экземплярами микросхем. Это фиксированная ошибка шкалы, поэтому измерьте её один раз для каждой платы: подайте известное напряжение, считайте отсчёты и вычислите настоящую опору. Сохраните эту константу. Это этап калибровки, а не необязательное уточнение.
  • Шум. Питание робота шумное, а усреднение — дешёвый ответ. Усреднение N отсчётов уменьшает случайный шум в √N раза: 16 отсчётов дают в 4 раза меньший шум, то есть один дополнительный бит полезного разрешения. Ошибку шкалы это не устраняет.
  • Выходное сопротивление источника. ADC заряжает небольшой конденсатор выборки и хранения через то, к чему он подключён. Источник с высоким сопротивлением не успевает заполнить его за отведённое время, и остаточный заряд от предыдущего канала всё ещё присутствует.

Последний случай даёт характерный сбой, который стоит назвать.

Каналы, которые читают как соседние

Читайте A0 и A1 по очереди с источниками высокого сопротивления, и каждый отсчёт будет смещаться в сторону предыдущего. Плавно изменяйте напряжение потенциометром, подключённым к A0, и посмотрите, как изменится A1. Ничего не подключено неправильно.

Есть два исправления, и можно использовать оба:

int readSettled(uint8_t pin) {
  analogRead(pin);        // charge the sample-and-hold on this channel
  return analogRead(pin); // then take the one you keep
}

Двойное чтение с отбрасыванием первого результата даёт конденсатору второе окно выборки, чтобы достичь нового напряжения. Второе исправление — снизить выходное сопротивление источника; поэтому в делителе выше используются 68 kΩ/10 kΩ, а не более аккуратные на вид 100 kΩ/100 kΩ с выходным сопротивлением 50 kΩ.

Где это проявляется на роботе

  • Мониторинг аккумулятора, как выше, — именно он делает отсечку по низкому напряжению достаточно достоверной для защиты Li-ion блока.
  • Аналоговые датчики линии. Массив ИК-датчиков отражения (IR) даёт непрерывное значение для каждого датчика, благодаря чему робот, следующий по линии, может вычислять плавное положение, а не флаг «слева/справа». Эти датчики считываются относительно той же шины, которую просаживают двигатели, — одна из причин, почему калибровка робота, следующего по линии, смещается по мере разряда аккумулятора.
  • Измерение тока шунтом или датчиком Холла, выход которого является напряжением.
  • Потенциометры как ручки настройки, самый удобный способ настроить коэффициент ПИД (PID) без повторной прошивки.
  • Не для датчика 5 V на плате 3.3 V. ESP32 или Raspberry Pi нужен делитель для защиты, а не только для диапазона.

Когда всё идёт не так

Симптом Обычно
Показание аккумулятора растёт при запуске двигателей Опора от просадшей шины; используйте INTERNAL
Показание зафиксировано на 1023 Вход выше опорного напряжения — отношение делителя слишком велико
Один канал повторяет предыдущий Слишком большое выходное сопротивление источника; читайте дважды и отбрасывайте первый результат
Показания скачут на десятки отсчётов Шум питания; усредняйте и проверьте силовые шины
Напряжение постоянно отличается на несколько процентов Нерассчитанная внутренняя опора — измерьте её один раз
Первое показание после analogReference() неверно Ожидаемо; отбросьте его
Калибровка датчика линии смещается при разряде аккумулятора Показания с опорой от падающей шины
Используется только 15% диапазона Делитель рассчитан на 5 V, а считывание идёт относительно 1.1 V

Правило, предотвращающее большую часть этих проблем: сначала решите, каким будет опорное напряжение, и только потом принимайте остальные решения. Каждое число, которое даёт ADC, зависит от него, поэтому движущаяся опора превращает каждое показание в результат сразу двух факторов.

Исследуйте граф

Входит в эти сборки

Проекты и учебные пути, в которые входит это руководство.

Дополнительные материалы

Источники