Аналогові виміри з Arduino. Створення зарядного пристрою, керованого Arduino

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

analogRead

Давайте подивимося скільки часу займає аналого-цифрове перетворення з використанням стандартної функції analogRead.

Для визначення моментів початку і кінця перетворення я використовуватиму 12 висновок як маркер. Для початку повторимо експеримент, який я описував у статті. Змінюватимемо рівень напруги на 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); // читаємо аналоговий сигнал

int analogPin = 3; // вхідний аналоговий пін

int analogValue = 0; // значення аналогового сигналу

void setup()

DDRB = B01000000; // встановлюємо 12 пін у режим виходу

void loop ()

while (1 )

PORTB = B01000000; // встановлюємо пін 12 стан HIGH

// читаємо аналоговий сигнал

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В ADRA 0xAC; // включаємо АЦП, дозволяємо переривання, дільник = 16 ADCSRB = 0x40; // включаємо АЦ конали MUX, режим ковзної вибірки bitWrite(ADCSRA, 6, 1); sei(); ( ) /*** Процедура обробки переривання АЦП ***/ ISR(ADC_vect) ( PORTB = B00000000; // пін 12 переводимо в стан LOW analogValue = ADCL; // зберігаємо молодший байт результату АЦП analogValue += ADCH<< 8; // сохраняем старший байт АЦП PORTB = B01000000; // пин 12 переводим в состояние HIGH }

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є три можливі джерела опорної напруги: AV cc- яке з'єднується з цифровою лінією живлення 5 В, внутрішня напруга 1.1 В (для Arduino Megaможливий ще варіант 2.56) і зовнішнє джерело опорної напруги. Через те, що вимірювання вхідних напруг виробляються щодо опорної напруги, флуктуації опорної напруги впливають на результат.

Опорну напругу можна встановити за допомогою функції або за допомогою бітів REFTу регістрі ADMUX.

Опорна напруга AV cc

AV ccє опорною напругою за умовчанням і воно використовується коли напруги, що вимірюваються, безпосередньо залежать від напруги джерела живлення. Наприклад, у випадку, де потрібно виміряти напругу в резисторному напівмості, як показано на малюнку нижче.

Використання опорної напруги 5В при вимірі опору в напівмості

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

Внутрішня опорна напруга 1.1

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

Використання зовнішньої опорної напруги або внутрішньої напруги 1.1 при вимірюванні зовнішніх напруг

Похибка

Відповідно до технічного посібника для мікроконтролерів 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);

int analogPin = 3; // вхідний аналоговий пін

void setup()

// вибираємо внутрішню опорну напругу 1.1В

Serial. begin (9600);

void loop ()

// Читаємо значення на аналоговому вході

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.

Схема, що подає регульовану постійну напругу на аналоговий вхід Arduino

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

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

Шумова складова на вході Arduinoвиглядає наступним чином:

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

Біннінг

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

Програми

Протестуємо шум, використовуючи функцію analogRead, а потім використовуємо переривання. Дві програми, по суті, роблять те саме: визначають масив, що складається з 1024 бін. У функції setupвсі біни ініціалізуються нулем і вибирається опорна напруга 1.1.

Обидві програми виробляють 10 000 фіктивних операцій читання аналогового значення. Після цього запускається біннінг і кожному результаті АЦП, відповідний бін збільшується на одиницю. Якщо один із 1024 бінів досягне максимуму з 255 значень, вибірка зупиняється і всі 1024 біни відправляються на комп'ютер.

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

Показати/приховати код

int analogPin = 3; // Вхідний аналоговий пін int sendStatus = 0; // Статус передачі int startDelay = 0; byte valueBin; // значення бінів 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 += 1; // увеличиваем значение бина if (valueBin == 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 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);

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; // значення бінів void setup() ( TIMSK0 = 0x00; // відключаємо таймер (через переривання) DIDR0 = 0x3F; // відключаємо цифрові входи ADMUX = 0xC3; // вимірюємо на ADC3, без коригування, внутр.опорне напр. 1.1В ADCSRA = 0xAC; // включаємо АЦП, дозволяємо переривання, дільник = 128 ADCSRB = 0x40; // Включаємо канали MUX АЦП, режим постійної вибірки bitWrite(ADCSRA, 6, 1); ) 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 += 1; // увеличиваем значение бина if (valueBin == 255) sendStatus = 1; { // останавливаемся, если бин полон } } }

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 ;

