Философия Do It Yourself или DIY звучит как "Развития навыков и уверенности в себе, получение удовольствия от процесса созидания." В современных реалиях на столе изобретателя сходятся автоматизированные системы управления, недорогие микроконтроллеры и облачные AI-сервисы. Энтузиасты строят умные устройства, домашние SCADA-приложения и даже цифровые двойники, интегрируя машинное обучение для анализа данных в реальном времени.
В этом проекте постараемся реализовать управление маломощным электроприводом по CAN-шине. А в следующем проекте добавим коммуникацию с другим PLC-устройством, интегрируем систему с IoT. А затем посмотрим можно ли переложить задачу анализа работы системы на AI-агента не прилагая значительных усилий.
Содержимое статьи излагается следующим образом. Вначале краткое описание CAN-шины и методов проектирования для пользовательских проектов. Затем модификация проекта управления приводом PMSM комплекта разработки STM32-IHM03 и тестирование полученного результата.

Цели проекта

Текущая статья

  1. Исследование возможности реализации пользовательской логики в исходном коде проекта P-IHM.

  2. Создание базы данных CAN-сообщений

  3. Реализация CAN-интерфейса согласно требованиям, описанным ниже.

  4. Тестирование обмена данными по CAN-шине

Следующая статья

  1. Проектирование простого альтернативного HMI-интерфейса.

  2. Интеграция IHM03 с другими MCU и протоколами обмена данными.

  3. Прототип системы анализа режима работы привода за короткий промежуток времени с помощью AI-агента.

CAN-шина краткий экскурс

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

По основам CANBUS можно почитать статьи:

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

Причины выбора CANBUS для проекта.

  • Необходимость коммуникации с другими устройствами, оснащенными CANBUS. В нашем примере комплект разработки STM32-IHM03 уже оснащен виртуальным ком-портом (USB) для связи с PC о чем я расскажу ниже. И это может быть вполне достаточно. Однако пользовательское решение передачи данных позволяет следующее:

    • передавать только необходимые значения

    • произвольно выбирать структуру и формат данных

    • устанавливать необходимую периодичность обмена данных

    • использовать удобный физический интерфейс

  • Надежная и устойчивая к электромагнитным помехам система. Для демо-проекта может не столь важно, но если речь идет о транспорте или других системах связанных с безопасностью движения, выбор CANBUS будет оправдан.

  • Относительно простая и дешевая в реализации коммуникация между электронными модулями. Для связи между устройствами в самом простом случае достаточно витой пары из двух проводов. Много недорогих MCU (микроконтроллеров) уже содержит CAN-контроллер, а производитель предоставляет SDK и документацию по программированию. В их числе известный модуль Blue pill STM32F106CT8, платы ESP32, выше упомянутый модуль STM32-IHM03 с чипом STM32G431RB. Для MCU без CAN-контроллера, как Arduino UNO, Raspberry pi и т.п. возможно подключить внешний CAN-модуль, например MCP2515. Для коммуникации MCU с CAN-контроллерами по CANBUS необходимо только CAN-трансивер и витая пара.

OSI уровни CAN-протокола

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

  • Скорость передачи данных: Узлы должны быть соединены двухпроводной шиной со скоростью передачи данных до 1 Мбит/с (классическая CAN) или 8 Мбит/с (CAN FD).

  • Длина кабеля: Максимальная длина кабеля CAN должна составлять от 500 метров (125 кбит/с) до 40 метров (1 Мбит/с).

  • Терминирующее сопротивление: Шина CAN должна быть терминирована с помощью резистора 120 Ом на каждом конце шины.

Канальный уровень определяет форматы кадров CAN, обработку ошибок, передачу данных и помогает обеспечить целостность данных. Например, канальный уровень определяет:

  • Форматы кадров: четыре типа (кадры данных, удаленные кадры, кадры ошибок, кадры перегрузки) и 11-битные/29-битные идентификаторы

  • Обработка ошибок: методы обнаружения/обработки ошибок CAN, включая CRC, слоты подтверждения, счетчики ошибок и многое другое

  • Арбитраж: неразрушающий побитовый арбитраж помогает управлять доступом к шине CAN и избегать коллизий с помощью приоритета на основе идентификаторов

Физическое устройство и типы сетей

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

  1. High-Speed CAN (ISO 11898-2): Самый популярный вариант. Скорость от 40 кбит/с до 1 Мбит/с. Используется для критичных систем (двигатель, тормоза).

  2. Low-Speed / Fault Tolerant CAN (ISO 11898-3): Скорость до 125 кбит/с. Особенность — работа продолжается даже при обрыве одного из проводов. Применяется в комфорт-системах (двери, зеркала).

  3. LIN-bus: Дешёвое дополнение к CAN для простых задач (стеклоподъемники, кондиционер).

  4. CAN FD (Flexible Data-rate): Новое поколение со скоростью до 8 Мбит/с и объемом данных до 64 байт (вместо 8 байт в классическом CAN).

High-Speed CAN (ISO 11898-2)

Это самый распространенный стандарт, используемый для управления критическими системами (двигатель, АКПП, ABS).

Топология

  • Линейная шина. Двухпроводная линейная топологии. Ответвления (stubs) от основной магистрали должны быть минимальными по длине.

  • Терминаторы. На обоих концах линии обязательно устанавливаются резисторы по 120 Ом. Они предотвращают отражение сигнала от концов кабеля.

  • Среда. Витая пара с волновым сопротивлением 120 Ом.

Форма сигнала и уровни напряжения

В High-Speed CAN используется дифференциальный сигнал между линиями CAN High (CAN_H) и CAN Low (CAN_L).

  • Рецессивное состояние (Логическая 1): Оба провода имеют напряжение около 2.5 В. Дифференциальное напряжение (V_{diff} = CAN\_H - CAN\_L) равно 0 В.

  • Доминантное состояние (Логический 0): CAN_H поднимается до 3.5 В, а CAN_L опускается до 1.5 В. Дифференциальное напряжение составляет 2.0 В.

Low-Speed / Fault Tolerant CAN (ISO 11898-3)

Этот стандарт разработан для систем комфорта, где важнее надежность соединения, чем скорость.

Топология

  • Гибкость. Допускает не только линейную топологию, но и звезду или смешанные варианты.

  • Распределенное терминирование. В отличие от High-Speed, здесь каждый узел имеет свою часть терминирующих резисторов (обычно около 2.2 кОм или 4.7 кОм на землю/питание). Общее сопротивление сети должно быть около 100 Ом, но оно распределено по всем устройствам.

Форма сигнала и уровни напряжения

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

  • Рецессивное состояние (Логическая «1»): CAN_H подтянут к 0 В, а CAN_L — к 5 В.

  • Доминантное состояние (Логический «0»): CAN_H поднимается до 3.6 В, а CAN_L опускается до 1.4 В.

Главная особенность ISO 11898-3 — способность трансиверов переходить в режим работы по одному проводу. Если один из проводов (CAN_H или CAN_L) обрывается, замыкается на массу или на питание, трансивер переключается на использование оставшегося целого провода относительно земли. Можно подробнее ознакомится в даташите трансивера TJA1055. В High-Speed CAN такое невозможно: там разница напряжений слишком мала, и без второго провода выделить чистый сигнал из шума практически невозможно.

Сравнительная таблица характеристик

Характеристика

High-Speed (11898-2)

Low-Speed (11898-3)

Макс. скорость

1 Мбит/с

125 кбит/с

Напряжение CAN_H (Dom)

3.5 В

3.6 В

Напряжение CAN_L (Dom)

1.5 В

1.4 В

Терминирование

2 x 120 Ом (на концах)

Распределенное (в каждом узле)

Отказоустойчивость

При обрыве одного провода связь теряется

Работает при обрыве или КЗ одного из проводов

Применение

Powertrain, ADAS, Диагностика

Кузовная электроника, Салон

