Введение
На шине CAN реализовано несколько различных форматов обмена данными. По формату заголовков они делятся на два вида — стандартный с заголовком 11 бит и расширенный с заголовком 29 бит.

В нашей работе мы будем рассматривать расширенный формат заголовка, так как именно его использует протокол DroneCAN. Несколько слов о его происхождении. Протокол DroneCAN является продолжением изначального формата UAVCAN v0 от которого пошли и другие форматы. Например, формат Cyphal, который является продолжением развития UAVCAN, более сложен и не поддерживает прежний формат передачи данных. Например, контрольная сумма пакета у Cyphal находится в конце, а не в начале данных, как у UAVCAN. Но в связи с тем, что много устройств поддерживало формат UAVCAN v0, было принято решение переименовать его в DroneCAN и продолжать использовать. Вот их сайт в интернете.

Инструментарий
Далее мы будем подробно разбирать структура пакетов DroneCan, но что бы перейти к разбору, необходимо иметь какой‑то инструментарий. Нужны, приборы для работы. Я предлагаю сначала остановиться на них. И здесь мы сразу столкнемся с неразберихой. Купить можно много чего, но, как правило, по большим ценам. Кроме того, софт верхнего уровня (GUI), часто привязан к конкретным железкам и с другими не работает. Опишу свой опыт погружения в эту область. Я увидел для себя три приемлемых пути:
Путь первый — покупаем на Алиэкспрессе модуль DroneCAN/UAVCAN USB‑CAN. Он начинает работать с программой DroneCAN GUI Tool «из упаковки». Саму программу можно скачать на сайте производителя.

При присоединении к USB, в диспетчере устройств появляется виртуальный COM порт. При запуске программы, показывается панель с настройками. На ней надо выбрать нужную скорость CAN‑а и COM порт. После нажатия кнопки OK, перед нами откроется рабочее окно программы в котором мы и будем в дальнейшем работать. Почитать об этом можно ниже в статье, а сейчас я опишу процесс спаривания донгла и DroneCAN GUI Tool.

На уровне пакетов, происходит обмен сообщениями (инициализация) между приложением и донглом. Для того, что бы она состоялась, между программой DroneCAN GUI Tool и донглом DroneCAN/UAVCAN должен состояться подобный диалог. Если процедура инициализации не пройдена, дальнейшая работа становится невозможной.

На рисунке представлен текстовой фрагмент обмена данными и управления между GUI и CAN‑USB донглом. Называется такой протокол Lawicel, а почитать о нем можно здесь. Если мы обратимся к формату Lawicel, то мы поймем, что сначала программа делает запрос к донглу, а он отвечает символом 0×0d. Это своеобразный ACK — NACK. Затем программа сбрасывает соединение (C.), устанавливает скорость CAN шины 1000kb/s (S8.), а затем открывает канал связи. Стандартная скорость протокола DroneCAN составляет 1000kb/s. На запрос флагов (F.) платка отвечает ошибкой, это не влияет на работу. Каждая команда подтверждается ответным сообщением. Использование готового донгла с протоколом DroneCAN позволяет быстро начать знакомство с ним. Но если купить его не получается, то можно купить другое устройство и перешить его.
Путь второй, сложный — покупаем на Алиэкспрессе дешевый модуль Canable. В магазине имеется несколько разновидностей плат с различными процессорами. Я купил вот такой.

Как правило, на нем стоит прошивка «candlelight». Если подключите к компьютеру, то в Диспетчере устройств увидите следующую картинку.

Далее заходите на сайт http://canable.io Там очень подробно описано что и как делать с этой платой. Она может работать под разными операционными системами Windows, Linux и Mac. Правда без WPN у меня зайти не получалось и вываливались различные предупреждения. Тем не менее, сайт интересный и выглядит так.

