Аналого-цифровые преобразования — АЦП. Аналоговые измерения с Arduino

27.04.2019

Описание портов, работающих как аналоговые входы, платформы Arduino (Atmega8, Atmega168, Atmega328, или Atmega1280)

Аналого-цифровой преобразователь

Микроконтроллеры Atmega, используемые в Arduino, содержат шестиканальный аналого-цифровой преобразователь (АЦП). Разрешение преобразователя составляет 10 бит, что позволяет на выходе получать значения от 0 до 1023. Основным применением аналоговых входов большинства платформ Arduino является чтение аналоговых датчиком, но в тоже время они имеют функциональность вводов/выводов широкого применения (GPIO) (то же, что и цифровые порты ввода/вывода 0 - 13).

Таким образом, при необходимости применения дополнительных портов ввода/вывода имеется возможность сконфигурировать неиспользуемые аналоговые входы.

Цоколевка

Выводы Arduino, соответствующие аналоговым входам, имеют номера от 14 до 19. Это относится только к выводам Arduino, а не к физическим номерам выводов микроконтроллера Atmega. Аналоговые входы могут использоваться как цифровые выводы портов ввода/вывода. Например, код программы для установки вывода 0 аналогового входа на порт вывода со значением HIGH:

pinMode(14, OUTPUT);
digitalWrite(14, HIGH);

Подтягивающие резисторы

Выводы аналоговые входов имеют подтягивающие резисторы работающие как на цифровых выводах. Включение резисторов производится командой

digitalWrite(14, HIGH); // включить резистор на выводе аналогового входа 0

пока вывод работает как порт ввода.

Подключение резистора повлияет на величину сообщаемую функцией analogRead() при использовании некоторых датчиков. Большинство пользователей использует подтягивающий резистор при применении вывода аналогового входа в его цифровом режиме.

Подробности и предостережения

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

Руководство на микроконтроллер Atmega не рекомендует производить быстрое переключение между аналоговыми входами для их чтения. Это может вызвать наложение сигналов и внести искажения в аналоговую систему. Однако после работы аналогового входа в цифровом режиме может потребоваться настроить паузу между чтением функцией analogRead() других входов.

Система Arduino поддерживает обработку аналоговых сигналов. Для входных сигналов мы имеем АЦП (аналогово-цифровой преобразователь), в случае выходного сигнал — возможна модуляция ШИМ (широтно-импульсная модуляция).

В Arduino, сердцем которой является микроконтроллер Atmega, имеется один 10-битный АЦП. Это означает, что считанное значение напряжения может находиться в диапазоне от 0 — 1023. В зависимости от опорного напряжения 1024 значений будут распределены на соответствующий диапазон. В результате мы можем получить различную точность и различный диапазон напряжений, считываемых аналого-цифровым преобразователем.

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

Следует отметить, что аналого-цифровые преобразователи не имеют идеальной линейной характеристики. Это означает, что в определенных интервалах может быть разница между фактическим и считанным значением напряжения. Из вышеизложенного вытекает, что увеличивая диапазон измерения, мы теряем на качестве измерения, уменьшая диапазон, мы увеличиваем точность измерения.

Примечание: Arduino имеет несколько (в зависимости от версии) аналоговых входов, однако АЦП в ней только один. Это означает, что одновременно может быть считано значение только с одного из датчиков, подключенных к аналоговым входам A0… A5 (A0… A15 для Arduino MEGA).

Функция analogReference()

Для правильной работы АЦП требуется опорное напряжение (эталон). Для Arduino опорное напряжение может быть в диапазоне 0…5В (или 0… 3,3В для Arduino с напряжением питания 3,3В). В зависимости от типа используемого микроконтроллера у нас могут быть разные виды опорного напряжения.

Мы можем использовать внутренний или внешний источник опорного напряжения. Функция AnalogReference() предназначена для того, чтобы установить соответствующий источник опорного напряжения. Доступны следующие параметры этой функции:

  • DEFAULT: опорное напряжение составляет 5В или 3,3В (в зависимости от питания) — то есть, оно равно напряжению питания микроконтроллера;
  • INTERNAL: опорное напряжения составляет 1,1В для ATmega168, ATmega328 и 2,56В для ATmega8;
  • INTERNAL1V1: опорное напряжение составляет 1,1В — только для Arduino MEGA;
  • INTERNAL2V56: опорное напряжение составляет 2,56В — только для Arduino MEGA;
  • EXTERNAL: внешнее опорное напряжение, приложенное к выводу AREF — от 0 до 5В.

Параметр DEFAULT выбираем, когда хотим воспользоваться опорным напряжением 5В (питание системы). Это самый простой и одновременно наименее точный способ. Здесь требуется хорошая стабильность питания.

Использование INTERNAL является хорошим вариантом, в ситуации, когда мы создаем проект, предназначенный для конкретной версии Arduino. Внутренние опорное напряжение является относительно стабильным и достаточным в большинстве случаев.

Наиболее точным вариантом является использование внешнего источника опорного напряжения. Существуют специальные источники опорного напряжения (ИОН). Плюсом является возможность получения необходимого точного опорного напряжения, например, 1,024В или 2,048В, что облегчает интерпретацию данных, считываемых АЦП. К недостаткам применения внешнего источника опорного напряжения можно отнести возможное увеличение стоимости проекта.