Структура Standard Data Frame (CAN 2.0A)

  1. SOF (Start of Frame): 1 доминантный бит. Он сигнализирует всем узлам о начале передачи и используется для жесткой синхронизации генераторов.

  2. Arbitration Field (Поле арбитража):

    • Identifier (ID): 11 бит. Определяет приоритет сообщения и его содержимое.

    • RTR (Remote Transmission Request): 1 бит. В Data Frame он всегда доминантный (0). Если он рецессивный (1), значит, это удаленный кадр запроса данных (Remote Frame).

  3. Control Field (Поле управления):

    • IDE (Identifier Extension): Доминантный (0) для стандартного кадра.

    • r0: Резервный бит (будущее использование).

    • DLC (Data Length Code): 4 бита. Указывают количество байт данных (от 0 до 8).

  4. Data Field (Поле данных): От 0 до 64 бит (0–8 байт). Сама полезная нагрузка.

  5. CRC Field:

    • CRC Sequence: 15-битная контрольная сумма.

    • CRC Delimiter: 1 рецессивный бит (разделитель).

  6. ACK Field (Подтверждение):

    • ACK Slot: Передатчик отправляет рецессивный бит, а все приемники, получившие кадр без ошибок, «перебивают» его доминантным.

    • ACK Delimiter: 1 рецессивный бит.

  7. EOF (End of Frame): 7 рецессивных бит. Обозначают конец кадра.

Пример отсылки CAN-сообщения в PulseView. В качестве анализатора использовался Raspberry pico (rp2040). Подробнее о проекте можно узнать в sigrok-pico репозитории. Сигнал измерялся на выводе RxD трансивера MCP2551.

CAN ID = 0x123
DATA = 0x11 0x22

Осциллограмма на выводах CANH и CANL трансивера MCP2551 при зацикленной передаче одного и того же сообщения

Extended Data Frame (CAN 2.0B)

Расширенный формат используется там, где 2048 комбинаций идентификаторов (11 бит) недостаточно (например, в протоколе J1939 для грузовой техники).

Ключевые отличия:

  • Идентификатор: Имеет 29 бит. Состоит из базовой части (11 бит) и расширения (18 бит).

  • SRR (Substitute Remote Request): Заменяет RTR в расширенном кадре, всегда рецессивный.

  • IDE: В расширенном кадре этот бит рецессивный (1), что дает контроллеру понять: дальше пойдут еще 18 бит идентификатора.

Битовый стаффинг (Bit Stuffing)

Чтобы приемники не теряли синхронизацию при длинных последовательностях одинаковых бит, в поля от SOF до CRC включительно применяется стаффинг: если контроллер видит 5 одинаковых бит подряд, он автоматически вставляет 6-й бит противоположной полярности. Приемник его автоматически удаляет. Примечание: Поля ACK и EOF имеют фиксированную форму, и стаффинг в них не применяется.

Сравнение с CAN FD

В стандарте CAN FD структура кадра меняется:

  • DLC: Позволяет адресовать до 64 байт данных.

  • BRS (Bit Rate Switch): Позволяет переключаться на более высокую скорость (например, 5 Мбит/с) внутри поля данных, возвращаясь к базовой скорости в конце кадра.

  • CRC: Используются более длинные контрольные суммы (17 или 21 бит) для обеспечения надежности при больших объемах данных.

Подробнее про CAN FD можно почитать здесь или в Wiki. Также можно ознакомится со статьей CAN Bus Explained - A Simple Intro

Арбитраж и обработка ошибок

Арбитраж в CAN-bus — это механизм, который позволяет нескольким узлам одновременно претендовать на доступ к шине без потери данных и без необходимости в диспетчере. Этот процесс основан на принципе CSMA/CD + AMP (Carrier Sense Multiple Access with Collision Detection and Arbitration on Message Priority). В основе арбитража лежат два физических правила:

  1. Доминантный бит (0) всегда подавляет рецессивный бит (1).

  2. Каждый узел во время передачи слушает шину, чтобы убедиться, что состояние сети совпадает с тем, что он отправил.

Все узлы одновременно отправляют SOF (Start of Frame) — один доминантный бит (0). На шине устанавливается «0». Все синхронизированы.
Узлы начинают передавать свой Идентификатор (ID), начиная со старшего бита.

  • Шаг 1. Все три узла отправляют первый бит своего ID. Если у всех это «0», на шине остается «0». Все продолжают передачу.

  • Шаг 2. Если Узел А отправляет «1» (рецессивный), а Узлы Б и В отправляют «0» (доминантный), то на шине установится «0».

  • Проигрыш Узла А. Узел А видит, что он отправил «1», но на шине «0». Он понимает, что есть кто-то с более высоким приоритетом, мгновенно прекращает передачу и переходит в режим приема.

Про арбитраж можно подробнее прочитать в этой статье или в википедии

На этой картинке узел A прекращает передачу напряжения, начиная с области, обозначенной оранжевым цветом. Узел B также не передает напряжение, начиная с бита идентификатора 0. Таким образов выигрывает узел C.

Обработка ошибок

CAN-контроллер использует пять независимых проверок для каждого кадра. Три из них работают на уровне сообщения, а две — на уровне битов. Подробнее можно почитать в CAN Bus Errors Explained.

На уровне сообщения:

  1. CRC Check (Проверка контрольной суммы). Передатчик вычисляет 15-битную контрольную сумму кадра. Приемник пересчитывает её. Если значения не совпадают — фиксируется ошибка.

  2. Frame Check (Проверка формата). Контроллер проверяет структуру кадра. Определенные поля (например, ACK-делитель, EOF) должны всегда состоять из рецессивных битов. Если там обнаруживается доминантный бит — это ошибка.

  3. ACK Check (Проверка подтверждения). После передачи данных передатчик ожидает «доминантный» бит в ACK-слоте от любого другого узла. Если шина осталась рецессивной (никто не подтвердил прием), фиксируется ошибка.

На уровне битов:

  1. Bit Monitoring (Мониторинг бита). Каждый узел во время передачи слушает шину. Если он отправил рецессивный бит, а увидел доминантный (кроме фазы арбитража или ACK), он понимает, что произошел конфликт или сбой.

  2. Bit Stuffing (Битовый стаффинг). По правилам CAN, после 5 одинаковых битов подряд контроллер автоматически вставляет один бит противоположной полярности. Если приемник видит 6 одинаковых битов подряд — это ошибка стаффинга.

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

  • Active Error Flag: Это 6 доминантных битов подряд. Они намеренно нарушают правило «битового стаффинга», заставляя все остальные узлы тоже обнаружить ошибку и отбросить текущий кадр.

  • Passive Error Flag: Это 6 рецессивных битов. Его отправляют только узлы, которые уже имеют много ошибок (Error Passive статус).

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

  • TEC (Transmit Error Counter) — счетчик ошибок передачи.

  • REC (Receive Error Counter) — счетчик ошибок приема.

  • Если передача прошла успешно: TEC - 1, REC - 1.

  • Если узел обнаружил ошибку при приеме: REC + 1.

  • Если ошибка произошла при передаче: TEC + 8 (передатчик наказывается строже)

  • Если узел «взбесился» и шлет флаги ошибок: значения стремительно растут.

Состояния узла

В зависимости от значений TEC и REC узел переходит в разные состояния:

  1. Error Active (TEC < 128 и REC < 128):

    • Нормальное состояние.

    • Узел может передавать данные и отправлять Active Error Flags (доминантные), останавливая всю шину при сбое.

  2. Error Passive (TEC > 127 или REC > 127):

    • Узел подозревается в неисправности.

    • Он всё еще может передавать данные, но при ошибке шлет только Passive Error Flags (рецессивные), которые не мешают другим.

    • После передачи кадра такой узел обязан выждать паузу (Suspend Transmission Time) перед следующим сообщением.

  3. Bus Off (TEC > 255):

    • Критическое состояние. Узел физически отключается от шины.

    • Он больше не может ничего передавать или подтверждать.

    • Вернуться в сеть он может только после программного сброса или специальной процедуры ожидания (128 последовательностей по 11 рецессивных битов).

Bit Timing

Bit Timing (Битовая синхронизация) — это важный аспект физического уровня CAN-bus. Поскольку в CAN нет отдельной линии тактового сигнала (как в SPI или I2C), каждый узел должен самостоятельно определять, когда именно нужно считывать значение бита с шины.
Чтобы все узлы понимали друг друга, один номинальный бит в CAN делится на четыре неперекрывающихся временных сегмента. Подробнее можно почитать в статье CAN (bxCAN) bit time configuration on STM32 MCUs

Структура бита (Segments)

