Как стать автором
Поиск
Написать публикацию
Обновить

Первый опыт портирования open-source прошивки на отечественный микроконтроллер К1946ВК035

Уровень сложностиСредний
Время на прочтение9 мин
Количество просмотров3.3K

Введение: почему К1946ВК035?

С ростом интереса к импортозамещению в embedded-сфере отечественный микроконтроллер К1946ВК035 (разработка НИИЭТ) выглядит привлекательной альтернативой STM32 и его аналогам. Но насколько он подходит для сложных real-time задач, таких как управление бесколлекторными двигателями (ESC)?

Своевременно открывайте и закрывайте транзисторы - иначе сквозной ток выпустит весь магический дым наружу!
Своевременно открывайте и закрывайте транзисторы - иначе сквозной ток выпустит весь магический дым наружу!

Для всех тех, кому интереснее сразу посмотреть результат - прошу:

https://github.com/GooDroneru/K19XXVK035_AM32

Ну а для всех остальных - прошу под кат, где мы подробно разберём все нюансы.

Для нашего эксперимента мы выбрали прошивку AM32 – популярное open-source решение для управления бесколлекторными двигателями (ESC), изначально разработанное под STM32. Этот проект интересен тем, что:

✅ Активно развивается и уже поддерживает ряд современных функций, востребованных в ESC:

  • 30 ms Telemetry и классическая телеметрия

  • Поддержка двустороннего DShot с передачей расширенных пакетов

  • DroneCan для сетевого взаимодействия

  • Гибкая конфигурация параметров регулятора: motor kv, motor poles, sine start, tune volume и др.

✅ Имеет модульную структуру, что упрощает портирование.
✅ Написана на С с использованием HAL/LL библиотек STM32, что делает её хорошим кандидатом для проверки совместимости с К1946ВК035.

Проверяем пригодность К1946ВК035 для реализации регулятора оборотов

Первым делом отправляемся на официальный сайт производителя и изучаем документацию. Уже в описании микроконтроллера находим важный пункт: одна из ключевых областей применения — электроприводы, что сразу вселяет оптимизм. Анализируя перечень периферии, видим всё необходимое для базового регулятора: три независимых блока ШИМ, 12-битный АЦП, гибко настраиваемые таймеры и достаточное количество GPIO для управления ключами и обработки сигналов обратной связи.
Давайте трезво оценим наш К1946ВК035 на фоне микроконтроллеров, традиционно используемых в прошивке AM32. Здесь нас ждёт приятный сюрприз - в то время как стандартные сборки AM32 рассчитаны на:

  1. STM32F0 (Cortex-M0) - базовый вариант:

    • Скромные 48 МГц

    • Отсутствие FPU

    • Ограниченный набор команд Thumb

  2. Китайские аналоги STM32F4 (Cortex-M4) - продвинутый вариант

1946ВК035 предлагает Cortex-M4F с тактовой частотой до 100 МГц и аппаратной поддержкой вычислений с плавающей точкой. Такого уровня вычислительной мощности хватит с головой.

Для понимания масштаба: существуют рабочие реализации ESC даже на:

  • 8-битных AVR (например, ATmega)

  • Простейших Cortex-M0

С таким железом вопрос "потянет ли?" даже не стоит. Это всё равно что спрашивать, хватит ли грузовика КамАЗ, чтобы съездить за хлебом в соседний магазин.

Сравнение периферии:

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

1.Таймеры — сердце ESC:

  • В STM32 — это единый продвинутый таймер (TIM1/8) с:

    • Встроенным dead-time генератором

    • Break-функциями для аварийного отключения

    • Гибкой системой событий (TRGO, slave mode)

    • Прямой связью разных модулей с ADC для синхронных замеров

    • Поделить частоту таймера можно на любое число от 1 до 65536

  • В К1946ВК035 реализация иная:

    • 3 раздельных ШИМ-блока, вместо одного цельного таймера

    • Dead-time присутствует, но настраивается менее интуитивно

    • Система событий есть, но имеет больше ограничений

    • Связь ШИМ-блока с ADC реализована довольно удобно, можно выставлять задержку перед измерением после получения триггера события

    • Делитель частоты ШИМ в К1946ВК035 имеет фиксированные значения, например - Div1, Div2, Div4, Div8 и т.д. Для управления мотором этого вполне хватает, но если захочется поиграть мелодии через мотор (да, так тоже можно), тут уже начинаются сложности.

