У нас в отделе 12 разных бинарных протоколов, мы решили сесть и разработать один универсальный протокол. Теперь у нас в отделе 13 разных бинарных протоколов.

Думаю каждому embedder-у известен этот бородатый анекдот.

В этом тексте я бы хотел рассказать про простой бинарный протокол, который я сам придумал много лет тому назад для всяческих практических нужд при разработке и тестировании приборов на микроконтроллерах. Я назвал этот протокол TBFP (Trivial Binary Frame Protocol).

Протокол TBFP обычно нужен в целях тестирования интерфейсов: BLE, RS485, LoRa, RS232 , GFSK, UWB, CAN, UART, LIN, 1wire , 100 BASE-T1 и т. п. Обычно этот протокол был нужен чисто для временных нагрузочных тестов интерфейсов, проведения испытаний, отладки оборудования, прозвонки кабелей, для тестов на потерю данных и прочее. Не более того. По сути TBFP это временный и кустарный аналог ICMP.

На самом деле я не очень люблю бинарные протоколы. Предпочитаю текстовые CLI-подобные протоколы. Однако CLI не всегда влезает в микроконтроллеры с экстремально малыми ресурсами. Либо есть ограничение на трафик. Поэтому и приходится иметь на низком старте какой-то бинарный протокол для отладки прошивки.

TBFP это master-slave протокол. Общение диалоговое: запрос, ответ.

Теоретический минимум

Пакет - массив байт, в котором прописана бинарная структура известного типа данных

Little-Endian - размещение в памяти переменных младший байтом вперед.

payload - полезные данные, которые передает пакет

Преамбула - константный данные в начале пакета, чтобы приемник мог понять, что вот начинается протокольный пакет. Передовая часть пакета.

Туннелирование - это когда бинарный протокол в области payload передает пакеты самого себя.

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

Требование к протоколу

--протокол должен быть бинарным
--протокол должен быть простым до предела. Чтобы просто нечему было ломаться.
--У пакета должна быть преамбула (синхробайты)
--Преамбула должна быть параметризируемая
--Должен быть порядковый номер пакета (Sequence Number). Разрядностью как минимум 16 бит.
--Должно быть поле, которое отвечает за длину полезных данных
--Многобайтовые поля в заголовках следует передавать в формате Little Endian
--В пакете должна быть контрольная сумма CRC
--Контрольная сумма должна быть в конце пакета
--Должен быть идентификатор типа полезных данных
--Должно быть подтверждение принятого пакета. Которое можно и отключить.
--Должна быть повторная отправка в случае отсутствия подтверждения (ReTx). Которую можно отключить
--Пакетная синхронизация должна производится по преамбуле
--Пакетная синхронизация должна производится также по time-stamp(у)
--Должен быть периодический Hello пакет для каждой node(ы) (keep alive messages)
--Сторожевой таймер на потерю соединения
--Протокол должен позволять обновлять прошивку, перезагружать плату, настраивать RTC, читать-писать физическую память, передавать пакеты самого себя и прочее. В общем позволять делать с электронной платой абсолютно всё.

Почему требования кристаллизировались именно такие можно почитать в тексте тут. Теперь обо всем по порядку...

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

1) Преамбула (1 байт)

Преамбула нужна, чтобы программный конечный автомат приема мог выхватывать пакеты из потока байтов. Чтобы на принимаемой стороне прошивка могла выхватить начало пакета. Поэтому в структуре кадра заложена преамбула. Желательно, чтобы значение преамбулы было уникальное. Благодаря преамбуле можно выцепить кандидатов в пакеты из потока случайных байт. Преамбула позволяет не проверять CRC по каждому смещению, а отбросить те данные, где точно не может быть начала пакета.

При этом преамбулу следует параметризировать так как должна быть предусмотрена возможность туннелирования пакетов одного и того же протокола. То есть матрешка TBFP пакетов из одного и того же протокола. Это особенно полезно когда физика трансивера за раз может отправить только N байт (256 байт), а сам пакет, например, M=2N байт ( или 1024 байт). При этом получается N < M. В этом случае маленькими TBFP пакетами передается большой TBFP пакет чисто как поток байтов, только уже в payload-е.

2) Флаги пакета (1 байт)

Каждый пакет имеет поле флагов

Битовое поле

Размер
поля, bit

Биты

Пояснение

lifetime

4

3-0

Время жизни

reserved

1

5

Зарезервировано

response

1

4

Этот бит говорит, что это ответный пакет

crc8_check_need

1

6

Нужно ли проверять CRC

ack_need

1

7

Нужно ли отвечать что принял

