UART: принцип работы и применение в проектах на Arduino

Как устроен самый простой последовательный интерфейс, какие у него параметры и как обмениваться данными через класс Serial.
Самый распространённый интерфейс
У всех плат Arduino аппаратно реализованы три интерфейса передачи данных: UART, SPI и I2C, а у некоторых есть ещё и встроенный адаптер CAN-шины. UART — самый простой и распространённый из них. Через него плата прошивается с компьютера: встроенный адаптер преобразует UART в привычный для ПК порт USB. По нему же на компьютер уходят диагностические сообщения и принимаются команды.
Название расшифровывается как Universal Asynchronous Receiver-Transmitter — универсальный асинхронный приёмопередатчик. Асинхронность означает, что биты не сопровождаются синхроимпульсами: единица отличается от нуля только временем между перепадами уровня, а оно задаётся заранее выбранной скоростью. Достоинства асинхронной передачи — простой протокол, минимум проводов и аппаратных средств, возможность полного дуплекса. Недостаток — меньшая помехозащищённость и, как следствие, ограничения по скорости и расстоянию.
Линии RX и TX
Интерфейс занимает два вывода контроллера: RX (приёмник) и TX (передатчик). Два устройства соединяются крест-накрест: RX первого с TX второго и наоборот. Если платы питаются от разных источников, обязательно объединяют их контакты GND, иначе у сигналов не будет общей опорной точки.

Количество аппаратных портов у плат разное: чаще всего один, у Mega — четыре. Благодаря простоте интерфейса порт можно эмулировать программно, назначив RX и TX почти на любые пины. Аппаратные порты на своих фиксированных выводах предпочтительнее, программные работают с некоторыми ограничениями, но при необходимости те и другие используются одновременно.
Как выглядит передача
Пока данные не передаются, на линии держится высокий уровень — для Arduino это +5 В, так называемая TTL-логика. Спад сигнала служит стартовым битом. Затем через равные промежутки времени, определяемые скоростью, следуют биты байта, а после восьмого — стоп-сигнал в виде высокого уровня. Так повторяется для каждого байта.


Для борьбы с ошибками, которые вызывают помехи в проводах и контактах, протокол предусматривает бит чётности. Он передаётся сразу за последним информационным битом, перед стоповым.

Основные параметры протокола — скорость, количество битов данных, паритет и длина стоп-сигнала. Оба устройства должны быть настроены одинаково, иначе они не поймут друг друга. В Arduino обычно используется вариант без бита чётности, с восемью битами данных и стоп-сигналом длиной в один бит.
Скорость обмена
Скорость измеряется в бодах, и выбирать её желательно из стандартного ряда значений. Чем она выше, тем больше вероятность ошибок. Для большинства задач рекомендуют 9600 бод: байт передаётся примерно за 1 мс, а связь остаётся надёжной даже на заметных расстояниях. Предельная скорость аппаратных UART на платах Arduino — 250000 бод, программных — 115200. Хотя формально интерфейс дуплексный, на деле приём и передача идут слегка поочерёдно; благодаря буферу порта это незаметно.
Класс Serial
В среде Arduino с UART работают через класс Serial:
- Serial.begin(скорость) — запуск порта; у Mega порты нумеруются, например Serial1.begin(9600);
- Serial.available() — число байтов в буфере приёма; ноль означает, что ничего не пришло;
- Serial.read() — очередной байт из буфера;
- Serial.print() — вывод данных разных типов, от байта до строки и числа с плавающей точкой;
- Serial.println() — то же с переводом строки в конце;
- Serial.write() — передача двоичных данных, возвращает число отправленных байтов.
Буфер приёма по умолчанию вмещает 64 байта. Если не успевать забирать из него данные, он переполнится и часть информации пропадёт. При необходимости размер увеличивают директивой препроцессора: #define SERIAL_RX_BUFFER_SIZE 128.
Три примера
Первый — вывод с платы на компьютер. Схема не нужна, достаточно кабеля USB. Скетч запускает порт на 9600 бод и раз в секунду печатает слово «timer: », значение millis() и подпись «ms» с переводом строки. Результат виден в мониторе порта среды Arduino.
Второй — управление с компьютера. Скетч проверяет Serial.available() и, получив символ «1», включает встроенный светодиод на пине 13, а получив «0» — гасит.
Третий — связь двух плат. К каждой подключены две кнопки (пины 2 и 3) и два светодиода (пины 4 и 5), линии RX и TX соединены крест-накрест. Программа на обеих платах одинаковая: кнопки опрашиваются каждые 50 мс для защиты от дребезга, их состояние упаковывается в один байт и при изменении отправляется через Serial.write(). Принятый байт разбирается, и по нему переключаются светодиоды. Кнопки одной платы управляют светодиодами другой, причём в обе стороны. Проверки ошибок здесь нет; её добавляют контрольной суммой и подтверждением приёма.
Ограничения и согласование уровней
К недостаткам UART относят ограничение по скорости, невысокую помехозащищённость и требование к точности тактовой частоты на обеих сторонах, что особенно критично для контроллеров с внутренним RC-генератором. Дальность зависит от провода, экранирования и скорости, но даже в лучшем случае не превышает пары десятков метров. Объединить больше двух устройств и увеличить расстояние позволяют адаптеры RS-485: они сводят линии всех участников в двухпроводную шину и используют более высокое напряжение и противофазные сигналы.
Не все устройства работают с логикой 5 В. Сигнал от 3,3-вольтового устройства к 5-вольтовому можно подавать без изменений, а в обратном направлении напряжение обязательно понижают, иначе можно повредить принимающий порт. Самый простой способ — резистивный делитель, лучше — стабилитрон на 3,3 В или специальная микросхема, особенно если каналов несколько.