Последовательный UART и как он работает

Если вы прочитали руководства по Arduino Serial, у вас должно быть хорошее понимание использования последовательной связи. Но задавались ли вы вопросом — а что, собственно, такое последовательный UART? Если да — вы попали по адресу.

UART расшифровывается как Universal Asynchronous Receiver-Transmitter (универсальный асинхронный приёмопередатчик):

  • Universal (универсальный) — каждый во Вселенной может им пользоваться. Нет, не совсем. Это просто означает, что в рамках протокола существует множество форматов и опций. Передаваемые данные могут быть сконфигурированы по-разному, и можно использовать множество различных скоростей (бод).

  • Asynchronous (асинхронный) — работает независимо от других процессов и не требует, чтобы два конца общались друг с другом. Один конец отправляет — если на другом конце кто-то слушает, отлично; если нет — отправителю всё равно, и он не ожидает ответа.

  • Receiver (приёмник) — можно принимать данные!

  • Transmitter (передатчик) — можно отправлять данные!

Что, вероятно, не особо помогает понять, что это такое.

Так что же такое UART?

UART — это протокол последовательной связи. Протокол определяет, как данные подготавливаются и затем передаются. «Последовательный» (serial) означает, что данные передаются последовательно, по одному элементу за раз.

Последовательный UART — это связь точка-точка, не требующая какой-либо реальной настройки и не имеющая адресации (это связь точка-точка — нет необходимости маршрутизировать трафик).

Существуют способы использования UART для подключения нескольких устройств, но UART не предназначен для соединения более двух устройств. Такое использование — в лучшем случае костыль, в худшем — хак. Для связи нескольких устройств существуют гораздо лучшие решения.

Протокол UART имеет множество различных опций и скоростей и выжил и процветает, отчасти благодаря своей гибкости и простоте. Он чрезвычайно популярен в мире встраиваемых систем и Arduino.

Связь осуществляется по двум проводам: один для передачи (transmit -> receive) и второй для приёма (receive <- transmit). Вот и всё. Никаких дополнительных проводов для тактовых сигналов или квитирования*. Обычно нужно соединить «земли» (GND), но это не считается.

Arduino Nano подключённый к другому Arduino Nano по аппаратному Serial Arduino Nano подключённый к USB-Serial UART адаптеру

RECEIVE (RX) одного — это TRANSMIT (TX) другого.

Помните: RX на одной стороне подключается к TX на другой.

*квитирование (handshaking)

UART может поддерживать аппаратное квитирование, называемое управлением потоком (Flow Control). Управление потоком использует сигналы RTS и CTS (Ready To Send и Clear To Receive). Каждый сигнал требует своего отдельного соединения/пина, и их можно рассматривать как необязательные дополнения, которые реализуются не так часто, особенно в мире Arduino.

Arduino использует сигнал RTS как способ сброса модуля, поэтому он обычно недоступен для использования.

Чем UART НЕ является

UART — это не стандарт. Для него не существует спецификации ISO или вообще какой-либо спецификации. Он просто есть. UART — это не RS-232 (и не RS-485). RS-232 (а также RS-485) определяют аппаратную часть и то, что представляет собой 0 или 1. UART ничего из этого не определяет.

  • UART. Логический 0 — пин притянут к GND. Логическая 1 — HIGH, обычно то же напряжение, которое использует микроконтроллер (обычно 3,3 В или 5 В).

  • RS-232. 0 определяется как напряжение от +3 В до +15 В. 1 — напряжение от -3 В до -15 В.

  • RS-485 использует две линии, A и B. 0 определяется как A — HIGH, B — LOW. 1 — когда A — LOW, B — HIGH.

Режимы передачи UART

UART передаёт данные последовательно в одном из трёх режимов:

  • Full duplex (полный дуплекс). Одновременная связь в обоих направлениях.

  • Half duplex (полудуплекс). Передача в одном направлении за раз.

  • Simplex (симплекс). Только однонаправленная связь.

