Методы разделения / синхронизации последовательного протокола


24

Поскольку асинхронная последовательная связь широко распространена среди электронных устройств даже сегодня, я считаю, что многие из нас время от времени сталкивались с таким вопросом. Рассмотрим электронное устройство Dи компьютер, PCсоединенные последовательной линией (RS-232 или аналогичные) и необходимые для непрерывного обмена информацией . Т.е. PCкаждый посылает командный кадр X msи Dотвечает с каждым отчетом о состоянии / телеметрическим кадром Y ms(отчет может быть отправлен как ответ на запросы или независимо - здесь это не имеет значения). Кадры связи могут содержать любые произвольные двоичные данные . Предполагая, что кадры связи представляют собой пакеты фиксированной длины.

Проблема:

Поскольку протокол является непрерывным, принимающая сторона может потерять синхронизацию или просто "присоединиться" в середине текущего отправленного кадра, поэтому она просто не будет знать, где находится начало кадра (SOF). Если данные имеют различное значение в зависимости от их положения относительно SOF, полученные данные будут повреждены, возможно, навсегда.

Требуемое решение

Надежная схема разграничения / синхронизации для обнаружения SOF с коротким временем восстановления (т. Е. Для повторной синхронизации не требуется, скажем, 1 кадр).

Существующие методы, которые я знаю (и использую некоторые) из:

1) Заголовок / контрольная сумма - SOF как предопределенное значение байта. Контрольная сумма в конце кадра.

  • Плюсы: просто.
  • Минусы: не надежны. Неизвестное время восстановления.

2) Байтовая начинка:

  • Плюсы: надежное, быстрое восстановление, можно использовать с любым оборудованием
  • Минусы: не подходит для фиксированной связи на основе кадров

3) Маркировка 9-го бита - каждый байт дополняется дополнительным битом, а SOF помечается, 1а байты данных помечаются 0:

  • Плюсы: надежное, быстрое восстановление
  • Минусы: Требуется аппаратная поддержка. Не поддерживается напрямую большинством PCаппаратного и программного обеспечения.

4) Маркировка 8-го бита - вид эмуляции вышеупомянутого, при этом используется 8-й бит вместо 9-го, что оставляет только 7 битов для каждого слова данных.

  • Плюсы: надежное, быстрое восстановление, можно использовать с любым оборудованием.
  • Минусы: Требуется схема кодирования / декодирования из / в обычное 8-битное представление в / из 7-битное представление. Несколько расточительно.

5) На основе тайм- аута - примите SOF в качестве первого байта после некоторого определенного времени простоя.

  • Плюсы: нет данных, просто.
  • Минусы: не так надежно. Не будет хорошо работать с плохими системами синхронизации, как, например, Windows PC. Потенциальная пропускная способность.

Вопрос: Какие существуют другие возможные методы / решения для решения проблемы? Можете ли вы указать на недостатки в приведенном выше списке, которые можно легко обойти, удалив их таким образом? Как вы (или вы) разрабатываете свой системный протокол?

serial  communication  protocol  brushless-dc-motor  hall-effect  hdd  scr  flipflop  state-machines  pic  c  uart  gps  arduino  gsm  microcontroller  can  resonance  memory  microprocessor  verilog  modelsim  transistors  relay  voltage-regulator  switch-mode-power-supply  resistance  bluetooth  emc  fcc  microcontroller  atmel  flash  microcontroller  pic  c  stm32  interrupts  freertos  oscilloscope  arduino  esp8266  pcb-assembly  microcontroller  uart  level  arduino  transistors  amplifier  audio  transistors  diodes  spice  ltspice  schmitt-trigger  voltage  digital-logic  microprocessor  clock-speed  overclocking  filter  passive-networks  arduino  mosfet  control  12v  switching  temperature  light  luminous-flux  photometry  circuit-analysis  integrated-circuit  memory  pwm  simulation  behavioral-source  usb  serial  rs232  converter  diy  energia  diodes  7segmentdisplay  keypad  pcb-design  schematics  fuses  fuse-holders  radio  transmitter  power-supply  voltage  multimeter  tools  control  servo  avr  adc  uc3  identification  wire  port  not-gate  dc-motor  microcontroller  c  spi  voltage-regulator  microcontroller  sensor  c  i2c  conversion  microcontroller  low-battery  arduino  resistors  voltage-divider  lipo  pic  microchip  gpio  remappable-pins  peripheral-pin-select  soldering  flux  cleaning  sampling  filter  noise  computers  interference  power-supply  switch-mode-power-supply  efficiency  lm78xx 

