Brushless DC motor controller design - Page 1
Forum section: electronics / projects

author: pyrohaz
author_rank: Regular Contributor
timestamp: August 02, 2014, 06:44:37 pm
content: Hi All,I'm trying to make a "generic" portable (Between the STM32 family) 3 phase brushless DC motor.I've got some code down using the capture compare registers for timer 2 and have some 3 phase looking pulses coming out from my chip.Realistically, I would use 5 capture compare channels to create my required waveform. Unfortunately, there are only 4. My efforts on how to produce these waveforms so far are:Key:CC1,2,3 (Capture compare 1,2,3)TP/3 (Timer period / 3)Phase A,B,C (Outputs wired to push pull Mosfet drivers)On Timer update (Cnt = 0): Set phase A high, set CC1register to PWM value (between 0 and Timer Period/3 -1)CC1 Interrupt: Set phase A lowCC2 Interrupt (initially set to TP/3): Set phase B high, set CC2 register to TP/3 + PWMCC2 Interrupt 2: Set phase B low, set CC2 to TP/3CC3 Interrupt (Initially set to 2*TP/3): Set phase C high, set CC3 register to 2*TP/3 + PWMCC3 Interrupt 2: Set phase C low, set CC3 register to 2*TP/3NOW! This does actually work for low frequencies and mid range PWM values. I'm currently testing at 1.57kHz though many commercial ESC's have frequencies at around 8khz. The PWM value doesn't work below 5 as phase B and phase C stick on until a whole timer period has elapsed.I've also tried to do this using a non interrupt method but that was too jitterful and temperamental. I'd rather not use dedicated timer pins within the STM32 but if thats the final option, I suppose I'll have to. I'm just wondering more so what other people do!For safety too, I'm thinking of having a 3 input and gate between each phase and the mosfet driver, the STM32 output connected to one input of the inputs, an external switch connected to another input and the final input connected to the STM32 output enable pin (not included in my code yet). Once I receive a hall effect throttle, I'll start thinking of fail safes if that decides to disconnect itself.The PWM variable can vary between 0(hopefully) and X. I'm not sure what X should be for maximum "BLDC" power because I assume that for maximum, it is when all three phases are on for maximum possible time? meaning that the maximum PWM value should be 99 (allowing for 99/100 ontime for each phase).Code:Code:#include <stm32f0xx_gpio.h>#include <stm32f0xx_rcc.h>#include <stm32f0xx_tim.h>#include <stm32f0xx_misc.h>GPIO_InitTypeDef G;TIM_TimeBaseInitTypeDef T;TIM_OCInitTypeDef TO;NVIC_InitTypeDef N;#define PhaseA GPIO_Pin_0#define PhaseB GPIO_Pin_1#define PhaseC GPIO_Pin_2#define PhaseGPIO GPIOA#define TPeriod 300#define PWMRefA (TPeriod/3)#define PWMRefB (PWMRefA*2)volatile uint8_t PWM = 5;void TIM2_IRQHandler(void){static uint8_t PStateA = 0, PStateB = 0, PStateC = 0;if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);GPIO_SetBits(PhaseGPIO, PhaseA);TIM2->CCR1 = PWM;}if(TIM_GetITStatus(TIM2, TIM_IT_CC1) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);GPIO_ResetBits(PhaseGPIO, PhaseA);}if(TIM_GetITStatus(TIM2, TIM_IT_CC2) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);if(PStateB){GPIO_SetBits(PhaseGPIO, PhaseB);TIM2->CCR2 = PWMRefA + PWM;}else{GPIO_ResetBits(PhaseGPIO, PhaseB);TIM2->CCR2 = PWMRefA;}PStateB^=1;}if(TIM_GetITStatus(TIM2, TIM_IT_CC3) == SET){TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);if(PStateC){GPIO_SetBits(PhaseGPIO, PhaseC);TIM2->CCR3 = PWMRefB + PWM;}else{GPIO_ResetBits(PhaseGPIO, PhaseC);TIM2->CCR3 = PWMRefB;}PStateC^=1;}}int main(void){RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);G.GPIO_Pin = PhaseA | PhaseB | PhaseC;G.GPIO_PuPd = GPIO_PuPd_DOWN;G.GPIO_OType = GPIO_OType_PP;G.GPIO_Mode = GPIO_Mode_OUT;G.GPIO_Speed = GPIO_Speed_Level_3;GPIO_Init(PhaseGPIO, &G);T.TIM_ClockDivision = TIM_CKD_DIV1;T.TIM_CounterMode = TIM_CounterMode_Up;T.TIM_Period = TPeriod;T.TIM_Prescaler = 100;TIM_TimeBaseInit(TIM2, &T);TIM_Cmd(TIM2, ENABLE);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);TIM_CCxCmd(TIM2, TIM_Channel_1, ENABLE);TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE);TIM2->CCR2 = PWMRefA;TIM_CCxCmd(TIM2, TIM_Channel_2, ENABLE);TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE);TIM2->CCR3 = PWMRefB;TIM_CCxCmd(TIM2, TIM_Channel_3, ENABLE);TIM_ITConfig(TIM2, TIM_IT_CC3, ENABLE);N.NVIC_IRQChannel = TIM2_IRQn;N.NVIC_IRQChannelCmd = ENABLE;N.NVIC_IRQChannelPriority = 0;NVIC_Init(&N);uint16_t CTim;while(1);}I've also included some pictures of my logic analyze'd phases to show it actually works.

author: void_error
author_rank: Frequent Contributor
timestamp: August 03, 2014, 01:44:29 pm
content: I've been planning to make a BLDC sensorless controller myself and I've done some research on the subject.One idea I had was to look for the zero crossing somewhere around half the PWM ON time using comparators to determine if zero crossing has occurred. For the commutation timing I'd use one of the available timers configured so if it overflows it'll generate an interrupt and force commutation to the next step. I have absolutely no idea if this will work but at least it's a starting point.Also, I know nothing about the STM32 family but I suppose it's similar to other brands in terms of basic features.Anyway, I hope this is helpful.Quote from: pyrohaz on August 02, 2014, 06:44:37 pmI guess you won't need to go below 5% duty cycle unless you want the motor to go really slow with no load, and yes, max PWM means max power. Also note that torque decreases with speed due to the winding inductance of the motor.

author: pyrohaz
author_rank: Regular Contributor
timestamp: August 03, 2014, 06:14:01 pm
content: Quote from: void_error on August 03, 2014, 01:44:29 pmThank you for the reply! I thought nobody was going to. Thats good to hear that I don't really need to go below 5% which makes sense, the motor is receiving minimal power at that point. After further consideration, I decided to create a program using my CPLD (I've been needing a use for it!) Which was relatively easy and only a few lines of code. I was interfacing it with my microcontroller over a 6bit parallel interface with a dedicated enable line (for safety reasons ofcourse!) but after further reading, it seems that a motor controller requires A LOT more thought than I had initially perceived.I have since decided that I'm just going to use a stock controller. Regardless, creating a simple program in VHDL was a good learning experience!

author: rob77
author_rank: Super Contributor
timestamp: August 03, 2014, 06:24:11 pm
content: i would suggest to have a look at the BLHELI code - that's a open implementation of sensorless BLDC motor control (blheli firmware is used by RC guys to mod existing controllers).