На нем можно перешить устройство под разный формат пакетов. И здесь стоит пояснить. Прошивка «candlelight» работает быстро, но для работы с ней нужна программа Cangaroo. Её можно скачать по ссылке с сайта. Если же вы перешьете плату прошивкой «Slcan with FD support», то формат пакетов будет другой — Lawicel. Мне лично этот вариант нравится больше. Во‑первых, вы не привязаны к конкретной программе и можете использовать обычный терминал. Во‑вторых, после непродолжительного времени вы начинаете «в лицо» узнавать CAN пакеты, которые будете анализировать. Ну и, в‑третьих, программа DroneCAN GUI Tool, а так же многим любимый CANHacker работают именно в этом формате.

Кроме того, на сайте github есть несколько репозиториев с прошивками для плат серии canable под разные процессоры. Здесь, здесь и здесь. Однако тут есть один неприятный подводный камень. Если вы скачаете нужный проект, откомпилируете его и перешьете донгл, то с программой DroneCAN GUI Tool перешитый модуль canable не заработает. Дело в том, что прошивка «Slcan with FD support» не поддерживает ACK — NACK. На запрос 0×0d(.) она не ответит. Почему так сделало — не понятно. Обойти эту фитчу очень просто. Надо зайти в файл usbd_cdc_if.c вашего проекта, найти там функцию cdc_process() и немного ее подправить.

Нужные изменения уже есть в прошивке, но закомментированы. Надо просто снять комментарии, перекомпилировать и прошить донгл программатором. После этого всё заработает.
Путь третий — собрать всё руками. Этот вариант позволит не только получить собственный донгл USB/DroneCAN, но и разобраться с софтом. Если всё заработает, вы сможете в будущем встраивать в свои разработки управление по CAN интерфейсу. Например в управление мотором, как вот в этой интересной статье на Хабре, которую написал Пирогов Владислав. По большому счету, я предлагаю создать проект с нуля, используя среду STM32CubeMx но подглядывать в проект canable в качестве примера. Этот путь вы должны будете пройти сами. Я лишь помогу вам правильно выбрать железо. Так как мы будем создавать клон MKS CANable V2.0, то надо будет купить на АлиЭкспрессе или Озоне модуль STM32G431. Берите квадратный модуль. Есть ещё узкий длинный на этом же процессоре. Он не подойдет. У него не все нужные нам ножки выведены на разъем. Надо взять вот такой.

Так же покупаете на АлиЭкспрессе или Озоне драйвер CAN линии на м/с tja1051.

После этого соединяете их проводами. Я это сделал на монтажной плате. Последний контакт на разных сторонах платы обозначен как NC и VIO. Он подходит на 5-ю ножку микросхему. Дело в том, что есть несколько разновидностей микросхемы tja1051. Если процессор, сопрягаемый с драйвером, питается от 5 вольт, то берется первый вариант. Если, как у нас, процессор питается от 3 вольт, то берется второй вариант. И в этом случае на ножку VIO подается уровень 3 вольта. Тем не менее, в магазинах различие не делается и мне всегда присылали второй вариант. Поэтому ножку VIO я всегда подключаю к 3 вольтам. Полная цоколевка приведена ниже.

Контакты на модуле STM32G431 |
Контакты на модуле tja1051 |
VCC (+5 вольт с USB) |
VCC |
GND |
GND |
PB9 |
CTX |
PB8 |
CRX |
CANH в линию CAN |
|
CANL в линию CAN |
|
GND |
S |
3V3 (+3.3 вольта) |
NC (VIO) |
Здесь нас поджидает ещё одна неприятность. Линия CAN_RX мультиплицирована с BOOT0. Вот кусок принципиальной схемы платы MKS CANable V2.0. Микросхема tja1051 при включении будет подтягивать эту линию к 3 вольтам и процессор будет уходить в режим BOOT.

Что бы этого не происходило, нужно изменить настройки Option Bytes микроконтроллера в STM32CubeProgrammer. Что бы процессор не уходил в BOOT снимите галку с nSWBOOT0.

