STM32 - BLDC Motor Control


15.02.2017

Керування безколекторним двигуном постійного струму (BLDC) за допомогою STM32

У якості прикладу розглянемо керування трьохфазним безколекторним двигуном (BLDC Motor) з датчиками Холла. Про алгоритм керування безколекторним двигуном з датчиками Холла я раніше писав у статті Управление бесколлекторным двигателем с датчиками Холла (Sensored brushless motors). У цій статті ми розглянемо лише питання програмування мікроконтролера. Мається на увазі що ви знайомі з алгоритмом керування BLDC з датчиками Холла та розумієтесь на структурі регулятора, а силова частина обрана відповідно до потужності Вашого двигуна. Тому у статті не буде наведена принципова схема, розглядатиметься лише структурна схема з описом призначення виводів мікроконтролера.

Загальна інформація

Цей простий приклад демонструє можливості мікроконтролера STM32, і основи керування BLDC двигуном з датчиками Холла. Цей приклад не є готовим рішенням. В ньому не реалізовані системи контролю чи стабілізації параметрів. Не реалізовані систем захисту. Це лише базовий приклад. Тому розглядаючи цей приклад Ви маєте усвідомлювати, що для використання у кінцевих виробах Вам доведеться над ним ґрунтовно попрацювати.

Структурна схема підключення STM32

Силова частина, тобто драйвери ключів і самі ключі, використовувалися з попередніх проектів: Схема регулятора скорости бесколлекторного двигателя (ESC). Детальніше про силову частину і ключі можна прочитати тут: Силова частина регулятора BLDC двигунів. Опис силової частини виходить за межі цієї статті. У цій статті розглядається лише структурна схема регулятора і програмна реалізація керуванням BLDC з датчиками Холла за допомогою мікроконтролера STM32. Схема контроллера для бесколлекторных моторов STM32

Генерація вихідних сигналів

Перший таймер TIM1 у STM32 дуже потужний інструмент і може виконувати різні цікаві речі. Як я і обіцяв, коли писав про таймери загального призначення, ми розглянемо на практиці деякі можливості першого таймеру. Перший таймер буде генерувати PWM сигнали для 6 ключів силової частини регулятора BLDC. Особливість таймера TIM1 полягає у тому, що він має декілька пар комплементарних виходів. Тобто виходів які можуть працювати у парі. Ми будимо використовувати три пари, кожна пара буде формувати PWM сигнали для верхнього і нижнього ключів. Для верхніх ключів це сигнали UH, VH, WH. Для нижніх - UL, VL, WL. Кожна пара виходів таймера має формувати сигнали таким чином, щоб коли ввімкнутий верхній ключ, нижній був вимкнутий, коли верхній вимикається, вмикається нижній. Крім того, нам треба забезпечити Deadtime. Тобто, проміжок часу для того щоб ключ встиг закритися до того, як відкриється комплементарний ключ. Інакше може виникнути коротке замикання. Цим також буде опікуватися таймер TIM1.

Вхідні сигнали

Сигнали від трьох датчиків Холла (H1, H2, H3) подаються на входи мікроконтролера. Комутація ключів відбувається в залежності від стану сигналів з датчиків Холла. Коли відбувається зміна стану датчиків Холла, виникає переривання. Обробник переривання змінює стан вихідних сигналів. Шпаруватість PWM задається потенціометром. Для зчитування даних з потенціометра використовується один з вхідних каналів ADC з використанням DMA. Для відключення вихідних сигналів у разі перевантаження по струму використовується вхід таймеру TIM1_BKIN. Передбачається що на вхід TIM1_BKIN буде подаватись дискретний сигнал з компаратора підключеного до датчика струму. Як бачите для аварійного відключення вихідних сигналів також використовуються ресурси першого таймеру. До того ж відключення відбувається на апаратному рівні. Це виключає виникнення навіть найменших затримок які можуть бути у випадках коли для цього використовується програмне рішення, наприклад використовуються переривання.

Програмне забезпечення

Опис налаштувань у файлі bldc.h: BLDC_CHOPPER_PERIOD - задає частоту PWM. Частота PWM у герцах = 72000000/BLDC_CHOPPER_PERIOD Приклади:

#define BLDC_CHOPPER_PERIOD 9000 # - 8КГц
#define BLDC_CHOPPER_PERIOD 4500 # - 16КГц
#define BLDC_CHOPPER_PERIOD 3000 # - 24КГц
#define BLDC_CHOPPER_PERIOD 2250 # - 32КГц
#define BLDC_CHOPPER_PERIOD 1125 # - 64КГц
BLDC_NOL - задає Dead Time. Dead time = BLDC_NOL/72000000  (on 72MHz: 7 is 98ns) Приклад:

#define BLDC_NOL 72
BLDC_PWMTOPKEYS - генерувати PWM лише на верхніх ключах BLDC_PWMBOTTOMKEYS - генерувати PWM лише на нижніх ключах BLDC_PWMTOPBOTTOMKEYS - генерувати PWM на верхніх і нижніх ключах BLDC_PWMCOMPLEMENTARYMODE - генерувати PWM з використанням комплементарної роботи ключів. Лише одна з цих опцій має бути ввімкнута. Приклад:

//#define BLDC_PWMTOPKEYS
//#define BLDC_PWMBOTTOMKEYS
//#define BLDC_PWMTOPBOTTOMKEYS
#define BLDC_PWMCOMPLEMENTARYMODE
Далі йдуть налаштування ADC, тобто пороги спрацювань потенціометра. Нагадаю, показники ADC лежать у межах від 0 до 4096. BLDC_ADC_START - задає нижній поріг нижче якого вважається мертвою зоною, двигун не запускається. BLDC_ADC_STOP - задає поріг нижче якого відбувається вимкнення двигуна. Обов`язково має бути менше за BLDC_ADC_START. BLDC_ADC_MAX - максимальний поріг ADC. Всі значення вище цього порога задають максимальні оберти. Приклад:

#define BLDC_ADC_START 200
#define BLDC_ADC_STOP 50
#define BLDC_ADC_MAX 4000
Нижче графік, що пояснює як працюють пороги для ADC. ADC пороги

main.c

Дивимось main.c:

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "misc.h"
#include "string.h"
#include "stdio.h"
#include "sysclk.h"
#include "adc_dma.h"
#include "bldc.h"
int main(void)
{
	// Set clock
	SetSysClockTo72();
	// ADC Init
	ADC_DMA_init();
	// TIM1, outputs, inputs, interrupts, etc. Init
	BLDC_Init();
	// Reverse pin Init
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
	GPIO_SetBits(GPIOB, GPIO_Pin_0);
    while(1)
    {
    	if (ADCBuffer[0] > BLDC_ADC_START) {
    		if (BLDC_MotorGetSpin() == BLDC_STOP) {
    			// Check Reverse pin
    			if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) != 0) {
    				// Forward
    				BLDC_MotorSetSpin(BLDC_CW);
    			}
    			else {
    				// Backward
    				BLDC_MotorSetSpin(BLDC_CCW);
    			}
    			BLDC_MotorCommutation(BLDC_HallSensorsGetPosition());
    		}
    		BLDC_SetPWM(BLDC_ADCToPWM(ADCBuffer[0]));
    	}
    	else {
    		if (BLDC_MotorGetSpin() != BLDC_STOP) {
    			if (ADCBuffer[0] < BLDC_ADC_STOP) {
    				BLDC_MotorStop();
    			}
    		}
    	}
    }
}
Мікроконтролер має працювати на частоті 72Мгц з використанням зовнішнього кварцового резонатора частотою 8Мгц. Ця функція налаштовує тактування мікроконтролера:

SetSysClockTo72();
Далі виконується ініціалізація ADC:

ADC_DMA_init();
ADC працює з використанням DMA. Результат перетворення з ADC_Channel_9 зберігається у ADCBuffer[0]; І на кінець виконується ініціалізація BLDC. Налаштовуються виходи і входи, переривання, перший таймер:

BLDC_Init();
У головному циклі програми керуємо двигуном в залежності від положення потенціометра. Зверніть увагу на наступний фрагмент:

if (BLDC_MotorGetSpin() == BLDC_STOP) {
  // Check Reverse pin
  if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) != 0) {
    // Forward
    BLDC_MotorSetSpin(BLDC_CW);
  }
  else {
    // Backward
    BLDC_MotorSetSpin(BLDC_CCW);
  }
  BLDC_MotorCommutation(BLDC_HallSensorsGetPosition());
}
BLDC_SetPWM(BLDC_ADCToPWM(ADCBuffer[0]));
Він відповідає за старт двигуна. Функція BLDC_MotorSetSpin(BLDC_CCW); задає напрямок обертання двигуна. Оскільки положення ротора вираховується у момент надходження переривань від датчиків Холла, відразу після старту програми контролер може і не знати положення двигуна, оскільки переривання ще не надходили. Тому треба обов`язково перед стартом викликати функцію BLDC_MotorCommutation(BLDC_HallSensorsGetPosition()); У головному циклі також встановлюється шпаруватість PWM. Дивись функцію BLDC_SetPWM. Функція BLDC_ADCToPWM перераховує показник ADC у значення для таймеру в залежності від встановлених лімітів ADC та налаштувань таймеру TIM1, зокрема в залежності від частоти PWM. Якщо Ви плануєте керувати обертами двигуна використовуючи не потенціометр, а інші канали (послідовний порт, тощо), слід використати свою аналогічну функцію яка буде вираховувати значення для запису у таймер. Детальніше про PWM читайте у статті STM32. Програмування STM32F103. TIMER. PWM