Синтаксис функции analogReference() показан в следующем примере:

AnalogReference(DEFAULT); //опорное напряжение = напряжение питания analogReference(INTERNAL); //опорное напряжение = 1,1В или 2,56В analogReference(EXTERNAL); //опорное напряжение = напряжение на AREF выводе

Функция analogRead()

Функция analogRead() обеспечивает считывание значения с одного из аналоговых входов. Считанное значение находится в диапазоне 0 — 1023 (10-битный АЦП). Необходимо указать номер аналогового входа, с которого будет происходить чтение данных.

Следующий пример иллюстрирует использование аналоговых входов:

#define analogPin 0 // потенциометр подключен к A0 int val = 0; // val — переменная, хранящая считанное значение void setup() { Serial.begin(9600); // инициализация последовательного порта } void loop() { val = analogRead(analogPin); // чтение значения напряжения с порта A0 Serial.println(val); // отправка измеренной величины на терминал }

Как видно, на приведенном выше примере, считанное значение напряжения передается через последовательный порт на компьютер.

В примере не использована функция analogReference(), так как по умолчанию система использует опорное напряжение от источника питания. Однако, лучше указывать в функции setup() явный выбор опорного напряжения (в нашем случае это analogReference(DEFAULT)), так как это облегчает понимание кода и его модификацию в будущем.

Функция analogWrite()

Функция analogWrite() позволяет управлять выходом с помощью сигнала ШИМ. ШИМ часто используется в качестве замены обычного аналогового сигнала. Количество доступных выводов ШИМ зависит от типа используемого микроконтроллера в Arduino.

Так у Arduino на микроконтроллере:

  • Atmega8 — выводы 9, 10, 11;
  • Atmega128, Atmega168 и Atmega328 — выводы 3, 5, 6, 9, 10, 11;
  • Atmega1280 — выводы 2…13 и 44…46.

Частота переключения ШИМ большинства контактов составляет 490 Гц. Степень заполнения формируется числом от 0 до 255 (0 — без заполнения, 255 – полное заполнение).

Если мы подключим светодиод к контакту PWM и будем менять заполнение ШИМ, мы увидим изменение интенсивности свечения светодиода. Ниже приведен пример программы изменения свечения светодиода при помощи потенциометра:

#define ledPin 11 // светодиод подключен к контакту 9 #define analogPin 0 // потенциометр на А0 int val = 0; // val — переменная, хранящая значение A0 void setup() { pinMode(ledPin, OUTPUT); // устанавливаем контакт 9 как выход } void loop() { val = analogRead(analogPin); // чтение с потенциометра analogWrite(ledPin, val / 4); // пишем в ШИМ }

Как вы можете видеть, значение, считанное с аналогового входа, преобразуется в соответствующее значение ШИМ.

Чтобы ШИМ работал пропорционально вращению потенциометра, значение, полученное с A0, следует разделить на четыре. Это связано с тем, что данные с потенциометра лежат в диапазоне от 0 до 1024, а ШИМ принимает диапазон данных от 0 до 255.

В этом примере используется простое деление. В Arduino IDE имеется специальная функция map(), которая предназначена для пропорционального преобразования данных в новый диапазон значений.

Продолжим знакомство с платформой Arduino и в данной статье рассмотрим аналоговые входы.

Основным применением аналоговых входов в тематике Arduino является чтение значений аналоговых датчиков. В тоже время стоит не забыть упомянуть, что аналоговые входы могут использоваться как цифровые порты входов/выходов рассмотренные в предыдущем уроке (об этом в конце статьи).

На плате Arduino UNO их 6 (A0-A5). У других плат количество может отличаться, смотрите в спецификации.

Благодаря встроенному АЦП (аналого-цифровой преобразователь), данные входы могут считывать напряжение подаваемое на них. Микроконтроллеры Atmega 328, используемые в Arduino UNO, содержат шестиканальный АЦП, разрешение которого составляет 10 бит. Это позволяет на выходе получать значения от 0 до 1023 (всего 1024 градации).

// Производим чтение с аналогового входа A0 analogRead (0);

Данная функция возвращает значение от 0 до 1023 пропорционально напряжению на аналоговом входе

Пример на практике

В качестве первого примера работы с аналоговыми входами подключим потенциометр.

Для нашей задачи отлично подойдут однообортные потенциометры изображенные на картинке. Не смотря на столь значительные внешние отличия, они мало чем отличаются. Для примера можно использовать любой из них. Большим потенциометром с крутилкой конечно удобнее пользоваться, но он обладает толстыми ножками которые быстро расшатывают контакты макетной платы. В случае, если под рукой имеется отвертка, то, при работе с макетной платой, лучше воспользоваться квадратным потенциометром.

Для эксперимента нам понадобятся:

Перевод значения аналогового сигнала в вольты

Для перевода получившегося значения в вольты достаточно вычислить шаг и умножить его на получаемое значение.

Для вычисления шага поделим опорное напряжение на 1024 градации

5В / 1024 = 0.0049 Вольт

Т.е. При получаемом аналоговом значении в 500, на порт контроллера приходит (500 * 0.0049) 2.45В.