Источник CAN пакетов
Итак, с железом для приема пакетов, мы разобрались. Далее нам потребуется устройство, которое будет отправлять эти самые DroneCAN пакеты в шину. Вы можете купить DroneCAN устройство и поэкспериментировать с ним. Для меня первым таким устройством стал популярный VESC регулятор, который китайцы клонируют тысячами. Это устройство разработал Бенджамина Веддера (Benjamin Vedder). Что бы считывать его CAN пакеты, сначала нужно сделать две вещи. Во‑первых, подключить к нему драйвер CAN линии, который описан выше.

Во‑вторых надо правильно настроить сам регулятор. Я лишь вскользь опишу, как это сделать. В интернете есть целые сообщества, которые дорабатывают и испытывают именно этот девайс в связи с его открытой схемой и прошивкой. Нам же интересен в первую очередь протокол DroneCAN, а не регулятор. Итак, в дополнение к самой плате регулятора, скачиваем программу VESC Tool на сайте производителя. Скачать ее можно бесплатно, но придется предварительно зарегистрироваться.

Чтобы регулятор передавал DronCAN пакеты, необходимо перевести его в нужный режим. Для этого присоединим нашу плату по одному из доступных интерфейсов (USB, COM и др.) к десктоп‑программе VESC Tool. Затем нажмем кнопку Autoconnect и, после установления связи, перейдем на страницу General. Здесь в пункте CAN Mode надо выбрать режим UAVCAN и сохранить настройки, нажав поле ↓A.

После этого, устройство будет 50 раз в секунду передавать по CAN‑интерфейсу пакеты.

Если рассмотреть пакет поближе, то мы увидим, что он состоит из 3-х частей.

Теперь настало время вернуться к программе DroneCAN GUI Tool, описанной выше. Что бы увидеть содержимое пакетов в цифровом виде, необходимо сделать следующее. При помощи USB‑CAN преобразователя подключаем GUI программу к нашей CAN линии. Я описывал выше как это сделать, только скорость надо выбрать 500 kbps/s, так как. Бенджамин Веддер использует именно её. Программа достаточно разнообразна и позволяет отслеживать и настраивать различные процессы на шине DroneCAN. Запустим программу и кликнем на кнопку с галочкой.


Вот некоторые ее панели. Нам для анализа нужна панель с CAN пакетами. Переходим в раздел Tools→ Bus Monitor. Здесь мы увидим их содержимое. В большинстве это пакеты статусов. Если у вас открыта панель ESC Management, то так же будут присутствовать RawCommand пакеты с данными для 4-х моторов. Сейчас мы проанализируем пакеты uavcan.equipmentesc.Status.

Формат пакетов DroneCAN
В протоколе DroneCAN используются три вида пакетов — сообщения, сервисные и анонимные. Большинство пакетов, которые передаются по шине, являются пакетами сообщений. Ниже приведены заголовки этих пакетов. Пять старших битов задают приоритет сообщений. Затем у пакетов сообщений идут два байта, которые задают ID сообщения, то есть что за данные будут содержатся в этом пакете. Последний байт (за исключением старшего бита) содержит ID передатчика этого пакета. Сервисный пакет похож на предыдущий, но поле ID сообщения разбивается на два других поля — ID сервиса и ID узла, для которого предназначен этот пакет.

Анонимные пакеты предназначены для DHCP адресации. Дело в том, что в CAN сети могут присутствовать 127 устройств и у каждого должен быть свой уникальный адрес. Можно заранее присвоить их каждому устройству. Это статическая адресация. Надо только убедиться, что ни один адрес не дублируется. Вторым способом присвоения адресов является их раздача в процессе подключения к шине — динамическая адресация. Тогда в системе должен быть узел, который ведет учет адресов и раздает их новым устройствам. Таким узлом является полетный контроллер или десктоп программа DroneCAN GUI Tool.
Давайте начнем разбор с заголовка CAN пакета. Посмотрим как он формируется. Для примера возьмем пакет с заголовком 0×18040A07 и сравним его с последовательностью полученной при помощи цифрового анализатора или осциллографа.