Номинальное время передачи одного бита (Nominal Bit Time) состоит из квантов времени (Time Quanta, t_q). Общая длительность бита — это сумма четырех сегментов:

  1. Sync_Seg (Synchronization Segment):

    • Всегда равен 1 \times t_q.

    • Используется для синхронизации узлов. Ожидается, что фронт сигнала (переход из рецессивного в доминантное состояние) попадет именно в этот сегмент.

  2. Prop_Seg (Propagation Segment):

    • Компенсирует задержки распространения сигнала по проводам и задержки в приемопередатчиках (трансиверах).

  3. Phase_Seg1 и Phase_Seg2 (Phase Buffer Segments):

    • Используются для компенсации фазовых сдвигов, вызванных разницей в частотах кварцевых резонаторов разных узлов.

    • Между ними находится Sample Point (точка сэмплирования) — момент, когда контроллер фактически считывает логический уровень шины.

Sample Point (Точка сэмплирования)

Это важнейший параметр настройки CAN-контроллера. Она выражается в процентах от общей длительности бита.

  • Если Sample Point настроена слишком рано, контроллер может считать бит до того, как сигнал успеет стабилизироваться.

  • Если слишком поздно, контроллер может промахнуться из-за накопленного дрейфа частоты.

  • Стандарт для автомобильной отрасли (CiA): Обычно рекомендуется устанавливать Sample Point на уровне 87.5%.

Механизмы синхронизации

Чтобы узлы не разбегались во время передачи длинных кадров, CAN использует два вида синхронизации:

Hard Synchronization (Жесткая синхронизация)

Происходит только при переходе из рецессивного в доминантное состояние в начале кадра (SOF). Внутренний таймер узла сбрасывается в ноль, принудительно начиная отсчет сегмента Sync_Seg.

Resynchronization (Ресинхронизация)

Происходит во время передачи кадра при каждом перепаде сигнала (благодаря bit stuffing, перепады происходят регулярно). Если фронт сигнала пришел не в Sync_Seg, контроллер корректирует длительность фазовых сегментов:

  • Если фронт пришел позже, Phase_Seg1 удлиняется.

  • Если фронт пришел раньше, Phase_Seg2 укорачивается.

Параметр SJW (Synchronization Jump Width) определяет максимальное количество квантов времени (t_q), на которое можно изменить длительность фазовых сегментов за один цикл ресинхронизации.

При объединении различных устройств в одну сеть CAN (например, контроллера двигателя и датчика на Arduino/STM32) может быть недостаточно просто указать скорость (например, 500 кбит/с). Если у узлов будут разные настройки сегментов (даже при одинаковой скорости), они могут иметь разные Sample Points. В результате один узел будет считать бит «0», а другой в тот же момент времени — уже «1» из-за дрейфа. Это приведет к ошибкам формата (Form Error) или битовым ошибкам, и шина ляжет в статус Error Passive или Bus Off.

Стандарт CAN FD и IoT

С развитием беспилотных технологий и облачных сервисов классического CAN становится недостаточно.

  • Переход на CAN FD: Позволяет передавать в 8 раз больше данных и быстрее, что критично для систем помощи водителю (ADAS).

  • V2X и безопасность: Подключение машин к интернету требует защиты данных. Новые протоколы включают функции аутентификации, чтобы злоумышленники не могли перехватить управление тормозами или рулем.

STM32 motor-control комплект

Если ввести в поисковик, каким образом запустить бесколлекторный двигатель (PMSM), и сделать это быстро, вы наверняка столкнетесь с STM32 MCSDK. На Aliexpress был приобретен комплект разработки P-NUCLEO-IHM03.

Комплект представляет собой связку из двух плат:

  1. NUCLEO-G431RB: «Мозги» на базе STM32G4 (Cortex-M4, 170 МГц), заточенного под задачи управления приводами.

  2. X-NUCLEO-IHM16M1: Драйвер на базе монолитного чипа STSPIN830, способный выдавать до 1.5 А RMS при напряжении до 45 В.

В комплекте также идет небольшой двигатель Gimbal GBM2804H-100T и блок питания на 12В 2А.

Getting started with STM32 Motor Control

На Youtube канале STMicroelectronics есть ролик 5 мин Getting started with STM32 Motor control SDK6.0. Выполнив пошагово руководство в результате получим проект с исходным кодом для NUCLEO-G431RB. Тезисно приведем шаги по созданию проекта согласно источника.

Подготовка оборудования

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

  • Сборка: Установите силовую плату X-NUCLEO-IHM16M1 поверх платы управления NUCLEO-G431RB через разъемы ST morpho (CN7 и CN10). Убедитесь, что кнопки B1 (синяя) и B2 (черная) на плате Nucleo не закрыты.

  • Подключение двигателя: Подключите три фазных провода мотора (U, V, W) к клеммнику CN1 на силовой плате.

  • Перемычки (Jumpers): Для работы алгоритма FOC (векторного управления) по умолчанию на плате IHM16M1 должны быть установлены перемычки J5 и J6 в положение ON. На плате Nucleo перемычка JP5 должна быть в положении для питания от USB.

  • Питание: Подключите входящий в комплект блок питания постоянного тока (12 В, 2 А) к разъему J4.

Создание проекта в ST Motor Control Workbench

Процесс создания проекта «из коробки» минимизирует риск ошибок в параметрах:

  • Запуск: Откройте программу ST Motor Control Workbench.

  • Новый проект: Нажмите кнопку "New Project" на главном экране.

  • Выбор оборудования (General Info): В разделе «Hardware» выберите вариант "Pack". Это позволит выбрать готовый набор, где параметры контроллера, силовой части и мотора уже синхронизированы.

  • Выбор комплекта: В появившемся списке выберите P-NUCLEO-IHM03. После выбора нажмите "OK", чтобы создать проект со всеми предустановленными значениями.

  • Просмотр: Откроется «Project View», отображающий схему системы с заданными параметрами (частота ШИМ, топология измерения тока и т.д.).

Генерация кода и прошивка

  • Генерация: Нажмите кнопку "Generate the project" (иконка подарка) в верхней панели управления.

  • Настройка генерации: В появившемся окне выберите целевую среду разработки (Target Toolchain) — STM32CubeIDE. Также укажите версию STM32CubeMX и нажмите "Generate".

  • STM32CubeIDE: Откройте сгенерированную папку проекта в STM32CubeIDE. Выполните сборку проекта (Build) и загрузите прошивку в микроконтроллер через встроенный программатор ST-LINK.

Первый запуск и проверка

После загрузки программы система готова к работе без дополнительной настройки кода:

  1. Нажмите черную кнопку Reset (B2) на плате Nucleo для инициализации.

  2. Нажмите синюю пользовательскую кнопку User (B1) — это команда на старт двигателя.

  3. Для регулировки скорости вращайте синий потенциометр на плате X-NUCLEO-IHM16M1.

  4. Светодиоды D8, D9 и D10 на силовой плате должны загореться, индицируя работу фаз.

  5. Повторное нажатие синей кнопки B1 остановит двигатель

Замечания по запуску привода

Привод работает в целом нормально в тестовом режиме с настройки по умолчанию и нагрузкой менее 1 Нм. Но в зависимости от конкретных условий и режимов работы потребуется изменение дефолтных значений. В дальнейшем все параметры и коэффициенты можно изменить в исходном коде или с помощью пользовательского интерфейса Motor Pilot.
Одна из проблем, с которой я столкнулся - это рывок двигателя при старте. Изменение PID-коэффициентов не дало результата. Как выяснилось, причиной оказался параметр Target speed, который по умолчанию равен 524 RPM.

В исходном коде это параметр OBS_MINIMUM_SPEED_RPM находится в файле drive_parameters.h или в CUBEMX конфигурации MotorControl.M1_OBS_MINIMUM_SPEED_RPM P-IHM03-Potentiometer.ioc, где можно изменить это значение из 524 на 10.

#define OBS_MINIMUM_SPEED_RPM 10

Motor Control SDK документация

Motor ControlWorkbench приложение устанавливается с достаточно объемным комплектом документации в HTML-формате. Порой быстро найти необходимую информацию не просто.

Можно сконвертировать все справочные файлы в один PDF-файл. А затем загрузить этот мануал в Notebook LM.

Стенд для двигателя GBM2804H-100T

