Связь RS485 Multimaster с предотвращением столкновений

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

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

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

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

Какому пути мне следовать? Можете ли вы порекомендовать какой-либо из предложенных мной путей? Есть ли лучший способ реализовать этот тип коммуникации?

, 👍3


4 ответа


Лучший ответ:

6

Предотвращение столкновений — сложная тема. Полностью избежать столкновений невозможно. Всё, что можно сделать, — это научиться а) обнаруживать их и б) восстанавливаться после них.

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

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

Это 100% гарантированный способ обнаружения коллизии в момент её возникновения. Если вы знаете, что отправленный вами пакет не дошёл до сети должным образом, значит, произошла коллизия.

Более простой способ — наблюдать за автобусом перед отправкой. Если в течение определённого времени нет никаких признаков движения, можно предположить, что автобус в данный момент тихий. Затем можно попробовать отправить данные. Однако вы не знаете, дошли ли данные успешно, поскольку что-то другое могло решить, что автобус достаточно долго был тихим, чтобы отправить данные.

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

Что делать при обнаружении коллизии (или отсутствии подтверждения) тоже довольно сложно. Приходится подождать и повторить отправку. Но если два устройства ждут повторной отправки, как можно быть уверенным, что они не будут ждать одновременно и не столкнутся снова?

Необходимо убедиться, что оба устройства задерживаются на случайный период, и что этот период действительно является случайным. Использование функции random() по умолчанию, если только вы не можете использовать для её создания действительно случайный источник энтропии, сгенерирует одинаковую последовательность чисел для всех устройств, так что каждое устройство получит одно и то же «случайное» число. Это приводит к коллизиям.

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

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

  • Предотвращение коллизий реализуется путём назначения приоритета сообщениям. Узел не начнёт передачу, пока шина не будет простаивать в течение времени, определяемого приоритетом. Обратите внимание, что наивысший приоритет присваивается сообщениям с наименьшим номером приоритета.

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

Он ждет, пока шина освободится от трафика в течение определенного периода времени (время варьируется в зависимости от приоритета сообщения), и ожидает, что сообщение будет отправлено обратно в форме ACK.

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

,

5

Готовое решение из коробки задокументировано в https://github.com/MichaelJonker/HardwareSerialRS485/wiki (эта вики описывает библиотеку, на которую вы ссылаетесь).

Проблема довольно сложная: предотвращение столкновений, обнаружение столкновений и повторная отправка сообщений, приоритет сообщений и фильтрация сообщений. Но библиотека решает всё. Программное обеспечение даже предоставляет решение для сопряжения хоста (ПК или Android) с RS485, обеспечивая те же функции.

В «понимании библиотеки» есть два аспекта: как она работает и как заставить ее работать.

Сама библиотека нетривиальна (потому что проблема тоже нетривиальна), поэтому понять, как она работает, тоже нетривиально (Маженко в своём ответе дал хорошее функциональное описание некоторых функций, реализуемых библиотекой. Реальная реализация имеет дополнительные сложности из-за прямого взаимодействия с USART на уровне прерываний). Полагаю, вам неинтересно (в первую очередь) знать, как она работает, в таких подробностях.

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

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

Предпочтительный способ связаться со мной по этим вопросам — создать задачу на странице моего проекта на Github:

https://github.com/MichaelJonker/HardwareSerialRS485/issues

Кстати, один из выпусков посвящен той же теме.

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

С наилучшими пожеланиями

Майкл Йонкер

,

-1

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

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

https://github.com/dukelec/cdbus_doc (Введение)
https://github.com/dukelec/cdbus_ip (Сведения о протоколе и IP-ядро ПЛИС)

cv_demo

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

Обновление: подключите контроллер CDCTL-Bx к Arduino: arduino и cdctl

,

2

Я сделал пост о вращающихся мастерах здесь, на Arduino Stack Exchange.

Основная идея заключается в том, что можно настроить несколько устройств Arduino так, чтобы они по очереди становились ведущими, ожидая своей «очереди» в определенной последовательности, с возможностью восстановления в случае, если одно из устройств не отвечает.

,