// зупиняємось, якщо бін повний

}

}

}

Тестовані частоти

Тест проводився з використанням функції 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у регістрі ADMUX. Особливу увагу слід приділити при використанні режиму безперервної вибірки: аналоговий канал потрібно вибрати перед стартом нового аналогового перетворення. У процедурі обробки переривання вибирається аналоговий вхід, який зчитуватиметься в момент наступного переривання.

Щоб перевірити рівень шуму та похибку при перемиканні входів, потрібно трохи змінити представлені вище програми. Друга напруга подається на аналоговий вхід 5. Крім того, проводиться бінгінг виміряних значень, як і при тестуванні шуму.

= 0xC3;

intanalogValue= 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

У цій статті наводиться цікава схема для любителів експериментів та Arduino. У ній представлений простий цифровий вольтметр, який може безпечно вимірювати постійну напругу в діапазоні від 0 до 30 В. Сама платня Arduino може живитися від стандартного джерела 9 В.



Як відомо, за допомогою аналогового входу Arduino можна виміряти напругу від 0 до 5 (при стандартному опорному напрузі 5 В). Але цей діапазон можна розширити, скориставшись дільником напруги.


Дільник знижує вимірювану напругу до прийнятного для аналогового входу рівня. Потім спеціально написаний код вираховує фактичну напругу.



Аналоговий датчик у складі Arduino визначає напругу на аналоговому вході і перетворює його на цифровий формат, що сприймається мікроконтролером. До аналогового входу A0 ми підключаємо дільник напруги, утворений опорами R1 (100K) та R2 (10K). З такими значеннями опорів на Arduino можна подавати до 55, оскільки коефіцієнт поділу в даному випадку виходить 11, тому 55В/11=5В. Для того, щоб бути впевненим у безпеці вимірів для плати, краще проводити вимір напруги в діапазоні від 0 до 30 В.



Якщо показання дисплея не відповідають показанням повіреного вольтметра, слід використовувати цифровий прецизійний мультиметр для знаходження точних значень R1 і R2. При цьому в коді потрібно буде замінити R1=100000.0 та R2=10000.0 своїми значеннями. Потім варто перевірити живлення, вимірявши на платі напругу між 5V та GND. Напруга може бути 4.95 В. Тоді в коді vout=(value*5.0)/1024.0 потрібно замінити 5.0 на 4.95. Бажано використовувати прецизійні резистори з похибкою трохи більше 1%. Пам'ятайте, що напруга вище 55 В може вивести плату Arduinoз ладу!



#include LiquidCrystal lcd(7, 8, 9, 10, 11, 12); int analogInput = 0; float vout = 0.0; float vin = 0.0; float R1 = 100000.0; // опір R1 (100K) float R2 = 10000.0; // Опір R2 (10K) int value = 0; void setup()( pinMode(analogInput, INPUT); lcd.begin(16, 2); lcd.print("DC VOLTMETER"); ) void loop()( // зчитування аналогового значення value = analogRead(analogInput); vout = (value * 5.0) / 1024.0; vin = vout / (R2/(R1+R2));<0.09) { vin=0.0;// обнуляем нежелательное значение } lcd.setCursor(0, 1); lcd.print("INPUT V= "); lcd.print(vin); delay(500); }


Використовувані елементи:


Плата Arduino Uno
Резистор 100 кому
Резистор 10 кім
Резистор 100 Ом
Потенціометр 10 КІМ
LCD-дисплей 16×2

У цій статті наводиться цікава схема для любителів експериментів та Arduino. У ній представлений простий цифровий вольтметр, який може безпечно вимірювати постійну напругу в діапазоні від 0 до 30 В. Сама платня Arduino може живитися від стандартного джерела 9 В.



Як відомо, за допомогою аналогового входу Arduino можна виміряти напругу від 0 до 5 (при стандартному опорному напрузі 5 В). Але цей діапазон можна розширити, скориставшись дільником напруги.


Дільник знижує вимірювану напругу до прийнятного для аналогового входу рівня. Потім спеціально написаний код вираховує фактичну напругу.