Режима по умолчанию как такового не существует — фактический используемый метод зависит от устройства. Например, аппаратный Serial на Arduino — полнодуплексный, может отправлять и принимать одновременно. Встроенная библиотека Software Serial — полудуплексная (не может отправлять и принимать одновременно). AltSoftSerial — полнодуплексная, но имеет фиксированные пины для TX и RX.

Формат пакета UART

UART упаковывает данные в маленькие пакеты. Каждый пакет имеет следующий формат:

  • Стартовый бит

  • Биты данных (собственно данные или значение)

  • Бит чётности (необязательный)

  • Стоповый бит (или 2)

Формат пакета данных UART

Стартовый бит

Начало пакета данных всегда содержит один стартовый бит. Стартовые биты всегда равны 0, так что можно сказать, что пакет всегда начинается с «ничего» :-) Стартовые биты информируют принимающее устройство о начале поступления данных.

Биты данных

Биты данных — это место, где находятся собственно данные. UART допускает различное количество битов: от 5 до 9. Однако 8 бит данных очень распространены и хорошо соответствуют способу хранения данных в микропроцессорах. 8 бит — это байт, и 1 байт обычно означает один символ.

Чётность

Бит чётности может использоваться как очень базовая контрольная сумма. Бит чётности необязателен, и его использование было гораздо более популярно в прошлом, чем сейчас. По большей части бит чётности больше не используется. Arduino — хороший пример: настройка UART по умолчанию (8N1) не включает бит чётности.

Варианты бита чётности:

  • Без бита чётности

  • Чётная чётность (Even parity) — бит чётности (1 или 0) добавляется, чтобы общее число единиц в данных было чётным. Если общее число единиц уже чётное — добавляется 0. Если нечётное — бит чётности устанавливается в 1.

  • Нечётная чётность (Odd parity) — бит чётности (1 или 0) добавляется, чтобы общее число единиц в данных было нечётным. Если общее число уже нечётное — добавляется 0.

Расчёт бита чётности основан на количестве единиц в данных/значении. Стартовый и стоповый биты не используются при вычислении.

Пример: значение 65, чётная чётность.

65 в двоичном виде — 1000001, что содержит 2 единицы. 2 — уже чётное число, поэтому бит чётности будет 0. Итого по-прежнему 2 единицы — чётное число.

Пример: значение 65, нечётная чётность.

При нечётной чётности бит чётности становится 1. Общее число единиц теперь 3, а 3 — нечётное число.

Стоповый бит (биты)

В конце пакета данных находится 1 или 2 стоповых бита. Стоповые биты всегда равны 1.

Стартовый и стоповый биты часто называют битами синхронизации.

Скорость передачи (Baud Rate)

Скорость передачи — это скорость, с которой передаются данные. UART не имеет фиксированной скорости — она полностью открыта, и теоретически можно использовать любую скорость. Существуют определённые скорости, которые считаются стандартными. Скорости обычно указываются в бит/с (bps).

Вот скорости передачи, которые Arduino IDE (а также многие другие устройства) используют по умолчанию: 300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 74880, 115200, 230400, 250000, 500000, 1000000, 2000000.

Для работы связи оба устройства — передающее и принимающее — должны использовать одинаковую скорость. При несовпадении скоростей вы можете получить «мусорные» символы или, что более вероятно, ничего вообще.

По мере использования UART вы заметите, что определённые скорости очень распространены. 9600, 38400 и 115200 — вероятно, самые популярные. 9600 и 115200 часто используются с Arduino. 9600 — безопасная скорость для библиотек Software Serial. 115200 — хорошая скорость для аппаратного Serial.

Насколько быстры бит/с

9600 — это 9600 бит в секунду, что означает, что 1 бит данных (бит, не байт) занимает:

1 секунда / 9600 = 0,000104166667 секунды, или 104,166667 микросекунд (мкс).

При использовании 8N1 (10 бит на символ, см. ниже) скорость 9600 позволяет передавать 960 символов в секунду. Звучит быстро, но 9600 считается медленной скоростью. Обычный 16 МГц Arduino способен передавать со скоростью 2 000 000 бит в секунду — это 200 000* символов в секунду. Неплохо.

