VBCores Документация
СайтGitHub
  • VBCores
  • Hardware
    • VBCore VB32G4
    • VB STM32 программатор
    • BLDC драйвер 30A
    • DC драйвер 15A
    • Stepper драйвер 10A
    • CAN-FD - Raspberry PI
      • Настройка CAN на RPI
      • Часто возникаемые ошибки при работе с CAN/CAN FD
      • Работа с CAN FD через Python
    • Power board 30A
    • SBus-HID
    • DC-DC преобразователь
    • Ethernet - CAN-FD
    • USB-HUB
    • IMU BNO055
    • IMU BHI360
    • Т-Энкодер
  • Настройка ПО
    • Arduino IDE
      • Подготовка модуля VBCore
      • Установка среды программирования на Windows
      • Установка среды программирования на Ubuntu
        • Возможные ошибки в Ubuntu при работе с Arduino
      • Выбор платы в Arduino IDE
      • Установка базовой библиотеки
      • Структура ПО для Arduino
      • Примеры
        • Работа с CAN и CAN FD на Arduino
        • Работа с I2C
        • I2C detect
        • Датчик BNO055 / I2C
        • Датчик AS5047P / SPI
        • Датчик AS5600 / I2C
        • Работа с бесколлекторными двигателями
          • Simple FOC. Управление скоростью. Нахождение количества пар полюсов.
          • Simple FOC. Управление моментом
          • Чтение данных с датчика тока
        • Работа с коллекторным двигателем
          • Вращение DC мотором
          • Чтение угла по энкодеру. Управление DC мотором по углу
          • Чтение скорости вращения мотора по энкодеру
        • Работа с шаговым двигателем
          • Вращение шагового двигателя.
          • Контроль двигателя по интерфейсу SPI
    • STM32 CUBE IDE
      • Типовые настройки
      • Подсказки начинающим
        • Cube IDE для начинающих
        • Clock configuration
        • Таймеры - прерывания
        • Таймеры - ШИМ
        • Отладка программ
        • Коммуникации - FDCAN
        • Управление DC-мотором
        • Backup программы
  • Cyphal CAN
    • Cyphal CAN
    • PyCyphal
    • Yakut
    • Cyphal Arduino
      • Отправка и получение сообщений по cyphal
  • Работа с ROS
    • Установка Ubuntu, ROS и Arduino
    • ROS_LIB
    • Примеры
      • Publisher. Hello World!
      • Publisher with Subscriber
      • Rotation by DC motor
  • Работа с научным ПО
    • TCP Server
    • LabView
    • Matlab
  • Инструкции
    • Стенд управления двигателями
    • Переделка датчика мотор-колеса
    • Стенд мотор-колеса
    • iPower Motor
    • AS5047p OEM
  • Практические занятия
    • Коллекторный двигатель
      • Устройство коллекторного двигателя
  • RPI Display
Powered by GitBook
On this page
  • libcxxcanard
  • Инициализация
  • Подписки и отправка сообщений
  1. Cyphal CAN

Cyphal CAN

PreviousBackup программыNextPyCyphal

Last updated 5 months ago

Когда устройство становится достаточно сложным, появляется потребность в коммуникации между контроллерами, или между контроллером и компьютером (например, Raspberry Pi). Для этого в микроконтроллерах STM32G4 встроен модуль FDCAN. Этот протокол является популярным и достаточно гибким способом построения коммуникационных сетей, однако он не предоставляет никаких абстракций над пакетами байтов, которыми оперирует.

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

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


libcxxcanard

Нами была разработана и проверена на множестве устройств универсальная C++ библиотека для работы с cyphal (на основе libcanard) - . Далее будет дан обзор возможностей и типичных примеров использования данной библиотеки.

Подробная документация -

Примеры -

Короткое сравнение с libcanard

libcxxcanard основана на libcanard и призвана упростить ее использование. Далее несколько наглядных сравнений "на пальцах" (на картинки лучше нажать, чтоб рассмотреть подробнее):

Дальше будут параллельно разбираться примеры для linux (raspberry pi) и stm32g4. Считается, что сделаны следующие инклюды:

#include <cyphal/cyphal.h>
#include <cyphal/allocators/o1/o1_allocator.h>
#include <cyphal/subscriptions/subscription.h>

Инициализация

Код инициализации короткий и кроссплатформенный, надо только выбрать нужный провайдер:

#include <cyphal/providers/LinuxCAN.h>

std::shared_ptr<CyphalInterface> cyphal_interface;

void main() {
    cyphal_interface = std::shared_ptr<CyphalInterface>(CyphalInterface::create_heap<LinuxCAN, O1Allocator>(
        NODE_ID,
        "can0",         // SocketCAN
        200,            // длина очереди
        DEFAULT_CONFIG  // дефолтные настройки для линукса, объявлены в библиотеке
    ));
}
#include <cyphal/providers/G4CAN.h>

static std::shared_ptr<CyphalInterface> cyphal_interface;

