Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Простой протокольчик под RS-485
Шарага > Soft - НЕ железо > Программирование МК
_pasha
Всем привет!
Пейсательство протоколов - вещь неумная, но модбас и извращения вокруг 9-битных форматов достали настолько, что нужен новый.
Предлагаю. Критика приветствуется.
CODE

1. Физ уровень
RS-485, 8-N-1, 8-N-2
Любая скорость.
При сопряжении - стандартная 300/1200/2400/4800/9600/19200/38400/57600/115200

2. Структура сети
Одномастерная сеть.
Число устройств в сегменте - не более 32, включая мастер.
Единица обмена - пакет.
Мастер(клиент) отправляет пакет данных серверу и ждет ответа.
Сервер отправляет ответный пакет.
При этом остальные серверы могут быть настроены мастером на режим "прослушивания" для
исключения избыточного обмена и организации каналов прямого доступа.

При широковещательной передаче мастер отправляет пакет, ответы от серверов запрещены.

3. Структура пакета
Максимальная длина пакета - 32 символа.
Последний байт пакета содержит сигнатуру CRC7 (по методу из библиотек Linux), старший бит
которой используется как идентификатор пакета PID, т.е сервер, ответивший клиенту, должен
проинвертировать принятый PID-бит и отправить его обратно. Сигнатурой защищены все остальные
байты пакета.

Разделитель пакетов - сигнал "BREAK" - длинная строка нулей, принимаемая всеми USART как
frame error. Таким образом, жесткие ограничения на межсимвольный интервал отсутствуют.

Первый байт пакета содержит управляющее слово(УС), если пакет посылается клиентом, либо
статус операции, если пакет посылается сервером. Множества значений УС и статуса не пересекаются,
что позволяет однозначно распознавать, пришел пакет от клиента или от сервера.

Поддерживаются операции чтения, записи и спец. команды.
Чтение или запись производится по 16-битному указателю адреса org, значение которого можно
изменить явно или неявно, в зависимости от УС.
***************************************************
Ответ сервера (статус операции)
0x00 st_OK выполнено
0x20 st_NAK не выполнено
0x40 st_ILADDR недопустимый адрес
0x60 st_ILDATA недопустимые данные


Сервер обрабатывает пакет только в том случае, если успешно выполнена проверка сигнатуры CRC7

***************************************************
Биты управляющего слова: CCCAAAAA, где ААААА - адрес сервера ССС-код команды.
----
000 - чтение с постинкрементом указателя.
001 - чтение с преддекрементом указателя.

00*ААААА [size] [org low] [org high] <crc7>
Если длина пакета 2 или 4 байта, размер принимаемого блока определен неявно исходя из значения org.
Если длина пакета 3 или 5 байт - указан явно размер блока
Если длина пакета 4 или 5 байт - указан и размер и указатель.
Если размер = 0, то операция чтения не производится.

----
010 - отмена установки адреса "слушателя"
011 - установка адреса "слушателя"
01*ААААА [пакет]

Данные команды могут применяться для организации обмена между серверами без перенапрвления данных
клиентом. Широковещательные передачи запрещены.
Если клиент отправил пустой пакет (УС+crc7) - адресуемый сервер обязан ответить пустым пакетом,
содержащим статус.

Если пакет не пустой, адресуемые устройства не отвечают.
Данные управляющие слова расцениваются как эскейп-коды. Если сервер принял непустой пакет,
первый байт которого - УС 010ААААА или 011ААААА - следующий байт в пакете будет также
управляющим словом, если и эти байты будут иметь значения 010ААААА или 011ААААА, следующий байт
опять-таки будет управляющим словом итд. Таким образом можно назначить или отменить серверы-
приемники ответов от других серверов.
Режим "слушателя" означает, что сервер принимает и обрабатывает все пакеты со статусом st_OK,
идущие от другого сервера.


----
100 - запись с постинкрементом указателя.
101 - запись с преддекрементом указателя.
110 - запись с постинкрементом указателя и установкой указателя
111 - запись с преддекрементом указателя и установкой указателя

Размер блока данных неявно задан длиной пакета.

10*ААААА [data0 [...dataN]] crc7
Если данных нет, выполняется модификация указателя

11*ААААА <org low> <org high> [...dataN] crc7
Если данных нет, устанавливается указатель, но модификация его не выполняется.
Широковещательные операции записи (ААААА=0) выполняются без ответа сервера.
******************************************************************************