4 только на 1/8 расточительнее, чем 3.
Ник Джонсон

@NickJohnson Согласитесь, но я только предлагаю добавить в «3» также и слова «Wasteful» :)
Eugene Sh.

Я не думаю, что вы полностью объяснили свои предположения об ошибках связи. Предполагаете ли вы, что связь является «идеальной», то есть без ошибок, или «достаточно совершенной», что все ошибки обнаруживаются и идентифицируются аппаратным обеспечением связи (например, связь использует четность, и они являются только однобитовыми ошибками)?
gbulmer

Beceiver может присоединиться к середине байта и может интерпретировать бит 8 как бит 4, например. Поэтому маркировка 9-го бита ненадежна.
Тимоти Болдуин

@gbulmer Исходное предположение состоит в том, что канал идеален, и проблема может возникнуть только из-за начальной несинхронизации. Согласно этим предположениям «надежность», о которой я говорил, относится только к повторной синхронизации. В приведенном выше списке все эти методы гарантируют 100% успеха, кроме первого. Но, вероятно, схему проверки ошибок и кадрирование не следует разделять так.
Евгений Ш.

Ответы:


15

Как вы (или вы) разрабатываете свой системный протокол?

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

Я рекомендую вам поиграть в разработку нескольких пользовательских протоколов - это весело и очень познавательно. Тем не менее, я также призываю вас взглянуть на уже существующие протоколы. Если бы мне нужно было передавать данные из одного места в другое, я бы очень старался использовать какой-то уже существующий протокол, который кто-то другой уже потратил много времени на отладку.

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

В протоколе Good на основе RS232 для встраиваемых в компьютерную связь перечислены десятки протоколов встроенных систем. Какой из них наиболее соответствует вашим требованиям?

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

плохие новости

Как я уже говорил ранее :

К сожалению, для любого протокола связи невозможно иметь все эти приятные возможности:

  • прозрачность: передача данных прозрачна и «8-битная чистота» - (а) любой возможный файл данных может быть передан, (б) байтовые последовательности в файле всегда обрабатываются как данные и никогда не интерпретируются как что-то другое, и (в) ) пункт назначения получает весь файл данных без ошибок, без каких-либо добавлений или удалений.
  • Простое копирование: формирование пакетов проще всего, если мы просто слепо копируем данные из источника в поле данных пакета без изменений.
  • уникальный старт: символ начала пакета легко распознать, потому что это известный постоянный байт, который никогда не встречается где-либо еще в заголовках, заголовке CRC, полезной нагрузке данных или CRC данных.
  • 8-битный: использует только 8-битные байты.

Я был бы удивлен и рад, если бы у протокола связи были все эти функции.

хорошие новости

Какие существуют другие возможные методы / решения для решения проблемы?

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

Некоторые протоколы позволяют отправлять сообщения в «текстовом» или «двоичном» режиме (и требуют, чтобы все возможные двоичные сообщения имели какое-то «эквивалентное» текстовое сообщение, означающее одно и то же). Это может помочь сделать отладку намного проще.

Некоторые люди считают, что ограничение протокола на использование только печатаемых символов «расточительно», но экономия времени отладки часто оправдывает это.

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

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

Можете ли вы указать на недостатки в приведенном выше списке, которые можно легко обойти, удалив их таким образом?

Как отметил Николас Кларк, заполнение байтов с постоянными издержками (COBS) имеет фиксированные издержки, которые хорошо работают с кадрами фиксированного размера.

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

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

Удачи.


