Некоторое время назад у нас появился объект, где в техническом задании стояло требование: «Протокол Ethercat». В процессе поиска информации я полез на хабр и с удивлением обнаружил, что там разбора этого протокола нет. Да и вообще информация о нем довольно фрагментирована (забегая вперед — я просто не там искал). Проект мы сделали, а эта статья — для «более молодого меня», попытка сэкономить время кому-нибудь еще, кто собирается использовать или просто интересуется этим протоколом.
Единственный нюанс — я опишу довольно низкий уровень работы протокола (Data Layer в терминологии EtherCAT). Потому что именно он нам понадобился и им мы ограничились. Неописанными (пока?) останутся Application Layer протоколы типа CanOPEN-over-EtherCAT (CoE).
EtherCAT одним предложением
Уж это нагуглить легко, но для порядка скажу своими словами. EtherCAT – промышленная шина обмена с высокой пропускной способностью (100 Мбит/c) и скоростью реакции (единицы микросекунд для десятков устройств на одной линии разнесенных на десятки метров), использующая на низком уровне Ethernet, но при этом отличающаяся от обычной концепции Ethernet «вопрос-ответ».
Обычно в Ethernet порядок работы такой — устройство принимает пакет целиком, обрабатывает его, потом формирует и отсылает ответ. А EtherCAT работает так — устройство принимает пакет, и как только оно приняло заголовок (и поняло что это EtherCAT-пакет) оно начинает этот пакет отсылать дальше, в процессе отсылки заменяя те биты пакета в которых оно должно отослать информацию на нужные.
В результате мастер может отослать килобайтный пакет со скоростями для ста моторчиков и пустыми местами для ста датчиков, каждый из датчиков запишет в него свои данные а каждый моторчик прочитает свои, пакет вернется к мастеру и всё это займет примерно микросекунду. При этом мастер — обычный компьютер или микроконтроллер, а устройства соединены витой парой. Правда есть и подвох — хоть это и витая пара, а обычными свитчами топологию не организовать, а главное — мастером конечно может быть любой кто может отсылать UDP, а вот слейва обрабатывающего EtherCAT пакеты на ходу из обычного микроконтроллера не сделать. EtherCAT-слейв — специализированное устройство, и да — оно не может работать со всякими ARP и вообще отсылать и принимать произвольные пакеты. Только EtherCAT-пакеты.
Как пощупать
В нашем случае и "слейв" и мастер были нашими устройствами, соответственно я даю рекомендации для нашего случая. Возможно, у Вас уже есть либо мастер либо слейв.
Когда мы стали искать на чем сделать слейва, мы нашли два легкодоступных варианта — микроконтроллеры Infineon XMC4300\4800 и модули Microchip LAN9252. Мы выбрали первый (во многом из-за доступности отладочной платы), но как я теперь понимаю особой разницы что использовать нет — взаимодействие с модулем EtherCAT стандартизировано (об этом позже).
Так что для тех кто хочет пощупать EtherCAT вживую я рекомендую взять пару отладочных плат XMC4800 Relax EtherCAT Kit. Почему пару? Чтобы понять как работает топология эзеркада — с одной платой вроде бы всё просто, а вот две (и роутер или свитч) уже дают возможность для экспериментов.
Кроме слейва потребуется мастер.
Тут есть такие варианты:
- Взять софт от Beckhoff, TwinCAT, поставить его на комп и начать разбираться. Плюсы — ну, если вы не напутаете с настройкой он уж точно заработает из коробки. Так что если связи нет — то проблема на стороне слейва. Минусы — там много того что можно настроить, но при этом контроля за тем что происходит все равно нет. Поэтому я использовал его на самом начальном этапе. Как только подключил вторую плату и вместо того чтобы нормально заработать посыпались непонятные ошибки, стало ясно что нужен свой мастер.
- Open source реализация, https://github.com/OpenEtherCATsociety/SOEM. Мы адаптировали его к STM32F207 и FreeRTOS и всё работает. Но, наверное, можно и на компе запустить. С моими теперешними знаниями я мог бы и сам мастера сделать, но на начальном этапе очень важно чтоб был работающий код в котором ты уверен.
Отслеживать что происходит можно обычным снифером WireShark. Он и тип пакетов отображает, и содержимое, и время до микросекунд.
Ну и документация, куда же без нее. Кому-то, возможно, достаточно просто взгляда на код опенсорсной реализации, но я так и не смог самостоятельно разобраться во всех этих wkc2 и APWRw. Все прояснилось и встало на свои места когда я просмотрел один единственный файлик: EtherCAT_Communication_EN.pdf. Правда есть нюанс — с сайта ethercat.org его так просто не скачать. Надо получить членство в EtherCAT association. Правда вы можете попробовать его нагуглить, либо я могу его строго по секрету отправить его вам (лично я не подписывал NDA, но с Beckhoff переписывался мой коллега и вполне возможно от имени нашей фирмы он что-то подписывал), ну или стать таки членом этой ассоциации, это бесплатно.
Если что — данная статья в значительной мере является пересказом содержимого данного документа, схемы тоже взяты оттуда.
Минуточку, что за членство
Да-да, как утверждается в документации, да и в нагугленных туторилалах случайные люди не могут делать устройств поддерживающих Ethercat, они должны стать членами ассоциации Ethercat. После это они смогут скачать документацию и стандарты, а также получат свой уникальный Vendor ID. Причем членство бесплатно, надо просто написать им и ответить на пару вопросов о своей компании. https://ethercat.org/en/membership_application.html
В итоге мы стали членами этой ассоциации и у нас есть свой VendorID! Правда польза от этого ограничилась вышеупомянутым файлом, даже VendorID мы в итоге не используем. Так что никто не мешает пользоваться протоколом и без этих формальностей — разве что "шашечек", т.е. формального соответствия стандарту EtherCAT не будет. Кому-то это важно, кому-то — нет.
Топология ethercat
С предварительными вопросами покончили, теперь собственно к протоколу. Логическая топология езеркат — шина. При этом физическая может быть и звездой и шиной и кольцом и разными комбинациями. Как это работает?
У любого езеркат устройства есть 4 порта, физически у большинства присутствует только 2 (port 0 и port 1). Они соединены вот так:
Словами: когда пакет приходит на порт, он отправляется на следующий порт (по часовой стрелке на картинке). Если этот следующий порт отключен он считается закороченным и пакет пойдет на следующий за ним. После порта 0 расположен ethercat processing unit, который собственно на лету подставляет данные устройства, в остальных случаях пакеты проходят без изменений.
К примеру, если соединить ethercat устройство с мастером одним патчкордом, который будет воткнут ну скажем в port0 слейва, то пакет придет в port 0, пройдет через ethercat processing unit, дальше пройдет по кругу и т.к. три других порта закрыты то вернется через port0 по патчкорду к мастеру.
Дальше мы можем соединить длинную цепочку таких устройств:
и как легко догадаться пакет пришедший первому устройству на port0 пройдет через модуль обработки и дальше в port1, оттуда к следующему устройству на port0 и так до конца цепочки, а т.к. в последнем будет открыт только один порт то он развернется и пройдет через все устройства назад, причем на обратном пути обработки пакета уже не будет (помните? обработчик находится после порта 0) и он будет просто пересылаться.
На фото я соединил мастера с двумя слейвами.
Пока всё просто? Ну да, я забыл сказать — Ethercat на самом деле довольно простой протокол.
Таким образом мы получили шину. Теперь как сделать "кольцо"? Последний слейв надо соединить с мастером. Один нюанс — у мастера должно быть два ethernet порта. Тогда пакеты будут доходить до последнего в цепочке устройства и вместо того чтобы разворачиваться и идти назад сразу уходить мастеру. Зачем это нужно? Ну, это сэкономит сколько-то наносекунд, и кроме того даст возможность работать при одном отказе: Сделали кольцо — пакеты ходят по кругу. Разорвали в любом месте провод или вырубили один из слейвов — у нас получилось две шины и по прежнему есть связь со всем устройствами (кроме вырубленного).
Тут надо сказать, что эта надежность не абсолютная. Например, если слейв сломать так, что физический разрыв кабеля детектироваться не будет, но при этом пакеты уходить не будут — он будет "кушать" пакеты приходящие что на порт1, что на порт 0 и сеть работать не будет. Более реальный вариант — воткнули провод из одного из устройств не в следующее устройство а в обычный свитч понятия не имеющий об этом вашем ethercat. В итоге разрыва кабеля нет, порт открыт и пакеты в него уходят, а назад уже не возвращаются.
В теории, мастер может посылать пакеты типа "4-му слейву закрыть выходной порт" и с их помощью "огородить" проблемное место, а учитывая скорость работы перебор вариантов много времени не займет, но лично мы так не пробовали.
Если нужна нормальная надежность, чтобы при отказе любого числа устройств связь с остальными не терялась, нужна топология "звезда". Обычным для Ethernet способом — воткнув все слейвы в один свитч\роутер ее не организовать. Ведь у слейвов нет IP адреса по определению (они не умеют обрабатывать ARP пакеты), а MAC адрес у них в лучшем случае у всех одинаковый. На помощь приходят слейвы с 4 портами.
Проблема в том, что и у решений от infineon, и у модулей microchip портов только 2.
На картинке от ethercat — вот эти блоки с кучей палочек это соединенные друг с другом блоки Beckhoff с 4-мя ethercat портами:
https://www.beckhoff.com/ru/default.htm?ethercat/ek1100.htm
https://www.beckhoff.com/ru/default.htm?ethercat/ek1122.htm
Без них топологию звезда не сделать, а стоят они не космических конечно денег, но все-таки подороже обычных ethernet switch. Наверное можно их сделать самим на ПЛИС, но это задача не для нашей конторы (во всяком случае пока).
Раз уж затронули тему обычных свитчей\роутеров. С их точки зрения Ethercat пакет — пакет с broadcast целевым MAC адресом, так что они просто разошлют его по всем подключенным портам. Когда к свитчу подключено два слейва или мастер и слейв — все будет ок (свитч будет играть роль просто патчкорда), если подключить, скажем, мастера вместе с двумя слейвами или одного слейв — дело плохо, связи не будет.
Программирование slave — DPRAM
С точки зрения программы, slave устроен типичным для микроконтроллеров способом — есть некая область памяти, мы можем ее читать и писать в нее. В случае с Infineon она маппится на адресное пространство МК, в случае с микрочипом к ней надо обращаться по SPI. Первые 4 килобайта (до 0x1000) — регистры, имеют специальное назначение описанное в стандарте ethercat, дальше идет пользовательская память где будут оказываться те данные которые пришли от мастера и куда надо помещать те данные которые к мастеру уходят.
Конкретно на XMC4800 этой пользовательской памяти 8 килобайт
Интересно что к этой памяти (а значит и настраивать все параметры обмена с помощью регистров) может обращаться как микроконтроллер слейва, так и мастер посылая EtherCAT пакеты. Логично что одновременный доступ двумя сторонами чреват гонками данных, но о том как это решено чуть позже.
Регистры описаны в даташите (в моем случае в даташите на XMC), упомяну самые интересные:
- 0x000 — TYPE = 0x98 (чтобы убедиться что связь вообще есть можно прочитать нулевой байт)
- 0x010 — STATION_ADR, логический адрес узла (мастер может обращаться к конкретному слейву либо с помощью его номера в топологии, либо по вот этому логическому адресу)
- 0x040 — RESET_ECAT, мастер может удаленно перезагрузить засбоивший слейв.
- 0x100 — DL_CONTROL, управление портами (открыт\закрыт)
- 0x110 — DL_STATUS, состояние портов (есть ли линк)
ну и хватит наверное. Подробности в документации.
Программирование master'a — структура ethercat пакета.
Отвлечемся ненадолго от слейва и вернемся к мастеру. От мастера требуется формировать ethercat пакеты заданной структуры и обрабатывать их когда они вернулись от слейва.
Пакеты имеют следующую структуру:
Как видим, пакеты могут иметь либо специализированный формат (тип пакета не TCP и не UDP, а 0x88A4), либо находиться внутри UDP пакета. С точки зрения слейва UDP заголовок просто ничего не значащие байты (адресация слейвов происходит уже внутри ethercat пакета), но зато он позволяет работать с Ethercat пакетами с компьютера — вместо каких-то низкоуровневых пакетов мы формируем и обрабатываем старые добрые UDP датаграммы.
Внутри идет заголовок и энное количество датаграмм:
Каждая датаграмма состоит из заголовка, основной части и важной штуки называемой Working counter (WKC).
Wkc — счетчик слейвов обработавших эту датаграмму. Когда датаграмма проходит через слейва, он инкрементирует wkc если читал ее данные и инкрементирует wkc если писал в нее данные (так что если он и читал и писал данные в одну и ту же датаграмму то wkc будет инкрементирован дважды). Можно, например, послать BRD датаграмму "прочитать нулевой байт" и по счетчику wkc понять сколько всего устройств находится на линии.
- Cmd — тип команды. Сейчас разберем его подробнее, но сначала пробежимся по остальным полям.
- Idx — просто любое число, слейв его не обрабатывает. Мастер может пользоваться им чтобы бороться с дубликатами датаграмм. В реализации SOEM туда пишется номер буфера отправки\приема.
- Address — адрес. Его смысл зависит от Cmd — либо он состоит из двух 16-битных слов называемых ADP и ADO (адрес устройства и адрес памяти внутри устройства), либо из одного логического 32-битного адреса (Logical Addr).
- Len — длина.
- C, R, M, IRQ — в документации есть описание, вкратце — просто оставьте их нулями. SOEM так и делает.
Итак, Cmd. Команды называются буквенными аббревиатурами из двух частей. Числовых кодов я никогда и не знал. BRD есть BRD.
Первая часть аббревиатуры | Расшифровка |
---|---|
APxx | Топологическая адресация. ADP содержит "минус индекс слейва к которому обращаемся". Почему минус? Потому что каждый слейв будет инкрементировать это поле, и тот слейв к которому в этом поле придет 0x0000 обработает датаграмму. |
FPxx | Физическая адресация. Ответит тот слейв у которого STATION_ADR совпадет с полем ADP |
Bxx | Широковещательный пакет. Ответят все слейвы. Ах да, если отвечает несколько слейвов, результат получается логическим OR от их значений. Ну т.е. мастер отправляет в данных нули, а слейвы могут только заменять в пакете данные на единички но не сбрасывать в 0 |
Lxx | Логическая адресация. Подробнее про нее рассказажу в разделе FMMU, но в этом случае в поле адреса находится 32-битный адрес, а слейвы в зависимости от своих настроек пишут или читают данные. Именно этими пакетами идет собственно опрос сотни моторчиков одним пакетом. |
Вторая часть аббревиатуры | Расшифровка |
---|---|
xxRD | Чтение. Выбранные слейвы пишут данные в пакет (а мастер читает) |
xxWR | Запись. Мастер записал данные, а выбранные слейвы их читают |
xxRW | Чтение И запись. Кроме как в виде LRW выглядит как-то глупо, слейв сначала прочитает данные а потом запишет туда же. А вот с логической адресацией это наоборот основной тип датаграмм. |
xxRMW | Один пишет остальные читают. Тот слейв у которого ADP совпал (см. расшифровку первых половин команд) запишет туда данные, а остальные будут читать. Учитывая что есть LRW не слишком полезная штука. |
SyncManager
EtherCAT решает возможные проблемы одновременного доступа к памяти (со стороны МК и со стороны пришедшего по сети пакета) с помощью концепции SyncManagerов.
SyncManager (SM) — специальный блок в модуле ethercat слейва, которому мы указываем на область адресов памяти слейва и он устанавливает особый режим доступа к этой памяти.
Управляются регистрами начиная с 0x800. Каждый SM имеет 8 регистров, т.е. 0x800-0x807 задают SM1, 0x808-0x80F задают SM2 и так далее. Всего у XMC4800 8 SyncManagerов.
В зависимости от настройки бывают двух типов — mailbox и буфер. Кроме того они могут быть настроены на запись либо на чтение. Картинки из презентации поясняют их довольно очевидно, но чтоб не передирать совсем внаглую распишу словами.
Mailbox — предназначены для организации своих протоколов вопрос-ответ поверх ethercat. Один пишет другой ждет, потом другой читает первый ждет.
Buffered — для обмена быстроменяющимися данными. Один все время пишет а второй читает последнюю согласованную копию.
- Mailbox режим Read. Пока слейв не записал данные — читать нельзя (датаграмма чтения вернет WKC=0). Если слейв начал писать (обратился к первому байту но не обратился к последнему байту области) — читать по-прежнему нельзя (датаграмма чтения вернет WKC=0). Как только слейв закончил писать (обратился к последнему байту области) — ящик закрывается и слейв туда писать больше не может (при попытке записи содержимое не изменится), зато мастер может прочитать содержимое датаграммой (WKC вернет 1) и факт этого чтения опять откроет ящик.
- Mailbox режим Write. Работает аналогично. Пока мастер ничего не записал слейв будет читать нули, как только датаграмма с записью прошла слейв сможет читать данные, и пока он не прочитает их до конца (не обратиться к последнему байту области) новые датаграммы записи будут возвращать WKC=0.
- Buffered режим Read. Это более хитрый режим предназначенный для обмена быстроменяющимися данными. В этом режим слейв всегда может данные не заботясь о том прочитал мастер их или нет, а мастер соответственно всегда получать при чтении последнюю согласованную копию данных. Организуется с помощью трех буферов, т.е. если мы настроили SyncManager на адреса 0x1000-0x1010, то в памяти слейва они займут 0x1000-0x1030. При этом слейву надо писать всегда в 0x1000-0x1010, а мастеру читать оттуда же, но по факту он прочитает ту из трех копий которая была записана целиком.
- Buffered режим Write. Аналогично, но для записи. Т.е. мастер может писать данные, они будут попадать в один из трех буферов, а слейв может спокойно читать не беспокоясь что они перетрутся новой посылкой в процессе чтения.
FMMU
Рассмотрю еще одну концепцию Ethercat, которая обеспечивает магию команд LRW. Это Fieldbus Memory Management Unit (FMMU). Она совсем простая. Проще SyncManagerов, да. Мы просто указываем область памяти в пространстве слейва, какому логическому адресу она соответствует и флаг пишет туда слейв или читает оттуда. Ах да, адресуется она с точностью до бита а не до байта, так что не повторяйте моей ошибки, если хотите работать с целым числом байтов, то StartBit ставьте 0, а EndBit — 7.
Если взять пример про который я сказал в самом начала, когда мы одной командой прочитали данные сотни датчиков и дали уставки сотне моторчиков. На каждом из датчиков мы используем FMMU0 установив логический адрес в 0x1000 (ну или где мы храним показания датчика), а логический адрес в 0x12345678+4*номер датчика
, размер 4 байта, ну и направление READ. На каждом из моторчиков мы используем FMMU0 установив логический адрес в 0x1000 (ну или где мы будем хранить уставку скорости), а логический адрес в 0x12345678+400+4*номер моторчика
, ну и направление WRITE. Теперь мастер может отправить один LRW пакет с логическим адресом 0x12345678, размером 800 байт, во вторую половину которого он запишет все уставки. А по приходу ответа прочитает из первой половины данные всех датчиков. Теоретически, моторчики могут даже читать данные которые отправил датчик, если находятся после него в топологии (так называемый slave2slave communication), но мы таким не заморачивались.
Разумеется, одно устройство может использовать несколько FMMU чтобы и писать и читать данные, ну и стоит настроить SyncManagerы на соответствующие адреса.
Спасибо за внимание
В EtherCAT заботливые Beckhoff положили еще много всяких наворотов. Например EEPROM который должен быть в каждом устройстве и который позволит мастеру настроить любой слейв и сохранить настройки. Граф переходов AL_STATUS, который будет гарантировать что слейв успел прочитать перед запуском обмена EEPROM и что мастер инициализировал выходы слейва нормальными значениями. CanOPEN-over-EtherCAT(CoE) который дает мастеру возможность узнать что этот слейв умеет и какие у него есть входы\выходы. Ethernet-over-EtherCAT(EoE) и Files-over-EtherCAT (FoE) чтобы пересылать разное всякое. Бинарные ESI-файлы c тем самым VendorID и на редкость глючной утилитой их генерации. Синхронизация времени до наносекунд. Мы обошлись без всего этого в нашем проекте, поэтому я не готов о них подробно рассказывать, но основы EtherCAT я, надеюсь, изложил.
shnegs
Заодно и о лицензиях на ведомые устройства, расскажите.
kipar Автор
ethercat.org/en/faq.html#789
За лицензию платить не надо, т.к. за нее уже заплатил производитель чипа.