Теперь давайте обратим внимание на тот факт, что у большинства производителей микроконтроллеров есть соответствующие этой теме аппноты.
В частности, у бывшего Atmel наиболее известен AVR444: Управление трехфазным бесколлекторным электродвигателем постоянного тока без датчиков. Ознакомиться с русским переводом можно здесь.
Исходный код из статьи выше, который мы будем смотреть: (UPD 16022020. Пофиксил битый архив.)
Постараюсь разобрать код и показать, что это поделие в его первичном виде неработоспособно в общем, не касаясь алгоритма коммутации и способа запуска двигателя. Эти вещи суть коммерческая тайна нашей разработки и не подлежат разглашению. Поехали.
1. Метод запуска BLDC двигателя, описанный в аппноте, построен на подборе времени каждого коммутационного шага экспериментально. Т.е. вы долго и нудно будете подбирать эти значения, на каком-то этапе добьетесь уверенного старта двигателя, но вдруг окажется, что изменение внешних условий приводит к невозможности запуска. Подключив другой двигатель (более или менее мощный, с другим количеством полюсов на статоре/роторе), вас также может ждать разочарование.
2. За токовую защиту отвечает микроконтроллер.
Любители пиротехнических эффектов с искрами и дымом будут рады. Особенно эффектно это выглядит при высоком (выпрямленном от сетевого) напряжении DC шины. Да, в микроконтроллере токовая защита заведена на компаратор, а не АЦП, но пока сработает прерывание и выполнится код в нем, отключать ключи уже поздно. Они пробиты, сожгли драйверы и прочие полупроводники. Тушите свет, кина не будет.
3. Системная частота и частота ШИМ.
Смотрим файл BLDC.h, находим такие строки:
Код: Виділити все
//! System clock frequecy. Used to calculate PWM TOP value.
#define SYSTEM_FREQUENCY 8000000
//! PWM base frequency. Used to calculate PWM TOP value.
#define PWM_BASE_FREQUENCY 20000
4. Старт и ожидание конца преобразования АЦП по флагу в прерывании таймера.
Код: Виділити все
#pragma vector=TIMER0_OVF_vect
__interrupt void MotorPWMBottom()
{
unsigned char temp;
// Disable ADC auto-triggering. This must be done here to avoid wrong channel being sampled on manual samples later.
ADCSRA &= ~((1 << ADATE) | (1 << ADIE));
// Wait for auto-triggered ADC sample to complete.
while (!(ADCSRA & (1 << ADIF)))
{
}
temp = ADCH;
В коде еще много интересных моментов. Предлагаю читателю найти их самостоятельно.