◆ Чому не Blink?
Майже кожен навчальний курс з мікроконтролерів починається з блимання світлодіодом. Це приємна вправа для перших п’яти хвилин, але вона майже нічого не пояснює про те, як мікроконтролер насправді працює з реальним світом.
Температура, світло, звук, тиск, струм, напруга — усе це аналогові сигнали. Світлодіод про них мовчить. А ось аналого-цифровий перетворювач (ADC, англ. Analog-to-Digital Converter) — це перше місце, де мікроконтролер починає “чути” фізичний світ. До речі, в українському законодавстві цей термін не абстракція з підручника — “аналого-цифровий перетворювач (АЦП)” прямо фігурує в ДСТУ 2869-94 “Обладнання периферійне. Терміни та визначення”.
Тому ця серія починається саме тут.
Куди веде цей курс:
|
1 |
Аналоговий світ → ADC → Фільтрація → Таймери → Регістри → DMA → UART + ESP32 → Wi-Fi осцилограф |
Наприкінці серії ви власноруч зберете простий бездротовий цифровий осцилограф. Перший крок до нього — зрозуміти, що взагалі відбувається всередині analogRead().
◆ Аналоговий сигнал і чому цифровий пін його “не бачить”
Аналоговий сигнал може приймати нескінченну кількість значень — плавно, без стрибків. Приклади: температура, освітленість, сигнал з мікрофона, положення потенціометра.
Цифровий вхід мікроконтролера бачить лише два стани: є напруга (HIGH/1) або немає (LOW/0). Подати на цифровий пін 2.5V і очікувати осмисленої відповіді — марно: він або зчитає це як HIGH, або як LOW, залежно від порогу перемикання, і ніякої “середини” не існує.
Щоб цифрова плата зрозуміла плавний сигнал, потрібен перекладач між двома світами — ADC.
◆ Як працює ADC — не магія, а бінарний пошук
Всередині ADC на класичному AVR (Uno, Nano, Mega) — це SAR-перетворювач (successive approximation register, послідовне наближення). Він не “вимірює” напругу одним махом — він її вгадує методом бінарного пошуку: генерує пробну напругу внутрішнім ЦАП, порівнює з реальним сигналом, і уточнює результат біт за бітом, від старшого до молодшого.
Цікаво, що ця архітектура — не якийсь новий винахід епохи Arduino. Перший комерційний SAR ADC — це DATRAC, представлений Бернардом Гордоном у компанії Epsco ще 1954 року: 11-бітний, повністю на електронних лампах, розміром із шафу і ціною $8500-10000. Той самий принцип бінарного зважування, до речі, простежується аж до математичної головоломки XVI століття авторства Тартальї. SAR прижився в hobby-мікроконтролерах не випадково — на відміну від інших архітектур ADC, він не має конвеєрної затримки: одне вимірювання — один результат, ідеально підходить для по черзі опитуваних сенсорів.
Розрядність (Resolution) визначає, наскільки “дрібно” ADC ділить діапазон напруги на кроки:
- 10 біт → 2¹⁰ = 1024 рівні
- 12 біт → 2¹² = 4096 рівнів
Формула переведення сирого значення в напругу:
|
1 |
Voltage = ADC × Vref / (2^N − 1) |
де ADC — результат вимірювання, Vref — опорна напруга, N — кількість біт.
Опорна напруга (Vref) — це довжина вашої “вимірювальної лінійки”. Якщо Vref = 5V, ADC вимірює лише в діапазоні 0-5V. Подасте 6V — ADC вперто покаже свій максимум і не помітить різниці.
⚠ Ніколи не подавайте на аналоговий вхід напругу, вищу за напругу живлення мікроконтролера. Для Uno межа — 5V, для ESP32 — 3.3V. Перевищення може незворотно пошкодити вхід.
◆ Битва розрядностей
| Плата | ADC | Діапазон |
|---|---|---|
| Arduino Uno | 10 біт | 0…1023 |
| Arduino Mega | 10 біт | 0…1023 |
| ESP32 | 12 біт | 0…4095 |
Більше біт → менший крок вимірювання → в теорії точніше. При Vref=5V крок 10-біт ADC становить 5/1024 ≈ 4.88 мВ; при 12-біт і Vref=3.3V (ESP32) — 3.3/4096 ≈ 0.8 мВ. Різниця відчутна: 12-бітна “лінійка” помічає значно дрібніші коливання сигналу.
Але “більше біт” не означає автоматично “точніше” — і тут криється пастка, в яку регулярно потрапляють навіть досвідчені розробники.
⚠ ADC ESP32 має відому нелінійність, задокументовану самими користувачами на форумі: “заявлені 12 біт — практично 9 біт корисного сигналу + 3 біти шуму. Перші ~0.21V вхідного сигналу просто ігноруються при типовій атенюації”. У професійних проєктах це калібрують окремою бібліотекою (
esp_adc_cal) чи таблицею поправок під конкретний екземпляр чипа.
Ще одна відома пастка ESP32, яку варто знати з першого дня: ADC2 конфліктує з Wi-Fi. Офіційна документація ESP-IDF прямо каже — “ADC2 використовується Wi-Fi-драйвером, тому застосунок може використовувати ADC2 тільки коли Wi-Fi ще не запущено”. На практиці це виглядає так: код працював на столі, а щойно ви додали WiFi.begin() — analogRead() на певних пінах починає повертати сміття чи завжди максимум. Рішення просте — використовуйте піни ADC1 (GPIO32-39), якщо паралельно потрібен Wi-Fi.
◆ Вправа 1.1 — Uno проти ESP32
Потрібно: Arduino Uno, ESP32, потенціометр 10 кОм.
Підключення:
|
1 2 3 4 5 |
3.3V ---- потенціометр ---- GND | +---- A0 (Uno) | +---- GPIO34 (ESP32, він же ADC1_CH6) |
GND обох плат обов’язково з’єднати між собою — без спільної землі порівнювати виміри між платами безглуздо, сигнали не мають спільної точки відліку.
Живимо схему саме 3.3V (не 5V) — це безпечно і для Uno (5V-логіка спокійно читає нижчу напругу), і критично важливо для ESP32, чий аналоговий вхід максимум на 3.3V.
Код для Arduino Uno:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#define ANALOG_PIN A0 void setup() { Serial.begin(115200); } void loop() { int rawValue = analogRead(ANALOG_PIN); // діапазон 0–1023 Serial.print("Uno_10bit:"); Serial.println(rawValue); delay(10); } |
Код для ESP32:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#define ANALOG_PIN 34 // GPIO34 = ADC1, безпечний з Wi-Fi void setup() { Serial.begin(115200); analogReadResolution(12); // явно, за замовчуванням теж 12, але краще не покладатись на дефолт } void loop() { int rawValue = analogRead(ANALOG_PIN); // діапазон 0–4095 Serial.print("ESP32_12bit:"); Serial.println(rawValue); delay(10); } |
Завдання: запустіть однаковий рух ручки потенціометра на обох платах одночасно. Відкрийте Serial Plotter (Ctrl+Shift+L в Arduino IDE) і порівняйте:
- плавність графіка;
- максимальні значення (графік ESP32 буде вчетверо “вищим” — це очікувано, 4095 проти 1023);
- реакцію на дрібні зміни — злегка постукайте пальцем по столу біля плати. Графік ESP32 підхопить мікроскопічні коливання напруги від вібрації; лінія Uno, найімовірніше, залишиться майже рівною — вона просто “не бачить” настільки дрібних змін.
◆ Як це роблять професіонали
Схема “напруга → ADC → обробка → відображення” — не навчальна спрощена модель, а буквально те, як влаштований будь-який цифровий мультиметр:
|
1 |
Напруга → ADC → Обробка → Екран |
Осцилограф теж починається рівно з цього самого блоку. Різниця між “домашнім” ADC і промисловим — не в принципі роботи, а в швидкості дискретизації, розрядності й якості калібрування.
◆ Вправа 1.2 — мікроскопічний режим Arduino Mega
Що робити, якщо потрібно виміряти дуже слабкий сигнал — наприклад, з термопари чи мікрофона, — який змінюється на соті долі вольта? Стандартна опорна напруга 5V для такого завдання занадто “груба” — крок 4.88 мВ просто не помітить дрібні коливання.
Arduino Mega 2560 має вбудовані внутрішні джерела опорної напруги — 1.1V і 2.56V. Перемкнувши “лінійку” на 1.1V, ви змушуєте ті самі 1024 рівні ADC ділити набагато менший діапазон — точність кроку зростає до ~1.07 мВ.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#define ANALOG_PIN A0 void setup() { Serial.begin(115200); // Перемикаємо внутрішню опорну напругу на 1.1V // (На звичайній Uno цей самий макрос називається просто INTERNAL, // на Mega їх два — 1V1 і 2V56) analogReference(INTERNAL1V1); } void loop() { int raw = analogRead(ANALOG_PIN); // Напруга в мілівольтах з урахуванням опорної 1100 мВ float voltageTarget = (raw * 1100.0) / 1023.0; Serial.print("RawValue:"); Serial.print(raw); Serial.print(",VoltagemV:"); Serial.println(voltageTarget); delay(100); } |
⚠ Напруга на піні не повинна перевищувати обраний внутрішній референс (тут — 1.1V), інакше ADC просто видасть максимум (1023) і “не помітить” перевищення. Джерело сигналу — щось справді слабке: майже сіла батарейка, вихід аналогового сенсора з малою амплітудою.
Практична порада, яку не знайдеш у мануалі: одразу після зміни analogReference() рекомендується зробити один “холостий” виклик analogRead(A0); і відкинути результат — перше вимірювання після перемикання референсу часто ще “пам’ятає” стару опорну напругу через перехідний процес у внутрішньому конденсаторі.
◆ Бонус: секретний вольтметр — вимір напруги без жодного зовнішнього компонента
Ось факт, який зазвичай ламає шаблон у розробників з software-бекграундом: analogRead() насправді не вимірює проти фіксованих 5V. Він вимірює проти того, чим Vcc є прямо зараз. З цього випливає елегантний трюк спільноти — виміряти власну напругу живлення плати, не підключаючи взагалі нічого зовнішнього.
Принцип: перемикаємо референс ADC на Vcc, а на вхід подаємо внутрішнє джерело 1.1V (той самий bandgap-референс, що й в INTERNAL1V1 вище, тільки тепер у ролі вимірюваного сигналу, а не опорного). Раз виміряне = Vcc × ADC/1023 = 1.1V, то:
|
1 |
Vcc = 1.1 × 1023 / ADC |
⚠ Точність цього трюку обмежена самим bandgap-референсом — за специфікацією він десь між 1.0V і 1.2V, тобто похибка “з коробки” може сягати 10%. Реальні виміри кількох чипів ATmega328P показали значення від 0.994V до 1.089V — усі нижче “типових” 1.1V. Для практичного застосування (наприклад, моніторинг заряду батареї в польовому пристрої) референс варто відкалібрувати індивідуально під конкретний чип мультиметром — тоді результат стає стабільним і повторюваним, хоч і трохи дрейфує з температурою.
Це вже вихід за межі базової вправи цієї статті — залишаю як тему для самостійного дослідження, чи для однієї з наступних статей серії.
◆ Типові помилки
- Подати 5V на аналоговий вхід ESP32 (межа 3.3V — не рекомендація, а жорсткий ліміт).
- Забути з’єднати GND двох плат при порівняльних вимірах.
- Вважати, що більше біт = завжди точніше (ADC ESP32 — живий контрприклад).
- Використовувати шумний USB-порт ноутбука як “еталонне” живлення для точних вимірів.
- Плутати піни ADC2 на ESP32 з активним Wi-Fi — код працює на столі, ламається після додавання мережі.
◆ Домашнє завдання
- Порівняйте Uno та ESP32 за вправою 1.1, побудуйте графік у Serial Plotter.
- Спробуйте торкнутись потенціометра пальцем під час виміру — запишіть, що змінилось і чому (натяк: ваше тіло — антена для наведень 50 Гц).
- Спробуйте вправу 1.2 на Mega, якщо плата є в наборі.
- Занотуйте власні спостереження — вони знадобляться, коли дійдемо до фільтрації сигналу.
◆ Запитання для самоперевірки
- Що таке ADC і навіщо він потрібен, якщо є цифрові піни?
- Що таке Vref і як він впливає на точність виміру?
- Чому цифровий пін не може коректно виміряти 2.5V?
- Чому ESP32 показує до 4095, а Uno — тільки до 1023?
- Чому “більше біт” не гарантує “точніше вимірювання”?
◆ Далі в серії
У наступній статті:
- повністю відмовляємось від
delay(); - вчимось будувати неблокуючий код через
millis(); - реалізуємо цифровий фільтр Moving Average — і побачимо, як “заспокоїти” той самий шумний сигнал з потенціометра, з яким щойно грались;
- перетворюємо Arduino на швидкий реєстратор даних.
Ласкаво просимо у світ інженерії мікроконтролерів.