Как работает последовательная связь на Arduino?

Относительно плат Arduino Uno, Mega2560, Leonardo и аналогичных:

  • Как работает последовательная связь?
  • Насколько быстр последовательный порт?
  • Как установить связь между отправителем и получателем?

Обратите внимание: это справочный вопрос.

, 👍21

Обсуждение

Возможно, вам будет интересно узнать о буферах по обе стороны Nano, подключенного к системе Raspian, на которой запущен регистратор данных Python, с использованием обычного кабеля USB для программирования между ними: https://arduinoprosto.ru/q/11710/does-data-came-on-serial-port-store-for-some-time, @SDsolar


1 ответ


Лучший ответ:

18

Асинхронная последовательная (обычно называемая последовательной) связь используется для отправки байтов с одного устройства на другое. Устройство может быть одним или несколькими из следующих:

  • Ардуино
  • ПК
  • GPS
  • Считыватель RFID-карт
  • ЖК-дисплей
  • Модем
  • Другое

Тактовая частота и выборка данных

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

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

Последовательная связь - отправка одного байта

На графике выше показана передаваемая буква «F». В ASCII это 0x46 (в шестнадцатеричном формате) или 0b01000110 (в двоичном формате). Наименее значимый (младший) бит передается первым, поэтому на графике выше вы видите биты, поступающие в следующем порядке: 01100010.

Время «простоя» между байтами передается как непрерывные биты «1» (фактически линия передачи постоянно удерживается на высоком уровне).

Чтобы указать начало байта, стартовый бит всегда указывается путем понижения линии, как показано на графике. Как только приемник видит стартовый бит, он ждет в течение 1,5 времени выборки, а затем делает выборку битов данных. Он ждет 1,5 времени, чтобы:

  • Пропускает начальный бит
  • Сэмплы на полпути к следующему биту

Если скорость передачи данных составляет, например, 9600 бод, то частота дискретизации составит 1/9600 = 0,00010416 секунд (104,16 мкс).

Таким образом, при скорости 9600 бод после получения стартового бита приемник ждет 156,25 мкс, а затем производит выборку каждые 104,16 мкс.

Синхронизация стартового бита

Цель стопового бита — гарантировать, что между каждым байтом определенно есть 1-бит. Без стопового бита, если байт заканчивается нулем, то оборудование не сможет отличить его от начального бита следующего байта.

Чтобы получить вышеуказанный вывод на Uno, вы можете написать этот код:

void setup()
  {
      Serial.begin(9600);
      Serial.print("F");
  }

void loop ()
  {
  }

Количество бит данных

Чтобы сэкономить время передачи (в старые времена, хех) вам разрешалось указывать разное количество бит данных. Аппаратное обеспечение AtMega поддерживает нумерацию бит данных от 5 до 9. Очевидно, что чем меньше бит данных, тем меньше информации вы можете отправить, но тем быстрее это будет.


Биты четности

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

Например, для буквы "F" (или 0x46 или 0b01000110) вы можете видеть, что там 3 единицы (в 01000110). Таким образом, у нас уже есть нечетная четность. Итак, бит четности будет следующим:

  • Нет четности: пропущено
  • Четность: 1 (3 + 1 четно)
  • Нечетность: 0 (3 + 0 нечетно)

Бит четности, если он присутствует, появляется после последнего бита данных, но перед стоповым битом.

Если получатель не получает правильный бит четности, это называется «ошибкой четности». Это указывает на наличие какой-то проблемы. Возможно, отправитель и получатель настроены на использование разных скоростей передачи данных (битов), или на линии был шум, который превратил ноль в единицу или наоборот.

Некоторые ранние системы также использовали «марковую» четность (где бит четности всегда был равен 1 независимо от данных) или «пробеловую» четность (где бит четности всегда был равен 0 независимо от данных).


9-битная передача

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


Количество стоповых битов

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

Если получатель не получает логическую 1, когда должен быть стоповый бит, это называется «ошибкой кадрирования». Это указывает на наличие какой-то проблемы. Вполне возможно, что отправитель и получатель настроены на использование разных скоростей передачи данных.


Обозначение

Обычно последовательная связь обозначается указанием скорости, количества битов данных, типа четности и количества стоповых битов, например:

9600/8-N-1

Это говорит нам:

  • 9600 бит в секунду
  • 8 бит данных
  • Нет четности (вместо этого вы можете увидеть: E=чет, O=нечет)
  • 1 стоповый бит

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


Распиновка

Arduino Uno имеет цифровые контакты 0 и 1, доступные для аппаратного последовательного порта:

Последовательные контакты Arduino Uno

Чтобы соединить два Arduino, нужно поменять Tx и Rx следующим образом:

Соединение двух Arduino


Скорость

Поддерживается широкий диапазон скоростей (см. рисунок ниже). «Стандартные» скорости обычно кратны 300 бод (например, 300/600/1200/2400 и т. д.).

Другие «нестандартные» скорости могут быть обработаны путем установки соответствующих регистров. Класс HardwareSerial делает это за вас. Например.