Аналоговий датчик у складі Arduino визначає напругу на аналоговому вході і перетворює його на цифровий формат, що сприймається мікроконтролером. До аналогового входу A0 ми підключаємо дільник напруги, утворений опорами R1 (100K) та R2 (10K). З такими значеннями опорів на Arduino можна подавати до 55, оскільки коефіцієнт поділу в даному випадку виходить 11, тому 55В/11=5В. Для того, щоб бути впевненим у безпеці вимірів для плати, краще проводити вимір напруги в діапазоні від 0 до 30 В.



Якщо показання дисплея не відповідають показанням повіреного вольтметра, слід використовувати цифровий прецизійний мультиметр для знаходження точних значень R1 і R2. При цьому в коді потрібно буде замінити R1=100000.0 та R2=10000.0 своїми значеннями. Потім варто перевірити живлення, вимірявши на платі напругу між 5V та GND. Напруга може бути 4.95 В. Тоді в коді vout=(value*5.0)/1024.0 потрібно замінити 5.0 на 4.95. Бажано використовувати прецизійні резистори з похибкою трохи більше 1%. Пам'ятайте, що напруга вище 55 В може вивести плату Arduino з ладу!



#include LiquidCrystal lcd(7, 8, 9, 10, 11, 12); int analogInput = 0; float vout = 0.0; float vin = 0.0; float R1 = 100000.0; // опір R1 (100K) float R2 = 10000.0; // Опір R2 (10K) int value = 0; void setup()( pinMode(analogInput, INPUT); lcd.begin(16, 2); lcd.print("DC VOLTMETER"); ) void loop()( // зчитування аналогового значення value = analogRead(analogInput); vout = (value * 5.0) / 1024.0; vin = vout / (R2/(R1+R2));<0.09) { vin=0.0;// обнуляем нежелательное значение } lcd.setCursor(0, 1); lcd.print("INPUT V= "); lcd.print(vin); delay(500); }


Використовувані елементи:


Плата Arduino Uno
Резистор 100 кому
Резистор 10 кім
Резистор 100 Ом
Потенціометр 10 КІМ
LCD-дисплей 16×2

Представлено корисну схему для любителів поекспериментувати з Ардуїно. Це простий цифровий вольтметр, яким можна надійно вимірювати постійну напругу в діапазоні 0 – 30В. Плату Ардуїно, як завжди, можна живити від 9В батареї.

Як вам відомо, аналогові входи Ардуїно можна використовувати для вимірювання постійної напруги в діапазоні 0 – 5В і цей діапазон можна збільшити,
використовуючи два резистори як дільник напруги. Дільник зменшить вимірювану напругу рівня аналогових входів Ардуино. А потім програма обчислить реальну величину напруги.

Аналоговий датчик на платі Ардуїно визначає наявність напруги на аналоговому вході та перетворює його на цифрову форму для подальшої обробки мікроконтролером. На малюнку напруга подається на аналоговий вхід (А0) через простий дільник напруги, що складається з резисторів R1 (100кОм) та R2 (10кОм).

За цих значень дільника на плату Ардуїно можна подавати напругу від 0 до
55В. На вході А0 маємо вимірювану напругу поділену на 11, тобто 55В / 11 = 5В. Інакше висловлюючись, при вимірі 55В на вході Ардуїно маємо максимально допустиме значення 5В. Насправді краще цьому вольтметрі написати діапазон “0 – 30В”, щоб залишався
Запас з безпеки!

Примітки

Якщо показання дисплея не збігаються зі показаннями промислового (лабораторного) вольтметра, то необхідно точним приладом виміряти величину опорів R1 та R2 і вставити ці значення замість R1=100000.0 та R2=10000.0 у коді програми. Потім слід виміряти лабораторним вольтметром реальну напругу між висновками 5В і Земля плати Ардуїно. Вийде значення менше, ніж 5В, наприклад, вийшло 4.95В. Це реальне значення слід вставити в кодовому рядку
vout = (value * 5.0)/1024.0 замість 5.0.
Крім того, намагайтеся застосовувати прецизійні резистори з допуском 1%.

Резистори R1 і R2 забезпечують деякий захист від підвищених вхідних напруг. Проте слід пам'ятати, що будь-які напруги вище 55В можуть вивести з ладу плату Ардуїно. Крім того, в цій конструкції не передбачені інші види захисту (від стрибків напруги, від переполюсування або підвищеної напруги).