пример программного кода:

float Step = 5.0F / 1024; void setup () { Serial .begin (9600); // Задаем скорость работы монитор порта } void loop () { int analogValue = analogRead (0); // Задаем переменную analogValue для считывания показаний float voltageValue = analogValue * Step; // Переводим в вольты (показание * шаг) Serial .println (voltageValue); // Выводим значение в вольтах в порт delay (500); // Ждем пол секунды }

Более точная работа аналогового входа

Для того чтобы добиться более точных показаний с аналогового входа можно использовать 2 варианта:

. Функция analogReference()​

Задает опорное напряжение относительно которого происходят аналоговые измерения.

analogReference (type);

Возможные настройки (type ):

DEFAULT : установлено по умолчанию. при данной конфигурации опорное напряжение автоматически принимается за напряжение питания платы Arduino. 5В (на платформах с напряжением питания 5 В) или за 3.3 В (на платформах с напряжением питания 3.3В)

На платформах Arduino "из коробки" вывод AREF не задействован. В этом случае при настройке DEFAULT к выводу подключается внутреннее напряжение AVCC . Соединение является низко-импедансным и любое напряжение подведенное к выводу в этот момент может повредить микросхему ATmega .

INTERNAL : встроенное опорное напряжение 1.1В на микроконтроллерах ATmega168 и ATmega328, и 2.56 В на ATmega8.

Это может пригодиться для более точного измерения напряжения лежащего в пределах ниже 1.1В либо 2.56В . Болле точная работа достигается за счет меньшего шага 5/1024 против 1.1/1024. Значения соответствующее или превышающее 1.1В (2.56В) будут конвертироваться АЦП в 1023.

EXTERNAL : внешний источник опорного напряжения, подключенный к выводу AREF.

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

. Ручная установка опорного напряжения

Актуальна для измерения крайне малого напряжения

Искажения при работе с аналоговыми входами появляются по причине того, что по дефолту за опорное напряжение принимается 5В, в то время как стабилизаторы напряжения на плате Arduino могут немного отклоняться от эталонного значения и выдавать к примеру 4.85В. 4.85 / 1024 = 0.0047 (при эталонном шаге в 0.0049)

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

float Step = 4.85F / 1024; // Вычисляем шаг Uопорн / на градацию

Использование аналоговых входов в качестве цифровых выводов

Аналоговые входы могут использоваться как цифровые порты входов/выходов рассмотренные в предыдущем уроке

Для этого, для UNO, в коде их нужно записывать как цифровые с 14 по 19. К примеру, для A0

// Инициализируем аналоговый pin 0 как выход pinMode (14, OUTPUT ); // Инициализируем аналоговый pin 0 как вход pinMode (14, INPUT );

Помимо цифровых сигналов, Arduino может использовать и аналоговые входные и выходные сигналы.

Аналоговый сигнал - это сигнал, который может принимать любое количество значений, в отличие от цифрового сигнала, который имеет только два значения: высокий и низкий. Для измерения значения аналоговых сигналов в Arduino имеется встроенный аналого-цифровой преобразователь (АЦП). АЦП преобразует аналоговое напряжение в цифровое значение. Функция, которая используется для получения значения аналогового сигнала: analogRead(pin) . Данная функция преобразует значение напряжения на аналоговом входном выводе и возвращает цифровое значение от 0 до 0123, относительно опорного значения. Для большинства Arduino опорное напряжение составляет 5В, 7В для Arduino Mini и Nano , и 15В для Arduino Mega . Она принимает лишь один параметр: номер вывода.

Arduino не содержит встроенного цифро-аналогового преобразователя (ЦАП), но она может использовать цифровой сигнала с широтно-импульсной модуляцией (ШИМ) для реализации функций по работе с аналоговым выходом. Функция, используемая для вывода ШИМ сигнала: analogWrite(pin, value) . pin - это номер вывода, используемого для ШИМ выхода. value - это число, пропорциональное коэффициенту заполнения сигнала. Когда value = 0 , на выходе всегда логический ноль. Когда value = 255 , на выходе всегда логическая единица. На большинстве плат Arduino, ШИМ функции доступны на выводах 3, 5, 6, 9, 10 и 11. Частота ШИМ сигнала на большинстве выводов составляет примерно 490 Гц. На Uno и подобных платах выводы 5 и 6 работают на частоте примерно 980 Гц. Выводы 3 и 11 на Leonardo также работают честоте на 980 Гц.

Чтобы сопоставить аналоговое входное значение, которое находится в диапазоне от 0 до 1023, с выходным ШИМ сигналом, который находится в диапазоне от 0 до 255, вы можете использовать функцию map(value, fromLow, fromHigh, toLow, toHigh) . Данная функция имеет пять параметров: в первом хранится аналоговое значение, а остальные равны соответственно 0, 1023, 0 и 255.

Эксперимент 1: управление яркостью светодиода

В данном эксперименте мы будем управлять яркостью светодиода с помощью ШИМ сигнала на аналоговом выходном выводе.

Необходимые компоненты

  • 1 x светодиод
  • 1 x резистор

Схема соединений

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

Код программы

const int pwm = 2; // обозначение вывода 2, как переменная ‘pwm’ void setup() { pinMode(pwm,OUTPUT); // установить режим вывода 2, как выход } void loop() { analogWrite(pwm,25); // установка коэффициента заполнения, равным 25 delay(50); // задержка 50 мс analogWrite(pwm,50); delay(50); analogWrite(pwm,75); delay(50); analogWrite(pwm,100); delay(50); analogWrite(pwm,125); delay(50); analogWrite(pwm,150); delay(50); analogWrite(pwm,175); delay(50); analogWrite(pwm,200); delay(50); analogWrite(pwm,225); delay(50); analogWrite(pwm,250); }

Эксперимент 2: управление яркостью светодиода с помощью потенциометра

В данном эксперименте мы будем управлять яркостью светодиода, используя потенциометр. Мы воспользуемся функцией analogRead() для чтения напряжения и функцией analogWrite() для вывода ШИМ сигнала, коэффициент заполнения которого пропорционален аналоговому напряжению.

Необходимые компоненты

  • 1 x потенциометр
  • 1 x светодиод
  • 1 x резистор

Схема соединений

Соберите схему, как показано ниже. Когда вы будете вращать ручку потенциометра, напряжение на выводе A0 будет меняться. После чего программа будет изменять коэффициент заполнения ШИМ сигнала на выводе 2, изменяя яркость светодиода.


Код программы

const int pwm = 2; // обозначение вывода 2, как переменная ‘pwm’ const int adc = 0; // обозначение вывода 0, используемого в качестве // аналогового входа, как переменная ‘adc’ void setup() { pinMode(pwm,OUTPUT); // установить режим вывода 2, как выход } void loop() { int adc = analogRead(0); // чтение аналогового напряжения и // сохранение его значения в целочисленной // переменной adc = map(adc, 0, 1023, 0, 255); /* ---------- функция map ------------ функция выше масштабирует выходное значение АЦП, который обладает разрядностью 10 бит и выдает значения между 0 и 1023, в значения между 0 и 255 для функции analogWrite, которая принимает значения только в этом диапазоне. */ analogWrite(pwm,adc) ; }

Цель работы: Рассмотрение особенностей ввода и отображения широкополосных сигналов.
Задача работы: Построение канала ввода, обработки и отображения сигналов на максимальной частоте преобразования АЦП контроллера Arduino.
Приборы и принадлежности: Контроллер Arduino UNO, пакет Simulink МатЛАБ (R2012).

ВВЕДЕНИЕ

Разработка программных средств для наблюдения, анализа и обработки сигналов на уровне контроллеров требует значительных временных затрат. Подключение контроллера к специализированной среде высокого уровня (Рис. 1) позволяет значительно сократить время проектирования алгоритма для контроллера с учетом ограничений его ресурсов.

Хорошим примером мощной специализированной среды для работы с сигналами является МатЛАБ. Для анализа сигналов зачастую требуется наблюдать его спектр в максимально широкой полосе частот. Для этого контроллер должен принимать сигналы на максимальной частоте преобразования АЦП.

Построение рабочего канала «Arduino UNO – МатЛАБ» для наблюдения и обработки сигналов в реальном времени на предельной частоте преобразования АЦП подробно излагается в этой работе. Особенностью этого канала является то, что такты реального времени задаются не МатЛАБ, а контроллером Arduino. Такое построение не требует компиляции Simulink модели с библиотекой реального времени (rtwin.tlc), что позволяет использовать в модели практически любые блоки библиотеки Simulink.

Рис. 1. Сравнение средств разработки алгоритмов. Для проектирование алгоритмов на уровне специализированной среды необходим канал передачи данных между контроллером и средой проектирования.

ОБЩИЕ СВЕДЕНИЯ

Средства накопления, анализа, обработки и отображения сигналов
В этой работе используется среда Simulink для приёма и отображения данных контроллера Arduino.

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

Состав пакетов расширения библиотеки Simulink на примере пакета цифровой обработки сигнала “DSP System Toolbox” показан на Рис. 2.


Рис. 2. Пример дополнительного пакета расширения Simulink для моделирования систем обработки сигналов: DSP System Toolbox . В пакете используются новейшие алгоритмы спектрального анализа. Выделено содержимое раздела Power Spectrum Estimation - блоки для спектральной оценки сигнала.

Потоковая передача данных
Использование двух буферов для накопления и передачи данных позволяет организовать без разрывов сбор, обработку и визуализацию данных (для избежания потери данных скорость последующего процесса должна быть не ниже скорости предыдущего процесса).

Примером применения такой организации может быть модуль E14-440 для многоканального ввода, вывода и обработки аналоговой и цифровой информации, подключаемый к компьютеру через шину USB.

Входные данные сначала заносятся в первую половинку FIFO буфера АЦП. После ее заполнения данные начинают передаваться в PC, в тоже время не прекращается сбор данных во вторую половинку FIFO буфера. После накопления данных во второй половинке FIFO буфера опять начинается передача данных в PC и параллельно продолжается сбор данных уже в первую половинку.

ПОСТРОЕНИЕ ОСЦИЛЛОГРАФА НА БАЗЕ КОНТРОЛЛЕРА ARDUINO

Максимальная скорость накопления данных АЦП
Используя вывод результата на монитор редактора Arduino на максимальной частоте (57600 бит/с) напишем программу подсчета преобразований АЦП за фиксированный период.

Программа измерения скорости преобразования АЦП:



Void setup() {
Serial.begin (57600); // 9600, 19200, 38400, 57600 and 115200 bit/s
}

Void loop(){
time_start = millis();
for (int i = 0; i < 1024; i++) {

}
time_end = millis();

Serial.println(period);


Рис. 3. Время (в мсек) 1024 и 512 преобразований АЦП. Среднее время преобразования АЦП: 0.1123 мсек (как 115/1024).

Время масштабирования данных АЦП
Для перевода 10 разрядных данных АЦП в 8 разрядные используется функция
map(val, 0, 1023, 0, 255);
где val – int переменная c 10 значимыми разрядами.
Программа измерения времени преобразования АЦП с масштабированием и записи в массив:

Const int adc_5 = A5; // ADC port number
unsigned long time_start; // Start of capturing, ms
unsigned long time_end; // End of capturing, ms

Void setup() {

}

Void loop(){
time_start = millis();
for (int i = 0; i < 1024; i++) {
int val = analogRead(adc_5);
}
time_end = millis();
int period = time_end - time_start;
Serial.println(period);
}


Рис. 4. Время (в мсек) 1024 преобразований АЦП, перевода 10 р. данных в 8 разрядные и запись в массив. Период АЦП преобразования с масштабированием: 0.1611 мсек (как 165/1024).

Поскольку время преобразования АЦП 0.13 мсек, то один перевод 10 разрядных данных в байтовый формат (масштабирование) составляет 0.0424 мсек.

Скорость канала последовательной передачи данных
Для определения скорости побайтовой передачи в последовательный канал в цикле передается код символа Serial.write(1) который не отображается на мониторе.

Основной блок программы определения скорости передачи:

Void loop(){ //Do stuff here
Serial.write(1);
rate = rate + 1;
if (time > set_time) {
set_time = set_time + 30; // 30 ms RT clock
Serial.println(rate);
rate = 0;
}
}

Рис. 5. Тестовые данные: количество переданных байт в последовательный канал за 30 мсек на скорости 57600 бит/сек.
Тест показал, что передача 173 байт занимает 30 мсек, с другой стороны за 30 мсек на скорости 57600 бит/с можно передать 1728 бит. Следовательно, на передачу одного байта расходуется время передачи 10 бит. Используя это отношение для режима передачи
Data bits: 8
Parity: none
Stop bits: 1
Flow control: none
можно подсчитать время потоковой передачи массива данных на разных скоростях.
Передача, например, 256 байт на скорости 9600 бод (бит/c) занимает 267 мсек, на скорости 57600 бод – 44 мсек; и на скорости 115200 бод – 22 мсек (как 256*10/115200).

Размер массива для накопления и передачи данных
Размер оперативной (SRAM) памяти Arduino UNO составляет 2 Кбайт. Тестирование программы циклического считывания АЦП, масштабирования 10 разрядных данных до 8 разрядных, тактирования и побайтной передачи данных показало, что максимальный размер массива для накопления и отправки данных не должен превышать 1800 байт.

Более сложные программы могут потребовать и большей дополнительной памяти SRAM. Поэтому массив для накопления и передачи данных АЦП ограничен 1024 байтами или 512 словами.


Рис. 6. Кусок провода, подсоединенный к аналоговому входу А5 контроллера Arduino для усиления наблюдаемой наводки сети 50 Гц.

Таблица 1 . Времена операций программы с учетом нестабильности циклов

Пример настройки канала отображения 256 масштабированных значений АЦП при максимальной скорости накопления и передачи данных.
Код программы контроллера Arduino:
const int adc_5 = A5; // ADC port number
byte adc_bytes; // Buffer for scaled ADC data

Void setup() {

}

Void loop(){

// ADC data capturing
for (int i = 0; i < 256; i++) {
int val = analogRead(adc_5);
adc_bytes[i] = map(val, 0, 1023, 0, 255);
}


for (int i = 0; i < 256; i++) {
Serial.write(adc_bytes[i]);
}

If (time > set_time) {
set_time = set_time + 70; // RT clock is 70 ms
}
}


Рис. 7. Определение номера порта а среде Arduino.


Рис. 8. Simulink модель для приёма АЦП данных контроллера, масштабирования вектора данных по времени, отображения данных в реальном времени и сохранения потока данных в памяти workspace.


Рис. 9. Параметры COM порта в среде Simulink (блок модели: Serial Configuration)


Рис. 10. Параметры блоков Simulink модели и режима моделирования.

Модель запускается нажатием на кнопку Start simulation:

Рис. 11. Кнопка запуска модели.


Рис. 12. Вид сетевой наводки (подключение показано на Рис. 6) с наложением кадров (левое окно) и в отдельном кадре (правое окно). Причина смещения сигнала при наложении кадров – отсутствие синхронизации отображения. Примечание: Simulink имеет достаточно средств для построения канала синхронизации.

ПРИМЕРЫ ПОЛУЧЕНИЯ ПРОВЕРЕННЫХ РЕЗУЛЬТАТОВ И ВАРИАНТЫ ДЛЯ САМОКОНТРОЛЯ

Задание 1 . Накопление, передача и отображение отмасштабированных данных (см. пример и таблицу времён на стр. 8).
1. Напишите для контроллера Arduino UNO программу циклического считывания показаний АЦП, масштабирования, записи данных в массив 1024 байт и передачи массива в последовательный канал. Программа должна выполняться с максимальной скоростью. Символ A – заголовок передаваемого массива.

Пример программы:

Const int adc_5 = A5; // ADC port number
unsigned long set_time; // Time of next clock
byte adc_bytes; // Buffer for ADC data

Void setup() {
Serial.begin (115200); // bit/s
}

Void loop(){
unsigned long time = millis(); // Current time in ms

// ADC data capturing
for (int i = 0; i < 1024; i++) {
int val = analogRead(adc_5);
adc_bytes[i] = map(val, 0, 1023, 0, 255);
}

// send ADC data into serial port
Serial.print(«A»); // «A» is header
for (int i = 0; i < 1024; i++) {
Serial.write(adc_bytes[i]);
}

If (time > set_time) {
set_time = set_time + 270; // RT clock is 270 ms
}
}

2. В среде МатЛАБ составьте программу из Simulink блоков для приема и отображения данных контроллера в реальном времени. Скорость, размер пакета, период принимаемых данных и такт работы модели должны соответствовать соответствующим параметрам контроллера. Отмасштабируйте время отображаемых данных.


Рис. 13. Simulink модель для приема данных на максимальной частоте: 115200 бод. Объединение векторов (Vector Concatenate) используется для масштабирования сигнала по шкале времени кадра (frame).

3. Проверьте качество канала «Вход АЦП – дисплей МатЛАБ», например по периоду сетевой 50 Гц наводки на входе АЦП. Для увеличения амплитуды наводки ко входу АЦП подсоедините кусок провода (см. Рис. 6). Амплитуда наводки зависит от расстояния между проводом и вашей рукой.


Рис. 14. Наложение 4 кадров при сканировании частоты 50Гц на входе АЦП контроллера Arduino.


Рис. 15. Частота сети на входе АЦП контроллера, 4 кадр.

Задание 2 . Накопление, передача и отображение 10 разрядных данных АЦП.
1. Для контроллера Arduino UNO напишите программу циклического считывания показаний АЦП, записи данных в массив 512 слов и побайтной передачи данных массива в последовательный канал. Программа должна выполняться с максимальной скоростью.

Пример программы:

Const int adc_5 = A5; // ADC port number
unsigned long set_time; // Time of next clock in ms
word adc_int; // Buffer for ADC data
int val;
byte val_Lo, val_Hi;

Void setup() {
Serial.begin (115200); // bit/s
}

Void loop(){
unsigned long time = millis();

// ADC data capturing
for (int i = 0; i < 512; i++) {
adc_int[i] = analogRead(adc_5);
}

// send ADC data into serial port
// first low bytes then high bytes
Serial.print(«A»); // «A» is header
for (int i = 0; i < 512; i++) {
val = adc_int[i];
val_Lo = (val << 1) & 0xFE;
Serial.write(val_Lo); // Lo byte
}
for (int i = 0; i < 512; i++) {
val = adc_int[i];
val_Hi = (val >> 6) & 0xE;
Serial.write(val_Hi); // Hi byte
}

If (time > set_time) {
set_time = set_time + 160; // RT clock is 160 ms
}
}

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


Рис. 16. Программа Simulink для приёма, восстановления и отображения массива данных АЦП контроллера Arduino UNO.
3. Запишите сетевые 50 Гц наводки.


Рис. 17. Наложение 15 кадров при сканировании сетевой наводки 50Гц на входе АЦП контроллера. Период программы: 160 мсек. Время заполнения массива данными АЦП: 58 мсек. Время передачи 512х2 байт на частоте 115200 бод: 89 мсек.


Рис. 18. Последний 15 кадр. Время 3.5 циклов 50 Гц сигнала: 70 мсек.

Задание 3 . Обработка сигнала m-программой МатЛАБ
1. Сохраните отображаемые в реальном времени данные в workspace памяти МатЛАБ, например, при помощи блока simout (см. Рис. 13).
2. Скопируйте сохраненные данные в рабочий каталог, например:
save("simout_50Hz","simout");
3. Разработайте m-программу МатЛАБ для отображения архивного АЦП сигнала контроллера.

Пример кода:

Clear all
load("simout_50Hz");


size_frame = size(simout.Data,1);
sampling = d_frame/(size_frame + 163*4); % dt
data_size = size(simout.Data,1)*size(simout.Data,2)*size(simout.Data,3);

% time = (0:data_size-1)*sampling;
time = ;
for i = 1:length(simout.Time)
time = ;
end

Adc = uint8();
for i = 1:size(simout.Data,3)
adc = ;
end

% frame_num = length(simout.Time) % or size(adc,3) % is 54 frame

If 1 %
figure
plot(time, adc, "b")
grid on
xlabel("Time, s");


end


Рис. 19. Покадровое изменение 50 Гц наводки на входе АЦП контроллера Arduino UNO: 24 кадра по 0.27 сек.

4. Разработайте m-программу для вычисления параметров сигнала, например, периода в заданном кадре.

Пример кода:

Clear all
load("simout_50Hz");

D_frame = simout.Time(2)-simout.Time(1);
sampling = d_frame/((256 + 176)*4); % dt
data_size = size(simout.Data,1)*size(simout.Data,2)*size(simout.Data,3); % <256 x 1 x 54>

%FRAME number
i = 5;
time = (0:1023)*sampling+simout.Time(i);
adc = simout.Data(:,:,i)";
if 1 %
figure
plot(time, adc, "b")
grid on
xlabel("Time, s");
ylabel("ADC , bit");
title("8 bit ADC frame against Time");
end
% period
comp_level = 60;
j = 1;
for i = 2:length(adc)
if (adc(i) >= comp_level) && (adc(i-1) < comp_level)
cell_num(j) = i;
j = j + 1;
end
end
s_period = diff(time(cell_num));


Рис. 20. Непрерывное и поточечное изменение сигнала в выбранном кадре. Время 5 кадра: 1.08… 1.24 сек. Размер вектора: 1024 байт. Следовательно время одного считывания и масштабирования сигнала АЦП: 0.156 мсек.


Рис. 21. Период наводки сети 5 кадра: 19.2… 19.4 мсек.

Задание 4 . Построение спектра сигнала в реальном времени.
1. Для наблюдения частотного спектра сигнала подключите к отображаемому сигналу модели блок быстрого преобразования Фурье (Spectrum Scope: FFT) из раздела библиотеки Simulink > DSP System Toolbox > Sinks.


Рис. 22. Модель со спектроскопом.


Рис. 23. Спектр сетевой наводки. Сигнал кадра включает 1024 амплитуды и 163x4 нулевых значений.

2. Выделите основную гармонику сигнала: 50 Гц.


Рис. 24. Гармоника сигнала на частоте 50 Гц.

3. Подключите блок Spectrum Scope: FFT к неотмасштабированному (по времени) сигналу.


Рис. 25. Перенос точки подключения спектрографа. На входе неотмасштабированный сигнал с меньшей зоной нулевых значений в конце массива (вектора).

4. Настройте блок. Выберите тип отображаемого спектра: Spectrum Type.


Рис. 26. Параметры спектрометра неотмасштабированного сигнала из 1024 амплитуд.

Задание 5 . Построение канала скоростной потоковой передачи и обработки 8р данных в реальном времени без пропуска данных.
1. Напишите для контроллера Arduino UNO программу циклического считывания показаний АЦП, масштабирования и передачи в последовательный канал 2048 байт с заголовком. Программа должна считывать показания АЦП на постоянной частоте без перерывов.

Пример программы:
const int adc_5 = A5; // ADC port number

Void setup() {
Serial.begin (115200); // bit/s
}

Void loop(){
for (int i = 0; i < 2048; i++) {
if (i == 0) Serial.print(«A „); // “A» is header
int val = analogRead(adc_5);
byte adc_byte = map(val, 0, 1023, 0, 255);
Serial.write(adc_byte);
}
}

2. Настройте модель Simulink (МатЛАБ) на прием данных контроллера.


Рис. 27. Пример модели для отображения непрерывного потока данных. Кадр содержит 2048 байт.

3. Настройте время моделирования модели (Меню > Simulation > Configuration Parameters > Solver > Fixed-step size) и такт блока Serial Receive > Block Sample Time, (см. Рис. 10) по периоду 50 Гц сети.
Расчетное время кадра по данным Таблица 1: 254 мсек (для 1024 байт) => 508 мсек для 2048 байт, В действительности, время кадра программы (в которой считывание АЦП и передача выполняются поочередно) составляет 375 мсек.


Рис. 28. Кадр графопостроителя Vector Scope. В кадре 18.75 периодов 50 Гц волны. Следовательно, время кадра должно быть 375 мсек, а период преобразования АЦП, масштабирования и передачи данных: 0.1831 мсек.

4. В командном окне МатЛАБ наберите команду формирования 5 кадрового сигнала.
sgnl = ;

5. Постройте график 5 первых кадров сигнала.


Рис. 29. Пять кадров входного сигнала модели.

6. Рассмотрите качество стыков кадров.


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

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

Рис. 31. Модель отображения спектра входного сигнала (АЦП Arduino UNO) в реальном времени.


Рис. 32. Спектр сетевой наводки на входе АЦП контроллера Arduino.

8. Подключите ко входному сигналу модели осциллограф Time Scope из библиотеки Simulink > DSP System Toolbox > Sinks.

Рис. 33. Осциллограф в модели для отображения входного сигнала контроллера Arduino.

9. Настройте осциллограф на отображение содержимого текущего кадра и частоты сигнала.


Рис. 34. Настройка осциллографа Time Scope > Menu > View > Properties.

10. Запустите модель и наблюдайте стабильность сигнальных параметров.


Рис. 35. Отображение сигнала и его параметров в реальном времени на осциллографе Simulink модели.

Последний вариант канала контроллер Arduino – МатЛАБ в сравнении с предыдущими вариантами имеет следующие преимущества.
не используется память контроллера для накопления АЦП данных;
обеспечивается малый такт преобразования АЦП с масштабированием, который чуть больше такта преобразования АЦП с масштабированием при отсутствии передачи;
не требуется масштабирование сигнала по времени в Simulink модели;
модель содержит меньше блоков;
практически не ограничен размер вектора и время кадра.

Задание 6. Увеличение частоты сэмплирования сигнала АЦП.

Частоту сэмплирования АЦП контроллера Arduino можно повысить до 15 кГц в 10-разрядном режиме и до 77 кГц в 8-разрядном режиме , заменив библиотечные функции на более быстрый вариант использования регистров микроконтроллера.
Функцию пользователя можно создать в программе *.ino или в системном файле контроллера
...\arduino-1.0.6\hardware\arduino\cores\arduino\ wiring_analog.c, зарегистрировав её в
...\arduino-1.0.6\hardware\arduino\cores\arduino\ Arduino.h

Для построения 8-разрядного высокоскоростного канала Arduino – МатЛАБ необходимо выполнить следующее.
1. Напишите программу определения времени заполнения массива АЦП данными с отображением результата в окне «Serial Monitor». Размер массива должен быть достаточно большим, например, в половину памяти SRAM. Для увеличения точности следует измерять время многократного заполнения массива.
Пример программы:

void setup() {
Serial.begin (57600); // bit/s

ADCSRA = (1 << ADEN) // Включение АЦП
|(1 << ADPS2); // Установка предделителя преобразователя на 8
ADMUX = (1<< ADLAR) | (1 << REFS0) // Подключение внешнего ИОН
|(1 << MUX2) |(0 << MUX1) |(1 << MUX0); // подключение АЦП A5 == 101
}

Void loop(){
unsigned long time_start = millis();
for (int j = 0; j < 100; j++) {
for (int i = 0; i < 1024; i++) {
ADCSRA |= (1 << ADSC); // Запуск преобразования АЦП
while ((ADCSRA & (1 << ADIF)) == 0);//Ожидание флага окончания преобразования
adc_bytes[i] = ADCH; // Считывание полученного значения
}
}
unsigned long time_end = millis();
unsigned int dt = time_end - time_start;
Serial.println (dt);
}

Сто заполнений массива из 1024 байт выполнено за 1542 мсек .

2. Дополните однократное заполнение массива данными АЦП последующей передачей всего массива в последовательный порт на максимальной скорости.
Пример программы:

Byte adc_bytes; // Резервирование массива для АЦП данных

Void setup() {
Serial.begin (115200); // bit/s
ADCSRA = (1 << ADEN) // Включение АЦП
|(1 << ADPS2); // Установка предделителя преобразователя на 8
ADMUX = (1<< ADLAR) | (1 << REFS0) // Подключение внешнего ИОН
|(1 << MUX2) |(0 << MUX1) |(1 << MUX0); // подключение АЦП A5 == 101
}

Void loop(){
for (int i = 0; i < 1024; i++) {
ADCSRA |= (1 << ADSC); // Запуск преобразования АЦП
while ((ADCSRA & (1 << ADIF)) == 0); //Ожидание флага окончания преобразования
adc_bytes[i] = ADCH; // Считываем полученное значение
}
// send ADC data into serial port
Serial.print(«A»); // «A» is header
for (int i = 0; i < 1024; i++) {
Serial.write(adc_bytes[i]);
}
}
3. В модели Simulink (Рис. 36) в формате 0.01542 пропишите экспериментальное значение времени записи в массив, а именно, в строке “Block sample time” блока “Serial Receive” и в строке меню > simulation > Configuration Parameters > Fixed-step size (fundamental sample time).


Рис. 36. Модель Simulink для приема и отображения данных из COM порта.

4. Подключите на вход АЦП тестовый сигнал. Запустите программу Arduino и, затем, модель Simulink (MatLAB). Сравните известные параметры сигнала с параметрами наблюдаемого сигнала. Проверить работу тракта можно по отображению поочередно подключаемых выходных напряжений платы Arduino: 0В; 3.3В и 5В.


Рис. 37. Отображение в реальном времени сетевой 50 Гц наводки. Кадр включает 1024 точки. Время кадра 15.42 мсек. Частота сэмплирования 66 КГц (как 1/(0.01542_сек/1024)). Отображаемый сигнал имеет разрывы: процесс записи прерывается передачей кадра в последовательный канал.


Рис. 38. Отображения в режиме реального времени пилообразного сигнала 0… 3.3 В, сформированного на контроллере Teensy 3.1 с 12 разрядным ЦАП и подключенного к шестому АЦП (A5) контроллера Arduino.


Рис. 39. Сигнал 500 Гц контроллера Teensy 3.1. Ошибка такта (15.42 мсек) Simulink модели без дополнительной корректировки меньше 1% (как 100%*(504.72Гц - 500Гц)/500Гц). Погрешность можно уменьшить корректировкой RT такта, как показано в п.3 этого задания.

КОНТРОЛЬНЫЕ ВОПРОСЫ

1. Сравните периоды преобразования АЦП первого и последнего заданий.
2. Почему для построения спектра сигнала рекомендуется брать выборку размером кратную двум?
3. Какова задержка потоковой передачи 1024 байт на частоте 115200 бит/c при следующих параметрах передачи?
Data bits: 8
Parity: none
Stop bits: 1
Flow control: none
Похожие статьи