Подключение внешнего ацп к ардуино. Частота выборки и разрешение. Использование аналоговых входов в качестве цифровых выводов

Помимо цифровых сигналов, 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) ; }

Очень полезный модуль в составе микроконтроллера — аналого-цифровой преобразователь. Он позволяет микроконтроллеру измерять произвольное напряжение.
В мы описывали, как можно считать логическое состояние входа, то есть "0" или "1". Аналого-цифровой преобразователь считывает величину напряжения на выводах A0-A5. Это дает возможность считать данные с датчика освещенности, измерить напряжение питания и т.д.

Подготовка к работе

На нашем для освоения работы с АЦП есть три переменных резистора. Для их подключения к выводам A0-A2 установите перемычке так, как показано на рисунке:

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

Первый пример

Для начала попробуем просто считать напряжение на выводе микроконтроллера A0 и отправить его в COM-порт.
Делается это при помощи функции analogRead() . Этой функции нужно передать номер вывода, напряжение на котором должно быть измерено и она вернет текущее значение.
Загрузите на плату следующий пример:

int val; void setup() { Serial. begin(9600 ) ; } void loop() { val = analogRead(A0) ; Serial. println(val) ; delay(1000 ) ; }

В микроконтроллере Atmega8A, который используется на нашей плате , есть модуль АЦП с разрешением 10 бит и возможностью мультиплексирования шести входов. Эти входы пронумерованы A0-A6 (или 14-19).
Измерение производится относительно напряжения питания. Ни в коем случае нельзя подавать на вход отрицательное напряжение или напряжение, превышающее питание! Мы подключили ко входу переменный резистор и наше входное напряжение точно не выйдет за рамки питания.
Теперь разберемся с тем, что нам будет присылать плата. Раз разрешение 10 бит — в десятичном виде значение будет меняться от 0 до 1023. Измерение производится относительно 5-ти вольт, поэтому изменение показаний на 1 соответствует фактическому напряжению 5/1023=4.9мВ. То есть средствами встроенного АЦП микроконтроллера можно измерить напряжение с точностью до 4.9мВ.
Вернемся к скетчу. В результате выполнения строчки …

Val = analogRead(A0) ;

… в переменную val будет записано оцифрованное напряжение, считанное на выводе A0. Откройте монитор порта (Ctrl+Shift+M) и посмотрите, как меняются показания АЦП при вращении вала переменного резистора. Обратите внимание, что нет нужды настраивать вывод при этом на вход.
Теперь пора немного улучшить работу с модулем аналого-цифрового преобразователя. На практике младшие разряды АЦП могут сильно флуктуировать из-за шумов и их обычно отбрасывают, причем сразу два разряда. При этом остается 8ми-битное число с которым гораздо удобнее работать. Точность при этом получается 5/255=19.6мВ, чего вполне достаточно для большинства ситуаций.
Измените код так, чтобы он присылал 8ми-битное значение. Замените строку с чтением состояния АЦП на это:

Val = analogRead(A0) > > 2 ;

Теперь переменной val мы присваиваем значение считанное из АЦП сдвинутое на два бита вправо. Остальные биты просто отбрасываются.

Второй пример

Теперь мы можем плавно изменять яркость светодиода при помощи ШИМ-модуляции, задавая ее переменным резистором. Установите перемычку "color" так, как описано в . Этим самым вы подключены к 9му, 10му и 11му выводу сегменты трехцветного светодиода.
Для начала попробуем изменять яркость только одного светодиода:

# define BLUE 9 int val; void setup() { pinMode(BLUE, OUTPUT) ; } void loop() { analogWrite(BLUE, (analogRead(A0) > > 2 ) ) ; }

Довольно простой код, если вы помните, как работает функция . При вращении вала переменного резистора светодиод будет менять свою яркость от минимума до максимума.
Кстати, если вы уберете сдвиг на два бита, при выполнении функции analogWrite() будет наступать переполнение, так как она может принимать только значения от 0 до 255. Попробуйте убрать этот сдвиг и посмотрите, что получится.
И в заключении добавим управление всеми тремя светодиодами. Остается только найти отвертку, чтобы покрутить двумя остальными подстроечными резисторами.