2.Работа с АЦП:

Теперь перейдём к рассмотрению АЦП. Четыре канала это самый минимум для нашей задачи. Наш регулятор работает в sensorless-режиме, то есть информацию о скорости и положении ротора мы получаем путём измерения обратной ЭДС свободной фазы (подробнее об этом методе можно почитать в приложенных материалах [1]).

Для базовой работы нам необходимо измерять:

  1. Напряжения на всех трёх фазах мотора (U, V, W)

  2. Напряжение общей точки (нейтрали)

Итого все 4 канала АЦП уже заняты. Но на этом требования не заканчиваются:

Напряжение батареи - критически важно контролировать, чтобы:

  • Не допустить глубокого разряда (что может привести к возгоранию LiPo-аккумуляторов)

  • Передавать данные на полётный контроллер

Ток потребления - необходим для:

  • Реализации продвинутых алгоритмов управления

  • Защиты от перегрузки

Температура - так как встроенного датчика нет, используем внешний NTC-термистор


В итоге ресурсов встроенного аналого-цифрового преобразователя оказалось недостаточно, поэтому придётся искать обходные решения.

Да, можно было бы пойти традиционным путём и использовать мультиплексоры, но это не решает все проблемы полностью:

Проблемы быстродействия:

  • Измерение обратной ЭДС требует точного временнóго соответствия

  • Мультиплексирование вносит задержки

  • Любая ошибка синхронизации ведёт к ошибкам коммутации

Проблемы с RC-цепями:

  • Каждый аналоговый вход, измеряющий напряжение через делитель, будет требовать фильтрующего конденсатора. Без него напряжение на входе АЦП выглядит так:

    Это называется "Voltage divider loading effect"[2]. Напряжение просаживается в момент измерения, полученные данные будут далеки от реальных. Изображение взято из интернета.
    Это называется "Voltage divider loading effect"[2]. Напряжение просаживается в момент измерения, полученные данные будут далеки от реальных. Изображение взято из интернета.

    Это называется "Voltage divider loading effect" [2]. Напряжение просаживается в момент измерения, полученные данные будут далеки от реальных.
    Бороться с этим эффектом можно разными способами, например, ОУ в режиме повторителя или низкоомные делители. Оба эти варианта мне не нравятся.
    Изображение взято из интернета.

  • Делители напряжения с фильтрующими конденсаторами на входах измерения напряжения свободной фазы создают дополнительные RC-цепи

  • Всё это приводит к неприемлемым задержкам сигнала, что приведёт к несвоевременным коммутациям, что приведёт к перегреву мотора и перегреву силовой части регулятора

Будем использовать компараторы:

  • Нам не нужно измерять абсолютное значение напряжения - только сравнение (то для чего они и созданы) фаз между собой

  • Компараторы работают практически мгновенно (задержки в наносекундах)

  • Они идеально подходят для определения момента перехода через ноль

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

  • Они дешёвые, надёжные, простые и не занимают много места на ПП

Реализация:

  1. Подключаем фазы к компараторам по схеме, выход компаратора подключается к GPIO


    Упрощённая схема, компараторов должно быть 3, по одному на каждую фазу.
    Упрощённая схема, компараторов должно быть 3, по одному на каждую фазу.
  2. В программе обрабатываем фронты сигналов как триггеры коммутации