Serial.begin (115200);  // установить скорость 115200 бод

Как правило, если вы используете 8-битные данные, вы можете оценить количество байтов, которые вы можете передать в секунду, разделив скорость передачи данных на 10 (из-за стартового бита и стопового бита).

Таким образом, при скорости 9600 бод вы можете передать 960 байт (9600 / 10 = 960) в секунду.


Ошибки скорости передачи данных

Скорость передачи данных на Atmega генерируется путем деления системных часов и последующего подсчета до заданного числа. Эта таблица из технического описания показывает значения регистров и процент ошибок для часов 16 МГц (например, на Arduino Uno).

Ошибки скорости передачи данных

Бит U2Xn влияет на делитель тактовой частоты (0 = деление на 16, 1 = деление на 8). Регистр UBRRn содержит число, до которого досчитывает процессор.

Итак, из таблицы выше мы видим, что получаем 9600 бод при частоте 16 МГц следующим образом :

16000000 / 16 / 104 = 9615

Мы делим на 104, а не на 103, потому что счетчик является нулевым относительно. Таким образом, ошибка здесь составляет 15 / 9600 = 0,0016, что близко к тому, что указано в таблице выше (0,02%).

Вы заметите, что некоторые скорости передачи данных имеют более высокий уровень ошибок, чем другие.

Согласно техническому описанию максимальный процент ошибок для 8 бит данных находится в диапазоне от 1,5% до 2,0% (более подробную информацию см. в техническом описании).


Ардуино Леонардо

Arduino Leonardo и Micro имеют разный подход к последовательной связи, поскольку они подключаются к хост-компьютеру напрямую через USB, а не через последовательный порт.

Из-за этого вам придется подождать, пока Serial станет «готовым» (пока программное обеспечение установит USB-соединение), с парой дополнительных строк, например:

void setup()
  {
      Serial.begin(115200);
      while (!Serial)
      {}  // дождитесь готовности последовательной связи
      Serial.print("Fab");
  }

void loop ()
  {
  }

Однако, если вы хотите на самом деле общаться через контакты D0 и D1 (а не через USB-кабель), то вам нужно использовать Serial1 вместо Serial. Это можно сделать так:

void setup()
  {
      Serial1.begin(115200);
      Serial1.print("Fab");
  }

void loop ()
  {
  }

Уровни напряжения

Обратите внимание, что Arduino использует уровни TTL для последовательной связи. Это означает, что он ожидает:

  • «Нулевой» бит — это 0 В
  • Единичный бит — это +5 В

Старое последовательное оборудование, предназначенное для подключения к последовательному порту ПК, вероятно, использует уровни напряжения RS232, а именно:

  • «Нулевой» бит — это от +3 до +15 вольт
  • Единичный бит — это от −3 до −15 вольт

Мало того, что это «инвертировано» по отношению к уровням TTL («единица» более отрицательна, чем «ноль»), Arduino не может обрабатывать отрицательные напряжения на своих входных контактах (а также положительные, превышающие 5 В).

Таким образом, вам нужна схема интерфейса для связи с такими устройствами. Для входа (в Arduino) достаточно простого транзистора, диода и пары резисторов:

Инвертирование буфера

Для двусторонней связи вам необходимо иметь возможность генерировать отрицательные напряжения, поэтому требуется более сложная схема. Например, чип MAX232 сделает это в сочетании с четырьмя конденсаторами по 1 мкФ, которые будут действовать как схемы подкачки заряда.


Серийный номер программного обеспечения

Существует библиотека SoftwareSerial, которая позволяет вам осуществлять последовательную связь (до определенного момента) программно, а не аппаратно. Это имеет то преимущество, что вы можете использовать различные конфигурации выводов для последовательной связи. Недостаток в том, что выполнение последовательной связи программным обеспечением более интенсивно использует процессор и более подвержено ошибкам. Подробнее см. в разделе Software Serial.


Мега2560

Arduino "Mega" имеет 3 дополнительных аппаратных последовательных порта. Они обозначены на плате как Tx1/Rx1, Tx2/Rx2, Tx3/Rx3. Их следует использовать вместо SoftwareSerial, если это возможно. Чтобы открыть эти другие порты, используйте имена Serial1, Serial2, Serial3, например:

Serial1.begin (115200);  // запуск аппаратного последовательного порта Tx1/Rx1
Serial2.begin (115200);  // запуск аппаратного последовательного порта Tx2/Rx2
Serial3.begin (115200);  // запуск аппаратного последовательного порта Tx3/Rx3

Прерывания

Как отправка, так и получение с использованием библиотеки HardwareSerial используют прерывания.

Отправка

Когда вы делаете Serial.print, данные, которые вы пытаетесь напечатать, помещаются во внутренний буфер «передачи». Если у вас 1024 байта или больше оперативной памяти (например, на Uno), вы получаете 64-байтовый буфер, в противном случае вы получаете 16-байтовый буфер. Если в буфере есть место, то Serial.print немедленно возвращается, таким образом не задерживая ваш код. Если места нет, то он «блокируется», ожидая, пока буфер не опустеет достаточно, чтобы там было место.