Битовое поле lifetime нужно только в тех случаях, когда интерфейсом является какой-то беспроводной интерфейс. И то не всегда. Например LoRa, UWB, BLE, GFSK, IR и т.п. При положительном lifetime устройство должно ретранслировать пакет уменьшив lifetime на единицу. Это позволяет кратно увеличить дальнобойность радиосвязи. В случае проводных интерфейсов lifetime надо просто обнулить.

Бит ACK нужен для беспроводных интерфейсов. Это повысит надежность передачи данных.

3) Номер пакета SN (2 байта)

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

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

4) Размер полезной нагрузки (2 байта)

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

5) Идентификатор полезной нагрузки (1 байт)

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

payload_id

Пояснение

0x01

Прыгнуть исполнять код по адресу, который указан в payload

0xFC

Пакет для чтения или записи памяти

0x41

Пакет подтверждения

0x43

Пакет передачи текста

0x44

Пакет передачи команды для CLI

0x54

Внутри такой же TBFP пакет. Матрёшка.

0x91

Внутри структура нажатой в клавиатуре кнопки

0xD3

RTCM3 GNSS поправки

0x67

внутри DECAWAVE пакет

0x51

Ping пакет

0x90

ответный pong пакет для команды ping

....

остальные значения зарезервированы

6) Непосредственно полезные данные (от нуля до 0xFFFF байт)

Так как поле задающее размер имеет разрядность 16 бит, то максимум можно передать 65535 байт. Ограничение задается только размером RAM памяти на принимаемой стороне. Этого более чем достаточно для микроконтроллерных прошивок. При этом допускаются и пакеты с нулевым значением payload. Такие пакеты имеют размер всего 8 байт, что позволяет их уместить в одно CAN сообщение. Пакеты нулевого payload можно использовать как раз для hi-load тестов на предмет потери отдельных промежуточных пакетов и нарушения непрерывности потока данных.

7) Контрольная сумма CRC8 (1 байт)

Чтобы защитить данные добавлена контрольная сумма. Как ее вычилсять решать Вам. Есть множество алгоритмов вычисления CRC8. Благодаря CRC на принимаемой стороне программа сможет выявить факт повреждения данных по пути от передатчика к приемнику. CRC не случайно помещена в конец. Это позволит проще вычислять СRС, захватывая как данные, так и сам заголовок.

Алгоритмы поведения протокола TBFP

Мастер должен периодически опрашивать каждую Node(у) даже если нет PayLoad для этой Node(ы). (Требование ISO-26262). Это позволит убедиться, что есть link. То есть, что провода не оторвались, разъёмы не расшатались, софт не завис и тому подобное. В случае пропадания link-а сгенерировать событие аварии и предложить предпринять какие-никаеие меры по ремонту сети. Это называется blink пакеты. Ещё называют Hello пакеты или heart beat пакеты.

На стороне приемника каждый раз, когда приходит любой TBFP пакет надо обнулять программный сторожевой таймер, который работает для этого конкретного соединения. Этот таймер считает вверх. Если сторожевой таймер досчитал до определенного таймаута (условно 10 секунд), то надо выдать в главную консоль управления предупреждение, что возможно что-то не так с link(ом).

Пакетную синхронизацию делать по time-out-у. То есть после продолжительного молчания на шине приемник просто сбрасывает конечный автомат приема в первоначальное состояние и ожидает новой преамбулы от нового пакета.

Достоинства

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

++Структура пакета не меняется от того какие задачи он решает. Даже ответный пакет имеет ту же самую структуру.

Недостатки

--Короткая преамбула. Могут возникать ложные срабатывания при синтаксическом разборе пакетов из потока байт

--Не хватает поля версии самого протокола. Чтобы обеспечить связь со старым оборудованием.

--Нет шифрования.

Итог

Удалось придумать простой бинарный протокол для тестировочных целей при разработке прошивок микроконтроллеров.

Словарь

Сокращение

Расшифровка

CRC

Cyclic redundancy check

RTC

real-time clock

CLI

Command-line interface

TBFP

Trivial Binary Frame Protocol

CAN

controller area network

Источники

Вопрос��

--В каких бинарных протоколах есть поле порядковый номер пакета разрядностью минимум 16 бит?

--В каких бинарных протоколах есть порядковый номер передаваемого пакета?

--Существую ли бинарные протоколы реализованные аппаратно?

Only registered users can participate in poll. Log in, please.
Вы писали какой-нибудь свой бинарный протокол?
72.22%да13
27.78%нет5
18 users voted. 1 user abstained.
Only registered users can participate in poll. Log in, please.
Вы делали туннелирование протоколов?
55.56%да5
44.44%нет4
9 users voted. Nobody abstained.