3.Обработка управляющих сигналов:

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

  • PWM - старый добрый аналоговый протокол:

    • Простая широтно-импульсная модуляция

    • Заполнение = значение тяги

    • Уже устарел, но до сих пор используется

  • DSHOT - цифровой протокол, самый распространённый [3]:

    • Имеет несколько скоростей (150, 300, 600, 1200)
      Протокол DSHOT поддерживает несколько скоростей передачи сигналов — 150, 300, 600, 1200. Чем выше число, тем быстрее передаётся управляющий сигнал от полётного контроллера к регуляторам оборотов (ESC).
      Высокие скорости, например DSHOT600 или DSHOT1200, особенно полезны для RPM-фильтрации — функции в прошивках полётных контроллеров (Betaflight, INAV и др.), которая измеряет реальную частоту вращения моторов с помощью телеметрии от ESC. Зная точные обороты каждого мотора, контроллер может автоматически настраивать фильтры для подавления шума именно на этих частотах и их гармониках. Это улучшает стабилизацию, снижает вибрации и делает полёт более плавным и отзывчивым.

    • Работает по принципу, схожему с WS2812 (адресные светодиоды)

    • Использует импульсы разной длительности для передачи 0 и 1

    Обычная реализация обработки этих сигналов на STM32 включает:

    • Таймер в режиме захвата сигнала позволяет точно фиксировать моменты времени при изменении сигнала с 0 на 1 или с 1 на 0 на входном пине.

    • DMA складывает эти данные в буфер, не отнимая процессорное время на рутинную работу

    • При заполнении буфера процессор
      Теперь, что имеем мы:

    Что имеем у К1946ВК035:

    • Есть подходящий для этой задачи модуль ECAP (что-то вроде Input Сapture Mode у таймеров STM32)

    • Среди списков начинки есть модуль DMA

    • Эти два блока(ECAP и DMA) физически не соединены между собой

    • ECAP не умеет инициировать DMA-передачи

    Чем это плохо:

    • Главная проблема в том, что теперь нам придётся обрабатывать каждый из 16 бит DSHOT-пакета через прерывания.

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

    • Процессору придётся постоянно отвлекаться на сохранение новых данных в буфер. И в самый неподходящий момент может произойти непоправимое - мы несвоевременно обработаем сигнал с компараторов и выпадем из синхронизации.

    Наше решение:

    • Настраиваем ECAP на захват фронтов

    • Пишем обработчик прерываний, который будет вызываться по готовности новых данных

    • Новые данные переносим в буфер

Как можно оптимизировать?

Для эффективного копирования значений заполнения (duty) и периода (period) из регистров ECAP в буфер можно применить следующий подход. Регистры CAP0–CAP3 расположены в памяти последовательно (со смещением 0x08 от базового адреса ECAP), поэтому их можно скопировать в буфер одной операцией memcpy, избегая лишних обращений к памяти.

Вместо:
        dma_buffer[counter++] = IC_TIMER_REGISTER->CAP0;
        dma_buffer[counter++] = IC_TIMER_REGISTER->CAP1;
        dma_buffer[counter++] = IC_TIMER_REGISTER->CAP2;
        dma_buffer[counter++] = IC_TIMER_REGISTER->CAP3;
Делаем так:
        memcpy(&dma_buffer[counter], &IC_TIMER_REGISTER->CAP0, 4 * sizeof(uint32_t));
        counter += 4;

Для достижения максимального быстродействия критически важных обработчиков прерываний рекомендуется размещать их в оперативной памяти (RAM).

Прирост скорости выполнения:

  • Flash: 3–5 тактов на доступ

  • RAM: 1 такт на доступ

Разница становится особенно ощутимой при работе с высокочастотными прерываниями (более 100 кГц).

Получаем в итоге

  • DSHOT150 - работал стабильно без оптимизаций

  • DSHOT300 - работал стабильно без оптимизаций

  • DSHOT600 - заработал только после всех оптимизаций

  • DSHOT1200 - пока не тестировался

4. Хранение настроек:

Любой нормальный регулятор оборотов требует тонкой настройки под конкретный мотор и задачи. Эти параметры надо где-то хранить, причём так, чтобы они не пропадали при отключении питания. Разберём работу с Flash-памятью на К1946ВК035:

  • Чтение/запись группами по 8 байт

  • Запись необходимо производить в предварительно очищенную ячейку памяти [4].
    Ячейку можно очистить, стерев всю страницу размером 1КБ.

  • Основная Flash-память дополнена блоками кэш-памяти по 1 Кбайт на каждую из шин I-code и D-code [4].
    Кэширование Flash-памяти — это механизм ускорения доступа к данным и инструкциям за счёт их временного хранения в высокоскоростной буферной памяти (кэше).
    Доступ к данным в кэше: 1 такт (vs 3-5 тактов для Flash).