Первый бит (сиреневый цвет) это стартовый бит, его не рассматриваем. Биты выделенные зеленым цветом, так же нужно убрать из рассмотрения, так как это «бит стаффинг». Если в последовательности идут 5 одинаковых битов, передатчик автоматически вставляет противоположный бит, а приемник его убирает. Это нужно для того, что бы бороться с «джиттером» на высокой скорости передачи данных. Голубым цветом выделены биты SRR и IDE. Их то же надо убрать из рассмотрения. В итоге получим следующий заголовок 0×18040A07. Последнее поле DLC — это длина последующих данных (8 байт), а предпоследнее поле — это служебные биты RTR, r1 и r0.
В рассмотренном примере 0×18 — это приоритет сообщения. Чем число меньше, тем приоритет выше. В нашем случае приоритет достаточно низкий. Следующие два байта 0×040A представляют ID сообщения. В описаниях протокола он обычно представлен в десятичном виде. Последний байт 0×07 — это номер узла. Более подробную информацию мы можем найти в документах «Vertiq Standard DroneCAN (aka UAVCANv0) Support» здесь и «TM‑UAVCAN‑V2.3» здесь. В качестве примера разберем некоторые заголовки сообщений.
Мы видим, что заголовок фрейма со статусом узла будет иметь ID равный 341 = 0×0155, а рассмотренный выше (0×18040A07), относится статусу регулятора, так как 1034 = 0×040A. Он имеет длину 14 байт и выставляет информацию о токе, напряжении, температуре и других параметрах регулятора. Вот описание его данных.

Для управления дроссельной заслонкой двигателя (регулировкой его скоростью) используется RawCommand с ID = 1030. В одной команде может быть информация для нескольких каналов (двигателей). Каждый канал управления дроссельной заслонкой занимает 14 битов и цифровой диапазон управления дроссельной заслонкой составляет от -8191 до 8191. Отрицательные значения тем не менее не поддерживаются.
А дальше мы сталкиваемся с определёнными сложностями. Дело в том, что в протоколе DroneCAN поле данных содержит 8 байт, последний из которых — служебный. Следовательно, всего для данных остается 7 байт. С их помощью можно передать данные для 4-х моторов (56 bit = 4*14 bit). Если нужно передать данные для большего количества моторов, мы должны задействовать несколько пакетов. Но в этом случае, мы должны изменить их формат, вычислив и записав контрольную сумму всех пакетов в начале первого. Для её вычисления в «TM‑UAVCAN‑V2.3» есть соответствующая функция. Она двухступенчатая. Сначала надо вычислить сигнатуру, используя для каждого типа пакетов свое 64-битное значение. Для RAW пакетов это значение равно 0×217F5C87D7EC951D. Потом, вычислить конечную CRC.

Это несколько нелогично. Из 64-битного значения получаем 16-ти битное, а затем его уже подставляем в формулу вычисления CRC. Почему так сделано мне не понятно. Я один раз вычислил для RAW пакетов SignatureCrc и дальше уже во всех вычислениях использовал это значение и функцию crcAdd();. Привожу эти две функции для вычисления CRC составных пакетов, приведенные в документе:
1. SignatureCrc = crcAddSignature (0xFFFF, SignatureValue);
2. CRC = crcAdd(SignatureCrc, NBytesOfPayload, N);
В конце этой главы я бы хотел прояснить чем отличаются node_ID и throttle_ID (ESC_ID). Иногда с этим возникает путаница. Значение node_ID может принимать значение от 1 до 127. Каждое устройство на шине должно иметь свой уникальный номер, либо статический, либо присваиваемый динамически. Я об этом писал выше. Это и сесть node_ID. Значение же throttle_ID или ESC_ID относится к моторам и редко бывает больше 20. Это номер мотора в системе. Например, регулятор имеющий node_ID=12 и throttle_ID=1 будет управлять первым мотором, а регулятор node_ID=1 и throttle_ID=2 будет управлять вторым мотором. Это сделано для гибкости системы, но что бы не путаться, обычно node_ID и throttle_ID делают одинаковым. На этом теоретическая часть в целом закончена. Что бы её закрепить, в следующей главе я приведу некоторые практические результаты из своей работы.
Практика. Hobbywing
Если вы думаете что на этом все сложности закончились, то вы ошибаетесь:‑) Каждая фирма, производящая регуляторы вправе использовать свои пакеты на CAN шине. И многие этим пользуются. Это и плохо и хорошо. У некоторых производителей длинны составных пакетов составляют многие десятки байтов. Например, в приведенном выше документе, пакет ParamGet с номером 1332 имеет длину 74 байта. С такими пакетами очень сложно работать.

