Поскольку асинхронная последовательная связь широко распространена среди электронных устройств даже сегодня, я считаю, что многие из нас время от времени сталкивались с таким вопросом. Рассмотрим электронное устройство 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. Потенциальная пропускная способность.
Вопрос: Какие существуют другие возможные методы / решения для решения проблемы? Можете ли вы указать на недостатки в приведенном выше списке, которые можно легко обойти, удалив их таким образом? Как вы (или вы) разрабатываете свой системный протокол?
источник
Ответы:
По моему опыту, все тратят гораздо больше времени на отладку систем связи, чем они когда-либо ожидали. И поэтому я настоятельно рекомендую, чтобы всякий раз, когда вам нужно сделать выбор для протокола связи, вы выбираете тот вариант, который облегчает отладку системы, если это вообще возможно.
Я рекомендую вам поиграть в разработку нескольких пользовательских протоколов - это весело и очень познавательно. Тем не менее, я также призываю вас взглянуть на уже существующие протоколы. Если бы мне нужно было передавать данные из одного места в другое, я бы очень старался использовать какой-то уже существующий протокол, который кто-то другой уже потратил много времени на отладку.
Написание собственного протокола связи с нуля, скорее всего, приведет к столкновению со многими из тех общих проблем, которые возникают у всех, когда они пишут новый протокол.
В протоколе Good на основе RS232 для встраиваемых в компьютерную связь перечислены десятки протоколов встроенных систем. Какой из них наиболее соответствует вашим требованиям?
Даже если какое-то обстоятельство сделало невозможным точное использование какого-либо ранее существовавшего протокола, я с большей вероятностью заставлю что-то работать быстрее, начав с какого-то протокола, который почти соответствует требованиям, и затем настроив его.
плохие новости
Как я уже говорил ранее :
К сожалению, для любого протокола связи невозможно иметь все эти приятные возможности:
Я был бы удивлен и рад, если бы у протокола связи были все эти функции.
хорошие новости
Часто это значительно облегчает отладку, если человек на текстовом терминале может заменить любое из взаимодействующих устройств. Это требует, чтобы протокол был разработан относительно не зависящим от времени (не выдерживает тайм-аут во время относительно длинных пауз между нажатиями клавиш человеком). Кроме того, такие протоколы ограничены видами байтов, которые человеку легко набирать, а затем читать на экране.
Некоторые протоколы позволяют отправлять сообщения в «текстовом» или «двоичном» режиме (и требуют, чтобы все возможные двоичные сообщения имели какое-то «эквивалентное» текстовое сообщение, означающее одно и то же). Это может помочь сделать отладку намного проще.
Некоторые люди считают, что ограничение протокола на использование только печатаемых символов «расточительно», но экономия времени отладки часто оправдывает это.
Как вы уже упоминали, если вы позволите полю данных содержать любой произвольный байт, в том числе байты начала заголовка и конца заголовка, при первом включении приемника, вероятно, что приемник не синхронизируется по что выглядит как байт начала заголовка (SOH) в поле данных в середине одного пакета. Обычно получатель получит несоответствующую контрольную сумму в конце этого псевдопакета (который обычно находится на полпути через второй реальный пакет). Очень заманчиво просто отбросить все псевдосообщения (включая первую половину этого второго пакета) перед поиском следующего SOH - в результате получатель может остаться не синхронизированным для многих сообщений.
Как указал alex.forencich, для получателя гораздо лучше сбросить байты в начале буфера до следующего SOH. Это позволяет получателю (возможно, после проработки нескольких байтов SOH в этом пакете данных) немедленно синхронизироваться на втором пакете.
Как отметил Николас Кларк, заполнение байтов с постоянными издержками (COBS) имеет фиксированные издержки, которые хорошо работают с кадрами фиксированного размера.
Одним из методов, который часто упускается из виду, является выделенный байт маркера конца кадра. Когда приемник включается в середине передачи, выделенный байт маркера конца кадра помогает приемнику быстрее синхронизироваться.
Когда приемник включен в середине пакета, и поле данных пакета содержит байты, которые кажутся началом пакета (начало псевдопакета), передатчик может вставить серию байтов маркера конца кадра после этого пакета, поэтому такие байты псевдо-начала пакета в поле данных не мешают немедленной синхронизации и правильному декодированию следующего пакета - даже если вам крайне не повезло и контрольная сумма этого псевдопакета выглядит правильно.
Удачи.
источник
Схемы заполнения байтов отлично сработали на протяжении многих лет. Они хороши тем, что их легко реализовать в программном или аппаратном обеспечении, вы можете использовать стандартный кабель USB-UART для отправки пакетов данных, и вы гарантированно получите качественное кадрирование, не беспокоясь о тайм-ауты, горячая замена или что-то подобное.
Я бы рекомендовал метод заполнения байтов в сочетании с байтом длины (длина пакета по модулю 256) и CRC уровня пакета, а затем использовать UART с битом четности. Длина байта обеспечивает обнаружение отброшенных байтов, что хорошо работает с битом четности (потому что большинство UART будут отбрасывать любые байты, которые не соответствуют четности). Тогда CRC на уровне пакетов обеспечивает дополнительную безопасность.
Что касается накладных расходов на заполнение байтов, вы смотрели на протокол COBS? Это гениальный способ наполнения байтами с фиксированными издержками в 1 байт на каждые 254 передаваемых сигнала (плюс кадрирование, CRC, LEN и т. Д.).
https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
источник
Ваш вариант № 1, контрольная сумма SOH плюс, является надежным и восстанавливается на следующем нетронутом кадре.
Я предполагаю, что вы либо уже знаете длину сообщения, либо длина закодирована в байтах, следующих непосредственно за SOH. Контрольные байты появляются в конце сообщения. Вам также необходим буфер на стороне приема для данных, который по крайней мере соответствует длине вашего самого длинного сообщения.
Каждый раз, когда вы видите байт SOH в начале буфера, это потенциально начало сообщения. Вы просматриваете буфер, чтобы вычислить проверочное значение для этого сообщения и посмотреть, совпадают ли оно с проверочными байтами в буфере. Если это так, вы сделали; в противном случае вы сбрасываете данные из буфера, пока не доберетесь до следующего байта SOH.
Обратите внимание, что если сообщение на самом деле содержит ошибки данных, этот алгоритм его отбросит, но вы, вероятно, все равно это сделаете. Если ваш алгоритм проверки включает прямое исправление ошибок, вы можете проверить каждое потенциальное выравнивание сообщений на наличие исправимых ошибок.
Если сообщения имеют фиксированную длину, вы можете вообще отказаться от байта SOH - просто проверьте КАЖДУЮ возможную начальную позицию на предмет правильного контрольного значения.
Вы также можете обойтись без алгоритма проверки и сохранить только байт SOH, но это делает алгоритм менее детерминированным. Идея состоит в том, что для корректного выравнивания сообщений SOH всегда будет появляться в начале сообщения. Если у вас неправильное выравнивание, следующий байт в потоке данных вряд ли будет другим SOH (зависит от того, как часто SOH появляется в данных сообщения). Вы можете выбрать действительные байты SOH только на этой основе. (Именно так работает кадрирование в синхронных телекоммуникационных сервисах, таких как T1 и E1.)
источник
Одним из вариантов, не упомянутых, но широко используемым (особенно в Интернете), является кодировка ASCII / текста (на самом деле, большинство современных реализаций предполагают UTF-8). По моему опыту, парни из аппаратного обеспечения ненавидят это делать, но люди, занимающиеся программным обеспечением, предпочитают это больше всего остального (в основном это происходит из-за традиции Unix делать все текстовые).
Преимущество кодирования текста в том, что вы можете использовать непечатаемые символы для кадрирования. Например, самым простым было бы использовать что-то вроде
0x00
указания начала кадра и0xff
конца кадра.Я видел два основных способа, которыми данные кодируются как текст:
Когда парня из отдела оборудования / сборки попросят сделать это, то, скорее всего, это будет реализовано как шестнадцатеричное кодирование. Это просто преобразование байтов в их шестнадцатеричные значения в ASCII. Накладные расходы большие. По сути, вы будете передавать два байта для каждого фактического байта данных.
Когда этого просят программисты, это, вероятно, будет реализовано как кодировка base64. Это де-факто кодировка интернета. Используется для всего, от вложений MIME электронной почты до кодирования данных URL. Накладные расходы составляют ровно 33%. Гораздо лучше, чем простое шестнадцатеричное кодирование.
Кроме того, вы можете полностью отказаться от двоичных данных и передавать текст. В этом случае наиболее распространенным методом является разделение данных с помощью новой строки (либо просто,
"\n"
либо"\r\n"
). NMEA (GPS), команды модема AT и датчики Adventech ADAM являются одними из наиболее распространенных примеров этого.Все эти текстовые протоколы / кадрирования имеют следующие плюсы и минусы:
Pro:
Против:
"0"
(0x30) вместо четырех байтов 0x00000000)sprintf()
функцию)Лично для меня плюсы сильно перевешивают минусы. Одна только простота отладки считается 5 баллами (так что одна только точка уже перевешивает оба минуса).
Кроме того, от разработчиков программного обеспечения часто поступают не очень тщательно продуманные решения: отправляйте закодированные данные, не задумываясь о кадрировании.
В прошлом мне приходилось взаимодействовать с оборудованием, которое отправляло необработанный XML. XML был всем созданием. К счастью, довольно просто определить границы фреймов по
<xml></xml>
тегам. Большой минус для меня в том, что он использует более одного байта для кадрирования. Кроме того, само кадрирование может не быть фиксированным, поскольку тег может содержать атрибуты:<tag foo="bar"></tag>
поэтому вам придется буферизовать наихудший случай, чтобы найти начало кадра.Недавно я видел, как люди начинают посылать JSON из последовательных портов. С JSON обрамление в лучшем случае является догадкой. У вас есть только символ
"{"
(или"["
) для определения фрейма, но они также содержатся в данных. Таким образом, вам понадобится парсер рекурсивного спуска (или, по крайней мере, счетчик фигурных скобок), чтобы вычислить кадр. По крайней мере, тривиально узнать, заканчивается ли текущий кадр преждевременно:"}{"
или он"]["
недопустим в JSON и, таким образом, указывает, что старый кадр закончился и новый кадр начался.источник
<
и в>
качестве разделителей, и я полагаю, что электронная почта использует новые строки. Примечание: да, электронная почта - это правильно оформленный формат это может быть передано через RS232. Мой друг использовал почтовый сервер для своего дома, используя RS232)То, что вы описываете как «маркировка X-го бита», можно обобщить в другие коды, обладающие свойством расширения данных на постоянную дробь, оставляя некоторые кодовые слова свободными для использования в качестве разделителей. Часто эти коды предоставляют и другие преимущества; Компакт-диски используют модуляцию от восьми до четырнадцати , что гарантирует максимальную длину прогона 0 битов между каждым 1. Другие примеры включают коды блоков прямого исправления ошибок , которые также используют дополнительные биты для кодирования информации об обнаружении и исправлении ошибок.
Другая система, о которой вы не упомянули, - это использование внеполосной информации, такой как строка выбора микросхемы, для разграничения транзакций или пакетов.
источник
Другой вариант - это так называемое линейное кодирование . Линейное кодирование придает сигналу определенные электрические характеристики, которые облегчают его передачу (сбалансированный постоянный ток и максимальная длина прогона гарантированы), и они поддерживают управляющие символы для кадрирования и синхронизации часов. Линейные коды используются во всех современных высокоскоростных последовательных протоколах - 10M, 100M, 1G и 10G Ethernet, последовательный ATA, FireWire, USB 3, PCIe и т. Д. Общие линейные коды - 8b / 10b , 64b / 66b и 128b / 130b, Существуют также более простые линейные коды, которые не предоставляют информацию об кадрировании, только баланс постоянного тока и синхронизацию часов. Примерами этого являются Machester и NRZ. Вы, вероятно, хотите использовать 8b / 10b, если хотите быстро выполнить синхронизацию; другие линейные коды не предназначены для синхронизации в спешке. Использование строкового кода, подобного предложенному выше, потребует использования специального оборудования для передачи и приема.
Что касается вашего варианта 5, предполагается, что стандартный последовательный порт RS232 поддерживает перерывы отправки и получения, когда линия простаивает в течение пары байтов. Однако это может поддерживаться не во всех системах.
Как правило, самый простой и надежный метод кадрирования - это ваш вариант 1 в сочетании с полем длины и простой процедурой CRC или контрольной суммы. Процедура декодирования проста: сбрасывать байты до тех пор, пока вы не получите начальный байт, прочитать поле длины, дождаться полного кадра, проверить контрольную сумму, сохранить, если она хорошая. Если контрольная сумма неверна, начните отбрасывать байты из буфера, пока не получите стартовый байт и повторите. Основная проблема с этой техникой - найти начало байта кадра, которое на самом деле не является началом байта кадра. Чтобы устранить эту проблему, один из методов состоит в том, чтобы экранировать байты с тем же значением, что и у начального байта кадра с другим управляющим символом, а затем изменять экранированный байт, чтобы он имел другое значение. В этом случае вам также придется сделать то же самое с новым управляющим байтом.
источник