Этот ответ стоит пересмотреть ранее принятый ответ (извините, @DaveTweed), и связанная статья, безусловно, должна быть прочитана по этой теме. Спасибо, что нашли время и напишите это.
Евгений Ш.

1
хорошо, что вы указываете на COBS, поэтому мне не нужно писать ответ :-)
Нильс Пипенбринк

11

Схемы заполнения байтов отлично сработали на протяжении многих лет. Они хороши тем, что их легко реализовать в программном или аппаратном обеспечении, вы можете использовать стандартный кабель USB-UART для отправки пакетов данных, и вы гарантированно получите качественное кадрирование, не беспокоясь о тайм-ауты, горячая замена или что-то подобное.

Я бы рекомендовал метод заполнения байтов в сочетании с байтом длины (длина пакета по модулю 256) и CRC уровня пакета, а затем использовать UART с битом четности. Длина байта обеспечивает обнаружение отброшенных байтов, что хорошо работает с битом четности (потому что большинство UART будут отбрасывать любые байты, которые не соответствуют четности). Тогда CRC на уровне пакетов обеспечивает дополнительную безопасность.

Что касается накладных расходов на заполнение байтов, вы смотрели на протокол COBS? Это гениальный способ наполнения байтами с фиксированными издержками в 1 байт на каждые 254 передаваемых сигнала (плюс кадрирование, CRC, LEN и т. Д.).

https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing


Это отличный способ избежать разбивки байтов на 2x в худшем случае. Я использовал похожие, но более специфичные для приложения схемы, но приятно видеть, что это описано стандартным способом. Я собираюсь использовать COBS с этого момента ...
WJL

1
Спасибо от меня за указание на COBS - очень аккуратный маленький алгоритм.
Ник Джонсон

6

Ваш вариант № 1, контрольная сумма SOH плюс, является надежным и восстанавливается на следующем нетронутом кадре.

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

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

Обратите внимание, что если сообщение на самом деле содержит ошибки данных, этот алгоритм его отбросит, но вы, вероятно, все равно это сделаете. Если ваш алгоритм проверки включает прямое исправление ошибок, вы можете проверить каждое потенциальное выравнивание сообщений на наличие исправимых ошибок.

Если сообщения имеют фиксированную длину, вы можете вообще отказаться от байта SOH - просто проверьте КАЖДУЮ возможную начальную позицию на предмет правильного контрольного значения.

Вы также можете обойтись без алгоритма проверки и сохранить только байт SOH, но это делает алгоритм менее детерминированным. Идея состоит в том, что для корректного выравнивания сообщений SOH всегда будет появляться в начале сообщения. Если у вас неправильное выравнивание, следующий байт в потоке данных вряд ли будет другим SOH (зависит от того, как часто SOH появляется в данных сообщения). Вы можете выбрать действительные байты SOH только на этой основе. (Именно так работает кадрирование в синхронных телекоммуникационных сервисах, таких как T1 и E1.)


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

Конечно, это возможно. Но на практике сравнительно легко выбрать достаточно надежный алгоритм проверки.
Дэйв Твид

Если у вас ненулевой уровень ошибок в данных, всегда есть ненулевой шанс, что вы все равно примете неверное сообщение.
Ник Джонсон

@NickJohnson Предполагая, что канал абсолютно чистый, теоретически все равно будут существовать несоответствия с этим подходом. Конечно, их вероятность может быть незначительной.
Евгений Ш.

1
Я знаю, что вы уже знаете это и уже упоминали об этом мимоходом, но версия, в которой вы не буферизуете целое сообщение или просто ленитесь о том, как вы декодируете, менее надежна. Если вы повторно синхронизируете следующий байт SOH после несоответствующей контрольной суммы, а не следующий байт SOH после «ложного» SOH, у вас есть очень хороший шанс отменить начало реального сообщения и остаться не синхронизированным для многих сообщений или, в худший случай, навсегда.
Хоббс

5

Одним из вариантов, не упомянутых, но широко используемым (особенно в Интернете), является кодировка ASCII / текста (на самом деле, большинство современных реализаций предполагают UTF-8). По моему опыту, парни из аппаратного обеспечения ненавидят это делать, но люди, занимающиеся программным обеспечением, предпочитают это больше всего остального (в основном это происходит из-за традиции Unix делать все текстовые).