Затем, по мере передачи каждого байта оборудованием, вызывается прерывание (прерывание «USART, Data Register Empty»), и процедура обработки прерывания отправляет следующий байт из буфера через последовательный порт.

Получение

По мере получения входящих данных вызывается процедура обработки прерывания (прерывание «USART Rx Complete»), и входящий байт помещается в буфер «приема» (того же размера, что и буфер передачи, упомянутый выше).

Когда вы вызываете Serial.available, вы узнаете, сколько байтов доступно в этом "приемном" буфере. Когда вы вызываете Serial.read, байт удаляется из приемного буфера и возвращается в ваш код.

На Arduino с 1000 байтами или более оперативной памяти нет необходимости спешить с удалением данных из буфера приема, если вы не даете ему заполниться. Если он заполняется, то все последующие входящие данные отбрасываются.

Обратите внимание, что из-за размера этого буфера нет смысла ждать поступления очень большого количества байтов, например:

while (Serial.available () < 200)
  { }  // ждем поступления 200 байт

Это никогда не сработает, потому что буфер не может вместить столько данных.


Советы

  • Перед чтением всегда проверяйте наличие данных. Например, это неверно:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // может быть недоступен
      }
    

    Тест Serial.available гарантирует, что у вас есть только один доступный байт, однако код пытается прочитать два. Это может сработать, если в буфере два байта, в противном случае вы получите -1, что будет выглядеть как 'ÿ' при печати.

  • Учитывайте, сколько времени занимает отправка данных. Как упоминалось выше, при скорости 9600 бод вы можете передавать только 960 байт в секунду, поэтому попытка отправить 1000 показаний с аналогового порта при скорости 9600 бод не будет успешной.


Ссылки

  • Асинхронный (последовательный) периферийный интерфейс - для Arduino
  • Как обрабатывать входящие последовательные данные без блокировки
  • RS-232 — Википедия
  • Справочник по Arduino — серийный номер
,

На 1-м графике: со стрелками похоже, что стоп-бит передается первым. Если бы вы поменяли местами Rx/Tx и направление стрелок, я бы подумал, что это менее запутанно., @ott--

Его предполагалось читать слева направо (как это предложение), и поэтому сначала происходит то, что слева. Скажем так: на осциллографе вы увидите след именно так., @Nick Gammon

Хорошо, с объяснением про осциллограф я согласен. :-), @ott--

Однако я думал, что ваша точка зрения имеет большой смысл. Что думают другие? Было бы яснее, если бы стрелки были перевернуты, и я бы поменял Rx/Tx?, @Nick Gammon

Я думаю, что, начиная с конца Tx (с левой стороны), если следовать по пути импульсов (в направлении стрелок), они сначала попадают на обозначение «стартового бита», затем на биты данных, а затем на «стоповый бит»., @Nick Gammon

Привет, Ник! Отличный пост. Не знаю, почему меня беспокоит твое использование слова "мусор" в первом абзаце. Я знаю, что сам использовал этот термин (и другие, вроде "gobledygoop") таким образом. Поскольку это справочная статья, я думаю, что можно использовать более удачный технический термин., @linhartr22

Это слово я использую не так уж часто («мусор» более употребимо там, где я нахожусь) — полагаю, я имел в виду «мусор на входе, мусор на выходе» — не то чтобы это применимо в данном случае). Думаю, «gobledygoop» может сбить с толку тех, для кого английский не является родным языком. Попробую придумать, как это лучше выразить. :)\, @Nick Gammon

@linhartr22 Я исправил это так, чтобы оно звучало как «бессмысленные данные», что, вероятно, ближе к истине., @Nick Gammon

@NickGammon: Отличный пост. Не могли бы вы рассказать, почему последовательный монитор Arduino IDE (1.8.9) без проблем взаимодействует с моим Arduino Micro, даже если они настроены на совершенно разные скорости передачи данных, например, 300 бит/с и 115200 бит/с соответственно? Я предполагаю, что это связано с прямым подключением по USB, но тогда имеет ли вообще значение настройка скорости передачи данных для Micro?, @djvg

Похоже, это говорит о том, что это не имеет значения: https://arduinoprosto.ru/q/31034 (если только я не установлю 1200 бит/с, а затем не выйду из последовательного монитора, потому что это вызовет сброс, как и ожидалось), @djvg

Да, см. [Начало работы с Arduino ... Micro](https://www.arduino.cc/en/Guide/ArduinoLeonardoMicro) *На Leonardo, Leonardo ETH и Micro основной класс Serial ссылается на виртуальный последовательный драйвер на плате для подключения к компьютеру через USB. Он не подключен к физическим контактам 0 и 1, как на платах Uno и более ранних. Чтобы использовать аппаратный последовательный порт (контакты 0 и 1, RX и TX), используйте Serial1*. *Настоящий* последовательный порт — Serial1, порт USB называется Serial. Используя Serial на Micro, вы можете установить скорость передачи данных на 1 и по-прежнему получать очень быструю последовательную печать., @Nick Gammon