Другие производители стараются дробить данные на части, которые вмещаются в 7 байт. В этом случае нет необходимости вычислять CRC для пакета, что существенно упрощает жизнь при работе с ними. Разумеется и заголовки у этих пакетов каждый производитель выбирает сам. На этой странице можно посмотреть список некоторых стандартных типов пакетов. Для других, нестандартных, надо обращаться к документации производителя. Из всех типов регуляторов, которые мне попадались, больше всего понравились Hoibbywing. Это не реклама, поэтому даже ссылку на них давать я не буду, сами найдете. Но приведу ссылку на документацию протокола. Мы с ним сейчас и поработаем.
Как я уже говорил, на шине могут присутствовать три типа фреймов: message frame, service frame и anonymous message frame. Последний я не буду рассматривать, так как гораздо проще заранее определить номера node_ID устройств на шине. Процедура присваивания node_ID, сама по себе не простая. Service frame пакеты мы рассмотрим чуть ниже. Они нужны для адресного обращения к конкретным устройствам на шине. А сейчас мы будем рассматривать message frame, которые отправляются всем устройствам сразу и не требуют ответа от других устройств.
.
Message пакеты Hobbywing
Основным message пакетом для регуляторов является uavcan.equipmentesc.RawCommand. В нем задается скорость вращения двигателей. Давайте разберем на реальном примере как он выглядит. Ниже привожу два тройных пакета, для управления 10-ю двигателями Hobbywing. Буква Т в начале каждого пакета является артефактом протокола Lawicel, но я специально пишу так, что бы сохранялась наглядность. А когда собираю общий пакет, букву не ставлю.

Слева мы видим пакет состоящий из трех частей, в котором для всех 10 двигателей задается скорость вращения 10% от максимальной. Если мы из этих трех частей соберем изначальный полный пакет, то он будет выглядеть так как показано внизу. Сначала идет CRC c первым младшим байтом, затем данные для 1–4 моторов, 5–8 и наконец для 9–10 моторов. Заголовок у всех пакетов одинаковый — 084E840F, где 0×08 — приоритет, 0×4E84 = RawCommand(20100), а 0×0F — node_ID отправителя. В конце каждого пакета мы видим одинаковое число, которое и указывает на то что они относятся к одному составному пакету. Справа я выделил это синим цветом. Даже если все моторы отключены, значение CRC составного пакета не равно нулю. Но если в системе будут всего 4 мотора с выставленными 10% значениями для каждого, то фрейм будет один, без CRC 084E840F 8 330CCC3330CCC3 CA, где в последнем байте будут стоять биты Start и Stop одновременно.
Давайте для примера восстановим значение уровня сигнала для первого мотора. Поле 0×330C = 0011001100001100, берем только первые 14 бит. Из них 0×33 = 00 110 011 младший байт, а 0×03 = 000 011 старший байт. Получаем число 0×0333 = 819 в десятичной системе счисления. Мы помним, что весь диапазон значений кодируется значением 8191. Поэтому 8191 / 819 = 10%. Кроме того, RAW пакеты должны посылаться достаточно часто. Из практического опыта: при частоте RAW пакетов 10 Гц, регулятор хоть и работает, но при включении не извещает звуковым сигналом о том, что он готов к работе. При частоте 40 Гц звук инициализации есть.
К message сообщениям относятся так же статусные пакеты. Если мы посмотрим их программе DroneCAN, то увидим что регулятор Hobbywing выставляет следующие.