Преимущество кодирования текста в том, что вы можете использовать непечатаемые символы для кадрирования. Например, самым простым было бы использовать что-то вроде 0x00указания начала кадра и 0xffконца кадра.

Я видел два основных способа, которыми данные кодируются как текст:

  1. Когда парня из отдела оборудования / сборки попросят сделать это, то, скорее всего, это будет реализовано как шестнадцатеричное кодирование. Это просто преобразование байтов в их шестнадцатеричные значения в ASCII. Накладные расходы большие. По сути, вы будете передавать два байта для каждого фактического байта данных.

  2. Когда этого просят программисты, это, вероятно, будет реализовано как кодировка base64. Это де-факто кодировка интернета. Используется для всего, от вложений MIME электронной почты до кодирования данных URL. Накладные расходы составляют ровно 33%. Гораздо лучше, чем простое шестнадцатеричное кодирование.

Кроме того, вы можете полностью отказаться от двоичных данных и передавать текст. В этом случае наиболее распространенным методом является разделение данных с помощью новой строки (либо просто, "\n"либо "\r\n"). NMEA (GPS), команды модема AT и датчики Adventech ADAM являются одними из наиболее распространенных примеров этого.

Все эти текстовые протоколы / кадрирования имеют следующие плюсы и минусы:

Pro:

  • Легко отлаживать
  • Легко реализовать на языке сценариев
  • Аппаратное обеспечение можно просто протестировать с помощью Hyperterminal / minicom.
  • Легко реализовать на оборудовании (если это не очень маленький микро, как PIC)
  • Может быть фиксированным размером кадра или разного размера.
  • Предсказуемое кадрирование и быстрое восстановление синхронизации (восстанавливается в конце текущего кадра)

Против:

  • Очень большие издержки по сравнению с чисто двоичной передачей (опять же, текстовый ввод / вывод также может «сжимать» числа, такие как отправка одного байта "0"(0x30) вместо четырех байтов 0x00000000)
  • Не очень чистая для реализации на очень маленьких микро, таких как PIC (если ваша библиотека не содержит sprintf()функцию)

Лично для меня плюсы сильно перевешивают минусы. Одна только простота отладки считается 5 баллами (так что одна только точка уже перевешивает оба минуса).


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

В прошлом мне приходилось взаимодействовать с оборудованием, которое отправляло необработанный XML. XML был всем созданием. К счастью, довольно просто определить границы фреймов по <xml></xml>тегам. Большой минус для меня в том, что он использует более одного байта для кадрирования. Кроме того, само кадрирование может не быть фиксированным, поскольку тег может содержать атрибуты: <tag foo="bar"></tag>поэтому вам придется буферизовать наихудший случай, чтобы найти начало кадра.

Недавно я видел, как люди начинают посылать JSON из последовательных портов. С JSON обрамление в лучшем случае является догадкой. У вас есть только символ "{"(или "[") для определения фрейма, но они также содержатся в данных. Таким образом, вам понадобится парсер рекурсивного спуска (или, по крайней мере, счетчик фигурных скобок), чтобы вычислить кадр. По крайней мере, тривиально узнать, заканчивается ли текущий кадр преждевременно: "}{"или он "]["недопустим в JSON и, таким образом, указывает, что старый кадр закончился и новый кадр начался.


Для кодировок текста есть также base85 , который имеет только 25% накладных расходов вместо 33%.
Дэйв Твид

Я бы посчитал это подмножеством / вариацией 4-го метода.
Евгений Ш.

@EugeneSh .: Технически это подмножество байтфуффинга. Опять же, поскольку вы считаете это подмножеством маркировки битов, вы можете понять, почему эта неоднозначность делает ее отдельной категорией. Кроме того, вы не можете рассматривать большинство реализаций кодирования текста как подмножество маркировки битов, потому что биты маркировки никогда не используются (например, я обычно использую <и в >качестве разделителей, и я полагаю, что электронная почта использует новые строки. Примечание: да, электронная почта - это правильно оформленный формат это может быть передано через RS232. Мой друг использовал почтовый сервер для своего дома, используя RS232)
slebetman

