Аналоговые измерения с Arduino

Тестирование шума АЦП Arduino

Arduino имеет несколько аналоговых входов, используя которые можно измерять параметры аналоговых величин. Это может быть напряжение, ток, сопротивление, температура, свет и так далее. В некоторых случаях для преобразования физических величин в электрические сигналы могут потребоваться специальные датчики. Сегодня я расскажу об использовании и проведу тест производительности аналого-цифрового преобразователя (АЦП) Arduino. Тест я буду производить, используя оригинальную плату Arduino Mega 2560, в основе которой лежит микроконтроллер ATMega2560, работающий на частоте 16 Мгц. Микроконтроллер ATMega328, на котором основаны Arduino Uno и Arduino Nano, также работает на частоте 16 МГц, так что все вышеизложенное, скорее всего, справедливо и для этих и аналогичных плат.

 

analogRead

Давайте посмотрим сколько же времени занимает аналого-цифровое преобразование с использованием стандартной функции analogRead.

Для определения моментов начала и конца преобразования я буду использовать 12 вывод в качестве маркера. Для начала повторим эксперимент, который я описывал в статье Оптимизируем digitalWrite на Arduino. Будем изменять уровень напряжения на 12 цифровом пине между состояниями LOW и HIGH. Для чистоты эксперимента я помещу внутрь loop бесконечный цикл.

Скетч, реализующий простые переключения на 12 цифровом выводе выглядит следующим образом:

Воспользуемся осциллографом и посмотрим на временные параметры работы этой программы:

Использование регистров Arduino для изменения значений портов

Отсюда видно, что время переключения состояния пина занимает у нас 62 нс (длительность положительного импульса).

Теперь немного изменим скетч и добавим между переключениями функцию чтения аналогового сигнала analogRead на 3 аналоговом пине:

Осцилограмма сигнала на 12 цифровом пине теперь будет выглядеть следующим образом:

Время работы analogRead

Длительность переключения в 62 нс и время циклического возврата к началу работы программы в 124 нс не превышают погрешность измерения на этом временном масштабе и мы можем пренебречь этими временными промежутками. Отсюда видно, что время, которое затрачивается на аналого-цифровое преобразование примерно равно 112 мкс, поэтому максимальная частота выборки при использовании функции analogRead не превышает 8.9 кГц.

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

 

Используем прерывания АЦП

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

Разовая выборка

Разовая выборка — это на самом деле то, что Arduino делает при вызове функции analogRead. Мы не сможем получить значительных преимуществ, реализовав разовую выборку с помощью других средств. Поскольку перед запуском АЦП, в первую очередь проверяется флаг готовности АЦП, то это означает, что проверка флага в цикле ничем не отличается от того, что делает Arduino.

Непрерывная выборка

Хорошей идеей при непрерывной выборке сигнала является использование прерываний. Микроконтроллеры ATMega328 и ATMega2560 могут быть переведены в режим непрерывной выборки (free running mode). В этом режиме АЦП запускается автоматически после завершения предыдущей обработки. Каждый раз преобразование заканчивается генерированием прерывания, которое вызывает функцию обработки прерывания ISR (ADC_vect), в которой результат аналого-цифрового преобразования может быть считан и обработан.

Для включения режима непрерывной выборки необходимо установить три регистра: ADMUX, ADCSRA и ADCSRB. Детальное описание этих регистров можно найти в технических руководствах к микроконтроллерам.

Внутреннее опорное напряжение 1.1 В и входной аналоговый канал ADC3 выбираются при помощи ADMUX. Тактовая частота задается при помощи ADCSRA и в нашем примере установлена в виде делителя ÷16. Одно аналоговое преобразование занимает 13 тактовых периодов. Частота дискретизации может быть вычислена, исходя из тактовой частоты микроконтроллера: 16 Мгц/(16*13) ≈ 77 кГц. Установкой 6 бита регистра ADCSRA в состояние HIGH,  запускается непрерывная выборка.