В первом пакете дается информация о RPM, PWM и Status. Если его проанализировать, то получим, что RPM = 0, PWM = 1719, Status = 0×10. Throttle signal source = PWM.

Из второго фрейма получаем: Напряжение = 24.0 вольт, Ток = 0А, Температура = 25 градусов.

Из последнего пакета видим, что температура MOSFET и конденсаторов равна 25 градусов.

По спецификации протокола DroneCan, каждое устройство должно, хотя бы раз в секунду, выставлять свой статус в виде пакета nodeID, в котором дает информацию о себе. Это позволит всем устройствам на шине понимать в каком окружении они работают. Кроме того, для пользования сервисными пакетами, так же нужно знать nodeID устройств. Однако регулятор hobbywing свой nodeID не выставляет :‑), поэтому не понятно каким мотором он управляет. Что бы узнать его nodeID и throttle_ID, нам необходимо самостоятельно формировать пакеты запросов и слушать ответы. Ниже привожу реальный пример запроса и ответа регулятора hobbywing.

Мы видим, что на запрос контроллера с nodeID = 0×0A пакетом 0×4E2D = 20013 пришел ответ от устройства с nodeID = 0×01. В поле данных, регулятор указал что у него nodeID = 0х01 и throttle_ID = 0х01. На этот запрос обязаны отвечать все устройства hobbywing на шине.

.
Service пакеты Hobbywing
Теперь, наконец, перейдем к сервисным пакетам. Они нужны для адресного обращения от одного устройства к другому на шине CAN. Для этого мы должны, как минимум, знать адрес получателя. На каждое адресное сообщение (service frame), устройство, к которому оно направлено, должно обязательно ответить. Таким образом мы поймем дошло сообщение до адресата или нет. Что бы узнать адреса всех устройств, достаточно прослушивать шину или делать специальные запросы.
Для полноценной работы с регулятором Hobbywing мне достаточно 5 сервисных пакетов. Начнем разбор с пакета, который изменяет nodeID и throttleID регулятора. Давайте разберем его.

Приоритет запроса 0х14, а ответа 0х10. Номер пакета в обоих случаях 0хD2 = 210. Запрос идет к устройству 0х02 с флагом (Request not response), получается 0х82. Запрос идет от устройства 0х7F с флагом (Service not message) получается 0хFF. В ответном пакете значение destination 0х7F (Request not response = 0), а source 0х02 с флагом (Service not message) получается 0х82. В поле данных в первом байте идет новый nodeID = 0х02 регулятора, а во втором - throttleID = 0х03.
Аналогично можно разобрать следующий пакет. Он выставляет приоритет линии управления между CAN и PWM. Номер пакета 0хD7 = 215, destination 0х01, source 0х55 в пакете запроса. В ответном пакете его номер тот же самый, destination 0х55, source 0х01.

Ниже я приведу ещё три фрейма, необходимые для работы. Первый это "изменение скорости работы" CAN шины, второй "изменения направления вращения", а третий "запроса направления вращения". Последние два фрейма имеют один и тот же номер пакета 0хD5=213. Разница только в поле данных. Если первым байтом идет 0хFF, то это запрос. Иначе - установка.

Вот собственно и всё что я хотел вам рассказать. Так как текст сложный, я по-максимуму постарался разбавить его картинками. Так проще воспринимать новые знания. Если будут вопросы или дополнения - пишите, постараюсь учесть. Но, как я уже писал в своих предыдущих статьях, лучше напишите что нибудь сами. Это обогатит вас самих и ваших коллег. А пока всё.
.
Печерских Владимир