Методи розмежування / синхронізації послідовного протоколу


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) :)
Євген Ш.

Я не думаю, що ви повністю пояснили свої припущення щодо помилок у спілкуванні. Чи вважаєте ви, що спілкування є "ідеальним", тобто без помилок, або "достатньо досконалим", щоб усі помилки були виявлені та ідентифіковані апаратним забезпеченням для зв'язку (наприклад, comms використовує паритет, і вони є лише єдиними бітовими помилками)?
gbulmer

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

@gbulmer Початкове припущення про те, що канал ідеальний, і проблема може виникнути лише через початкову неправильну синхронізацію. Згідно з цими припущеннями, "надійність", про яку я посилався, пов'язана лише з пересинхронізацією. У наведеному вище списку всі ці методи гарантують 100% успіх, крім першого. Але, ймовірно, схему перевірки помилок та обрамлення не слід розділяти так.
Євген Ш.

Відповіді:


15

Як ви (або ви б) проектували ваш системний протокол?

На мій досвід, кожен витрачає набагато більше часу на налагодження систем зв'язку, ніж коли-небудь очікували. І тому я наполегливо пропоную, щоразу, коли вам потрібно зробити вибір протоколу зв'язку, ви вибираєте той варіант, який полегшує систему налагодження, якщо це можливо.

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

Написання власного протоколу комунікації з нуля, велика ймовірність зіткнутися з багатьма тими ж загальними проблемами, які виникають у кожного, коли вони пишуть новий протокол.

Є десяток вбудованих системних протоколів, перелічених у Хороших протоколах RS232 для вбудованої в комп'ютерну комунікацію - який з них найбільше відповідає вашим вимогам?

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

погані новини

Як я вже говорив раніше :

На жаль, неможливо, щоб жоден протокол зв'язку мав усі ці приємні функції:

  • прозорість: передача даних є прозорою і "8-бітною чистою" - (a) будь-який можливий файл даних може бути переданий, (b) послідовності байтів у файлі завжди обробляються як дані, і ніколи не трактуються неправильно як щось інше, і (c ) адресат отримує весь файл даних без помилок, без будь-яких доповнень та видалень.
  • проста копія: формувати пакети найпростіше, якщо ми просто сліпо копіюємо дані з джерела в поле даних пакета без змін.
  • унікальний початок: символ запуску пакету легко розпізнати, оскільки це відомий постійний байт, який ніколи не зустрічається ніде в заголовках, CRC заголовка, корисному навантаженні даних або CRC даних.
  • 8-бітний: використовує лише 8-бітні байти.

Я був би здивований і радий, якби якийсь протокол зв'язку мав усі ці функції.

гарні новини

Які ще можливі методи / рішення існують для вирішення проблеми?

Часто це дає налагодження набагато, набагато простіше, якщо людина в текстовому терміналі може замінити будь-який з комунікаційних пристроїв. Це вимагає, щоб протокол був розроблений відносно незалежно від часу (не вичерпується час відносно довгих пауз між натисканнями клавіш, набраними людиною). Також такі протоколи обмежуються типом байтів, які людині легко набрати, а потім прочитати на екрані.

Деякі протоколи дозволяють надсилати повідомлення в режимі "текст" або "двійковий" (і вимагають, щоб усі можливі бінарні повідомлення містили якесь "еквівалентне" текстове повідомлення, що означає те саме). Це може допомогти зробити налагодження набагато простішим.

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

Як ви вже згадували, якщо ви дозволяєте, щоб поле даних містило будь-який довільний байт, включаючи байти початку заголовка та кінця заголовка, коли приймач вперше увімкнено, ймовірно, що приймач неправильно синхронізується на як виглядає байт запуску заголовка (SOH) у полі даних посередині одного пакету. Зазвичай одержувач отримує невідповідну контрольну суму в кінці цього псевдопакету (що зазвичай знаходиться на півдорозі через другий реальний пакет). Дуже спокусливо просто відкинути все псевдоповідомлення (включаючи першу половину цього другого пакету), перш ніж шукати наступний SOH - з тим наслідком, що приймач може не синхронізуватися для багатьох повідомлень.

Як зазначав alex.forencich, набагато кращим підходом є приймач для відкидання байтів на початку буфера до наступного SOH. Це дозволяє одержувачу (після можливої ​​роботи через кілька байтів SOH у цьому пакеті даних) негайно синхронізуватися на другому пакеті.

Чи можете ви вказати на мінуси у наведеному вище списку, які можна легко обробити, тим самим усуваючи їх?

Як зазначив Ніколас Кларк, послідовне набивання байтів (COBS) має фіксовану накладну, яка добре працює з рамками фіксованого розміру.

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

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

Удачі.


Ця відповідь варто переглянути раніше прийняту відповідь (вибачте, @DaveTweed), і пов'язану статтю, безумовно, треба прочитати на цю тему. Дякую, що знайшли час і напишіть його.
Євген Ш.

1
приємно, що ти вказуєш на COBS, тому мені не потрібно писати відповідь :-)
Nils Pipenbrinck

11

Схеми набивання байтів протягом багатьох років спрацювали для мене чудово. Вони приємні, тому що їх легко реалізувати в програмному чи апаратному забезпеченні, ви можете використовувати стандартний кабель USB-UART для надсилання пакетів даних, і ви гарантовано отримаєте якісну рамку, не турбуючись про це тайм-аути, гаряча заміна чи щось подібне.

Я б виступав за метод набивання байтів, поєднаний з байтом довжини (довжина пакета по модулю 256) і CRC рівня пакетів, а потім використовувати UART з бітом парності. Байт довжини забезпечує виявлення у спадному байті, що добре працює з бітом парності (тому що більшість UART скидає будь-які байти, які не відповідають парності). Тоді CRC на рівні пакетів надає вам додаткову безпеку.