Т.к. при ошибках пакета сервер не отправляет ответ, обнаружение ошибки CRC выполняется
клиентом по тайм-ауту.

В режиме полудуплексной связи по RS-485 сигнал "BREAK" используется также для "продувки"
канала при переключении драйвера на передачу.
one_man_show
На вскидку пара замечаний:
1) на мой взгляд, на современном этапе не стоит к описанию протокола прикладывать тербования по физическому уровню
2) использования кодировки на уровне бит не есть гуд

По поводу п.1) такая привязка ограничивает применение протокола, что было приемлемо лет 20 назад, сейчас принято использовать хорошо развитый протокол на любом физическом уровне без ограничений.

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


Цитата
модбас и извращения вокруг 9-битных форматов достали

Используйте готовые библиотеки. Широко используемый протокол тем и хорош, что для него уже давно все есть в готовом виде. даже для сверхвысокоуровнего языка Ruby есть поддержка Modbus, про остальыне языки и применения даже не говорю.
_pasha
Цитата(one_man_show @ 12.8.2010, 11:13) *
1)

Не возражаю. Просто там заложена фича, повышающая производительность сети именно на полудуплексном 485-м. Я имею ввиду Frame error для разделения пакетов

Цитата
2) использования кодировки на уровне бит не есть гуд


Это не битовая кодировка, это привязка к диапазонам или множествам значений. Битовых операций минимум миниморум.

Цитата
Используйте готовые библиотеки.


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

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

Создание своего проприетарного протокола вызывает необходимость написания кучи вспомогательного софта с нуля. Например, для модбас есть масса приложений и боевых, есть куча вариантов OPC-серверов, куча готовых библиотек. Для нового портокола пока ничего нет, все нужно делать самому. Вопрос: ради чего?
MrYuran
Вот единственное расширение к модбасу, которое мне бы очень хотелось, это автонумерация слейвов.
Остальное в целом вполне устраивает. Главное, что устраивает - стандартность и множество готового инструментария, как свободного, так и слегка платного.
one_man_show
Цитата
Вот единственное расширение к модбасу, которое мне бы очень хотелось, это автонумерация слейвов.

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

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

Для того, чтобы предоставить пользователю побольше вкусностей, мы по-своему используем функцию 17 (0x11) Read System Info. Эта функция может быть реализована на свой лад, что мы и делаем. В возвращаемой информации даем максимум того, что могло бы позволить делат подобие plug-and-play, повторю подобие.
Прохожий
Цитата(one_man_show @ 12.8.2010, 20:14) *
На мой взгляд, проблемы нет, если устройства подключать по одиночке. Делаем каждому новому устройству (неинициированному) недопустимый адрес 255. Подключаем устройство, прошиваем необходимый (определенный в БД или еще где-то в программе) адрес, потом включаем в общую сеть.

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

Для того, чтобы предоставить пользователю побольше вкусностей, мы по-своему используем функцию 17 (0x11) Read System Info. Эта функция может быть реализована на свой лад, что мы и делаем. В возвращаемой информации даем максимум того, что могло бы позволить делат подобие plug-and-play, повторю подобие.

Очень разумный подход.
У меня, аналогичный.
Правда, исходя из опыта применения считаю необходимым задавать адреса с помощью элементарных перемычек.
Плюс подробнейшая инструкция с рисунками.
Господа разработчики!
Уважайте труд тех, кто будет потом обслуживать и эксплуатировать ваши девайсы.
Использовать автоматическую раздачу адресов нельзя - оборудование железное, а те, кто на нем работает из мяса и костей.
Кроме этого, чем меньше инструмента на объекте, тем лучше.
Компьютер там совершенно лишний.
И слесарь КИПиА по должностной инструкции вовсе не обязан уметь пользоваться вашим вспомогательным софтом.
MrYuran
Цитата(one_man_show @ 12.8.2010, 20:14) *
На мой взгляд, проблемы нет, если устройства подключать по одиночке. Делаем каждому новому устройству (неинициированному) недопустимый адрес 255. Подключаем устройство, прошиваем необходимый (определенный в БД или еще где-то в программе) адрес, потом включаем в общую сеть.

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


Цитата(Огурцов @ 12.8.2010, 23:57) *
Спорно. Автоматически в онлайне назначаются _короткие_ (8 бит) адреса, которые вычисляются по фиксированным уникальным длинным (128 бит). Так что можно сказать, что адрес как таковой вовсе и не назначается автоматически, а задается жестко в момент настройки.