Результат аналого-цифрового преобразования считывается в функцию обработки прерывания ISR (ADC_vect). Поскольку, результат имеет длину 10 бит, то он делится на два регистра ADCL и ADCH, размером в один байт каждый. Для корректного чтения значения сначала нужно считать значение регистра ADCL, а затем — регистра ADCH.

Пример скетча, в котором результат, полученный из АЦП копируется в целочисленную переменную analogValue:

Результат работы программы на экране осциллографа:

Осциллограмма использования прерываний АЦП

Для измерения времени выполнения мы переводим состояние пина в LOW, затем считываем АЦП, после чего вновь устанавливаем высокий уровень. На вызов обработчика прерывания требуется время, с этим и связана достаточно большая продолжительность положительной части периода.

Цикл loop теперь полностью свободен и может использоваться для обработки какого-либо кода.

 

Опорное напряжение

Для измерения аналогового сигнала у нас должен быть некоторый уровень напряжения, с которым мы будем производить сравнение. В микроконтроллерах ATMega328 и ATMega2560, которые используются в Arduino опорное напряжение также является максимальным напряжением, которое может быть измерено. Напряжения всегда измеряются относительно земли. В Arduino есть три возможных источника опорного напряжения:  AVcc — которое соединяется с цифровой линией питания 5 В, внутреннее напряжение 1.1 В (для Arduino Mega возможен еще вариант 2.56 В) и внешний источник опорного напряжения. Из-за того, что измерение входных напряжений производятся относительно опорного напряжения, флуктуации опорного напряжение оказывают влияние  на результат.

Опорное напряжение можно установить, используя функцию analogReference или при помощи битов REFT[1:0] в регистре ADMUX.

Опорное напряжение AVcc

AVcc является опорным напряжением по умолчанию и оно используется когда измеряемые напряжения напрямую зависят от напряжения источника питания. Например, в случае, где нужно измерить напряжение в резисторном полумосте, как показано на рисунке ниже.

Опорное напряжение 5В в Arduino

Использование опорного напряжения 5В при измерении сопротивления в полумосте

Если по каким-то причинам напряжение источника питания упадет, то и напряжение в точке соединения двух резисторов упадет пропорционально. Из-за того, что теперь опорное и входное напряжение изменяются пропорционально, то и результат АЦП останется таким же.

Внутренне опорное напряжение 1.1 В

Используйте внутреннее опорное напряжение 1.1 В для точных измерений внешних напряжений. Опорное напряжение 1.1 В более стабильно и не зависит от изменения напряжения питания или температуры. Таким образом, можно производить измерения абсолютных значений. В Arduino Mega также возможен вариант опорного напряжения 2.56 В. примере на рисунке ниже используется опорное напряжение 1.1 В и делитель напряжения 10:1 для измерения внешнего напряжения в диапазоне от 0 до 11 В.

Внешнее опорное напряжение Arduino

Использование внешнего опорного напряжения или внутреннего напряжения 1.1 В при измерении внешних напряжений


 

Погрешность

В соответствии с техническим руководством для микроконтроллеров ATMega328 и ATMega2560 опорное напряжение составляет 1.1 ±  0.1 В. Это достаточно большой допуск. Измеренное опорное напряжение тестируемой Arduino Mega 2560 было 1.089 В при температуре окружающего воздуха 21 °С и температура корпуса микроконтроллера была 29 ºC.

Охлаждающий спрей FREEZE 75/200

Я охладил корпус микроконтроллера, не проводящим ток охлаждающим спреем Kontakt Chemie FREEZE 75/200 до температуры -18 °С, при этом измеренное опорное напряжение снизилось до 1.084 В. Таким образом, температурный дрейф составил примерно 100 ppm(миллионных долей) / °C.

Тестовый скетч:

Аналоговый пин 3 был подключен к источнику напряжения 0.545 В. При температуре 29 °C результат должен быть: (0.545/1.089)*1024 = 512 (реально полученное значение — 511). При температуре -18 °C должно быть (0.545/1.084) * 1024 = 515 (реально полученное значение тоже 515).

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

 

Шум