*Применяются условия и ограничения.

Какая скорость лучше?

Все и никакая. Лучшая скорость — та, которая надёжно работает для вашей задачи.

Поскольку UART использует только два провода, сигнал UART деградирует с расстоянием, особенно при 5 В и 3,3 В. Поэтому при передаче данных на любое расстояние более медленные скорости надёжнее. Если Arduino стоит рядом с компьютером — более быстрые скорости без проблем.

Иногда выбора особо нет. Если Bluetooth-модуль подключён к Arduino через библиотеку Software Serial, вы ограничены максимальной скоростью 38400, а возможно, и меньше, если Arduino выполняет другие задачи во время общения с модулем.

Сокращённая запись UART

Вместо полного описания всех свойств часто используется сокращённая запись. Если вы читали введение в Arduino Serial, вы уже знакомы с 8N1. 8N1 означает: 8 бит данных, без чётности, 1 стоповый бит. Поскольку стартовые биты используются всегда, их не нужно упоминать.

8N1 очень распространён и используется как настройка по умолчанию для Arduino, а также многих других устройств.

Существует множество возможных комбинаций, и каждая имеет свой код. Вот некоторые из них для примера:

  • 5O1 — 5 бит данных, нечётная чётность, 1 стоповый бит

  • 6N2 — 6 бит данных, без чётности, 2 стоповых бита

  • 7N1 — 7 бит данных, без чётности, 1 стоповый бит

  • 8N1 — 8 бит данных, без чётности, 1 стоповый бит

  • 8E2 — 8 бит данных, чётная чётность, 2 стоповых бита

5 и 6 бит данных имеют ограниченное применение в мире Arduino. 5 бит имеют максимальное значение 31, что не покрывает алфавит + цифры.

6 двоичных разрядов имеют максимальное значение 61. Недостаточно, чтобы покрыть все печатаемые символы, не говоря уже об остальных символах ASCII.

Помимо указания свойств UART, в начале сокращённого кода может указываться скорость, например:

  • 9600 8N1

  • 38400 7E1

Как передаются данные UART

Данные UART передаются последовательно, бит за битом. При отправке ASCII-символов символы разбиваются на отдельные биты, и биты передаются по одному. Принимающее устройство собирает все биты и восстанавливает данные. Это похоже на работу транспортёра в «Звёздном пути». Вы не отправляете объект целиком — вы отправляете все биты, из которых он состоит.

Когда вы используете Serial.Write("ABCD") в скетче Arduino, библиотека Serial берёт каждый символ по очереди (A, B, C, D), разбивает на биты и добавляет дополнительные биты для создания пакета UART (см. выше). Затем пакет передаётся бит за битом, устанавливая пин TX в HIGH/LOW/HIGH/LOW по необходимости.

Следует отметить, что UART использует нормально HIGH сигнальную линию.

Передача UART-данных

Формат пакета определяется настройками (такими как 7N1, 8N1), указанными при инициализации последовательного соединения. Если настройка не указана, Arduino использует 8N1 по умолчанию.

Serial.begin(9600);  // use the default 8N1 packet format

Serial.begin(9600,SERIAL_7E1);  // 7E1 packet format

Serial.begin(9600,SERIAL_8N2); //   2 stop bits

Именно протокол UART определяет, как собирается пакет данных, а при передаче скорость определяет, как быстро (а точнее — когда) сигнал TX переходит в HIGH и LOW.

Время передачи бита

Время передачи бита — это количество времени, используемое для передачи одного бита данных. Одинаковая длительность используется для всех битов в пакете и определяется скоростью. Поскольку скорость указывается в бит/с, мы можем вычислить время передачи бита, разделив 1 на скорость.

Для 9600: 1 / 9600 = 104,166667 микросекунд

Для 115200: 1 / 115200 = 8,68055556 микросекунд

Из-за особенностей работы микропроцессоров (они выполняют действия в определённые моменты времени на основе тактовых импульсов) они могут не точно соответствовать скорости передачи. Но обычно расхождение достаточно мало, чтобы это не имело значения.