Если изначально не подразумевается установка двигателя на готовое устройство, то стенд для него позволяет удобно проводить эксперименты с комплектом. Иначе двигатель нужно либо держать в руках во время запуска, или каким-либо другим образом закреплять. Подразумеваю, что владелец IHM-блока придумает свою оригинальную конструкцию. У меня в коробке пылился разобранный привод от старого магнитофона. Я скрепил каркас с помощью алюминиевого уголка и круга из фанеры диаметром 10 см. Для двигателя подобрал пластиковый шкив и с помощью резинового пассика соединил со шкивом кассетника.

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

База данных CAN-сообщений

При проектировании систем управления и передачи данных по CAN-шине создание файла DBC (Data Base CAN) является этапом разработки протокола взаимодействия. Это документ, который определяет, как именно байты в цифровой шине превращаются в реальные физические параметры: амперы, вольты и обороты в минуту.

Проектирование базы данных CAN — это процесс формализации всех данных, циркулирующих в сети. Вместо того чтобы каждый раз вручную разбирать пакеты в коде, вы создаете единый файл-словарь, который описывает:

  • Структуру кадра: Какие биты за какой параметр отвечают.

  • Масштабирование: Коэффициенты пересчета (Factor/Offset) для передачи дробных чисел (например, точность 0.0001 для тока I_Q в вашем проекте).

  • Логику узлов: Кто в сети является «издателем» данных (например, PMSM_NODE), а кто — «подписчиком».

  • Диагностику: Текстовую расшифровку состояний (например, 22 = OTF_BRAKE), что критично при отладке электропривода.

Это позволяет автоматизировать разработку: один и тот же DBC-файл используется и в прошивке микроконтроллера, и в софте для мониторинга на ПК.

Формат DBC

Формат DBC (Data Base CAN) — это текстовый стандарт описания обмена данными в CAN-сети, который служит переводчиком между бинарными кадрами шины и понятными параметрами (ток, скорость, температура). Подробнее можно почитать статью CAN DBC File Explained.

Основные секции формата:

  • Nodes (BU_): Список узлов (блоков управления), участвующих в обмене данными. В нашем примере - это основной узел PMSM_NODE.

  • Messages (BO_): Описание кадров (сообщений). Каждое сообщение имеет уникальный ID (например, 0х100 или 0х200), имя, длину в байтах (DLC) и имя отправителя.

  • Signals (SG_): Определение конкретных параметров внутри сообщения. Для каждого сигнала задается:

    • Bit Layout: Начальный бит и длина (например, 32|32 для Power).

    • Byte Order: @1 указывает на Intel (Little Endian), а + или - на признак знака (Unsigned/Signed).

    • Scaling: Коэффициент (Factor) и смещение (Offset) для перевода в физические величины. Например, фактор 0.0001 используется для высокой точности тока I_Q.

    • Limits: Минимальное и максимальное значения в квадратных скобках.

  • Value Tables (VAL_): Таблицы перечислений, которые связывают числовые коды с текстовыми названиями состояний. Это позволяет видеть RUN или FAULT вместо абстрактных чисел 6 или 10.

  • Comments (CM_): Описательные строки для сигналов или сообщений, помогающие понять назначение данных (например, "I_Q current").

  • Attributes (BA_): Метаданные всей сети, такие как скорость шины (BusType) или настройки мультиплексирования.

Пример описания CAN-сообщения

Этот формат позволяет любому софту (от анализаторов до генераторов кода) мгновенно понять, как интерпретировать 8 байт данных из сообщения Speed_feedback_MSG как конкретные обороты RPM.

Инструментарий для работы с DBC

Для реализации этого проектирования существуют разные инструменты — от тяжелых индустриальных стандартов до легких бесплатных утилит.

1. Vector CANdb++

CANdb++ - это наиболее распространенный в автомобильной и промышленной индустрии редактор. На Youtube можно найти материалы по CANdb++ или по пакету Vector CANoe, в составе которого находится редактор базы.

  • Плюсы: Строгая проверка на ошибки, поддержка сложных атрибутов сети и работа с мультиплексированием.

  • Особенность: Является частью экосистемы Vector, поэтому идеально подходит, если в разработке используются анализаторы CANalyzer или CANoe.

2. Kvaser Database Editor

Популярная альтернатива от шведской компании Kvaser.

  • Для кого: Отличный выбор для тех, кто использует интерфейсы Kvaser.

  • Особенность: Обладает более современным и интуитивно понятным интерфейсом по сравнению со «строгим» CANdb++. Позволяет быстро набрасывать структуру сообщений и сигналов, поддерживая все стандартные функции формата DBC.

3. Open-source и альтернативные редакторы

  • SavvyCAN: Мощный бесплатный инструмент с открытым кодом. В нем есть встроенный редактор DBC, который позволяет сразу же видеть результат декодирования трафика с шины.

  • Cantools (Python): Не графический редактор, а библиотека. Позволяет описывать базу данных прямо в коде или конвертировать DBC в другие форматы (например, в JSON или C-структуры).

  • DBC-редакторы VS Code: Существуют плагины, позволяющие редактировать текстовую структуру DBC с подсветкой синтаксиса, что удобно для быстрой правки комментариев или ID.

Реализация базы данных

Проектировать базу данных DBC лучше всего начинать со спецификации как правило в Excel-таблице. Где указывается:

  • имя сообщения,

  • периодичность обмена данными,

  • сигналы и их функции,

  • размер сигналов в байтах

  • пределы значений сигналов

Для простоты и ясности опишем CAN-сообщения и сигналы в виде списка. Для демо-проекта определим минимальный набор данных и команд, необходимый для управления и контроля работы двигателя. За основу возьмем интерфейс пользователя Motor Pilot, с которым будем сравнивать текущие значения величин. Забегая наперед скажу, что исходный проект IHM позволяет передавать данные с частотой 1кГц. Но для удобства обмен данными был настроен на частоту 0.5 Гц, т.е. все CAN-сообщения обновляются каждые пол-секунды. Управляющие команды отправляются без задержек.

DBC-файл будем создавать в CANdb++. Коротко приведу описание порядка создания CAN-сообщений из источника. В Vector CANdb++ этот процесс сводится к созданию иерархии: Network → Node → Message → Signal. Можно и в другом порядке, если так удобнее.

1. Создание сигналов (Signals)

Сигналы — это переменные (физические значения).

  • Перейти в раздел Signals и создать новый (например, Speed_RPM).

  • Data Type: выбрать Signed для значений с минусом (как в примере для оборотов [-1750|1750] ) или Unsigned для токов и напряжений.

  • Factor/Offset: если нужно передать дробное число, используй коэффициент. В нашем примере для тока I_Q стоит фактор 0.0001, что позволяет передавать точность до десятых долей миллиампера в обычном целом числе.

Добавление нового сигнала
Добавление нового сигнала
Диалоговое окно создания сигнала
Диалоговое окно создания сигнала

2. Создание сообщений (Messages)

Сообщение — это контейнер (фрейм), который объединяет сигналы.

  • Создать сообщение в разделе Messages (например, Speed_feedback_MSG).

  • ID: Указать идентификатор в Hex или Decimal.

  • DLC: Указать длину в байтах (от 1 до 8).

  • Layout: Перетащить созданные сигналы в сообщение и распределить их по битовой сетке. Следить, чтобы сигналы не перекрывали друг друга.

Добавление нового сообщения
Добавление нового сообщения
Диалоговое окно создания сообщения
Диалоговое окно создания сообщения
Диалоговое окно создания сообщения, вкладка с сигналами
Диалоговое окно создания сообщения, вкладка с сигналами
Диалоговое окно создания сообщения, расположение сигналов побайтно
Диалоговое окно создания сообщения, расположение сигналов побайтно

3. Определение узлов (Network Nodes)

Узлы — это участники сети (контроллеры).

  • В разделе Nodes создать узел PMSM_NODE.

  • Привязать сообщения к узлу: указать, какие сообщения он отправляет (Transmitted), а какие принимает (Received). Это важно для фильтрации и логики работы ПО.

Создание нового узла
Создание нового узла

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

Диалоговое окно создания узла, добавление сообщений
Диалоговое окно создания узла, добавление сообщений

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

4. Таблицы значений (Value Tables)