Одним из способов измерить уровень шума является определение разброса значений, получаемых с АЦП. Для этого подадим стабилизированное постоянное напряжение на один из аналоговых входов и преобразованные при помощи АЦП значения используем для построения гистограммы.

Тестовая цепь

Схема на рисунке ниже обеспечивает тестовое напряжение для Arduino.

Тестовая схема шума АЦП Arduino

Схема, подающая регулируемое постоянное напряжение на аналоговый вход Arduino

Стабилизированный регулируемый источник питания выдает напряжение 0.55 В, что составляет половину от опорного напряжения в 1.1 В. На фотографии ниже видно, что встроенный в мой регулируемый источник питания вольтметр явно привирает, показывая напряжение на выходе 0.4 В.

Стабилизированное напряжение 0.55В

Сигнал дополнительно фильтруется при помощи цепочки R1, C1, C2 и подключается через резистор R2, имеющий сопротивление 100 Ом к аналоговому входу A3 Arduino.  Земля подключается к пину GND Arduino.

Шумовая составляющая на входе Arduino выглядит следующим образом:

Шумовая составляющая на входе АЦП Arduino

Отсюда видно, что среднеквадратическое значение амплитуды переменной составляющей измеряемого напряжения на входе АЦП  Arduino составляет лишь единицы милливольт.

Биннинг

АЦП микроконтроллеров ATMega328 и ATMega2560 имеет разрешение 210 = 1024 бита. Идея биннинга состоит в подсчете частоты наблюдения определенного значения. Создается массив со 1024 значениями, называемых бинами, которые представляют каждое из возможных значений АЦП. Так как доступная память ограничена, могут быть созданы бины только размером в байт. Число отсчетов, следовательно, ограничивается 255.

Программы

Протестируем шум, используя функцию analogRead, а затем используем прерывания. Две программы, по сути, делают одно и то же: определяют массив, состоящий из 1024 бин. В функции setup все бины инициализируются нулем и выбирается опорное напряжение 1.1 В.

Обе программы производят 10000 фиктивных операций чтения аналогового значения. После этого запускается биннинг и на каждом результате АЦП, соответствующий бин увеличивается на единицу. Если один из 1024 бинов достигнет максимума из 255 значений, выборка останавливается и все 1024 значения бина отправляются на компьютер.

Код примера биннинга измеренных значений, используя функцию analogRead:

Показать/скрыть код

Код примера биннинга измеренных значений, используя прерывания:

Показать/скрыть код

Тестируемые частоты

Тест проводился с использованием функции analogRead и используя режим непрерывной выборки. Так как в последнем случае частоту выборки можно изменять, то тестировались четыре различные частоты выборки, задаваемые путем изменения значения в строке ADCSRA = 0xAC. Тестируемые частоты: 9.6 кГц (тактовая частота clk÷128), 19.2 кГц (clk÷64), 38.4 кГц (clk÷32) и 76.9 кГц (clk÷16). Частота выборки при использовании функции analogRead, как мы выяснили выше примерно равна 8.9 кГц.

Результаты

Результаты для обоих способов и различных частот выборки оказались похожими. Выборки разделились на 2 бина и лишь некоторые выборочные значения попали в третий бин. Это значит, что уровень шума во всех случаях достаточно низкий.

Для каждой частоты дискретизации АЦП измеренное напряжение постоянного тока дает узкий пик на гистограмме

Для каждой частоты дискретизации АЦП измеренное напряжение постоянного тока дает узкий пик на гистограмме


 

Переключение между входами

Выбор аналогового входа осуществляется в строке analogPin=n где n является номером аналогового пина или изменением битов выбора аналогового канала MUX[3:0] в регистре ADMUX. Особое внимание должно быть уделено при использовании режима непрерывной выборки: аналоговый канал нужно выбрать перед стартом нового аналогового преобразования. В процедуре обработки прерывания выбирается аналоговый вход, который будет считываться в момент следующего прерывания.

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

Изменения в коде для функции analogRead при переключении между аналоговыми входами:

Изменения в коде для режима непрерывной выборки при переключении между аналоговыми входами:

Результаты