Програма цифрового вольтметра

/*
DC Voltmeter
An Arduino DVM базується на voltage divider concept
T.K.Hareendran
*/
#include
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int analogInput = 0;
float vout = 0.0;
float vin = 0.0;
float R1 = 100000.0; // Resistance of R1 (100K) -see text!
float R2 = 10000.0; // Resistance of R2 (10K) – see text!
int value = 0;
void setup()(
pinMode(analogInput, INPUT);
lcd.begin(16, 2);
lcd.print("DC VOLTMETER");
}
void loop()(
// read the value at analog input
value = analogRead(analogInput);
vout = (value * 5.0)/1024.0; // see text
vin = vout / (R2/(R1+R2));
if (vin<0.09) {
vin=0.0;//statement to quash undesired reading!
}
lcd.setCursor(0, 1);
lcd.print(“INPUT V=”);
lcd.print(vin);
delay(500);
}

Принципова схема Ардуїно-вольтметра

Перелік компонентів

Плата Arduino Uno
100 ком резистор
10 ком резистор
100 Ом резистор
10кОм Підстроювальний резистор
LCD дисплей 16×2 (Hitachi HD44780)

Arduino та додана до неї схема заряду можуть бути використані для моніторингу та управління зарядкою нікель-метал-гідридних акумуляторів, наприклад, так:

Закінчений пристрій

Акумуляторні батареї є відмінним способом живлення вашої портативної електроніки. Вони можуть заощадити багато грошей при правильній зарядці. Для того, щоб ви могли отримати максимальну віддачу від акумуляторних батарей, їх необхідно правильно заряджати. Це означає, що вам потрібний гарний зарядний пристрій. Ви можете витратити купу грошей, купивши готовий зарядний пристрій, а можете отримати задоволення, зробивши його самі. У цій статті ми розглянемо, як створити зарядний пристрій, керований Arduino.

По-перше, важливо відзначити, що немає універсального способу зарядки, який підходив би для всіх акумуляторів. Різні типи акумуляторів використовують різні хімічні процеси, що забезпечують їхню роботу. У результаті різні типи акумуляторів необхідно заряджати по-різному. У цій статті ми не зможемо охопити всі типи акумуляторних батарей та методи заряджання. Тому для простоти ми зосередимо увагу на найбільш поширеному типі акумуляторних батарей розміру AA, на нікель-метал-гідридних акумуляторах (NiMH).

Комплектуючі

Список комплектуючих зліва направо:

  • потужний резистор 10 Ом (мінімум 5 Вт);
  • резистор 1 МОм;
  • конденсатор 1 мкФ;
  • MOSFET транзистор IRF510;
  • датчик температури TMP36;
  • джерело живлення 5 вольт;

Як заряджати NiMH AA акумулятори

Збільшення швидкості заряду збільшує ризик пошкодження акумулятора.

Існує багато способів заряджання NiMH акумуляторів. Вибір методу, який ви використовуєте, в основному залежить від того, як швидко ви хочете зарядити акумулятор. Швидкість заряду вимірюється по відношенню до ємності батареї. Якщо ваша батарея має ємність 2500 мАч, і ви заряджаєте її струмом 2500 мА, то ви заряджаєте її зі швидкістю 1C. Якщо ви заряджаєте цей акумулятор струмом 250 мА, то ви заряджаєте його зі швидкістю C/10.

Під час швидкого заряджання акумулятора (зі швидкістю вище C/10), необхідно ретельно контролювати напругу на батареї та її температуру, щоб не перезарядити її. Це може пошкодити акумулятор. Тим не менше, коли ви заряджаєте акумулятор повільно (зі швидкістю нижче C/10), у вас набагато менше шансів пошкодити батарею, якщо її випадково перезарядите. Тому повільні методи заряджання, як правило, вважаються безпечнішими і допоможуть вам збільшити термін служби батареї. Тому в нашому саморобному зарядному пристрої ми будемо використовувати швидкість заряду C/10.

Ланцюг заряду