Практические советы по применению:

  • Хранение

    • Разместите ваши данные в начале отдельной страницы (1 КБ).

  • Запись

    • Перед записью новых значений стирайте всю страницу.

    • Пишите минимум по 8 байт за раз.

  • Чтение

    • Читайте также кратно 8 байтам.

Теперь давайте поговорим о том, что оказалось удобнее при работе с этим микроконтроллером по сравнению с STM32:

Работа с UART:

Модуль UART/USART в регуляторах оборотов служит для передачи телеметрии на полётный контроллер. В состав данных входят напряжение и ток, температура, а также обороты двигателя, вычисленные по времени переключения между фазами.
UART в К1946ВК035 прост в настройке, оснащён встроенным FIFO объёмом 32 байта, не требует использования DMA в рамках нашей задачи и позволяет передавать данные по байтам, самостоятельно формируя последовательный поток.

FPU:

Оказалось, что аппаратный модуль для работы с числами с плавающей точкой (FPU) в Cortex-M4F очень даже пригодился. Особенно для точного расчёта температуры по NTC.

SDK :

Есть базовый plib aka HAL. Есть примеры.
По большей части код писался на регистрах. Особенно порадовала реализация доступа к отдельным битам регистров через union — это избавляет от необходимости использовать битовые маски и ручные операции (|, &, ~).  

регистр_bit.ИМЯ_БИТА = 1;  // Новый стиль

регистр |= (1 << N);  // Старый стиль

Например: 
      IC_TIMER_REGISTER->ECCLR_bit.CEVT3 = 1;
Вместо:
      IC_TIMER_REGISTER->ECCLR |= ECAP_ECCLR_CEVT3_Msk; 

Заключение

Портирование AM32 на К1946ВК035 оказалось задачей не из простых — не столько из-за недостатка вычислительной мощности, сколько из-за особенностей периферии. Микроконтроллер уверенно тянет любые алгоритмы управления BLDC-моторами, но разработчику придётся вложиться в адаптацию к архитектуре:

  • Таймеры и ШИМ — функционал есть, но он разбит на отдельные блоки, что требует иной логики конфигурации.

  • АЦП — минимально достаточное число каналов заставляет искать обходные пути, такие как компараторы, вместо привычных схем на мультиплексорах.

  • Обработка DSHOT — отсутствие прямой связки ECAP с DMA превращает обработку пакетов в задачу с балансировкой прерываний.

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

В то же время, у К1946ВК035 есть и сильные стороны, которые приятно удивили:

  • Производительность Cortex-M4F с FPU обеспечивает запас по мощности.

  • Удобный UART с FIFO делает телеметрию проще в реализации.

  • Прозрачная работа с регистрами и примеры в SDK ускоряют отладку.

Если ваша задача — сделать рабочий, надёжный ESC под отечественную элементную базу, К1946ВК035 — вполне жизнеспособный вариант. Но он требует от разработчика готовности копаться в низкоуровневых нюансах и изобретать свои «велосипеды» там, где на STM32 всё уже решено.

В сухом остатке: возможно — да, просто — а, мы не любим, когда просто.

  1. Baciu V. Sensorless-BLDC-controller [Электронный ресурс] // GitHub. — URL: https://github.com/vladBaciu/Sensorless-BLDC-controller.

  2. How does the resistance of the load affect a voltage divider? [Электронный ресурс] // Electronics Stack Exchange. — URL: https://electronics.stackexchange.com/questions/153637/how-does-the-resistance-of-the-load-affects-a-voltage-divider.

  3. Dshot API [Электронный ресурс] // Betaflight Documentation. — URL: https://betaflight.com/docs/development/API/Dshot.

  4. К1946ВК035. Руководство по применению [Электронный ресурс] / АО «НИИЭТ». — 2025. — URL: https://niiet.ru/wp-content/uploads/2025/02/РП-К1946ВК035.pdf.

Теги:
Хабы:
+8
Комментарии10

Публикации

Ближайшие события