Оба измеренных напряжения видны как два выступа на гистограммах. На рисунке ниже представлены гистограммы пяти тестов: с использованием функции analogRead, непрерывная выборка с clk÷128, clk÷64, clk÷32 и clk÷16. Измеренные значения первого напряжения (результат обработки АЦП = 511) не отклоняются от предыдущего теста шума. Измерение по-прежнему точное. Окружающих бинов очень мало, это означает, что уровень шума не увеличился.

Гистограммы измерения двух напряжений на Arduino

На каждой из пяти гистограмм показаны две области с выступами, представляющие два измеренных напряжения


 

Частота выборки и разрешение

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

Генератор функций подключен к аналоговому входу Arduino

Генератор функций подключен к аналоговому входу Arduino

Генератор функций подает треугольную волну с напряжением размаха в 25 мВ и напряжением смещения (= среднему значению) в 0.55 В. На каждом измерении частота сигнала выбирается таким образом, чтобы частота выборки была в 163 раза выше.

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

Результаты

Результаты тестирования показали, что функция analogRead, работающая с низкой частотой дискретизации и непрерывная выборка с частотой clk÷128 имеют достаточную плоскую вершину: все значения в диапазоне встречаются с одним и тем же числом повторений. Но на более высоких частотах дискретизации (clk÷64, clk÷32 и clk÷16) возникают провалы в области биннинга и с ростом частоты ситуация ухудшается.

Болшая частота выборки приводит к провалам

Большая частота выборки приводит к провалам

В технических описаниях на микроконтроллеры ATmega об этом явлении упоминается: на частоте АЦП-преобразования выше 200 кГц разрешение снижается. На тактовой частоте 16 МГц и, используя коэффициент деления 64, частота работы АЦП становится равной 250 кГц, что чуть выше порога, где снижается разрешение, что и подтверждается проведенными измерениями.

 

Как вы оцениваете эту публикацию? 1 звезда2 звезды3 звезды4 звезды5 звезд (59 голосов, средняя оценка: 4.90 из 5)
Loading ... Loading ...

Вы можете пропустить чтение записи и оставить комментарий. Размещение ссылок запрещено.

