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

16

Применительно к платам Arduino Uno, Mega2560, Leonardo и аналогичным:

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

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

Ник Гаммон
источник
Это может быть интересным для буферов на обеих сторонах Nano, подключенного к системе Raspian, на которой работает регистратор данных Python, с помощью обычного USB-кабеля для программирования между ними: arduino.stackexchange.com/questions/11710/…
SDsolar

Ответы:

16

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

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

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

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

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

Serial comms - отправка одного байта

На рисунке выше показана передаваемая буква «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. Понятно, что чем меньше бит данных, тем меньше информации вы можете отправлять, но тем быстрее это будет.


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

При желании вы можете иметь бит четности. Это вычисляется, если требуется, путем подсчета числа 1 в символе, а затем убедитесь, что это число нечетное или четное, установив бит четности в 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 стоповый бит

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


Pin-ауты

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

Серийные булавки Arduino Uno

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

Соединение двух Arduinos вместе


скорость

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

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

Serial.begin (115200);  // set speed to 115200 baud

Как правило, предполагая, что вы используете 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)
      {}  // wait for Serial comms to become ready
      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 для более подробной информации.


Mega2560

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

Serial1.begin (115200);  // start hardware serial port Tx1/Rx1
Serial2.begin (115200);  // start hardware serial port Tx2/Rx2
Serial3.begin (115200);  // start hardware serial port Tx3/Rx3

Прерывания

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

Отправка

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

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

получающий

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

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

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

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

while (Serial.available () < 200)
  { }  // wait for 200 bytes to arrive

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


подсказки

  • Перед чтением всегда убедитесь, что данные доступны. Например, это неправильно:

    if (Serial.available ())
      {
          char a = Serial.read ();
          char b = Serial.read ();  // may not be available
      }

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

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


Ссылки

Ник Гаммон
источник
На 1-м графике: со стрелками похоже, что стоп-бит передается первым. Если бы вы обменяли Rx / Tx и направление стрелок, я бы подумал, что это менее запутанно.
ot--
Он был предназначен для чтения слева направо (как это предложение), и, таким образом, вещи слева происходят в первую очередь. Поместите это так: на осциллографе именно так вы бы увидели след.
Ник Гэммон
Хорошо с объяснением осциллографа, я покупаю это. :-)
ott--
Однако я думал, что ваша точка зрения имеет большой смысл. Что думают другие? Было бы яснее, если бы стрелки были перевернуты, и я обменял Rx / Tx?
Ник Гэммон
1
@ linhartr22 Я изменил его, чтобы прочитать «бессмысленные данные», что, вероятно, ближе.
Ник Гэммон