В проекте удобно использовать текстовые названия для состояний работы двигателя, которые передаются в числовом виде. Данные чисел и названий взяты из исходного кода.
Чтобы не гадать, что значит Motor_State = 6, лучше использовать перечисления.

  • Создать таблицу в Value Tables (например, MotorStates).

  • Прописать пары число-текст: 0IDLE, 6RUN, 10FAULT.

  • Привязать эту таблицу к конкретному сигналу во вкладке Value Descriptions.

Меню создания таблицы с константами (Value Tables)
Меню создания таблицы с константами (Value Tables)
Создание новой Value Table
Создание новой Value Table

В проекте создано две таблицы:

  • состояние работы двигателя

Value Table - состояние двигателя
Value Table - состояние двигателя
  • управляющие команды

Value Table - управляющие команды
Value Table - управляющие команды

Перечень CAN-сообщений и сигналов в проекте

Curr_Amp_feedback_MSG - текущие значения токов d и q осей

  • CAN-ID 0x210

  • Размер - 8 байтов

  • Сигналы

    • I_D - ток d-оси

    • I_Q - ток q-оси

    • Параметры сигналов выбраны одинаково для двух токов:

      • размер - 4 байта (32 бита) знаковые

      • порядок байтов - Intel

      • коэффициент пропорциональности 0.0001 выбран для одинакового значения с Motor Pilot

      • пределы 0 ... 5 (в нашем случае это больше для справки)

      • распределение сигналов в Layout - автоматически

PID_feedback_MSG - значения коэффициентов PID-регулятора скорости и момента

  • CAN-ID 0x230

  • Размер - 8 байтов

  • Сигналы:

    • PID_SPEED_KI

    • PID_SPEED_KP

    • PID_TORQUE_KI

    • PID_TORQUE_KP

    • Параметры сигналов:

      • Размер два байта (16 бит) беззнаковые

      • порядок байтов - Intel

      • распределение сигналов в Layout - автоматически

Speed_control_MSG - задание команд: запуска, останова, сброса ошибок и значение скорости для двигателя

  • CAN-ID 0x100

  • Размер - 6 байтов

  • Сигналы:

    • Control_CMD

      • Размер 1 байт (8 бит) беззнаковый

    • Speed_RPM

      • размер - 4 байта (32 бита) знаковые

      • порядок байтов - Intel

      • диапазон: минус 1750 ... 1750

Speed_feedback_MSG - текущие значения скорости и состояния двигателя

  • CAN-ID 0x200

  • Размер - 6 байтов

  • Сигналы:

    • Motor_State

      • Размер 1 байт (8 бит) беззнаковый

    • Speed_RPM

      • размер - 4 байта (32 бита) знаковые

      • порядок байтов - Intel

      • диапазон: минус 1750 ... 1750

Torque_Power_feedback_MSG - текущие значения и момента

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

  • CAN-ID 0x240

  • Размер - 8 байтов

  • Сигналы:

    • Torque

      • размер - 4 байта (32 бита) знаковые

      • порядок байтов - Intel

      • диапазон: минус 2 ... 2

    • Power

      • размер - 4 байта (32 бита) беззнаковые

      • порядок байтов - Intel

      • диапазон: 0 ... 24

Voltage_Temper_feedback_MSG - текущие значения напряжения питания двигателя и температуры

  • CAN-ID 0x220

  • Размер - 8 байтов

  • Сигналы:

    • Bus_Voltage

      • Размер два байта (16 бит) беззнаковые

      • порядок байтов - Intel

      • диапазон: 0 ... 45

    • Temperature

      • Размер два байта (16 бит) беззнаковые

      • порядок байтов - Intel

      • диапазон: 0 ... 200

Сохраняем проект как PMSM-demo.dbc

PMSM-demo.dbc
PMSM-demo.dbc

Использование библиотеки cantools

cantools - это набор утилит командной строки для Python 3, которая умеет парсить, декодировать, редактировать и визуализировать трафик CAN-шины. Но её ценность для разработчика — возможность генерации статического кода для упаковки и распаковки CAN-сообщений.

Библиотека устанавливается стандартным пакетным менеджером Python:

python3 -m pip install cantools

Генерация C-кода из DBC

Команда generate_c_source берет ваш файл описания (например, PMSM-demo.dbc ) и создает пару файлов (.h и .c), которые содержат:

  • Структуры данных для каждого сообщения.

  • Функции упаковки (pack) и распаковки (unpack).

  • Функции масштабирования сигналов (применение Factor и Offset).

Пример команды:

cantools generate_c_source --use-float --database-name can_driver PMSM-demo.dbc

Разбор параметров:

  • --use-float: Принуждает генератор использовать тип float для сигналов, имеющих дробный коэффициент. Таким образом, числа с плавающей запятой в сгенерированном коде имеют одинарную точность (float), а не двойную (double).

  • --database-name can_driver: Задает префикс для имен функций и структур (например, can_driver_pmsm_node_unpack). Это помогает избежать конфликтов имен в больших проектах.

  • PMSM-demo.dbc: Исходный файл вашей базы данных.

Результат работы

После выполнения команды вы получите файлы can_driver.h и can_driver.c. Теперь в основном коде прошивки (например, для STM32 или ESP32) вам достаточно вызвать одну функцию, чтобы получить готовые значения скорости или тока:

struct can_driver_speed_feedback_msg_t speed_msg;
can_driver_speed_feedback_msg_unpack(&speed_msg, can_rx_data, 6);

printf("Current speed: %f RPM\n", speed_msg.speed_rpm);

Реализация CAN-интерфейса в проекте P-IHM03

Аппаратная часть и конфигурация

Микроконтроллер STM32G431RBT оснащен CAN-контроллером, поддерживающем протокол FDCAN совместимый с классическим CANBUS. В статье Getting Started with FDCAN Normal Mode on STM32 показан пример, как настроить и использовать FDCAN в STM32. Выполним настройку подобным образом но с форматом фрейма Classic Mode для совместимости с чипом MCP2515.

Для подключения к CAN-шине платы NUCLEO-G431RB выбран модуль CAN-трансивера SN65HVD230

SN65HVD230 - трансивер
SN65HVD230 - трансивер

Для подключение CAN-шины к USB на PC были приобретены CAN-конверторы, совместимые с TSMaster:

CANable V1.0 Nano

  • Форм-фактор: Nano (v 1.0)

  • Чип: STM32F072 + TJA1050

  • LED индикация статусов состояния и работы устройства.

  • Кнопка BOOT (DFU режим).

  • Терминальный переключатель сопротивления.

  • Разъем: Type-C USB

  • Питание: 5В (USB)

  • Подключение: CAN-H, CAN-L.

  • Разъемы (4 шт): CANH, CANL, 5v (output), GND.

  • Поддержка CAN 2.0A, CAN 2.0B.

  • Скорость передачи данных: до 1M.

  • Прошивка: Pcan (Canable)

  • Совместимое ПО: Cangaroo, PCAN-View.

  • Прошивка https://github.com/moonglow/pcan_cantact

CAN CANable v1.0 Pro

  • Форм-фактор: CANable Pro (v 1.0)

  • Чип: STM32F072 + ADM3053 (ADM3053BRWZ)

  • LED индикация статусов состояния и работы устройства.

  • Кнопка BOOT (DFU режим).

  • Терминальная перемычка сопротивления.

  • Разъем: Type-C USB

  • Питание: 5В (USB)

  • Подключение: CAN-H, CAN-L.

  • Разъемы: CANH, CANL, 5v (output), GND.

  • Поддержка CAN 2.0A, CAN 2.0B.

  • Сокрость передачи данных: до 1M.

  • Прошивка: Canable

  • Совместимое ПО: Cangaroo

Согласно CUBEMX FDCAN GPIO Settings:

  • PA12 - FDCAN1_TX

  • PB8 - FDCAN1_RX

На скриншоте схематически показаны выводы RX, TX, +3.3V, GND на плате

Для простоты подключение трансивера к плате NUCLEO-G431RB выполнено на макетной плате

Далее показана CUBEMX-конфигурация STM32G431RBT микроконтроллера проекта P-HMI, но уже с настройками FDCAN. Первым делом необходимо установить чекбокс Activated. А затем настроить Basic Parameters.

  • Frame Format выбираем Classic mode

  • Mode выбираем Normal mode Auto retransmission, Transmit pause, Protocol Exception оставляем по умолчанию. Tx Fifo Queue Mode выбираем FIFO mode. Примем скорость передачи данных для нашего проекта 500 kbit/sec - как в большинстве автомобилей. В Basic Parameters мы не можем напрямую установить значение скорости передачи данных. Для этого нам необходимо задать значения предделителя, сегмента синхронизации и сегментов буфера (см Bit Timing). Для этого воспользуемся онлайн калькулятором Bit Timing Calculator for CAN FD.

