Arduino имеет несколько аналоговых входов, используя которые можно измерять параметры аналоговых величин. Это может быть напряжение, ток, сопротивление, температура, свет и так далее. В некоторых случаях для преобразования физических величин в электрические сигналы могут потребоваться специальные датчики. Сегодня я расскажу об использовании и проведу тест производительности аналого-цифрового преобразователя (АЦП) Arduino. Тест я буду производить, используя оригинальную плату Arduino Mega 2560, в основе которой лежит микроконтроллер ATMega2560, работающий на частоте 16 Мгц. Микроконтроллер ATMega328, на котором основаны Arduino Uno и Arduino Nano, также работает на частоте 16 МГц, так что все вышеизложенное, скорее всего, справедливо и для этих и аналогичных плат.
analogRead
Давайте посмотрим сколько же времени занимает аналого-цифровое преобразование с использованием стандартной функции analogRead.
Для определения моментов начала и конца преобразования я буду использовать 12 вывод в качестве маркера. Для начала повторим эксперимент, который я описывал в статье Оптимизируем digitalWrite на Arduino. Будем изменять уровень напряжения на 12 цифровом пине между состояниями LOW и HIGH. Для чистоты эксперимента я помещу внутрь loop бесконечный цикл.
Скетч, реализующий простые переключения на 12 цифровом выводе выглядит следующим образом:
void setup() { DDRB = B01000000; //устанавливаем 12 пин в режим выхода } void loop() { while(1) { PORTB = B01000000; // устанавливаем пин 12 в состояние HIGH PORTB = B00000000; // устанавливаем пин 12 в состояние LOW } }
Воспользуемся осциллографом и посмотрим на временные параметры работы этой программы:
Отсюда видно, что время переключения состояния пина занимает у нас 62 нс (длительность положительного импульса).
Теперь немного изменим скетч и добавим между переключениями функцию чтения аналогового сигнала analogRead на 3 аналоговом пине:
int analogPin = 3; // входной аналоговый пин int analogValue = 0; // значение аналогового сигнала void setup() { DDRB = B01000000; // устанавливаем 12 пин в режим выхода } void loop() { while(1) { PORTB = B01000000; // устанавливаем пин 12 в состояние HIGH analogValue = analogRead(analogPin); // читаем аналоговый сигнал PORTB = B00000000; // устанавливаем пин 12 в состояние LOW analogValue = analogRead(analogPin); // читаем аналоговый сигнал } }
Осцилограмма сигнала на 12 цифровом пине теперь будет выглядеть следующим образом:
Длительность переключения в 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:
int analogValue = 0; // значение аналогового сигнала void setup() { DDRB = B01000000; // pin 12 в режиме OUTPUT DIDR0 = 0x3F; // отключаем цифровые входы ADMUX = 0x43; // измеряем на ADC3, используем внутреннее опорное напр.= 1.1В ADCSRA = 0xAC; // включаем АЦП, разрешаем прерывания, делитель = 16 ADCSRB = 0x40; // включаем АЦ коналы MUX, режим скользящей выборки bitWrite(ADCSRA, 6, 1); // Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA sei(); // устанавливаем флаг прерывания } void loop() { } /*** Процедура обработки прерывания АЦП ***/ ISR(ADC_vect) { PORTB = B00000000; // пин 12 переводим в состояние LOW analogValue = ADCL; // сохраняем младший байт результата АЦП analogValue += ADCH << 8; // сохраняем старший байт АЦП PORTB = B01000000; // пин 12 переводим в состояние HIGH }
Результат работы программы на экране осциллографа:
Для измерения времени выполнения мы переводим состояние пина в LOW, затем считываем АЦП, после чего вновь устанавливаем высокий уровень. На вызов обработчика прерывания требуется время, с этим и связана достаточно большая продолжительность положительной части периода.
Цикл loop теперь полностью свободен и может использоваться для обработки какого-либо кода.
Опорное напряжение
Для измерения аналогового сигнала у нас должен быть некоторый уровень напряжения, с которым мы будем производить сравнение. В микроконтроллерах ATMega328 и ATMega2560, которые используются в Arduino опорное напряжение также является максимальным напряжением, которое может быть измерено. Напряжения всегда измеряются относительно земли. В Arduino есть три возможных источника опорного напряжения: AVcc — которое соединяется с цифровой линией питания 5 В, внутреннее напряжение 1.1 В (для Arduino Mega возможен еще вариант 2.56 В) и внешний источник опорного напряжения. Из-за того, что измерение входных напряжений производятся относительно опорного напряжения, флуктуации опорного напряжение оказывают влияние на результат.
Опорное напряжение можно установить, используя функцию analogReference или при помощи битов REFT[1:0] в регистре ADMUX.
Опорное напряжение AVcc
AVcc является опорным напряжением по умолчанию и оно используется когда измеряемые напряжения напрямую зависят от напряжения источника питания. Например, в случае, где нужно измерить напряжение в резисторном полумосте, как показано на рисунке ниже.
Если по каким-то причинам напряжение источника питания упадет, то и напряжение в точке соединения двух резисторов упадет пропорционально. Из-за того, что теперь опорное и входное напряжение изменяются пропорционально, то и результат АЦП останется таким же.
Внутренне опорное напряжение 1.1 В
Используйте внутреннее опорное напряжение 1.1 В для точных измерений внешних напряжений. Опорное напряжение 1.1 В более стабильно и не зависит от изменения напряжения питания или температуры. Таким образом, можно производить измерения абсолютных значений. В Arduino Mega также возможен вариант опорного напряжения 2.56 В. примере на рисунке ниже используется опорное напряжение 1.1 В и делитель напряжения 10:1 для измерения внешнего напряжения в диапазоне от 0 до 11 В.
Погрешность
В соответствии с техническим руководством для микроконтроллеров ATMega328 и ATMega2560 опорное напряжение составляет 1.1 ± 0.1 В. Это достаточно большой допуск. Измеренное опорное напряжение тестируемой Arduino Mega 2560 было 1.089 В при температуре окружающего воздуха 21 °С и температура корпуса микроконтроллера была 29 ºC.
Я охладил корпус микроконтроллера, не проводящим ток охлаждающим спреем Kontakt Chemie FREEZE 75/200 до температуры -18 °С, при этом измеренное опорное напряжение снизилось до 1.084 В. Таким образом, температурный дрейф составил примерно 100 ppm(миллионных долей) / °C.
Тестовый скетч:
int analogPin = 3; // входной аналоговый пин void setup() { analogReference(INTERNAL1V1); // выбираем внутреннее опорное напряжение 1.1В Serial.begin(9600); } void loop() { int analogValue = analogRead(analogPin); // читаем значение на аналоговом входе Serial.println(analogValue); // выводим его в последовательный порт delay(300); }
Аналоговый пин 3 был подключен к источнику напряжения 0.545 В. При температуре 29 °C результат должен быть: (0.545/1.089)*1024 = 512 (реально полученное значение — 511). При температуре -18 °C должно быть (0.545/1.084) * 1024 = 515 (реально полученное значение тоже 515).
Как показал эксперимент, температурный дрейф небольшой и для точных измерений при использовании Arduino его нужно откалибровать из-за его большой общей неопределенности опорного напряжения, составляющей около 10%.
Шум
Одним из способов измерить уровень шума является определение разброса значений, получаемых с АЦП. Для этого подадим стабилизированное постоянное напряжение на один из аналоговых входов и преобразованные при помощи АЦП значения используем для построения гистограммы.
Тестовая цепь
Схема на рисунке ниже обеспечивает тестовое напряжение для Arduino.
Стабилизированный регулируемый источник питания выдает напряжение 0.55 В, что составляет половину от опорного напряжения в 1.1 В. На фотографии ниже видно, что встроенный в мой регулируемый источник питания вольтметр явно привирает, показывая напряжение на выходе 0.4 В.
Сигнал дополнительно фильтруется при помощи цепочки R1, C1, C2 и подключается через резистор R2, имеющий сопротивление 100 Ом к аналоговому входу A3 Arduino. Земля подключается к пину GND Arduino.
Шумовая составляющая на входе Arduino выглядит следующим образом:
Отсюда видно, что среднеквадратическое значение амплитуды переменной составляющей измеряемого напряжения на входе АЦП Arduino составляет лишь единицы милливольт.
Биннинг
АЦП микроконтроллеров ATMega328 и ATMega2560 имеет разрешение 210 = 1024 бита. Идея биннинга состоит в подсчете частоты наблюдения определенного значения. Создается массив со 1024 значениями, называемых бинами, которые представляют каждое из возможных значений АЦП. Так как доступная память ограничена, могут быть созданы бины только размером в байт. Число отсчетов, следовательно, ограничивается 255.
Программы
Протестируем шум, используя функцию analogRead, а затем используем прерывания. Две программы, по сути, делают одно и то же: определяют массив, состоящий из 1024 бин. В функции setup все бины инициализируются нулем и выбирается опорное напряжение 1.1 В.
Обе программы производят 10000 фиктивных операций чтения аналогового значения. После этого запускается биннинг и на каждом результате АЦП, соответствующий бин увеличивается на единицу. Если один из 1024 бинов достигнет максимума из 255 значений, выборка останавливается и все 1024 значения бина отправляются на компьютер.
Код примера биннинга измеренных значений, используя функцию analogRead:
int analogPin = 3; // входной аналоговый пин int sendStatus = 0; // статус передачи int startDelay = 0; byte valueBin[1024]; // значения бинов void setup() { analogReference(INTERNAL1V1); // выбираем опорное напряжение 1.1В for (int i=0; i<=1023; i++) valueBin[i] = 0; // очищаем бины Serial.begin(9600); Serial.println("Start"); } void loop() { int analogValue = analogRead(analogPin); // выборка аналогового входа if (sendStatus == 0) { // ничего не делаем первые 10000 выборок if (startDelay < 10000) startDelay++; else { valueBin[analogValue] += 1; // увеличиваем значение бина // останавливаемся, если бин полон if (valueBin[analogValue] == 255) sendStatus = 1; } } if (sendStatus == 1) { for (int i=0; i<=1023; i++) { // выводим значение бина Serial.print(i); Serial.print("\t"); Serial.println(valueBin[i]); } Serial.println("Done"); sendStatus = 2; } }
Код примера биннинга измеренных значений, используя прерывания:
int sendStatus = 0; // статус передачи int startDelay = 0; byte valueBin[1024]; // значения бинов void setup() { TIMSK0 = 0x00; // отключаем таймер (из-за прерываний) DIDR0 = 0x3F; // отключаем цифровые входы ADMUX = 0xC3; // измеряем на ADC3, без корректировки, внутр.опорное напр. 1.1В ADCSRA = 0xAC; // включаем АЦП, разрешаем прерывания, делитель = 128 ADCSRB = 0x40; // Включаем каналы MUX АЦП, режим постоянной выборки bitWrite(ADCSRA, 6, 1); // Запускаем преобразование установкой бита 6 (=ADSC) в ADCSRA sei(); // устанавливаем глобальный флаг прерываний for (int i=0; i<=1023; i++) valueBin[i] = 0; // очищаем бины Serial.begin(9600); Serial.println("Start"); } void loop() { if (sendStatus == 1) { for (int i=0; i<=1023; i++) { // выводим значения бинов Serial.print(i); Serial.print("\t"); Serial.println(valueBin[i]); } Serial.println("Done"); sendStatus = 2; } } /*** Процедура обработки прерывания АЦП ***/ ISR(ADC_vect) { int analogValue = ADCL; // сохраняем младший байт АЦП analogValue += ADCH << 8; // сохраняем старший байт АЦП if (sendStatus == 0) { // ничего не делаем первые 10000 выборок if (startDelay < 10000) startDelay++; else { valueBin[analogValue] += 1; // увеличиваем значение бина if (valueBin[aval] == 255) sendStatus = 1; { // останавливаемся, если бин полон } } }
Тестируемые частоты
Тест проводился с использованием функции 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 при переключении между аналоговыми входами:
void loop() { if (analogPin == 3) analogPin = 5; else analogPin = 3; int analogValue = analogRead(analogPin); ...
Изменения в коде для режима непрерывной выборки при переключении между аналоговыми входами:
ISR(ADC_vect) { if (ADMUX == 0xC3) ADMUX = 0xC5; else ADMUX = 0xC3; int analogValue = ADCL; ...
Результаты
Оба измеренных напряжения видны как два выступа на гистограммах. На рисунке ниже представлены гистограммы пяти тестов: с использованием функции analogRead, непрерывная выборка с clk÷128, clk÷64, clk÷32 и clk÷16. Измеренные значения первого напряжения (результат обработки АЦП = 511) не отклоняются от предыдущего теста шума. Измерение по-прежнему точное. Окружающих бинов очень мало, это означает, что уровень шума не увеличился.
Частота выборки и разрешение
Коды, реализующие биннинг использованы и для анализа разрешения и частоты выборки. Для этого теста к аналоговому входу Arduino был подключен генератор функций, как показано на рисунке ниже.
Генератор функций подает треугольную волну с напряжением размаха в 25 мВ и напряжением смещения (= среднему значению) в 0.55 В. На каждом измерении частота сигнала выбирается таким образом, чтобы частота выборки была в 163 раза выше.
Треугольный сигнал выбран из-за того, что каждое значение при квантовании встречается одинаково часто. При биннинге такого сигнала каждое значение бина с минимальным и максимальным значениями напряжения может иметь то же самое число повторений.
Результаты
Результаты тестирования показали, что функция analogRead, работающая с низкой частотой дискретизации и непрерывная выборка с частотой clk÷128 имеют достаточную плоскую вершину: все значения в диапазоне встречаются с одним и тем же числом повторений. Но на более высоких частотах дискретизации (clk÷64, clk÷32 и clk÷16) возникают провалы в области биннинга и с ростом частоты ситуация ухудшается.
В технических описаниях на микроконтроллеры ATmega об этом явлении упоминается: на частоте АЦП-преобразования выше 200 кГц разрешение снижается. На тактовой частоте 16 МГц и, используя коэффициент деления 64, частота работы АЦП становится равной 250 кГц, что чуть выше порога, где снижается разрешение, что и подтверждается проведенными измерениями.
[add_ratings]
Здравствуйте Андрей! Почему напряжение измеряют среднеквадратичным значением, а не средним арифметическим?
Рашит, здравствуйте. Потому, что это связано с мощностью. Среднеквадратическим является такое значение напряжения или тока, при котором на нагрузке рассеивается та же мощность, что и при постоянном напряжении или токе. Подробнее в статье Основные понятия в электроизмерениях.
Потому что тогда положительные и отрицательные значения взаимоуничтожатся, что не всегда приемлемо.
Спасибо!
Добрые дяди... Наконец руки дошли и я сделал вот так:
Возникла пренеприятнейшая гадость... Всё в принцыпе работает как и должно, НО иногда вылезают какие-то помехи что-ли... Вобщем раз-два в сутки стабильно происходит ложная сработка (датчик в покое). Закономерности (день/ночь/час/минута/вздох/скрип/мат) не выявлено. Разве что питание... Думаю на него, но с какого фига? Оно ведь DC 10В через БП. Или что-то ещё может быть?
Запитать от аккумулятора для теста конечно можно... Но сколько выжидать для этого придётся...
Чего греха таить — использую FLprog. Вот таким образом считываю сигнал с датчика:
Хотелось бы использовать значение 1 или 2. Но блин помехи...
PS: Могу решить проблему — тупо программно сильнее загрубив чувствительность. Но в таком случае толку от датчика уже не будет. Поможете советом?
PS2: Готов заняться поиском источника помех. Но КАК реализовать что-то типа осциллографа в домашних условиях с минимальными затратами? Что-то типа отображения текущего сигнала визуально на экране монитора... Видеть бы это — думаю нашёл бы проблему...
PS3: Какую роль выполняет диод Зенера здесь?
PS4: Вопрос немного не по теме... Как вычислить где плюс, где минус на пьезо излучателе, если все залито клеем и оба провода белые? )))
Работает и так и так)) Помехам пофигу полярность))
Спасибо большое))
Я полагаю, вам проще всего создать программный фильтр, подобный тому, что устраняет эффект дребезга контактов на кнопках. Но вам нужно пропускать однократные срабатывания датчика, если между ними проходит слишком большое время. Можно экспериментально потестить, какой именно поток данных порождает истинное срабатывание датчика, и программно отсеять «нетипичное поведение». Программные фильтры помех используются довольно часто, потому что это возможность корректировать работу устройств обновлением прошивки, не меняя железо. Как вариант пришло в голову, сам не пробовал еще это, но видел примеры скетчей недавно,случайно, где пограммными фильтрами отсеивают очень специфические помехи.)))
1. Пьезоэлектрический датчик обладает большим сопротивлением, поэтому соединительные провода являются источником достаточно сильных помех. Нужно либо подключить датчик напрямую к микроконтроллеру, либо, непосредственно к датчику подключить повторитель на полевых транзисторах или операционном усилителе (они обладают большим входным и маленьким выходным сопротивлением). Еще вариант — использовать дифференциальный усилитель, опять же, на полевых транзисторах или ОУ.
2. Самый дешевый вариант осциллографа — использовать АЦП звуковой карты компьютера. Погуглите на тему «осциллограф звуковая карта». Правда, полоса пропускания будет ограничена звуковой частотой где-то в районе 20 кГц. Помеха же может быть и на частоте 100 кГц и выше.
3. Стабилитрон защищает вход микроконтроллера от повышенного напряжения (пьезоизлучатель может сгенерировать напряжение, превышающее допустимое по входу микроконтроллера), хотя, по идее, в микроконтроллере встроены по входам защитные диоды — так что использование стабилитрона может быть лишним.
4. Обычно, полярность подключения для пьезоилучателя не имеет значения. Исключение — пьезоизлучатель со встроенным генератором. При неправильном подключении просто не работает генератор. Но на излучателях с генератором «+» помечается, значит — у вас без генератора и можно подключать как угодно.
Ок. С измерениями всё понятно.
А как ещё можно использовать аналоговые входы? Они на OUT не переключаются?
Неа, не переключаются. Аналоговый вход — это вход аналого-цифрового преобразователя. Аналоговый выход можно реализовать, используя выходы ШИМ (PWM) со сглаживающим фильтром (например, в виде RC-цепочки) на выходе.
Неверно сформулировал вопрос и получил не тот ответ 🙂
В документации написано, если не вру, что их можно использовать как Digital IN и Digital OUT.
Ага, неправильно вас понял 🙂 Ножки микроконтроллера Atmega, на которые выведены каналы АЦП можно использовать как обычные цифровые входы и выходы, сконфигурировав их соответствующим образом.
Как можно программно увеличить разрядность до 16 бит? (квантование там, дизеринг, не помню точно как это названо). С тем, чтобы получить в итоге 16бит при 44 кГц оцифровке для записи звука с микрофона в wav файл. В сети есть инфо про разгон ардуино про мини на кварце 25МГц. Возможно ли все это осуществить?
Повысить разрядность АЦП можно, используя метод передискретизации (оверсемплинг). Однако в этом случае, для добавления разрядности на 1 бит, необходимо увеличить частоту дискретизации в 4 раза. Частота дискретизации в 10-разрядном АЦП в микроконтроллерах с архитектурой AVR ограничивается величиной в 1 МГц. Чтобы увеличить разрядность с 10 до 16 бит, потребуется частота работы АЦП в районе 45 МГц. Для МК Atmega это нереально. Но может я чего не догоняю...
Дизеринг, в случае со звуком, используется для улучшения его восприятия. Для этого к исходному сигналу подмешивается псевдослучайный сигнал (dither, от староанглийского didderen — дрожать) — «белый» шум небольшой амплитуды. Точно также делают и для улучшения визуального восприятия графических изображений — картинка искусственно зашумляется, границы цвета и яркости становятся не такими резкими.
Еще было бы интересно узнать о влиянии входного сопротивления, на точность измерения. Не всегда есть возможность работать с источником напряжения с сопротивлением 100 ом.
Про понятия Погрешность, точность и разрешение — это чтобы мы на одном языке разговаривали и понимали друг друга.
Если у нас ко входу АЦП подключить источник с бОльшим выходным сопротивлением, то мы получим на входе сигнал с меньшей амплитудой, а разброс значений в абсолютном выражении (шум) останется таким же. Соответственно, точность снизится. Идеальный вольтметр (и вход АЦП) должен обладать бесконечно большим сопротивлением и бесконечно малыми емкостью и индуктивностью по отношению к нагрузке.
Спасибо за статью!
Наткнулся на забавный факт — если вместо «ISR (ADC_vect)» написать «ISR (ADC_Vect)» (V заглавная), то ошибок компиляции не возникает — программа просто перестает работать.
Потерял 2 дня вылавливая эту опечатку в своем коде.
Статья отличная! Спасибо.
Но покритикую последний этап. Особенно последняя серия графиков выглядит не очень внушительно. На большой частоте у вас сумма в бинах гораздо ниже. При большей частоте, это означает гораздо меньшее время измерений, что не есть хорошо. И если у вас устройство выполняет этот код сразу после перезагрузки, совсем не хорошо. У вас 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. Между двумя кодами АЦП. Что собственно вы и получили. Не уверен, что в таком ключе можно что-то говорить об уровне шума, вспоминая про малое время измерений.
Если я что-то неверно понял, заранее извиняюсь, в любом случае статья интересная.
можно на ардуино собрать инвертор приближенной к чистой синусоиде и как вы бы это сделали?
Здесь прекрасно описана теория ШИМ-преобразователей. В статье для генерации ШИМ-модулированного сигнала используется специализированная микросхема TL494 (отечественный аналог м/сх К1006ЕУ4). Среди аналогичных микросхем можно еще посмотреть усовершенствованные версии этой микросхемы Texas Instruments: TL594 (улучшенная точность усилителей ошибки и компаратора), TL598 (с двухтактным (pnp-npn) повторителем на выходе) или же от ON Semiconductor SG3525. SG6105 частенько можно встретить в компьютерных блоках питания. Однако, вполне можно генерировать ШИМ и используя Arduino. Здесь можно посмотреть схемное решение трехфазного инвертора на базе Arduino MEGA2650. Здесь есть пример кода генерации необходимого ШИМ-сигнала, используя таблицу для вычисления скважности.
Может быть вопрос не в тему. Но зачем нужен шим, да еще и с частотой 490Гц. Если можно реализовать c помощью digitalWrite ,переключая между HIHG и LOW с нужной задержкой. Получить любые сигналы с частотой уже 12-16 МГц??
Затем, что аппаратный ШИМ работает сам по себе, а в вшем случае контроллер только и будет делать что дергать ногой, а на все остальное не останится времени.
Здравствуйте! Я программировании ардуно ещё учусь, но мне очень надо сделать преобразование аналового входа таким образом чтобы в зависимости от изменений входящего тока 0-5 В выходящий давал нужное напряжение посредством VQ таблиц. Напр.: входящий 1,0 В — выходной 0,78 В, входящий 1,5 В — выходной 1,2 В. Подскажите как это проделать с Arduino Duemilanove?
Вы пишете: DIDR0 = 0x3F; // отключаем цифровые входы
Объясните пожалуйста зачем эта строчка, а то я не понимаю, стоит ли мне это делать в моем проекте или не обязательно,
Спасибо!
Добрый день. Нужно измерить сигнал с верхней частотой 30 кГц. Частота дискретизации должна быть в 5 а лучше в 10 раз выше. Здесь вы упоминаете о частоте 250 кГц, а в комментариях 1 МГц. Как это сделать на Ардуино Мега?