Ініціалізація таймера

Ініціалізація першого таймеру, налаштування виходів і TIM1_BKIN відбуваються у функції BLDC_PWMTimerInit. Налаштовується частота PWM:

TIM_TimeBaseStructure.TIM_Period = BLDC_CHOPPER_PERIOD;
Також встановлюється DeadTime:

TIM_BDTRInitStructure.TIM_DeadTime = BLDC_NOL;
Також налаштовується вхід TIM1_BKIN. Зверніть увагу можна задавати рівень сигналу при якому відбувається відключення:

TIM_BDTRInitStructure.TIM_Break = TIM_Break_Enable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_Low;
Шпаруватість PWM встановлюється для кожного з трьох каналів окремо. Це виконує функція BLDC_SetPWM:

void BLDC_SetPWM(uint16_t PWM)
{
  TIM1->CCR1 = PWM;
  TIM1->CCR2 = PWM;
  TIM1->CCR3 = PWM;
}

Відео

Керування BLDC двигунами з датчиками - це один з найпростіших алгоритмів керування безколекторними двигунами. Простота цього алгоритму досі забезпечує його широке використання. Але...далі буде. Скачати проект можна за наступним посиланням: https://github.com/avislab/STM32F103/tree/master/Example_BLDC Дивись також:

Статті по безколекторним двигунам:

Дивись також:

Бажаю успіхів.

Brushless Motors STM32
Коментарі:
Ангел Генчев говорить:
16.02.2017 01:33
Замечательно ! Вы обещали и так быстро создали этот материал, что я запоздало, прежде чем увидел это, написал комментарий на общей странице для STM32. 
Я читал http://www.avislab.com/blog/brushless04/ где коментари запрещени, а хотел сообщить, что есть схема с исползованием компараторa LM339 для детекция BEMF zero-crossing, е.t. без применение ADC, которая будет хорошо проанализироват.

golf2109 говорить:
11.11.2017 03:50
Дуже дякую за наведений матеріал. 
Наскільки я зрозумів, програмна реалізація алгоритму має на увазі, що ми керуемо драйвером з 
прямими входами. Отже, для керування драйвером у яких інверсний вхід необхідно або інвертувати сигнал для керування драйверами, або змінити алгоритм генерування PWM?

andre говорить:
13.11.2017 09:38
Так, якщо драйвери мають інверсний вхід необхідно просто інвертувати сигнал.

Ali говорить:
15.05.2020 02:19
خیلی عالی هست درصورت امکان فایل کامل نرم افزار کنترلی رو میشه دانلود کرد تابتونیم از آن بسازیم متشکرم

Алексей говорить:
17.05.2020 07:03
а как инвертировать сигнал ?

AMRENDRA BAHADUR SINGH говорить:
08.09.2021 17:24
Hi fantastic work dear engineer. is this code can be modified for STM32F072C8T6, if yes Please let me know how ?? also i want to add the current and voltage sense feature with this 

MD Shah говорить:
29.12.2023 06:56
which Compiler is used for Programing ? BLDC motor Con & Other STM32 Code Example 
Keil IDE ?

Додати коментар
Code
* - обов'язкові поля

Архіви