// Необходимо определить для репорта ошибок - просто вызовем дефолтный хэндлер
// Но можно делать и что-то более разумное - например:
// https://github.com/voltbro/gyrobro_foc/blob/feature/mech-angle/App/communication.cpp#L47
void error_handler() {
    Error_Handler();
}
// micros_64 - надо объявить отдельно, с помощью таймера
// или для ленивых HAL_GetTick()*1000
UtilityConfig utilities(micros_64, cyphal_error_handler);

void main() {
    cyphal_interface = std::shared_ptr<CyphalInterface>(CyphalInterface::create_heap<G4CAN, O1Allocator>(
        NODE_ID,
        &hfdcan1,  // считаем что настроен, например через CubeMX
        200,       // длина очереди
        utilities  // объявлен выше
    ));
}

Подписки и отправка сообщений

По сравнению с libcanard, работа с отправкой/приемом сообщений гораздо проще. Есть единственное неудобство: из-за того что libcxxcanard работает поверх сишного libcanard, для любого используемого типа сообщений нужно (единожды) использовать макрос TYPE_ALIAS - TYPE_ALIAS(УдобноеНовоеНазваниеТипа, сгенерированный_cyhal_тип_сообщения).

Минимальный пример, echo-сервиса:

#include <voltbro/echo/echo_servoce_1_0.h>

TYPE_ALIAS(EchoRequest, voltbro_echo_echo_service_Request_1_0)
TYPE_ALIAS(EchoResponse, voltbro_echo_echo_service_Response_1_0)

// Подписки наследуются от шаблонного класса AbstractSubscription
// 1000 - port_id для сервиса
class EchoSub: public AbstractSubscription<EchoRequest> {
public:
    EchoSub(InterfacePtr interface):
        AbstractSubscription<EchoRequest>(interface, 1000) 
        {};
    void handler(const EchoRequest::Type& request, CanardRxTransfer* transfer) override {
        EchoResponse::Type response = {.pong = request.ping};
        interface->send_response<EchoResponse>(&response, transfer);
    }
};

std::shared_ptr<EchoSub> echo_sub;

void main() {
    // ... сначала тут описанная выше инициализация cyphal
    echo_sub = std::make_shared<EchoSub>(cyphal_interface);
}
#include <voltbro/echo/echo_servoce_1_0.h>

TYPE_ALIAS(EchoRequest, voltbro_echo_echo_service_Request_1_0)
TYPE_ALIAS(EchoResponse, voltbro_echo_echo_service_Response_1_0)

// Подписки наследуются от шаблонного класса AbstractSubscription
// 1000 - port_id для сервиса
class EchoSub: public AbstractSubscription<EchoRequest> {
public:
    EchoSub(InterfacePtr interface):
        AbstractSubscription<EchoRequest>(interface, 1000) 
        {};
    void handler(const EchoRequest::Type& request, CanardRxTransfer* transfer) override {
        EchoResponse::Type response = {.pong = request.ping};
        interface->send_response<EchoResponse>(&response, transfer);
    }
};

std::shared_ptr<EchoSub> echo_sub;

void main() {
    // ... сначала тут описанная выше инициализация cyphal
    echo_sub = std::make_shared<EchoSub>(cyphal_interface);
}

// Пример настройки хардварных фильтров для подписки, актуально только для STM32
void setup_filters() {
    HAL_FDCAN_ConfigGlobalFilter(
        &hfdcan1,
        FDCAN_REJECT,
        FDCAN_REJECT,
        FDCAN_REJECT_REMOTE,
        FDCAN_REJECT_REMOTE
    );

    static FDCAN_FilterTypeDef sFilterConfig;

    // Хардварный фильтр для пордписки. apply_filter - из libcxxcanard
    apply_filter(&hfdcan1, &sFilterConfig,echo_sub->make_filter(NODE_ID))
}

Развернутый пример подписчика на обычные сообщения, связанного с ROS (для linux):

#include <voltbro/battery/state_1_0.h>
#include <sensor_msgs/msg/battery_state.hpp>

TYPE_ALIAS(BatteryState, voltbro_battery_state_1_0)

class BatteryService : public AbstractSubscription<BatteryState> {
private:
    rclcpp::Publisher<sensor_msgs::msg::BatteryState>::SharedPtr publisher;
    rclcpp::Logger logger;
public:
    BatteryService(rclcpp::Node* node, const std::shared_ptr<CyphalInterface> interface)
        : AbstractSubscription<BatteryState>(interface, 7993),
          logger(node->get_logger().get_child("battery")) {
        std::string topic_name = "/bat";
        publisher = node->create_publisher<sensor_msgs::msg::BatteryState>(topic_name, 5);
        RCLCPP_INFO(logger, "Publishing topic <%s>", topic_name.c_str());
    }
    void handler(
        const BatteryState::Type& bat_info,
        CanardRxTransfer* transfer
    ) {
        sensor_msgs::msg::BatteryState battery;
        
        // Копируем поля
        battery.voltage = bat_info.voltage.volt;
        // ... и т.д.
        
        publisher->publish(battery);
    }
};
Cyphal
тут
libcxxcanard
read-the-docs
libcyphal-docs