Если использовать PulseView для измерения времени бита при скорости 9600, видно, что оно составляет около 104,125 микросекунд, что соответствует фактической скорости 9603,84. К счастью, UART имеет определённый запас допустимой погрешности, и пока фактическая скорость находится в пределах примерно 2% от желаемой — всё работает.

Время передачи бита в PulseView

Разница между желаемой скоростью и скоростью, которую производит микропроцессор, называется ошибкой передачи. Она более заметна на определённых, обычно более медленных, скоростях. При скорости 1 000 000, например, Arduino может точно соответствовать заданной скорости.

Более детальный взгляд на UART

Думаю, теории достаточно, давайте поиграем. У меня Arduino Nano подключён к логическому анализатору (LA) — это дешёвый экземпляр с AliExpress (хотя я купил его на Taobao ещё дешевле ;-)

  • Аппаратный TX к каналу 0 LA

  • Аппаратный RX к каналу 1 LA (не используется)

  • Пин 13 (встроенный светодиод) к каналу 3 LA. Я использую пин 13 для запуска захвата.

Arduino подключённый к логическому анализатору

Скетч Arduino

У Arduino очень простой скетч. Он лишь:

  • Включает встроенный светодиод

  • Запускает Serial на скорости 9600

  • Выключает светодиод

  • Отправляет U по Serial (заглавная U)

Я использую светодиод, чтобы знать, когда запускать логический анализатор — вижу светодиод, у меня 1 секунда, чтобы нажать кнопку RUN. Логический анализатор начинает фактический захват, когда светодиод гаснет (пин 13 переходит в LOW и запускает захват данных).

const int LED_PIN = 13;
void setup()
{
    pinMode(LED_PIN, OUTPUT);
    digitalWrite(LED_PIN, HIGH);

    // 8N1 is the default setting for the Arduino serial monitor
    Serial.begin(9600,SERIAL_8N1 );

    // when the LED turns on it is my mark to start the logic analyser.
    // the logic analyser is monitoring only until the pin goes low
    delay(1000);

    // when pin13 goes LOW the actual capture starts
    digitalWrite(LED_PIN, LOW);

    Serial.print("U");
}

void loop() { ; }

Пакет данных

Вот символ U при скорости 9600 с использованием 8N1. Полагаю, вы помните, что означает 8N1.

Символ U в PulseView

Красивые равномерные подъёмы и спады (на самом деле спады и подъёмы, но всё равно красиво)

Заглавная U — хороший символ для примера, потому что её ASCII-значение имеет красивый битовый паттерн. ASCII-код U — десятичное 85, а 85 в двоичном — 1010101. Никакие два соседних разряда не одинаковы. Это делает каждый бит хорошо видимым на диаграмме.

Первое, что следует заметить — базовая линия UART находится в состоянии HIGH. Она начинается HIGH и заканчивается HIGH, и именно поэтому стартовый бит — LOW. Стартовый бит HIGH на линии HIGH было бы сложно увидеть :-) Линия переходит в LOW при передаче данных. 0 передаются как LOW, 1 — как HIGH.

Принимающее устройство знает скорость (или должно знать), поэтому оно знает, как долго передаётся каждый бит.

При использовании декодера UART в PulseView мы получаем удобные метки, показывающие, что представляют биты. Видно, что первый бит — действительно стартовый.

Стартовый бит в PulseView

Затем идут 8 бит данных, представляющие фактические данные или значение. В данном случае — заглавная U. PulseView любезно подписал данные.

У меня одновременно отображаются 2 декодера UART: один показывает десятичное значение, второй — ASCII.

8 бит данных идеально подходят для ASCII, и исходное ASCII-значение используется как есть, без изменений или преобразований. На диаграмме ниже вы должны увидеть двоичное значение 10101010. Каждый HIGH — это 1, каждый LOW — это 0.

Биты данных символа U

8N1 не имеет бита чётности, поэтому его не видно. Но в конце есть стоповый бит. Стоповый бит — это 1, поэтому сигнал HIGH. После стопового бита сигнал возвращается в состояние ожидания — HIGH.