Вот, что-то типа этого и хочется.
Некое подобие уникального МАК-адреса, например, вида _тип_:_класс_:_серийник_
Кто хочет, пусть вручную раздаёт. Но иногда нужно, чтобы всё происходило автоматически.

Теперь, насчёт самописных протоколов.
Если нет принципиальных противоречий, нужно пользоваться стандартными протоколами.
Я уже _ВОТ_ТАК_ наелся этой самодеятельности wacko.gif

Приведу пример, где уникальный протокол был оправдан.
Был у нас РЛК вертолётного базирования (и сейчас есть, возможно, даже на вооружение принят)
ФАР состоит из 512 твердотельных ППМ, управление через RS-485.
Адресация либо индивидуальная (для технологических целей), либо групповая (синхронно выставить номер луча, например).

Адреса раздавались динамически при включении питания. На случай, если какой-то модуль отсутствует, либо неисправен.
Но однажды пуд говна пришлось съесть, из-за маленькой неточности в протоколе... И выяснилось, как обычно, за день до сдачи, на кону этап стоимостью 180млн руб, над душой всё техническое руководство, вплоть до главного инженера...
one_man_show
Цитата
Автоматически в онлайне назначаются _короткие_ (8 бит) адреса, которые вычисляются по фиксированным уникальным длинным (128 бит). Так что можно сказать, что адрес как таковой вовсе и не назначается автоматически, а задается жестко в момент настройки.

Мы так делаем, предварительно установив в оборудование 8-миногую микросхемку DS28CN01, которая дает уникальный 64-битный номер. Микросхемка имеет интерфейс I2C, места практически не занимает в корпусе uSOP, имеет дополнительно 1К EEPROM. Один раз ее объездить в железе и софте, дальше можно использовать по-умолчанию в своих адресуемых железках одно удовольствие: там где нужен MAC-адрес, делаем на основе зашитого номера, там где нужен какй-то другой адрес, порождаем опять из зашитого и т.д.

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


На почти математическом языке: имеется уникальный идентификатор, допустим, 64 или 128 бит.
Далее, имеется некий 8-разрядный регистр, в который мастер пишет в режиме широковещательной передачи. Необходимо из этого добра сделать генерацию 255 уникальных адресов так чтобы вероятность совпадения 31 из них была минимальной.

Цитата
Я предлагал уже, на элхе. Что-то тогда никто не поддержал.


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


Цитата
Теперь, насчёт самописных протоколов.
Если нет принципиальных противоречий, нужно пользоваться стандартными протоколами.
Я уже _ВОТ_ТАК_ наелся этой самодеятельности wacko.gif

Я тоже. Но слежу за уровнем простоты -при превышении некоторого порога - нуегонах.

Цитата
Лучше желание не отбивать, раз хочется попробовать что-то новое.

Ага, так я других и послушал! wink.gif
MrYuran
Цитата(_pasha @ 13.8.2010, 14:08) *
На почти математическом языке: имеется уникальный идентификатор, допустим, 64 или 128 бит.
Далее, имеется некий 8-разрядный регистр, в который мастер пишет в режиме широковещательной передачи. Необходимо из этого добра сделать генерацию 255 уникальных адресов так чтобы вероятность совпадения 31 из них была минимальной.

Не, не так...
на широковещательную команду запроса ID слейвы должны отвечать с квантованной рнд-задержкой.
Если кто-то начал вещать, остальные перезапускают свою задержку.
Таким образом, получаем в ответ цепочку пакетов с уникальными ID.
Далее, мастер говорит: эй, ты, 04:06:0126, назначаю тебе адрес 0х01. Аминь.
Ну и так далее.

Плюс, должна быть команда сброса адреса. Правда, с защитой от дурака
_pasha
Цитата(MrYuran @ 13.8.2010, 12:25) *
эй, ты, 04:06:0126, назначаю тебе адрес 0х01. Аминь.