# define BLUE 9 # define ORANGE 10 # define GREEN 11 int val; void setup() { pinMode(BLUE, OUTPUT) ; pinMode(ORANGE, OUTPUT) ; pinMode(GREEN, OUTPUT) ; } void loop() { analogWrite(BLUE, (analogRead(A0) > > 2 ) ) ; analogWrite(ORANGE, (analogRead(A1) > > 2 ) ) ; analogWrite(GREEN, (analogRead(A2) > > 2 ) ) ; }

Индивидуальные задания

  1. Оставьте на шилде только перемычку от резистора на выводе A0 и подключите пьезоизлучатель также, как

Продолжим знакомство с платформой 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 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

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

Все эти устройства имеют шкалу, которую мы используем для фиксации их показаний. Рассмотрим простой пример — определение температуры с помощью обычного градусника. Человек решает эту задачу очень просто: мы смотрим, к какому из делений ближе всего приблизился уровень жидкости в градуснике. Полученное таким образом значение и будет измеренной температурой. Иными словами, мы осуществляем преобразование аналоговой непрерывной величины в дискретную, которую можно записать на бумаге с помощью цифр.

Чтобы автоматизировать процесс измерения аналоговых величин, и возложить эту задачу на электронные приборы, инженеры создали особое устройство, называемое аналого-цифровым преобразователем (АЦП). Это устройство позволяет превращать аналоговый сигнал в цифровой код, пригодный для использования в ЭВМ.

В робототехнике АЦП являются важной составляющей системы датчиков машины. Акселерометр, гироскоп (гиротахометр), барометр, магнитометр, и даже видеокамера — все эти приборы соединяются с центральным процессором с помощью АЦП.

Конструктивно, АЦП может находиться в одном корпусе с микропроцессором или микроконтроллером, как в случае Arduino Uno. В противном случае, как и все современные электронные устройства, АЦП может быть оформлен в виде отдельной микросхемы, например MCP3008:

Следует отметить, что существует и устройство с обратной функцией, называемое цифро-аналоговым преобразователем (ЦАП, DAC). Оно позволяет переводить цифровой сигнал в аналоговый. Например, во время проигрывания мелодии на мобильном телефоне происходит преобразование цифрового кода из MP3 файла в звук, который вы слышите у себя в наушниках.

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

1. Функции работы с АЦП

На этом уроке изучать работу АЦП мы будем с помощью платформы Arduino. В используемой нами модели Arduino Uno, наряду с обычными выводами общего назначения (к которым мы уже подключали и ) есть целых шесть аналоговых входов . В других версиях Arduino таких входов может быть и больше, например, у Arduino Mega их 16.

На карте Arduino Uno аналоговые входы имеют буквенно-цифровые обозначения A0, A1, …, A5 (снизу слева).

Во время работы всё с теми же , мы познакомились с функцией digitalRead , которая умеет считывать цифровой сигнал с определенного входа контроллера. У этой функции существует аналоговая версия analogRead , которая может делать то же самое, но только для аналогового сигнала.

результат = analogRead(номер_контакта);

после вызова этой функции, микроконтроллер измерит уровень аналогового сигнала на заданном контакте, и сохранит результат работы АЦП в переменную «результат». При этом результатом функции analogRead будет число от 0 до 1023.

2. Разрядность АЦП

Надо заметить, что число 1023 здесь появилось неспроста. Дело в том, что у каждого устройства АЦП есть такой важный параметр как разрядность . Чем больше значение этого параметра, тем точнее работает прибор. Предположим, что у нас есть АЦП с разрядностью 1. Подавая на вход любое напряжения от 0 до 2,5 Вольт, на выходе мы получим 0. Любое же напряжение от 2,5 до 5 вольт даст нам единицу. То есть 1-битный АЦП сможет распознать только два уровня напряжения. Графически это можно изобразить следующим образом:

АЦП с разрядностью 2 распознает уже четыре уровня напряжения:

  • от 0 до 1,25 — это 0;
  • от 1,25 до 2,5 — это 1;
  • от 2,5 до 3,75 — это 2;
  • наконец, от 3,75 до 5 — это 3.

На следующих двух картинках изображена работа АЦП с разрядностью 2 и 3 бит:

В Arduino Uno установлен 10-битный АЦП, и это значит, что любое напряжение на аналоговом входе в диапазоне от 0 до 5 вольт будет преобразовано в число с точностью 1/1024 вольта. На графике будет сложно изобразить столько ступенек. Имея такую точность, 10-битный АЦП может «почувствовать» изменение напряжение на входе величиной всего 5 милливольт.

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

Есть нюанс, который может стать причиной ошибки измерения с помощью АПЦ. Помните тот диапазон от 0 до 5 вольт в котором работает устройство? В общем случае этот диапазон выглядит иначе:

от 0 до опорного напряжения

Это изменение повлечет за собой изменение формулы расчет точности АЦП:

точность = опорное напряжение/1024

Опорное напряжение определяет границу диапазона, с которым будет работать АЦП.

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

Что если вы питаете микроконтроллер от другого источника? Допустим у вас есть четыре NiMh аккумулятора на 1.2 Вольта. В сумме они дадут 4.8 Вольта (пусть они немного разряжены, ведь в действительности их заряжают до 1.4 Вольта). Точность измерения будет равна 4.8/1024. Это следует учесть в нашей программе.

Наконец рассмотрим случай, когда мы питаем Arduino Uno одним напряжением, а в качестве опорного хотим установить совсем другое, например, 3.3 Вольта. Что делать? Для такого варианта на Arduino Uno есть специальный вывод Vref. Чтобы решить проблему, нам нужно подать на этот контакт напряжение 3.3 Вольта, и разрешить использование внешнего источника опорного напряжения функцией:

AnalogReference(EXTERNAL);

которую следует вызвать внутри функции setup нашей программы.

Также следует учитывать, что результат измерения значения напряжения не может превышать границы диапазона. Если мы выбираем в качестве опорного напряжения 3.3 Вольта, а поступающий сигнал будет с большим напряжением, то мы получим неправильное значение напряжения, поскольку АЦП «не знает» о наличии более высокого напряжения.

4. Программа

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

Int val = 0; void setup() { Serial.begin(9600); pinMode(A0, INPUT); } void loop() { val = analogRead(A0); Serial.println(val); delay(1000); }

Теперь загружаем программу на Arduino, и переходим к измерениям.

5. Подключение

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

Теперь откроем окно COM-монитора в Arduino IDE, и посмотрим какие значение выдает нам АЦП:

Что означает число 314? Вспомним, что 10-битный АЦП разбивает диапазон от 0 до 5 вольт на 1024 части. Значит точность 10-битного АЦП — 5/1024. Зная точность, мы можем записать формулу для преобразования показаний АЦП к вольтам:

V = (5/1024)*ADC

где V — измеренное напряжение на батарейке;
ADC — результат работы функции analogRead.

Подставим эту формулу в программу и снова попробуем измерить заряд батарейки!

Int val = 0; void setup() { Serial.begin(9600); pinMode(A0, INPUT); } void loop() { val = analogRead(A0); Serial.println((5/1024.0)*val); delay(1000); }

Результат измерений:

Уже больше похоже на правду.

6. Итог

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

На нашем портале можно найти несколько уроков, выполнение которых зависит от понимания темы АЦП: , ёмкостный датчик, потенциометр и аналоговый джойстик. А в совокупности с еще одной важной темой — ШИМ, применение АЦП позволит создать диммер светодиодной лампы и регулятор хода двигателя. Успехов!