3.12. Чтение аналогового сигнала с помощью ADC

До сих пор камера считывала цифровые сигналы – вывод находится либо в состоянии 0, либо 1, переключатель разомкнут или замкнут. Большинство сигналов, поступающих от реальных датчиков, являются аналоговыми: это непрерывное напряжение, которое плавно изменяется в некотором диапазоне. Фоторезистор проходит через все напряжения между шинами питания по мере изменения окружающей яркости. Выход датчика температуры смещается на несколько милливольт по мере нагрева помещения. Выход микрофона повышается и понижается вместе с окружающим звуком.

Мостом между ними служит аналого-цифровой преобразователь (ADC). Он измеряет напряжение на выводе и возвращает целое число, которое Python может прочитать как любое другое значение.

3.12.1. Квантование

Цифровое значение не может точно представить непрерывное напряжение. Задача ADC – квантовать, то есть привязывать каждый отсчёт к ближайшему из фиксированного набора уровней. N-битный ADC имеет 2^N уровней; 12-битный преобразователь имеет 4096 уровней, распределённых по своему входному диапазону.

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

Квантование: каждый отсчёт аналогового сигнала (сплошная линия) округляется до одного из конечного набора цифровых уровней (ступенчатая пунктирная линия).

Напряжение между двумя соседними уровнями – это шаг квантования ADC; всё, что меньше него, исчезает при округлении. 12-битный ADC в диапазоне 3,3 В имеет шаг квантования около 3.3 / 4096 0.8 mV – достаточно мелкий, чтобы большинство сигналов выглядели в программе фактически непрерывными.

3.12.2. Класс machine.ADC

machine.ADC оборачивает один аналоговый входной канал. Создайте его с указанием вывода, который вы хотите считывать, затем вызовите read_u16():

from machine import ADC

adc = ADC("P6")
value = adc.read_u16()
print(value)

read_u16() всегда возвращает беззнаковое 16-битное целое число от 0 до 65535. Собственное разрешение ADC зависит от платы (12 бит на STM32, в других случаях зависит от порта); результат выравнивается по левому краю в 16 битах, так что аппаратные подробности не проникают в Python – значение 65535 соответствует полной шкале независимо от чипа.

Опорное напряжение – вход, соответствующий полной шкале, – зависит от платы. Посмотрите значение для вашей камеры в Платы OpenMV. Всё, что выше опорного напряжения, считывается как полная шкала (и может повредить вывод, если превысит абсолютно максимальное входное напряжение).

3.12.2.1. Преобразование отсчётов в напряжение

Отображение отсчётов в напряжение линейно, причём отсчёт полной шкалы точно соответствует Vref:

voltage = counts × Vref / 65535

В коде:

VREF = 3.3  # cam-dependent; see the quickref
counts = adc.read_u16()
voltage = counts * VREF / 65535
print(voltage, "V")

3.12.3. Делители напряжения

Два последовательно соединённых резистора между шиной напряжения и землёй образуют делитель напряжения. Узел между ними находится под напряжением, заданным отношением двух резисторов:

Делитель напряжения. Vin наверху соединяется через R1 с узлом, отведённым как V_out, который затем соединяется через R2 с землёй.

Делитель напряжения: R1 и R2, соединённые последовательно, понижают Vin до V_out.

V_out = Vin × R2 / (R1 + R2)

Равные резисторы дают половину напряжения шины; R2, намного меньший R1, помещает отвод близко к земле; намного больший R2 помещает его близко к шине.

Формула предполагает, что ничто иное не потребляет заметного тока с V_out. Вывод ADC обладает высоким импедансом (мегаомы, наноамперы) и легко удовлетворяет этому условию, поэтому делитель, питающий ADC, ведёт себя так, как предсказывает формула.

3.12.4. Потенциометры

Потенциометр – это единственный физический компонент, который представляет собой именно делитель напряжения со скользящим движком, перемещающим отвод между двумя концами. Поворот ручки изменяет R1 и R2 одновременно, сохраняя постоянной их сумму (полное сопротивление потенциометра).

Потенциометр, подключённый между 3,3 В и землёй. Движок отведён к выводу ADC.

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

Потенциометр – это классическое устройство ввода для испытания ADC. Подключите один конец к 3.3 V, другой к земле, а движок к выводу с поддержкой ADC; поворот ручки проводит движок через все напряжения между шинами.

import time
from machine import ADC

pot = ADC("P6")
VREF = 3.3

while True:
    counts = pot.read_u16()
    voltage = counts * VREF / 65535
    print(voltage, "V")
    time.sleep_ms(100)

3.12.5. Чтение более высоких напряжений с помощью делителя

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

Делитель напряжения, понижающий высокое V_in до вывода ADC. R1 идёт от V_in вниз к соединению, которое отведено горизонтально к выводу ADC; R2 продолжается от соединения вниз к земле.

Масштабирование высоковольтного источника под ADC: R1 и R2 образуют фиксированный делитель напряжения, отвод которого питает вывод ADC.

Подберите R1 и R2 так, чтобы поделённое напряжение оставалось в диапазоне ADC при наибольшем ожидаемом вами входном напряжении:

V_adc = V_in × R2 / (R1 + R2)

Для максимального V_in = 12 V и опорного напряжения 3,3 В отношение R2 / (R1 + R2) должно быть не более 3.3 / 12 0.275. Распространённый выбор с небольшим запасом – R1 = 33 , R2 = 10 . Отношение составляет 10 / 43 0.233, поэтому V_adc достигает максимум около 12 × 0.233 2.79 V – безопасно ниже Vref.

Чтобы восстановить исходное V_in из показания ADC, обратите формулу делителя:

V_in = V_adc × (R1 + R2) / R2

В коде:

from machine import ADC

R1 = 33_000
R2 = 10_000
VREF = 3.3

adc = ADC("P6")

counts = adc.read_u16()
v_adc = counts * VREF / 65535
v_in = v_adc * (R1 + R2) / R2
print(v_in, "V")

Несколько практических замечаний:

  • Делитель непрерывно потребляет V_in / (R1 + R2). При R1 + R2 = 43 и V_in = 12 V это около 280 мкА – обычно пренебрежимо мало, но если источник работает от аккумулятора, рассмотрите более крупные резисторы (от 100 кОм до 1 МОм), чтобы сократить ток покоя.

  • Допуск резисторов (обычно ±1 % или ±5 %) напрямую влияет на точность измерения. Два резистора с допуском ±5 % могут дать восстановленному V_in погрешность в худшем случае около ±10 %.

  • Выходной импеданс делителя в сочетании с любой паразитной ёмкостью образует фильтр нижних частот для входа. Для быстро меняющихся сигналов это имеет значение; для проверки напряжения аккумулятора – нет.