Що стосується накладних витрат на байт-начинку, ви подивилися на протокол COBS? Це геніальний спосіб робити набивання байтів із фіксованим накладним покриттям у 1 байт на кожні 254 передані (плюс ваш обрамлення, CRC, LEN тощо).

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


Це чудовий спосіб уникнути набивання байтів у 2 рази в найгірших випадках. Я використовував подібні, але більш конкретні програми, але це чудово бачити це описано стандартним чином. Я буду використовувати 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), команди Modem AT та датчики Adventech ADAM - одні з найпоширеніших прикладів цього.

Усі ці текстові протоколи / обрамлення мають такі плюси і мінуси:

Про:

  • Легко налагоджувати
  • Легко реалізувати на мові сценаріїв
  • Обладнання можна просто протестувати за допомогою Hyperterminal / minicom
  • Легкий у використанні апаратний засіб (якщо це не дуже маленький мікрофон, як PIC)
  • Це може бути як фіксований розмір кадру, так і різний розмір.
  • Передбачуване кадрування та швидкий час відновлення синхронізації (відновлює в кінці поточного кадру)

Con:

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

Особисто для мене плюси значно переважають мінуси. Легкість налагодження поодинці нараховує 5 балів (так що одна точка вже переважає обидва мінуси).


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

Мені довелося взаємодіяти з обладнанням, яке в минулому надсилало необроблений XML. XML був усе обрамлення там. На щастя, досить легко визначити межі кадру за <xml></xml>тегами. Велике заперечення для мене полягає в тому, що він використовує більше одного байта для обрамлення. Крім того, сам кадр може не бути виправлений, оскільки тег може містити атрибути: <tag foo="bar"></tag>тож вам доведеться буферувати в гіршому випадку, щоб знайти початок кадру.

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


Для кодування тексту існує також база85 , яка має лише 25% накладних витрат замість 33%.
Трейд Дейв

Я вважав би це підмножиною / варіацією 4-го методу.
Євген Ш.

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

4

Те, що ви описуєте як "X-бітове маркування", можна узагальнити до інших кодів, які мають властивість розширювати дані постійною дробою, залишаючи деякі кодові слова вільними для використання в якості роздільників. Часто ці коди надають і інші переваги; На компакт-дисках використовується вісім-чотирнадцять модуляцій , що гарантує максимальну довжину виконання 0 біт між кожним 1. Іншими прикладами є коди блоків виправлення помилок вперед , які також використовують додаткові біти для кодування інформації про виявлення помилок та виправлення.

Іншою системою, про яку ви не згадували, є використання позадіапазонної інформації, наприклад, лінії вибору чіпа, для розмежування транзакцій або пакетів.


Коди виправлення помилок дещо осторонь питання. Вони все одно повинні бути додані до будь-якої з цих схем. Я думаю, що "поза межами смуги", на яку ви посилаєтесь, те саме, що "апаратний контроль потоку"?
Євген Ш.

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

@DaveTweed Ну, це майже все, що я мав на увазі під першою технікою. Або я вас нерозумію?
Євген Ш.

Ні, ти не розумієш; ось про що я говорив. Однак ваш "кон" помиляється - він надійний, і його можна зробити надійним і щодо фактичних помилок передачі.
Трейд Дейва

@DaveTweed Що з часом відновлення? Чи є у вас приклади того, як це можна зробити надійним?
Євген Ш.

3

Інший варіант - це те, що відомо як кодування рядків . Лінійне кодування надає сигналу певні електричні характеристики, які полегшують передачу (збалансований постійний струм та гарантії максимальної тривалості пробігу), і вони підтримують символи управління для обрамлення та синхронізації годин. Лінійні коди використовуються у всіх сучасних серійних протоколах швидкості - 10M, 100M, 1G та 10G Ethernet, serial ATA, FireWire, USB 3, PCIe тощо. Загальні коди ліній: 8b / 10b , 64b / 66b та 128b / 130b. Існують і більш прості коди ліній, які не надають інформацію про обрамлення, лише баланс постійного струму та синхронізацію годин. Прикладами цього є Мачестер і NRZ. Ви, ймовірно, хочете використовувати 8b / 10b, якщо хочете швидко синхронізувати; інші коди рядків не розроблені для синхронізації. Використання кодового рядка, подібного до запропонованого вище, вимагатиме використання спеціального обладнання для передачі та прийому.

Що стосується Вашого варіанту 5, стандартний серій RS232 повинен підтримувати надсилання та отримання перерв, коли лінія простоює пару байтів. Однак це може підтримуватися не у всіх системах.

Як правило, найпростіший та найнадійніший метод обрамлення - це ваш варіант 1 у поєднанні з полем довжини та простим контролем CRC або контрольної суми. Процедура декодування проста: відкиньте байти, поки не отримаєте початковий байт, прочитайте поле довжини, дочекайтеся всього кадру, перевірте контрольну суму, збережіть, чи добре. Якщо контрольна сума погана, починайте відкидати байти з буфера, поки не отримаєте початковий байт і повторіть. Основна проблема цієї методики - це пошук байта кадру, який насправді не є початком байта кадру. Щоб полегшити цю проблему, одна методика полягає в тому, щоб вийти з байтів з тим же значенням, що і початок байта кадру з іншим керуючим символом, а потім змінити байт, що вийшов, щоб він отримав інше значення. У цьому випадку вам також доведеться зробити те ж саме з новим контрольним байтом.


Це те саме, що відповідь Ніка Джонсона.
Трейд Дейв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.