Первым делом нам нужно знать частоту шины Clock Frequency на FDCAN. Идем в CUBEMX Clock Configuration. После активации значение частоты равно 170 MHz. Вопрос тактирования STM32 - это отдельная большая тема. Оставим значение тактирования FDCAN как есть, чтобы не переконфигурировать настройки всего проекта.

Потом необходимо определить точность кварцевого резонатора - Clock Tolerance. На плате NUCLEO-G431RB расположен резонатор NX2016SA. В даташите указано Frequency Tolerance (25 ±3 °C) = ±10 × 10^{-6} или 1 ppm.
Далее из даташита определяем величину задержки распространения Propagation Delay трансивера. В модуле SN65HVD230 между выводом RS трансивера и GND подключен резистор 10к, поэтому нужно смотреть параметр Propagation delay RS with 10 kΩ to ground, выбираем максимальное значение 125ns.

Выбор точки выборки Sample point - это всегда компромисс между устойчивостью к задержкам в кабеле и устойчивостью к нестабильности генераторов.

  • Для Nominal Bit (арбитраж): Рекомендуемым и наиболее часто используемым значением является 87.5%. Это значение является стандартным для протоколов CANopen и DeviceNet. Для скорости 1 Мбит/с диапазон может варьироваться от 75% до 90%.

  • Для Data Bit (фаза данных): Из-за очень коротких битовых интервалов и эффектов физического уровня (асимметрия сигнала) в фазе данных точку выборки часто устанавливают раньше, чем в номинальной. Типичные значения для высоких скоростей (2-5 Мбит/с) находятся в диапазоне 60–80%.

Для демо-проекта с короткими проводниками CAN-шины устойчивость к задержкам будет играть намного меньшую роль, чем в реальном оборудовании. Методом подбора в Kvaser-калькуляторе выбраны Sample point 85% для получения целого значения Nominal baud rate и приемлемых коэффициентов для CUBEMX. Но возможны и другие сочетания, зависит от топологии сети. Выставив параметры:

  • Nominal Prescaler = 17

  • Nominal Time Seg 1 = 16

  • Nominal time Seg 2 = 3 Получим Nominal Baud Rate = 500000 bit/s, что нам и требуется. При других сочетаниях предделителя и сегментов может получится значение скорости 499999 bit/s, это связано с погрешностью вычислений, и в большинстве случаев можно оставить значение таким.

Std Filters Nbr — это количество стандартных (ID) фильтров, которые мы используем, в данном случае 1 фильтр для сообщения с ID 0x100.
Параметр Ext Filters Nbr установлен на 0, поскольку мы не используем расширенные идентификаторы.
Для приема сообщения мы будем использовать RX FIFO 0. Всего можно использовать до 64 элементов FIFO, и каждый элемент может хранить до 64 байт данных.

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

Обработку входящих CAN-сообщений мы собираемся выполнять в callback-процедуре, поэтому установим флаг Enabled во вкладке NVIC settings

Настройки пинов RX, TX оставим по умолчанию о чем уже говорилось выше, т.к. другие конфигурации уже заняты.

В процессе тестирования обнаружилась проблема конфликта обработки прерывания FDCAN с одним из каналов системного таймера TIM1. Т.е. во время прибытия нового CAN-сообщения система могла зависнуть или упасть в ошибку. Проблема решилась установкой приоритета обработки прерываний FDCAN1 ниже, чем у таймера. Это значение можно установить равным 5.

Программная реализация

Прошивка ihm03 реализует векторное управление (FOC) для двигателя BLDC/PMSM. Подробнее теорию можно почитать в руководстве STM32 Motor Control SDK или в статье Моделирование управления AC двигателя — Field oriented control.

Скорость может задаваться как встроенным потенциометром, так и удалённо - через пользовательский интерфейс. Согласно документации в исходном проекте ihm03, созданном через MCSDK, передача данных (включая измеренную скорость) в приложение ST Motor Pilot организована через протокол MCP (Motor Control Protocol) и транспортный уровень ASPEP (Advanced Serial Protocol for Embedded Performer).

Передача измеренной скорости и других параметров в Motor Pilot не происходит в одном конкретном месте в виде простой отправки строки. Она распределена по нескольким уровням:

  • Уровень протокола (MCP): Основная логика обработки команд и подготовки данных находится в файле mcp.c. Скорость считывается из регистра MC_REG_SPEED_MEAS.

  • Асинхронная передача (Datalogging): ST Motor Pilot использует механизм даталоггера для построения графиков скорости в реальном времени. Это реализовано через функции MCPA_dataLog() mc_tasks.c.

  • Транспортный уровень (ASPEP): Файлы aspep.c и usart_aspep_driver.c отвечают за непосредственную работу с USART2 и передачу сформированных пакетов.

  • Диспетчеризация задач: Все функции связи вызываются внутри задачи пользовательского интерфейса (User Interface Task), которая является частью функции MC_RunMotorControlTasks(). Эта функция обычно вызывается периодически из обработчика системного таймера (SysTick) или основного цикла main.c.

Для реализации периодической обработки пользовательских функций следует использовать специально предусмотренные хуки (callback-функции). Вместо модификации системных файлов протокола MCP рекомендуется интегрировать дополнительную логику в модуле mc_app_hooks.c, функция MC_APP_PostMediumFrequencyHook_M1().

Реализация отправки CAN-сообщений

Cогласно логики работы все основные процессы управления в MCSDK вызываются последовательно внутри планировщика MC_RunMotorControlTasks(), и хук выполняется первым в этой очереди. В SDK предусмотрен специальный код ошибки MC_DURATION. Эта ошибка генерируется, когда вычисления или выполнение функций занимают слишком много времени и не успевают завершиться до начала следующего цикла PWM. Блокировка процессора в цикле while - кратчайший путь к остановке мотора по этой ошибке. Передача большого количества CAN-сообщений может стать проблемой. В данном проекте мы собираемся передавать пять CAN-сообщений.

void CAN_Send_Feedback(FDCAN_HandleTypeDef *hfdcan)
{
  static uint32_t s_Counter = 0U;

  if (++s_Counter < CAN_TELEMETRY_DECIMATION_COUNT) { return; }
  s_Counter = 0U;

  CAN_Send_CurrAmpFeedback(hfdcan);
  CAN_Send_VoltageTempFeedback(hfdcan);
  CAN_Send_PidFeedback(hfdcan);
  CAN_Send_TorquePowerFeedback(hfdcan);
  CAN_Send_SpeedFeedback(hfdcan);
}

Если в CUBEMX CANFD параметр TX Fifo Queue Mode установить в Queue mode (в MX_FDCAN1_Init hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_QUEUE_OPERATION;), то в буфер будут отправляться только первые три сообщения, остальные игнорироваться.

Есть несколько компромиссных решений.

  • Распределенная отправка - Вместо того чтобы раз в 500 мс пытаться "протолкнуть" сразу 5 сообщений в буфер из 3 ячеек, мы будем отправлять по одному сообщению каждые 100 мс. В итоге каждое из 5 сообщений будет уходить со своей частотой 2 Гц, но они никогда не встретятся в очереди одновременно.

  • CUBEMX CANFD параметр TX Fifo Queue Mode установить в FIFO mode (в MX_FDCAN1_Init hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;). А перед отправкой нового сообщения проверять очередь HAL_FDCAN_GetTxFifoFreeLevel(hfdcan) == 0U и инкрементировать счетчик до установленного практическим способом значения.

  • Изменение архитектуры проекта (FreeRTOS или CMSIS-OS), использование других чипов

Возможно кто-либо знает более интересные варианты решения подобных задач, буду рад почитать в комментариях.
Я остановился на втором варианте - FIFO mode со счетчиком до 2000. Для данной конфигурации решение получилось оптимальным. Если вместо счетчика считать миллисекунды, то задержка будет значительной. И программа не будет работать, если к CAN-шине не будет подключено другое устройства, способное активно принимать сообщения.