Когда-то рассматривался вариант такой.
Что такое <BREAK>?
С точки зрения моноканала, все устройства его генерящие, не вызывают катастрофических последствий для драйвера, т.к. линии витой пары под одинаковым потенциалом.
Что такое не- <BREAK>? Вот и именно! Это - выключенный передатчик. На такую физику натягивается процедура назначения как а 1wire
one_man_show
Павел, Вам же советовали, уберите физические зависимости из протокола, иначе он зависнет, не родившись
stells
Цитата(MrYuran @ 13.8.2010, 14:25) *
Не, не так...
на широковещательную команду запроса ID слейвы должны отвечать с квантованной рнд-задержкой.
Если кто-то начал вещать, остальные перезапускают свою задержку.
Таким образом, получаем в ответ цепочку пакетов с уникальными ID.

когда-то один коллега предложил интересную процедуру раздачи приоритетов/адресов в мультимастерной сети... при включении все устройства начинают самотестирование, по окончании которого каждый дергает шину. кто дернул первый, тот мастер с первым адресом, и т.д.. по окончании некоторого тайм-аута начинается сеанс, кто не успел - тот опоздал (считается неисправным)
MrYuran
Цитата(stells @ 13.8.2010, 14:49) *
по окончании некоторого тайм-аута начинается сеанс, кто не успел - тот опоздал (считается неисправным)

Не совсем понятно... а если они не одновременно запустились?
stells
Цитата(MrYuran @ 13.8.2010, 15:00) *
Не совсем понятно... а если они не одновременно запустились?

то была бортовая система с усеченным МКИО, питание одновременно подавалось на устройства. плюс решалась задача живучести - мастером в сети мог стать любой
_pasha
Цитата(one_man_show @ 13.8.2010, 12:47) *
Павел, Вам же советовали, уберите физические зависимости из протокола, иначе он зависнет, не родившись


Зависимость 1(одна) - ограничение на число девайсов в сегменте
Разделитель пакетов BREAK не может рассматриваться как зависимость. Это проблемы физ. уровня.
Пожалста - виртуальных сегментов сколько угодно можно создавать - это проблемы транспортного уровня, никоим образом нас не трогают.
Между сегментами могут быть репитеры - с одного конца он мастер, с другого - слейв. Все равно получается одномастерная сеть.
Устройства используют чтение/запись по логическим адресам.
ГДЕ ЗАВИСИМОСТИ?
В структуре пакета таковых нет, в отличие, кстати, от модбасов, даже профибасов и прочих гондурасов с прописанными временнЫми интервалами. Тот же LIN, DALI и не знаю чего еще - нахрена, спрашивается в протокол вбивать автонастройку скорости передачи, еесли это можно сделать записью в какой-нить виртуальный reg/nul ? А?
one_man_show
ОК, тогда, чтобы попробовать Ваш протокол, дайте сишную библиотечки с примером использования, чтобы можно было потестить. Иначе будет только один треп.
Кроме того, придумайте ему название, чтобы легче как-то было к нему адресоваться.
_pasha
Цитата(one_man_show @ 13.8.2010, 13:41) *
придумайте ему название, чтобы легче как-то было к нему адресоваться.

Может, кто-то что-нить очень близкое находил или создавал?
Так, конечно, название типа RootBus напрашивается.
_pasha
Цитата(Огурцов @ 13.8.2010, 16:29) *
Так там несколько уровней абстракции. Самый нижний - проще некуда.

Ну блин, сложно же! Мне, все-же склероз не изменил. Не нравится, дубль 2. dash1.gif
_pasha
Рассматривается вариант формата пакета, в котором байт адреса - не первый байт, а предпоследний, и он реально не передается, а принимающая сторона может восстановить его 3/4/8 вариантами проверок CRC. Конечно, 3 варианта лучше.
Это все позволяет снизить вдвое длину буфера пакета (до 16 байт) и впихивать его ужЕ во всякую мелочь без проблем.
Это и будет протокольчег.
Если у кого-то есть аргументы, дескать, использовать неявные данные - это ненадежно, отпишитесь плз.
_pasha
Цитата(Огурцов @ 14.8.2010, 9:54) *
Конечно ненадежно. В случае ошибки при передаче пакета при восстановлении адреса окажется, что пакет адресован другому слейву. Так вот он его и исполнит.

Понятно, что if(CRC_valid && ADDR_match)receive() и if(CRC_valid){if(ADDR_match) receive();} это в вобщем случае не одно и то же.
Проверил. Вероятность ошибки не меняется и для случайных одиночных искажений составит
0.78% для пакетов макс длины
0.23% Для пакетов случайной длины
Нажмите для просмотра прикрепленного файла
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2020 IPS, Inc.