На графике не виден конец стопового бита, но поскольку принимающее устройство знает скорость, оно может вычислить, когда стоповый бит заканчивается.

Стоповый бит в PulseView

Вот тот же символ с чётным битом чётности и 1 стоповым битом — он же 8E1:

Символ U с чётной чётностью (8E1)

Поскольку 10101010 имеет чётное число единиц, бит чётности равен 0, чтобы общее число оставалось чётным.

Бит чётности — чётная чётность

Изменим UART на 8O1 (8 бит данных, нечётная чётность, 1 стоповый бит). Чтобы общее число единиц стало нечётным, бит чётности становится 1.

Символ U с нечётной чётностью (8O1)

Вернёмся к 8N1 и попробуем другие символы. Вот ABCD, 1234.

Serial.print("ABCD");
Serial.print("1234");
ABCD 1234 в PulseView

Не так легко читать, как U. Просто помните: LOW — это 0, HIGH — это 1, и следуйте по двоичному значению.

Вот крупный план символа A. Двоичное ASCII-значение A — 10000010:

Символ A крупным планом

Вот и всё. Данные передаются по одному биту за раз с помощью сигналов LOW/HIGH. LOW — пин притянут к GND, HIGH — пин притянут к высокому уровню напряжения микроконтроллера (обычно 3,3 В или 5 В). В моём случае я использую 5 В Arduino Nano, поэтому сигнал HIGH — это 5 В.

Длительность передачи бита задаётся скоростью. Чем медленнее скорость — тем длиннее время передачи бита; чем быстрее — тем короче.

Таблица ASCII

ASCII расшифровывается как American Standard Code for Information Interchange. Изначально был разработан для использования с телетайпами.

Dec  Char
----  ----
  0  NUL (null)
  1  SOH (start of heading)
  2  STX (start of text)
  3  ETX (end of text)
  4  EOT (end of transmission)
  5  ENQ (enquiry)
  6  ACK (acknowledge)
  7  BEL (bell)
  8  BS  (backspace)
  9  TAB (horizontal tab)
 10  LF  (NL line feed, new line)
 11  VT  (vertical tab)
 12  FF  (NP form feed, new page)
 13  CR  (carriage return)
 14  SO  (shift out)
 15  SI  (shift in)
 16  DLE (data link escape)
 17  DC1 (device control 1)
 18  DC2 (device control 2)
 19  DC3 (device control 3)
 20  DC4 (device control 4)
 21  NAK (negative acknowledge)
 22  SYN (synchronous idle)
 23  ETB (end of trans. block)
 24  CAN (cancel)
 25  EM  (end of medium)
 26  SUB (substitute)
 27  ESC (escape)
 28  FS  (file separator)
 29  GS  (group separator)
 30  RS  (record separator)
 31  US  (unit separator)
Dec  Char
----  -----
 32  SPACE
 33  !
 34  "
 35  #
 36  $
 37  %
 38  &
 39  '
 40  (
 41  )
 42  *
 43  +
 44  ,
 45  -
 46  .
 47  /
 48  0
 49  1
 50  2
 51  3
 52  4
 53  5
 54  6
 55  7
 56  8
 57  9
 58  :
 59  ;
 60  <
 61  =
 62  >
 63  ?
Dec  Char
----  -----
 64  @
 65  A
 66  B
 67  C
 68  D
 69  E
 70  F
 71  G
 72  H
 73  I
 74  J
 75  K
 76  L
 77  M
 78  N
 79  O
 80  P
 81  Q
 82  R
 83  S
 84  T
 85  U
 86  V
 87  W
 88  X
 89  Y
 90  Z
 91  [
 92  \
 93  ]
 94  ^
 95  _
Dec  Char
----  ------
 96  `
 97  a
 98  b
 99  c
100  d
101  e
102  f
103  g
104  h
105  i
106  j
107  k
108  l
109  m
110  n
111  o
112  p
113  q
114  r
115  s
116  t
117  u
118  v
119  w
120  x
121  y
122  z
123  {
124  |
125  }
126  ~
127  DEL