#define CAN_TELEMETRY_DELAY_COUNT 2000U


static HAL_StatusTypeDef CAN_Transmit(FDCAN_HandleTypeDef *hfdcan,
                                      uint32_t id, uint32_t dlc,
                                      const uint8_t *data)
{
  static uint32_t s_SkippedMsgCount = 0U;

  uint32_t tickstart = HAL_GetTick();
  while (HAL_FDCAN_GetTxFifoFreeLevel(hfdcan) == 0U)
  {
	s_SkippedMsgCount++;
    if (s_SkippedMsgCount > CAN_TELEMETRY_DELAY_COUNT || (HAL_GetTick() - tickstart) > 2U)
    {
    	s_SkippedMsgCount = 0;
      return HAL_TIMEOUT; /* FIFO still full – abort this frame */
    }
  }

  FDCAN_TxHeaderTypeDef h;
  h.Identifier          = id;
  h.IdType              = FDCAN_STANDARD_ID;
  h.TxFrameType         = FDCAN_DATA_FRAME;
  h.DataLength          = dlc;
  h.ErrorStateIndicator = FDCAN_ESI_ACTIVE;
  h.BitRateSwitch       = FDCAN_BRS_OFF;
  h.FDFormat            = FDCAN_CLASSIC_CAN;
  h.TxEventFifoControl  = FDCAN_NO_TX_EVENTS;
  h.MessageMarker       = 0U;

  return HAL_FDCAN_AddMessageToTxFifoQ(hfdcan, &h, data);
}

Модуль CAN Bus — иерархия файлов и назначение

После настройки конфигурации FDCAN в CUBEMX и генерации проекта (подробнее можно посмотреть на Youtube) получим функцию инициализации периферии FDCAN в main.c