18 комментариев к записи “Аналоговые измерения с Arduino”

  1. Рашит пишет:

    Здравствуйте Андрей! Почему напряжение измеряют среднеквадратичным значением, а не средним арифметическим?

  2. Рашит пишет:

    Спасибо!

  3. Bazilik пишет:

    Добрые дяди... Наконец руки дошли и я сделал вот так:

    Cхема

    Возникла пренеприятнейшая гадость... Всё в принцыпе работает как и должно, НО иногда вылезают какие-то помехи что-ли... Вобщем раз-два в сутки стабильно происходит ложная сработка (датчик в покое). Закономерности (день/ночь/час/минута/вздох/скрип/мат) не выявлено. Разве что питание... Думаю на него, но с какого фига? Оно ведь DC 10В через БП. Или что-то ещё может быть?

    Запитать от аккумулятора для теста конечно можно... Но сколько выжидать для этого придётся...

    Чего греха таить — использую FLprog. Вот таким образом считываю сигнал с датчика:

    Хотелось бы использовать значение 1 или 2. Но блин помехи...

    PS: Могу решить проблему — тупо программно сильнее загрубив чувствительность. Но в таком случае толку от датчика уже не будет. Поможете советом?

    PS2: Готов заняться поиском источника помех. Но КАК реализовать что-то типа осциллографа в домашних условиях с минимальными затратами? Что-то типа отображения текущего сигнала визуально на экране монитора... Видеть бы это — думаю нашёл бы проблему...

    PS3: Какую роль выполняет диод Зенера здесь?

    PS4: Вопрос немного не по теме... Как вычислить где плюс, где минус на пьезо излучателе, если все залито клеем и оба провода белые? )))

    Работает и так и так)) Помехам пофигу полярность))

    Спасибо большое))

    • Андрей пишет:

      Я полагаю, вам проще всего создать программный фильтр, подобный тому, что устраняет эффект дребезга контактов на кнопках. Но вам нужно пропускать однократные срабатывания датчика, если между ними проходит слишком большое время. Можно экспериментально потестить, какой именно поток данных порождает истинное срабатывание датчика, и программно отсеять «нетипичное поведение». Программные фильтры помех используются довольно часто, потому что это возможность корректировать работу устройств обновлением прошивки, не меняя железо. Как вариант пришло в голову, сам не пробовал еще это, но видел примеры скетчей недавно,случайно, где пограммными фильтрами отсеивают очень специфические помехи.)))

  4. 1. Пьезоэлектрический датчик обладает большим сопротивлением, поэтому соединительные провода являются источником достаточно сильных помех. Нужно либо подключить датчик напрямую к микроконтроллеру, либо, непосредственно к датчику подключить повторитель на полевых транзисторах или операционном усилителе (они обладают большим входным и маленьким выходным сопротивлением). Еще вариант — использовать дифференциальный усилитель, опять же, на полевых транзисторах или ОУ.

    2. Самый дешевый вариант осциллографа — использовать АЦП звуковой карты компьютера. Погуглите на тему «осциллограф звуковая карта». Правда, полоса пропускания будет ограничена звуковой частотой где-то в районе 20 кГц. Помеха же может быть и на частоте 100 кГц и выше.

    3. Стабилитрон защищает вход микроконтроллера от повышенного напряжения (пьезоизлучатель может сгенерировать напряжение, превышающее допустимое по входу микроконтроллера), хотя, по идее, в микроконтроллере встроены по входам защитные диоды — так что использование стабилитрона может быть лишним.

    4. Обычно, полярность подключения для пьезоилучателя не имеет значения. Исключение — пьезоизлучатель со встроенным генератором. При неправильном подключении просто не работает генератор. Но на излучателях с генератором «+» помечается, значит — у вас без генератора и можно подключать как угодно.

  5. Феофан пишет:

    Ок. С измерениями всё понятно.

    А как ещё можно использовать аналоговые входы? Они на OUT не переключаются?

    • Неа, не переключаются. Аналоговый вход — это вход аналого-цифрового преобразователя. Аналоговый выход можно реализовать, используя выходы ШИМ (PWM) со сглаживающим фильтром (например, в виде RC-цепочки) на выходе.

      • Феофан пишет:

        Неверно сформулировал вопрос и получил не тот ответ :)

        В документации написано, если не вру, что их можно использовать как Digital IN и Digital OUT.

        • Ага, неправильно вас понял :-) Ножки микроконтроллера Atmega, на которые выведены каналы АЦП можно использовать как обычные цифровые входы и выходы, сконфигурировав их соответствующим образом.

  6. Саша пишет:

    Как можно программно увеличить разрядность до 16 бит? (квантование там, дизеринг, не помню точно как это названо). С тем, чтобы получить в итоге 16бит при 44 кГц оцифровке для записи звука с микрофона в wav файл. В сети есть инфо про разгон ардуино про мини на кварце 25МГц. Возможно ли все это осуществить?

    • Повысить разрядность АЦП можно, используя метод передискретизации (оверсемплинг). Однако в этом случае, для добавления разрядности на 1 бит, необходимо увеличить частоту дискретизации в 4 раза. Частота дискретизации в 10-разрядном АЦП в микроконтроллерах с архитектурой AVR ограничивается величиной в 1 МГц. Чтобы увеличить разрядность с 10 до 16 бит, потребуется частота работы АЦП в районе 45 МГц. Для МК Atmega это нереально. Но может я чего не догоняю...

      Дизеринг, в случае со звуком, используется для улучшения его восприятия. Для этого к исходному сигналу подмешивается псевдослучайный сигнал (dither, от староанглийского didderen — дрожать) — «белый» шум небольшой амплитуды. Точно также делают и для улучшения визуального восприятия графических изображений — картинка искусственно зашумляется, границы цвета и яркости становятся не такими резкими.

  7. sim31 пишет:

    Еще было бы интересно узнать о влиянии входного сопротивления, на точность измерения. Не всегда есть возможность работать с источником напряжения с сопротивлением 100 ом.

  8. Про понятия Погрешность, точность и разрешение — это чтобы мы на одном языке разговаривали и понимали друг друга.

    Если у нас ко входу АЦП подключить источник с бОльшим выходным сопротивлением, то мы получим на входе сигнал с меньшей амплитудой, а разброс значений в абсолютном выражении (шум) останется таким же. Соответственно, точность снизится. Идеальный вольтметр (и вход АЦП) должен обладать бесконечно большим сопротивлением и бесконечно малыми емкостью и индуктивностью по отношению к нагрузке.

  9. Иван Б пишет:

    Спасибо за статью!

    Наткнулся на забавный факт — если вместо «ISR (ADC_vect)» написать «ISR (ADC_Vect)» (V заглавная), то ошибок компиляции не возникает — программа просто перестает работать.

    Потерял 2 дня вылавливая эту опечатку в своем коде.

  10. Vasily пишет:

    Статья отличная! Спасибо.

    Но покритикую последний этап. Особенно последняя серия графиков выглядит не очень внушительно. На большой частоте у вас сумма в бинах гораздо ниже. При большей частоте, это означает гораздо меньшее время измерений, что не есть хорошо. И если у вас устройство выполняет этот код сразу после перезагрузки, совсем не хорошо. У вас 1.5 секунды загружается Arduino и сразу измеряет что-то за десятую долю секунды. Как правило сразу после старта лично у меня АЦП вообще выдает ерунду первые десятые доли секунды, может это проблема моего экземпляра. Но если я вас правильно понял, то с делителем 16 у вас сумма порядка 2000 выборок, плюс 10000 вы ожидаете, это чуть более 12+60 периодов, на частоте около 500Гц это как раз где-то полторы десятых доли секунды. Так что может быть вы в бины подсчитали просто ерунду. Правильнее было выйти сначала на рабочий режим (и считать его не в выборках, а в секундах, либо если в выборках, то гораздо больше выборок взять), а уже потом считать что-то. И считать гораздо дольше чем один десяток периодов. Не уверен, что вешать на Arduino подобный учет бинов это правильно. Я бы попробовал менять скорость порта в соответствии со скоростью АЦП и передавать всю считанную информацию на ПК, где уже ее и считать нормально на протяжении достаточно большого интервала. Идея вообще замечательная, но в текущей реализации в результаты не верю, хотя они вполне могут оказаться достоверными.

    И еще в одном месте поправлю вас: «результат должен быть:(0.545/1.089)*1024 = 512 (реально полученное значение — 511)». Вообще АЦП выдает значение от 0 до 1023 в соответствие напряжению от 0 до 1.089 в вашем случае. Так что на самом деле должно быть так: (0.545/1.089)*1024 = 511.5. Между двумя кодами АЦП. Что собственно вы и получили. Не уверен, что в таком ключе можно что-то говорить об уровне шума, вспоминая про малое время измерений.

    Если я что-то неверно понял, заранее извиняюсь, в любом случае статья интересная.

  11. elisej пишет:

    можно на ардуино собрать инвертор приближенной к чистой синусоиде и как вы бы это сделали?

    • Здесь прекрасно описана теория ШИМ-преобразователей. В статье для генерации ШИМ-модулированного сигнала используется специализированная микросхема TL494 (отечественный аналог м/сх К1006ЕУ4). Среди аналогичных микросхем можно еще посмотреть усовершенствованные версии этой микросхемы Texas Instruments: TL594 (улучшенная точность усилителей ошибки и компаратора), TL598 (с двухтактным (pnp-npn) повторителем на выходе) или же от ON Semiconductor SG3525. SG6105 частенько можно встретить в компьютерных блоках питания. Однако, вполне можно генерировать ШИМ и используя Arduino. Здесь можно посмотреть схемное решение трехфазного инвертора на базе Arduino MEGA2650. Здесь есть пример кода генерации необходимого ШИМ-сигнала, используя таблицу для вычисления скважности.

Оставить комментарий