Для даного зарядного пристрою основою є схема управління джерелом живлення за допомогою Arduino. Схема живиться від напруги 5 вольт, наприклад, від адаптера змінного струму або комп'ютерного блоку живлення. Більшість USB портів не підходить для цього проекту через обмеження струму. Джерело 5В заряджає батарею через потужний резистор 10 Ом та потужний MOSFET транзистор. MOSFET транзистор встановлює величину струму, що протікає через батарею. Резистор доданий як простий спосіб контролю струму. Контроль величини струму виконується підключенням кожного виведення резистора до аналогових вхідних висновків Arduino та вимірюванням напруги з кожної сторони. MOSFET транзистор управляється вихідним ШИМ виводом Arduino. Імпульси сигналу широтно-імпульсної модуляції згладжуються до постійної напруги фільтром на резисторі 1 МОм та конденсаторі 1 мкФ. Дана схема дозволяє Arduino відстежувати та керувати струмом, що протікає через батарею.


Датчик температури

Датчик температури служить для запобігання перезаряду батареї та забезпечення безпеки.

Як додатковий запобіжний засіб до зарядного пристрою доданий датчик температури TMP36 для контролю температури батареї. Даний датчик видає напругу, яка лінійно залежить від температури. Тому він, на відміну від термісторів, не потребує калібрування чи балансування. Датчик встановлюється в просвердленому отворі в корпусі тримача батареї та приклеюється в отворі так, щоб він притискався до батареї, коли та буде встановлена ​​у тримач. Висновки датчика підключаються до шини 5В, корпусу і аналогового вхідного висновку Arduino.

Утримувач AA батареї перед та після встановлення на макетну плату

Код


Код для цього проекту досить простий. Змінні на початку вихідного коду дозволяють налаштувати зарядний пристрій шляхом введення значень ємності батареї та точного опору потужного резистора. Також додані змінні безпечних порогів. Максимально допустима напруга на батареї встановлюється значення 1,6 вольта. Максимальну температуру батареї встановлено на 35 градусів за Цельсієм. Максимальний час заряду встановлено на 13 годин. Якщо будь-який з цих порогів безпеки буде перевищено, зарядний пристрій вимикається.

У тілі програми можна побачити, що система постійно вимірює напруги на висновках потужного резистора. Це використовується для розрахунку значень напруги на батареї і струму, що протікає через неї. Струм порівнюється з цільовим значенням, яке становить C/10. Якщо розрахований струм відрізняється від цільового значення більш ніж на 10 мА, система автоматично підлаштовує вихідне значення, щоб підкоригувати його.

Arduino використовує послідовний інтерфейс для відображення поточних даних. Якщо ви хочете проконтролювати роботу зарядного пристрою, то можете підключити Arduino до USB порту комп'ютера, але це необов'язково, оскільки Arduino живиться від джерела напруги 5В зарядного пристрою.