static void MX_FDCAN1_Init(void)
{
  hfdcan1.Instance = FDCAN1;
  hfdcan1.Init.ClockDivider = FDCAN_CLOCK_DIV1;
  hfdcan1.Init.FrameFormat = FDCAN_FRAME_CLASSIC;
  hfdcan1.Init.Mode = FDCAN_MODE_NORMAL;
  hfdcan1.Init.AutoRetransmission = ENABLE;
  hfdcan1.Init.TransmitPause = DISABLE;
  hfdcan1.Init.ProtocolException = DISABLE;
  hfdcan1.Init.NominalPrescaler = 17;
  hfdcan1.Init.NominalSyncJumpWidth = 3;
  hfdcan1.Init.NominalTimeSeg1 = 16;
  hfdcan1.Init.NominalTimeSeg2 = 3;
  hfdcan1.Init.DataPrescaler = 17;
  hfdcan1.Init.DataSyncJumpWidth = 6;
  hfdcan1.Init.DataTimeSeg1 = 16;
  hfdcan1.Init.DataTimeSeg2 = 3;
  hfdcan1.Init.StdFiltersNbr = 1;
  hfdcan1.Init.ExtFiltersNbr = 0;
  hfdcan1.Init.TxFifoQueueMode = FDCAN_TX_FIFO_OPERATION;
  if (HAL_FDCAN_Init(&hfdcan1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN FDCAN1_Init 2 */
  CAN_Filter_Config(&hfdcan1);
  /* USER CODE END FDCAN1_Init 2 */

}

где CAN_Filter_Config - пользовательская функция настройки фильтра приема CAN-сообщения, подробнее про фильтры можно почитать в статье Getting Started with FDCAN или на Youtube.

#define CAN_DRIVER_SPEED_CONTROL_MSG_FRAME_ID (0x100u)

void CAN_Filter_Config(FDCAN_HandleTypeDef *hfdcan)
{
  FDCAN_FilterTypeDef filterConfig;

  filterConfig.IdType       = FDCAN_STANDARD_ID;
  filterConfig.FilterIndex  = 0U;
  filterConfig.FilterType   = FDCAN_FILTER_MASK; /* ID & Mask match             */
  filterConfig.FilterConfig = FDCAN_FILTER_TO_RXFIFO0; /* Store match in FIFO 0  */
  filterConfig.FilterID1    = CAN_DRIVER_SPEED_CONTROL_MSG_FRAME_ID; 
  filterConfig.FilterID2    = 0x7FFU;            /* Mask: all 11 bits must match */

  if (HAL_FDCAN_ConfigFilter(hfdcan, &filterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  /*
   * Reject all frames that do not match any configured filter.
   * Non-matching standard frames → reject FIFO (not forwarded to Rx FIFOs).
   */
  if (HAL_FDCAN_ConfigGlobalFilter(hfdcan,
                                   FDCAN_REJECT,         
                                   FDCAN_REJECT,         
                                   FDCAN_FILTER_REMOTE,  
                                   FDCAN_FILTER_REMOTE)  
      != HAL_OK)
  {
    Error_Handler();
  }
}

Добавление кастомных файлов в проекте предусматривается по пути ..\STM32CubeIDE\Application\User\

Application/User/
├── can_driver.h      ← Cгенерированный cantools кодек сообщений  (заголовок)
├── can_driver.c      ← Cгенерированный cantools кодек сообщений  (реализация)
└── can_interface.c   ← Cвязующий слой приложения (реализация)
    [can_interface.h] ← Cвязующий слой приложения (заголовок)

can_driver.h / can_driver.c — Слой кодека сообщений, автоматически сгенерированы утилитой cantools из файла базы данных .dbc.

#

Функционал

1

Определить идентификаторы фреймов как константы #define (например, CAN_DRIVER_SPEED_CONTROL_MSG_FRAME_ID 0x100u)

2

Определить константы DLC (Data Length Code) для каждого фрейма

3

Определить структуры C, которые отображаются 1-к-1 на набор сигналов каждого CAN-сообщения

4

Предоставить функции pack() / unpack() для сериализации/десериализации структур в/из массивов байт

5

Предоставить вспомогательные функции encode() / decode() для применения масштабирования и смещения к физическим значениям

6

Предоставить валидаторы isin_range() для каждого сигнала

7

Перечислить варианты значений сигналов (перечисления состояний двигателя, коды команд)

Ключевые структуры

/* Входящие */
struct can_driver_speed_control_msg_t   { uint8_t control_cmd; int32_t speed_rpm; };

/* Исходящие */
struct can_driver_speed_feedback_msg_t      { uint8_t motor_state; int32_t speed_rpm; };
struct can_driver_curr_amp_feedback_msg_t   { int32_t i_d; int32_t i_q; };
struct can_driver_voltage_temper_feedback_msg_t { uint16_t bus_voltage; uint16_t temperature; };
struct can_driver_pid_feedback_msg_t        { uint16_t pid_speed_kp; uint16_t pid_speed_ki;
                                              uint16_t pid_torque_kp; uint16_t pid_torque_ki; };
struct can_driver_torque_power_feedback_msg_t   { int32_t torque; uint32_t power; };

can_interface.h / can_interface.c — Слой интеграции с приложением. Связующий код, соединяющий слой кодека с API управления двигателем MCSDK и драйвером STM32 HAL FDCAN.

#

Функционал

1

Настройка фильтра — CAN_Filter_Config() программирует аппаратный фильтр FDCAN на приём только фреймов с ID 0x100, отклоняя все остальные ID на аппаратном уровне (без нагрузки на CPU)

2

RX-коллбэк — HAL_FDCAN_RxFifo0Callback() переопределяет слабый символ HAL; декодирует входящий Speed_control_MSG и вызывает соответствующий API MCSDK

3

TX-помощники — по одной приватной static-функции на каждый телеметрический фрейм; каждая считывает живые данные двигателя, пакует их и ставит в очередь через общий помощник CAN_Transmit()

4

Планировщик телеметрии — CAN_Send_Feedback() понижает частоту тиков задачи 1 кГц до 2 Гц с помощью статического счётчика

5

Защита FIFO — CAN_Transmit() опрашивает уровень свободных слотов TX FIFO с таймаутом 2 мс по реальному времени, чтобы перегруженная шина никогда не блокировала контур управления

Публичный API

/* Вызвать один раз после MX_FDCAN1_Init() и до HAL_FDCAN_Start() */
void CAN_Filter_Config(FDCAN_HandleTypeDef *hfdcan);

/* Вызывать на каждом тике средней частоты (1 кГц) — внутри понижается до 2 Гц */
void CAN_Send_Feedback(FDCAN_HandleTypeDef *hfdcan);

/* Переопределение слабого символа HAL — вызывается автоматически из обработчика FDCAN IRQ */
void HAL_FDCAN_RxFifo0Callback(FDCAN_HandleTypeDef *hfdcan, uint32_t RxFifo0ITs);

Функция CAN_Filter_Config используется для настройки фильтра FDCAN для приема сообщений с ID 0x100.

Функция CAN_Send_Feedback служит для циклической передачи всех CAN-сообщений. Вызывается в mc_app_hooks.c, функция MC_APP_PostMediumFrequencyHook_M1() с частотой 1 кГц.

/* USER CODE BEGIN 0 */
#include "can_interface.h"
extern FDCAN_HandleTypeDef hfdcan1;
/* USER CODE END 0 */

.....
__weak void MC_APP_PostMediumFrequencyHook_M1(void)
{
  uint16_t rawValue = RCM_GetRegularConv(&PotRegConv_M1);
  (void)SPDPOT_Run(&SpeedPotentiometer_M1, rawValue);

/* USER SECTION BEGIN PostMediumFrequencyHookM1 */
 CAN_Send_Feedback(&hfdcan1);
/* USER SECTION END PostMediumFrequencyHookM1 */
}

Для демо-проекта прием данных с частотой 1 кГц избыточен, поэтому внутри функции CAN_Send_Feedback выполнен счетчик, при достижении значения CAN_TELEMETRY_DECIMATION_COUNT = 500, т.е. пол-секунды выполняется отсылка всех CAN-сообщений.

API MCSDK, используемые внутри can_interface.c согласно документации и исходных файлов проекта.

Функция

Назначение

MC_StartMotor1()

Запуск конечного автомата FOC

MC_StopMotor1()

Остановка двигателя

MC_AcknowledgeFaultMotor1()

Сброс зафиксированной неисправности

MC_ProgramSpeedRampMotor1_F()

Задать целевую скорость и время разгона

MC_GetSTMStateMotor1()

Прочитать текущее состояние конечного автомата

MC_GetAverageMecSpeedMotor1_F()

Прочитать среднюю механическую скорость (об/мин)

MC_GetIqdMotor1() / MC_GetIqdMotor1_F()

Прочитать токи D/Q

MC_GetAveragePowerMotor1_F()

Прочитать среднюю электрическую мощность (Вт)

VBS_GetAvBusVoltage_V()

Прочитать напряжение шины постоянного тока

NTC_GetAvTemp_C()

Прочитать температуру радиатора

PID_GetKP() / PID_GetKI()

Прочитать актуальные коэффициенты ПИД-регулятора

В обработчике прерывания HAL_FDCAN_RxFifo0Callback мы вызываем функции высокоуровневого API:

  • Запуск/Останов: MC_StartMotor1() и MC_StopMotor1().

  • Задание скорости: MC_ProgramSpeedRampMotor1(target_speed, duration).

  • Сброс ошибок: MC_AcknowledgeFaultMotor1().

Общая диаграмма потока данных

  CAN Bus
  ────────────────────────────────────────────────────────────────
  Мастер           STM32G431 (периферия FDCAN1)
  ──────           ──────────────────────────────────────────────
                   ┌─ Аппаратный фильтр (только 0x100) ───────────-┐
  0x100 ──────────►│  HAL_FDCAN_RxFifo0Callback()                  │
  Speed_control    │    └─ can_driver_speed_control_msg_unpack()   │
                   │    └─ MC_StartMotor1() / MC_StopMotor1() /    │
                   │       MC_ProgramSpeedRampMotor1_F() /         │
                   │       MC_AcknowledgeFaultMotor1()             │
                   └───────────────────────────────────────────────┘

                   ┌─ MC_APP_PostMediumFrequencyHook_M1 (1 кГц) ───┐
                   │  CAN_Send_Feedback()  ←  делитель /500        │
                   │    ├─ CAN_Send_SpeedFeedback()      → 0x200   │──► 0x200
                   │    ├─ CAN_Send_CurrAmpFeedback()    → 0x210   │──► 0x210
                   │    ├─ CAN_Send_VoltageTempFeedback()→ 0x220   │──► 0x220
                   │    ├─ CAN_Send_PidFeedback()        → 0x230   │──► 0x230
                   │    └─ CAN_Send_TorquePowerFeedback()→ 0x240   │──► 0x240
                   └───────────────────────────────────────────────┘

Исходный код всего проекта можно посмотреть в P-IHM03 репозитории.

Тестирование CAN-шины

TSMaster — это мощная программная платформа от компании TOSUN, предназначенная для разработки, моделирования и тестирования встраиваемых систем по протоколам CAN, CAN FD, LIN, FlexRay и Ethernet. Скачать можно из репозитория. Документация устанавливается вместе с софтом. Уроки можно посмотреть на канале Software Motion

TSMaster проект
TSMaster проект

Лицензия для некоммерческого использования

TSMaster придерживается политики "Hardware-Free, Software-Free". Это означает:

  • Бесплатная база: Базовый функционал программы полностью бесплатен и не требует покупки специального ключа.

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

  • Ограничения: Платными являются только специализированные плагины (например, для диагностики по протоколу UDS, калибровки по XCP или специфических аэрокосмических стандартов).

Настройка подключения конверторов интуитивно понятная, выполняется на панели Hardvare. Могут быть баги и вылеты, если выбрано некорректное устройство или не подключено ранее выбранное. Вначале желательно выбрать тип устройства с помощью Vendor Selection

Vendor selection
Vendor selection

А затем настроить канал в диалоговом окне Channel Selection. Как правило, устройство автоматически обнаружится в строке CAN1.

Channel selection
Channel selection

Настройка скорости CAN-шины расположена в диалогом окне Hardware Configuration, в которое можно перейти с помощью иконки Network Hardware.

Network hardware
Network hardware

Загрузка базы данных (.dbc)

На панели Analysis кликните иконку Database и выберете Show CAN database.
В диалоговом окне CAN Database кликните иконку Load CAN database file и выберете ранее созданный .dbc файл.

Load DBC
Load DBC

После этого диалоговые окна CAN FD Transmit и CAN FD Trace будут содержать конвертированные сигналы для CAN-сообщений. В окне CAN FD Transmit будет возможность задавать физические величины сигналов, которые затем автоматически конвертируются в последовательность байтов.

CAN trace
CAN trace
CAN transmit
CAN transmit

Короткое видео с результатом тестирования устройства

## Что удалось реализовать

В ходе работы над проектом были спроектированы и проверены следующие компоненты системы:

  • CAN-протокол взаимодействия — разработана база данных DBC с набором сообщений для телеметрии и управления двигателем, охватывающая скорость, токи d/q-осей, напряжение шины, температуру, мощность, момент и коэффициенты ПИД-регулятора.

  • Автоматическая генерация кода — утилита cantools позволила избежать ручного написания функций упаковки/распаковки байтов и снизила вероятность ошибок при масштабировании сигналов.

  • Интеграция FDCAN в прошивку IHM03 — модуль can_interface.c встраивается в хук MC_APP_PostMediumFrequencyHook_M1() без модификации системных файлов MCSDK, что важно для сопровождения проекта при обновлении SDK.

  • Управление двигателем по CAN — реализован прием команд запуска, останова, сброса ошибок и задания скорости с аппаратной фильтрацией на уровне FDCAN-контроллера.

  • Тестовый стенд и инструментарий — подобраны доступные CAN-конверторы (CANable) и бесплатное ПО (TSMaster) для мониторинга трафика и декодирования сигналов через DBC-файл.

Ограничения и возможные улучшения

  • Реальное время — архитектура на базе FreeRTOS или CMSIS-OS позволила бы разделить задачи управления и коммуникации по отдельным приоритетам без применения счётчика-делителя частоты и задержек.

  • Необходимость в дополнительных CAN-устройств. Для обеспечения коммуникации между узлами CAN-шины необходимо, чтобы каждый узел был оснащен CAN-контроллером и CAN-трансивером. Следует также учитывать топологию сети и согласующие сопротивления. Для локальных устройств, подключенных point-to-point CAN-шина это может быть избыточно.

  • HMI и аналитика — следующим логичным шагом является интеграция с IoT для визуализации данных в реальном времени и автоматический анализа режимов работы привода.