Hi!
I have a small high speed sensorless BLDC motor (It has 6 pole pairs and 5 ohm internal winding resistance - it's a small high speed driller) that with it's original driver used to run from 2K RPM to 40K RPM, with good torque even at the lower speed setting (2k RPM). It used to draw no more than 3A when in highest torque, and it needs 30V-33V DC to run.
I've being trying for some time to re-create the driver within the same specs with help of ready-available chinese BLDC Sensorless controllers, but, as these drivers are not specific for this motor, there's always some things that don't work as expected, or some trade-offs.
Some of these controllers have a RPM pin output, so I managed to do a Speed closed loop PID, to keep speed constant under load.
So It's being easy to drive the motor with any of the drivers I tested to max speed, but I'm not being able to get to the lowest speed I know it works with any of them.
I even tried some cheap Drone ESCS, but few can withstand 30V or more, and the ones that do are designed for a lot of AMPS (and are really expensive), and I need only 3-5Amps most. And they have all that whistles and startup beeps and routines that for me aren't needed.
I've looked into Benjamin Vedder's VESC specs also, but it seems that the VESC algorithm top speed limit is lower than 40K RPM max speed I need.
Usually, thoses cheap ready-made all-around sensorless chinese controllers sets the min speed possible at 10% PWM at the potentiometer control, or at the pwm input pin, which in my case would be 4-5K RPM.
Some set the min speed to even more, 20% PWM (Like 8-10K RPM). And also their open loop start up sequence are not designed for initial high speed with higher torque . Usually are made to fans, blowers and skateboards, and you can't change their parameters, as their code are written in their MCU and not "programmable" or updatable. I've tested quite a few.
So finally I bought a development board below, based on the DRV8302 chipset that has filtered and buffered Back-EMF outputs (and these outputs are brought to a lower voltage by voltage dividers in each phase), and has current sensors for all three phases.
Using an Arduino PRO Mini and with the 6 step PWM driving Sensorless code made by Simple-Circuit, I managed to adapt the code to this DRV8302 board, and make it work really well in this development board, with almost no noise, to more than 40K at the highest speeds! In fact to 47K RPM max, at 33V. Schematics of the original setup below:
In fact, in terms of being silent and current draw it performs better than the all-around-controllers I tested, with little current draw, vibration-free.
The original code by Simple-Circuit is this one: I uses an interrupt to detect the zero-crossing of the non energized-phase to time the 6 step switching frequency. it runs at 31khz frequency PWM.
/* Sensorless brushless DC motor control with Arduino UNO and IR2101 (Arduino DIY ESC).
* BLDC motor speed is controlled with a potentiometer connected to A0.
* This is a free software with NO WARRANTY.
* https://simple-circuit.com/
*/
#define PWM_MAX_DUTY 255
#define PWM_MIN_DUTY 50
#define PWM_START_DUTY 100
byte bldc_step = 0, motor_speed, pin_state;
void setup()
{
DDRD |= 0xE0; // configure pins 5, 6 and 7 as outputs
PORTD = 0x00;
DDRB |= 0x0E; // configure pins 9, 10 and 11 as outputs
PORTB = 0x31;
// Timer1 module setting: set clock source to clkI/O / 1 (no prescaling)
TCCR1A = 0;
TCCR1B = 0x01;
// Timer2 module setting: set clock source to clkI/O / 1 (no prescaling)
TCCR2A = 0;
TCCR2B = 0x01;
// ADC module configuration
ADMUX = 0x60; // configure ADC module and select channel 0
ADCSRA = 0x84; // enable ADC module with 16 division factor (ADC clock = 1MHz)
PCICR = EIMSK = 0; // disable all external interrupts
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
pinMode(4, INPUT_PULLUP);
}
// pin change interrupt 2 (PCINT2) ISR
ISR (PCINT2_vect)
{
if( (PIND & PCMSK2) != pin_state )
return;
// BEMF debounce
for(byte i = 0; i < 20; i++)
{
if(bldc_step & 1){
if(PIND & PCMSK2) i -= 1;
}
else {
if(!(PIND & PCMSK2)) i -= 1;
}
}
bldc_move();
bldc_step++;
bldc_step %= 6;
}
// BLDC motor commutation function
void bldc_move()
{
switch(bldc_step)
{
case 0:
AH_BL();
BEMF_C_FALLING();
break;
case 1:
AH_CL();
BEMF_B_RISING();
break;
case 2:
BH_CL();
BEMF_A_FALLING();
break;
case 3:
BH_AL();
BEMF_C_RISING();
break;
case 4:
CH_AL();
BEMF_B_FALLING();
break;
case 5:
CH_BL();
BEMF_A_RISING();
}
}
void loop()
{
SET_PWM_DUTY(PWM_START_DUTY); // setup starting PWM with duty cycle = PWM_START_DUTY
int i = 5000;
// motor start
while(i > 100)
{
delayMicroseconds(i);
bldc_move();
bldc_step++;
bldc_step %= 6;
i = i - 20;
}
motor_speed = PWM_START_DUTY;
PCICR = 4; // enable pin change interrupt for pins PCINT23..16 (Arduino 0 to 7)
while(1)
{
ADCSRA |= 1 << ADSC; // start conversion
while(ADCSRA & 0x40); // wait for conversion complete
motor_speed = ADCH; // read ADC data (8 bits only)
if(motor_speed < PWM_MIN_DUTY)
motor_speed = PWM_MIN_DUTY;
SET_PWM_DUTY(motor_speed);
}
}
void BEMF_A_RISING()
{
PCMSK2 = 0x04; // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
pin_state = 0x04;
}
void BEMF_A_FALLING()
{
PCMSK2 = 0x04; // enable Arduino pin 2 (PCINT18) interrupt, others are disabled
pin_state = 0;
}
void BEMF_B_RISING()
{
PCMSK2 = 0x08; // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
pin_state = 0x08;
}
void BEMF_B_FALLING()
{
PCMSK2 = 0x08; // enable Arduino pin 3 (PCINT19) interrupt, others are disabled
pin_state = 0;
}
void BEMF_C_RISING()
{
PCMSK2 = 0x10; // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
pin_state = 0x10;
}
void BEMF_C_FALLING()
{
PCMSK2 = 0x10; // enable Arduino pin 4 (PCINT20) interrupt, others are disabled
pin_state = 0;
}
void AH_BL()
{
PORTD &= ~0xA0;
PORTD |= 0x40;
TCCR1A = 0; // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
TCCR2A = 0x81; //
}
void AH_CL()
{
PORTD &= ~0xC0;
PORTD |= 0x20;
TCCR1A = 0; // turn pin 11 (OC2A) PWM ON (pin 9 & pin 10 OFF)
TCCR2A = 0x81; //
}
void BH_CL()
{
PORTD &= ~0xC0;
PORTD |= 0x20;
TCCR2A = 0; // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
TCCR1A = 0x21; //
}
void BH_AL()
{
PORTD &= ~0x60;
PORTD |= 0x80;
TCCR2A = 0; // turn pin 10 (OC1B) PWM ON (pin 9 & pin 11 OFF)
TCCR1A = 0x21; //
}
void CH_AL()
{
PORTD &= ~0x60;
PORTD |= 0x80;
TCCR2A = 0; // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
TCCR1A = 0x81; //
}
void CH_BL()
{
PORTD &= ~0xA0;
PORTD |= 0x40;
TCCR2A = 0; // turn pin 9 (OC1A) PWM ON (pin 10 & pin 11 OFF)
TCCR1A = 0x81; //
}
void SET_PWM_DUTY(byte duty)
{
OCR1A = duty; // set pin 9 PWM duty cycle
OCR1B = duty; // set pin 10 PWM duty cycle
OCR2A = duty; // set pin 11 PWM duty cycle
}
I managed even to read the RPM using the interrupt from the zero crossing detection, when each of the the commutation step happens. I used the Gyver-PID library.
But... still.. even being able to tweak the code, I can't get to less than 4-5K RPM in my motor. When I decrease PWM switching to a certain limit, the motor stops. As there's not Back-EMF anymore available to keep the commutation sequence.
But I know the motor must generate Back-Emf at 2K RPM because the original driver did it and the motor could run at 2K RPM normally with high torque (PID) (so it would mean 5% of the max PWM, or max speed).
So I 'm trying to figure it out why I can't get lower than 4-5K RPM with my "own" driver and software.
Things that I've tried so far:
Instead of 33V, I tried to run the motors at 18VDC. It made no difference at lower speeds, just the max speed is not 40K anymore (as expected).
I tested decreasing the input voltage because with one of the chinese all-around controllers, when I decrease the input voltage for the motor, the max speed decreased (as expected) but the lower speed also decreased to even less that the 2K RPM needed. (so it seems the 10% limit is mantained, but based on a lower voltage)
But even then with PID I had almost no torque at this lower speeds. The response to change in PWM is slower in these drivers ready-made drivers I tested, to make fast PID work.
The Simple-Circuit code I adapted to my DRV8302 board has the switch PWM frequency set to 31Khz. So I changed it to 62.5k Fast PWM, and noticed no difference. Even the (small) noise was the same.
The Back-EMF in this development board is filtered and buffered and then I fed the 3 signals and a Virtual Neutral, reconstructed with the 3 filtered and buffered Back EMF and 3 10k resistors, into the external comparator, as in the Simple-Circuit schematics. A LM339 comparator is used.
So the 33V DC for the motor gets to 2.6V at the BEMF A, B or C connection pins on the DRV8302 board. Could this decreased voltage get to a point where it's no more being "sensed" on the comparators?
Could the fact that the Back-EMF, by being filtered, buffered and scaled down to less than 3V in the board, makes it's generated signals slower to be detected?
I even tried to read the Back-EMF signals "raw", by connecting them directly from the motor, with voltage dividers, bypassing the filtering and buffering steps. But then the motor rotates with a lot of noise and not smoothly, and also, don't get to slower speeds. In fact, doesn't even rotate properly. Maybe too much electrical noise from the switching PWM mosfets.
I'm running out of ideas on what to do next.
I always read that lower speeds can't generate Back-EMF. But how much in fact is a lower speed, for any motor? Is there a relation to top Speed? Can I calculate it? Can I make it's detection better?
Is there a way to improve the readings of back-emf at lower speeds? By lower to me I mean 2K RPM, in my case.
Thanks for reading this long post! If any more info is needed, just ask!