4

То, что вы описываете как «маркировка X-го бита», можно обобщить в другие коды, обладающие свойством расширения данных на постоянную дробь, оставляя некоторые кодовые слова свободными для использования в качестве разделителей. Часто эти коды предоставляют и другие преимущества; Компакт-диски используют модуляцию от восьми до четырнадцати , что гарантирует максимальную длину прогона 0 битов между каждым 1. Другие примеры включают коды блоков прямого исправления ошибок , которые также используют дополнительные биты для кодирования информации об обнаружении и исправлении ошибок.

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


Коды исправления ошибок немного в стороне от вопроса. Они должны быть добавлены к любой из этих схем в любом случае. Я полагаю, что «внешняя информация», на которую вы ссылаетесь, такая же, как «аппаратное управление потоком»?
Евгений Ш.

@EugeneSh. - На самом деле, использование битов проверки ошибок для кадрирования вполне допустимо, хотя вычислительно дорого на стороне приема. Вы просто делаете расчет ошибки для каждого возможного выравнивания данных, и тот, который успешно выполняется, является действительным выравниванием на нетронутом кадре. Конечно, если кадр будет поврежден, вы не найдете его.
Дэйв Твид

@DaveTweed Ну, это почти то, что я имел в виду под первой техникой. Или я вас неправильно понимаю?
Евгений Ш.

Нет, вы не ошиблись; это то, о чем я говорил. Тем не менее, ваш «обман» неверен - он надежен, и его можно сделать надежным и в отношении реальных ошибок передачи.
Дэйв Твид

@DaveTweed Как насчет времени восстановления? У вас есть примеры того, как это можно сделать надежным?
Евгений Ш.

3

Другой вариант - это так называемое линейное кодирование . Линейное кодирование придает сигналу определенные электрические характеристики, которые облегчают его передачу (сбалансированный постоянный ток и максимальная длина прогона гарантированы), и они поддерживают управляющие символы для кадрирования и синхронизации часов. Линейные коды используются во всех современных высокоскоростных последовательных протоколах - 10M, 100M, 1G и 10G Ethernet, последовательный ATA, FireWire, USB 3, PCIe и т. Д. Общие линейные коды - 8b / 10b , 64b / 66b и 128b / 130b, Существуют также более простые линейные коды, которые не предоставляют информацию об кадрировании, только баланс постоянного тока и синхронизацию часов. Примерами этого являются Machester и NRZ. Вы, вероятно, хотите использовать 8b / 10b, если хотите быстро выполнить синхронизацию; другие линейные коды не предназначены для синхронизации в спешке. Использование строкового кода, подобного предложенному выше, потребует использования специального оборудования для передачи и приема.

Что касается вашего варианта 5, предполагается, что стандартный последовательный порт RS232 поддерживает перерывы отправки и получения, когда линия простаивает в течение пары байтов. Однако это может поддерживаться не во всех системах.

Как правило, самый простой и надежный метод кадрирования - это ваш вариант 1 в сочетании с полем длины и простой процедурой CRC или контрольной суммы. Процедура декодирования проста: сбрасывать байты до тех пор, пока вы не получите начальный байт, прочитать поле длины, дождаться полного кадра, проверить контрольную сумму, сохранить, если она хорошая. Если контрольная сумма неверна, начните отбрасывать байты из буфера, пока не получите стартовый байт и повторите. Основная проблема с этой техникой - найти начало байта кадра, которое на самом деле не является началом байта кадра. Чтобы устранить эту проблему, один из методов состоит в том, чтобы экранировать байты с тем же значением, что и у начального байта кадра с другим управляющим символом, а затем изменять экранированный байт, чтобы он имел другое значение. В этом случае вам также придется сделать то же самое с новым управляющим байтом.


Это так же, как ответ Ника Джонсона.
Дэйв Твид
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.