Мікроконтролери мають універсальні цифрові виводи — GPIO. З їх допомогою ми можемо вмикати та вимикати пристрої, а також програмно формувати сигнали, наприклад прямокутні імпульси або навіть прості послідовності бітів. Фактично, використовуючи лише GPIO, можна “вручну” реалізувати передачу даних, керуючи рівнями напруги у потрібні моменти часу.
Однак у реальних задачах цього часто недостатньо. Коли потрібно передавати не окремі сигнали, а цілі набори даних між пристроями — швидко, стабільно та без помилок — такий підхід стає складним і ненадійним. Необхідно враховувати синхронізацію, швидкість передачі, формат даних і багато інших нюансів.
Наприклад, мікроконтролеру може знадобитися обмін даними з GPS-модулем, Bluetooth-адаптером, з командною консоллю користувача або іншим контролером. Саме для вирішення цих задач існують цифрові інтерфейси зв’язку — стандартизовані способи обміну даними між пристроями. Вони визначають, як саме передаються біти, з якою швидкістю, і як пристрої “розуміють” один одного. У цій статті ми розглянемо один із найпростіших і найпоширеніших інтерфейсів — UART.
UART (від англ. Universal Asynchronous Receiver-Transmitter) — це протокол асинхронного послідовного зв'язку з повнодуплексною передачею даних (одночасна передача та прийом даних). Він використовує два дроти: Tx для передачі даних і Rx для прийому. Пристрої підключаються так, що Tx одного з’єднується з Rx другого. Для коротких відстаней (<10–50 см) передача даних можлива при рівнях напруги TTL (від англ. Transistor-Transistor Logic). Це стандарт цифрових сигналів, де логічна «1» відповідає високій напрузі (зазвичай 3,3 В або 5 В), а логічний «0» — низькій (0 В).
UART у більшості мікроконтролерів підтримується апаратно — для цього вбудовані спеціальні модулі, які беруть на себе формування та прийом даних без постійної участі процесора. Для роботи з ними використовуються звичайні GPIO-виводи, які можуть перемикатися в альтернативний режим і виконувати функції Tx та Rx. При цьому в різних мікроконтролерах реалізація може відрізнятися: кількість UART-модулів, доступні виводи, а також спосіб їх налаштування залежать від конкретної архітектури та виробника.
Підтримується лише зв’язок типу «точка-точка», тобто обмін даними можливий тільки між двома пристроями, на відміну від I2C або SPI, де до однієї шини можуть бути підключені кілька пристроїв. Крім того, швидкість передачі даних у UART зазвичай нижча порівняно з I2C та SPI.
UART зазвичай використовується для налагодження через послідовний монітор, обміну даними з Bluetooth/Wi-Fi модулями, GPS-приймачами, сенсорами тощо.
Швидкість передачі (baud rate)
Baud rate — це кількість бітів, переданих за секунду. Наприклад, поширені значення: 4800, 9600, 19200, 57600, 115200 тощо. UART є асинхронним інтерфейсом (без використання тактового сигналу), тому передавач і приймач повинні працювати з однаковою швидкістю, інакше дані будуть інтерпретовані неправильно! Тривалість передачі одного біта визначається як 1 / baud rate, наприклад при 9600 один біт передається приблизно за 104,167 мкс, що відповідає 9600 бітам за секунду.
Процес обміну даними
Передача даних по UART починається з того, що лінія Tx перебуває у високому логічному рівні (HIGH), сигналізуючи про готовність до обміну. Коли передавач готовий, він посилає біт старту (LOW), за яким слідують біти даних, паритетний біт (якщо використовується) і один або кілька стоп-бітів (HIGH). Кожен біт передається у відповідності зі швидкістю передачі (baud rate), а приймач зчитує їх у правильному порядку, синхронізуючись по біту старту та часу передачі. Такий процес дозволяє надійно передавати дані між двома пристроями навіть без спільного тактового сигналу.
На зображенні представлена структура фрейму даних UART і часова діаграма сигналі. За приклад взято передача 1 байту зі значенням 82, яке в двійковій системі має вигляд 01010010:

Опис бітів фрейму:
- Стан спокою - лінії Tx і Rx перебувають у високому рівні (HIGH).
- Стартовий біт (ST) - завжди логічний 0, коли Tx переходить зі стану спокою (HIGH) у LOW, приймач (Rx) починає зчитування бітів.
- Біти даних - кількість бітів даних може становити 5, 6, 7, 8 або 9 (в прикладі вище 8), ця кількість узгоджується між приладами при налаштуванні (як і баудрейт).
- Біт парності (Parity) - 1 біт (HIGH або LOW залежно від режиму Even/Odd), використовується для перевірки помилок, є необов’язковим (в прикладі вище відсутній).
- Стоп-біти (Sp1, Sp2) - 1 або 2 біти (завжди логічна «1»). Після останнього біта лінія повертається у стан HIGH, кадр передачі завершено.
Рівні сигналів UART: TTL UART та RS-232
Як було зазначено раніше, на коротких відстанях (до 10–50 см) UART може працювати на рівнях напруги TTL, при цьому Tx і Rx підключаються безпосередньо між пінами мікроконтролерів або модулів за допомогою звичайних проводів. Для довших відстаней така схема стає ненадійною через падіння сигналу та зашумлення. У таких випадках застосовують стандарт RS-232, який використовує перетворювачі рівнів для сумісності з UART. Виходи UART підключаються до модулів-конвертерів, а між ними прокладається кабель (зазвичай вита пара або спеціальні RS-232 кабелі). Завдяки цьому забезпечується надійна передача даних між мікроконтролерами на відстані до 15 метрів.
Таблиця порівняння рівнів TTL і RS-232:
| TTL UART | RS-232 | |
| Логічні рівні |
LOW: 0 Вольт HIGH: 3.3 або 5 Вольт |
LOW: +3 до +25 Вольт HIGH: -3 до -25 Вольт |
| Полярність рівнів | Однополярний | Двополярний |
| Дистанція | Коротка дистанція (менше 10 - 50 см) | На довгу дистанцію (до 15 метрів та більше) |
| Взаємодія | Пряме підключення Tx/Rx пінів мікроконтролерів | Перетворювач рівнів (наприклад, MAX232) використовується для підключення RS-232 до контролера. Пряме підключення контролера до RS-232 може пошкодити його через несумісні рівні напруги. |
| Використання | Мікроконтролери, вбудовані системи | Старі ПК, промислові пристрої |
Виявлення помилок
Виявлення помилок у передачі даних здійснюється за допомогою спеціальних механізмів контролю, одним із яких є біт парності. Він дозволяє перевірити цілісність даних, визначаючи, чи є кількість одиниць у переданому байті парною або непарною: у режимі парної парності ця кількість має бути парною, а в режимі непарної — відповідно непарною. Також можливий режим без використання біта парності, коли додаткова перевірка не виконується. Окрім цього, система здатна виявляти й інші помилки передачі: наприклад, помилка кадрування виникає, якщо стоп-біт прийнято некоректно, що свідчить про порушення структури кадру, а помилка переповнення сигналізує про те, що буфер приймача заповнений і нові дані не можуть бути оброблені вчасно. У більшості випадків усі ці перевірки виконуються автоматично апаратурою UART: передавач сам формує біт парності, приймач перевіряє його, а також фіксує помилки кадрування та переповнення. При цьому мікроконтролер лише встановлює відповідні прапорці у регістрах, а подальша обробка помилок виконується вже програмою.
Регістри мікроконтролера для роботи UART
Як важливий протокол обміну даними, UART присутній у більшості мікроконтролерів, зокрема AVR, PIC та ARM. Розглянемо принципи роботи UART на прикладі AVR-мікроконтролера ATmega328P. Загалом для організації UART-зв’язку використовуються такі регістри:
| Регістр | Опис |
| UCSRA (8-біт) | Регістр керування та стану USART A містить прапорці, які відображають стан передачі та прийому UART, порожність регістра даних і наявність помилок. |
| UCSRB (8-біт) | Регістр керування та стану USART B відповідає за увімкнення та вимкнення передачі, прийому та переривань. |
| UCSRC (8-біт) | Регістр керування та стану USART C використовується для налаштування швидкості передачі (baud rate), формату кадру та режиму роботи UART. |
| UBRR (16-біт) | Регістри швидкості передачі USART (Baud Rate Registers) містять налаштування швидкості обміну даними для UART. |
| UDR (8-біт) | Регістр даних USART (USART Data Register) містить дані, які будуть передані або прийняті через UART. |
Драйвери UART
Зазвичай працювати з UART безпосередньо через регістри не дуже зручно, тому часто використовують програмні бібліотеки-драйвери, які надають зручний інтерфейс, приховуючи низькорівневі деталі.
Драйвер передачі даних UART описує процес відправлення даних. Спочатку необхідно налаштувати швидкість обміну (baud rate) і формат кадру: кількість бітів даних, парність і стоп-біти. Після цього вмикається передавач. Перед кожною передачею потрібно дочекатися, поки регістр даних стане порожнім. Коли він готовий, байт записується в буфер UART для відправлення. Далі слід дочекатися завершення передачі, після чого, за потреби, передавач можна вимкнути.
Драйвер прийому даних UART описує процес отримання даних. Спочатку вмикається приймач, після чого система очікує надходження даних у регістр UART. Коли дані з’являються, їх зчитують із регістра (UDR). Під час прийому також варто перевіряти можливі помилки: помилку кадрування, парності або переповнення. Після цього отримані дані обробляються програмою, і за потреби приймач можна вимкнути.
UART піни в мікроконтролерах
UART має апаратну підтримку в мікроконтролерах, тому кожен із них має визначений набір пінів, що забезпечують роботу цього інтерфейсу. Ці піни описані в документації до конкретного чіпа — зокрема, на схемах розпіновки.
На платах розробки піни UART часто підписують як TX і RX. Наприклад, фрагмент розпіновки плати з даташиту для ESP8684-MINI виглядає так:

Функції Arduino для роботи з протоколом UART
Фреймворки часто мають вбудований програмний інтерфейс для роботи з протоколом UART, наприклад Arduino має бібліотеку Serial, яка надає такі функції:
| Функція | Опис |
Serial.begin(baud) |
Ініціалізація UART |
Serial.print(string_data) |
Відправити дані |
Serial.println(string_data) |
Надіслати дані з символами нового рядка та повернення каретки |
Serial.read() |
Зчитати 1 байт |
Serail.available() |
Перевірити чи є дані для читання в буфері |
Serail.write(raw_data) |
Відправити дані у виді байтів |
Serial.end() |
Відключити UART |
Serial.availableForWrite() |
Отримати кількість байтів, доступних для запису в буфер Serial |
Serial.find() |
Зчитує дані з буфера Serial до моменту, поки не буде знайдено задане значення |
Serial.flush() |
Очікує завершення передачі вихідних даних через Serial |
Serial.parseFloat() |
Повертає перше коректне число з плаваючою комою |
Serial.peek() |
Повертає наступний байт у буфері RX без його видалення |
*Примітки:
1. Апаратна бібліотека Serial має 64-байтний буфер для передачі та прийому даних.
2. Бібліотека Serial використовує переривання для передачі даних.
Приклад
Передамо деякі дані по UART з однієї плати ESP на іншу. Схема з'єднань має бути такою:

Код для першої плати (фреймворк Arduino):
void setup() {Serial.begin(9600); //Ініціалізуємо UART на бауд 9600}void loop() {Serial.write('Q'); //Передаємо знак Qdelay(1000); //Відправляємо кожну секунду}
Код другої плати:
void setup() {Serial.begin(9600); //Ініціалізуємо UART на бауд 9600pinMode(6, OUTPUT); //Шостий пін в режим виходу (на схемі підключено діод)}void loop() {if (Serial.available() > 0) { //перевіряємо чи прийшли якісь даніchar receivedChar = Serial.read(); //Зчитуємо прийнятий знакif('Q' == receivedChar) //якщо прийнято знак Q//перемикаємо стан світлодіодаdigitalWrite(6, !digitalRead(6));}}
Також рекомендуємо ознайомитися з проєктом «Простий UART-конфігуратор мікроконтролера» як прикладом використання UART для комунікації між мікроконтролером і користувачем через текстову консоль.