Int batteryCapacity = 2500; // значення ємності батареї мАч float resistance = 10.0; // Виміряний опір потужного резистора int cutoffVoltage = 1600; // максимальна напруга на батареї (мВ), яка не повинна бути перевищена float cutoffTemperatureC = 35; // максимальна температура батареї, яка повинна бути перевищена (у градусах C) //float cutoffTemperatureF = 95; // максимальна температура батареї, яка повинна бути перевищена (у градусах F) long cutoffTime = 46800000; // максимальний час заряду о 13 годині, який має бути перевищено int outputPin = 9; // Провід вихідного сигналу підключений до цифрового виведення 9 int outputValue = 150; // значення вихідного ШІМ сигналу int analogPinOne = 0; // Перший датчик напруги підключений до аналогового висновку 0 float valueProbeOne = 0; // Змінна для зберігання значення на analogPinOne float voltageProbeOne = 0; // Розрахована напруга на analogPinOne int analogPinTwo = 1; // другий датчик напруги підключений до аналогового висновку 1 float valueProbeTwo = 0; // Змінна для зберігання значення на analogPinTwo float voltageProbeTwo = 0; // Розрахована напруга на analogPinTwo int analogPinThree = 2; // Третій датчик напруги підключений до аналогового висновку 2 float valueProbeThree = 0; // Змінна для зберігання значення на analogPinThree float tmp36Voltage = 0; // Розрахована напруга на analogPinThree float temperatureC = 0; //Розрахована температура датчика в градусах C //float temperatureF = 0; // Розрахована температура датчика у градусах F float voltageDifference = 0; // Різниця між напругами на analogPinOne і analogPinTwo float batteryVoltage = 0; // Розрахована напруга батареї float current = 0; // Розрахований струм, що протікає через навантаження (мА) float targetCurrent = batteryCapacity / 10; // цільовий вихідний струм (в мА) встановлюється значення // C/10 або 1/10 від ємності батареї float currentError = 0; // різниця між цільовим і фактичним струмами (в мА) void setup() ( Serial.begin(9600); // налаштування послідовного інтерфейсу pinMode(outputPin, OUTPUT); // встановити висновок, як вихід) void loop() (analogWrite (outputPin, outputValue); // записати вихідне значення у вихідний висновок Serial.print("Output: "); // показати вихідні значення для контролю на комп'ютері Serial.println(outputValue); valueProbeOne = analogRead(analogPinOne); // вважати вхідне значення на першому пробнику voltageProbeOne = (valueProbeOne*5000)/1023;// розрахувати напругу на першому пробнику в мілівольтах Serial.print("Voltage Probe One (mV): "); voltageProbeOne), valueProbeTwo = analogRead(analogPinTwo); // вважати вхідне значення на другому пробнику voltageProbeTwo = (valueProbeTwo*5000)/1023; // розрахувати напругу на другому пробнику в мілівольтах Serial.print("V );// показати напругу на другому пробнику Serial.println(voltageProbeTwo); batteryVoltage = 5000 - voltageProbeTwo; // Розрахувати напругу на батареї Serial.print("Battery Voltage (mV):"); // показати напругу батареї Serial.println(batteryVoltage); current = (voltageProbeTwo - voltageProbeOne) / resistance; // Розрахувати струм заряду Serial.print("Target Current (mA):"); // показати цільовий струм Serial.println(targetCurrent); Serial.print("Battery Current (mA): "); // показати фактичний струм Serial.println (current); currentError = targetCurrent - current; // Різниця між цільовим та виміряним струмами Serial.print("Current Error (mA): "); // показати помилку встановлення струму Serial.println(currentError); valueProbeThree = analogRead(analogPinThree); // Вважати вхідне значення третього пробника, tmp36Voltage = valueProbeThree * 5. 0; // Перетворюючи його в напругу tmp36Voltage / = 1024.0; TemperatureC = (tmp36Voltage - 0.5) * 100; // перетворення, виходячи із залежності в 10 мВ на градус зі здвиком в 500 мВ // ((напруга - 500 мВ) помножити на 100) Serial.print("Temperature (degrees C)"); //Показати температуру в градусах Цельсія Serial.println(temperatureC); /* temperatureF = (temperatureC * 9.0/5.0) + 32.0; //перетворити на градуси Фаренгейта Serial.print("Temperature (degrees F) "); Serial.println(temperatureF); */ Serial.println(); // Додаткові порожні рядки, щоб полегшити читання даних під час налагодження Serial.println(); if(abs(currentError) > 10) // якщо помилка установки струму досить велика, то підлаштувати вихідну напругу ( outputValue = outputValue + currentError / 10; if(outputValue< 1) // выходное значение никогда не может быть ниже 0 { outputValue = 0; } if(outputValue >254) // Вихідне значення ніколи не може бути вище 255 (outputValue = 255;) analogWrite(outputPin, outputValue); // записати нове вихідне значення ) if(temperatureC > cutoffTemperatureC) // зупинити зарядку, якщо температура батареї перевищила безпечний поріг ( outputValue = 0; Serial.print("Max Temperature Exceeded"); ) /* if(temperatureF > cutoffTempera / зупинити зарядку, якщо температура батареї перевищила безпечний поріг ( outputValue = 0; ) */ if(batteryVoltage > cutoffVoltage) // зупинити зарядку, якщо напруга на батареї перевищила безпечний поріг ( outputValue = 0; Serial.print("Max Voltage ); ) if(millis() > cutoffTime) // зупинити зарядку, якщо час заряду перевищив поріг ( outputValue = 0; Serial.print("Max Charge Time Exceeded"); ) delay(10000); // затримка в 10 секунд перед наступною ітерацією циклу)

Завантажену версію вихідного коду можна знайти за посиланням, наведеним нижче.