Mega 2560 BLDC Controller

Hello everyone,

I am currently attempting to make a BLDC controller for a motor from scratch. I need to make it from scratch because I have more plans for this project beyond what it currently is. My motor runs but I find that at times it goes in the wrong direction. This is even after I confirmed each individual state drives the motor clockwise by commenting all other states. I assuming it is a timing problem but i am not sure how to rectify it.

A few brief details about my project…

  1. I am driving my motor using a 3 phase bridge with bootstrapped NMOSfets.
  2. I am using 6 PWM signals but only driving the top side with PWM while I keep the corresponding low side fet ON during the power cycle.
  3. My high side and low side are electrically inverted. Meaning on power on without the arduino doing anything there is no shoot through possible. Because of this I MAP my duty cycle for my top side 255 to 0 and not 0 to 255.
  4. I am using a sensor design with 3 Hall sensors.

The code is below. Thank you for any assistance and direction you can lend!

//Last update Oct 14 2017
#include <Servo.h>
// Initialization of global variables
//there is a variable called time!!
//Define pins by variables
double duty = 0;
//int cerrorpin = 13; //error LED out pin (pin 13 for UNO)
int qgateHApin = 6; //Q1 pin 6 designation HS
int qgateHBpin = 4; //Q3 pin 4 designation HS
int qgateHCpin = 2; //Q5 pin 2 designation HS
int qgateLApin = 7; //Q0 pin 5 designation LS
int qgateLBpin = 5; //Q2 pin 3 designation LS
int qgateLCpin = 3; //Q4 pin 1 designation LS

//Coding variables
int HA = 0; //hall1
int HB = 0; //hall2
int HC = 0; //hall3
int cerror = 0; //Error code
int cerrordelay = 0; //error LED flasher delay
int counter = 0; //simple reusable counter
int cstate = 8; //current state motor is presently in, make default val not real state val
int pstate = 8; //previous state motor was last in, make default val not real state val
int staterate = 0; //Rate of motor turning
int statecount = 0;//state counter, how many times motor stays in same state based on throttle
int ustatecount = 500; //upper limit to statcount, multiply by Tper to get approx time
double throttle = 0; //throttle position 0k to 10k,
double throttlel = 100; //Throttle min resolution
//double throttleh = 1023; //throttle max resolution
double throttleh = 438; //throttle max resolution
double throttleavg = 0;
int throttleDelay = 100;
double dutyl = 255;
double dutyh = 0;
int hipot = 0; //throttle hi detection on start only
int hipotlimit = 25; //100 = 0.489V, hipot detection maximum

void setup() {
// initialize digital gate pins as an output.
//INVERTED LOGIC USED TO PREVENT SHOOT THROUGH AT BOOT UP
//HIGHSIDE
Serial.begin(115200);
analogWrite(qgateHApin, 255);//inverted logic prevents shoot through
analogWrite(qgateHBpin, 255);
analogWrite(qgateHCpin, 255);
//LOWSIDE
analogWrite(qgateLApin, 0);
analogWrite(qgateLBpin, 0);
analogWrite(qgateLCpin, 0);

delay(1000); //wait 1s during initialization, then check throttle position
throttle = analogRead(A12); //Initial read of throttle on start, disables motor if high
if (throttle >= hipotlimit) { //Check resolution on A4, if greater than high throttle limit, throttle is high
hipot = 1;
cerror = 1;
statecount = ustatecount;
}
//digitalWrite(cerrorpin, HIGH); //turn on error light, default on
}

void loop() {//Begin Driver Control
//Enable main power relay here
//If I use analogRead here, I can do advance and recessed timing using voltage thresholds
//Advance timing increases RPM and Temp, Torque and efficiency down
//Recess timing increases Torque and efficiency, RPM and Temp down

//HA = digitalRead(A15); //Digital Read of Analog0, Hall gives HIGH with North Polarity
//HB = digitalRead(A14); //Digital Read of Analog1, Hall gives HIGH with North Polarity
//HC = digitalRead(A13); //Digital Read of Analog2, Hall gives HIGH with North Polarity

HA = analogRead(A15);
HB = analogRead(A14);
HC = analogRead(A13);

if (HA >= 300) {
HA = HIGH;
}
else {
HA = LOW;
}
if (HB >= 300) {
HB = HIGH;
}
else {
HB = LOW;
}
if (HC >= 300) {
HC = HIGH;
}
else {
HC = LOW;
}

/*State Block, state table; ctate
state 0 = 000, error (HC HB HA values)
state 1 = 001
state 2 = 010
state 3 = 011
state 4 = 100
state 5 = 101
state 6 = 110
state 7 = 111, errror
*/
pstate = cstate; //pstate retains past value of cstate
//Logic here is that HA is least significant, careful of this
if (HC == LOW && HB == LOW && HA == LOW) {//lookup table code 000
cstate = 0;
}
else if (HC == LOW && HB == LOW && HA == HIGH) { //lookup table code 001
cstate = 1;
}
else if (HC == LOW && HB == HIGH && HA == LOW) {//lookup table code 010
cstate = 2;
}
else if (HC == LOW && HB == HIGH && HA == HIGH) {//lookup table code 011
cstate = 3;
}
else if (HC == HIGH && HB == LOW && HA == LOW) {//lookup table code 100
cstate = 4;
}
else if (HC == HIGH && HB == LOW && HA == HIGH) {//lookup table code 101
cstate = 5;
}
else if (HC == HIGH && HB == HIGH && HA == LOW) {//lookup table code 110
cstate = 6;
}
else if (HC == HIGH && HB == HIGH && HA == HIGH) {//lookup table code 111
cstate = 7;
}

Serial.print("HA: ");
Serial.println(HA);
Serial.print("HB: ");
Serial.println(HB);
Serial.print("HC: ");
Serial.println(HC);

throttle = analogRead(A12);
if (throttle >= throttlel) { //Prevents forcing duty high even though throttle is not on
//if (throttle >= throttlel && hipot == 0 && cerror == 0) {
//dutyl and dutyh swapped values in setup since High side is inverted
for (int i = 0; i < 200; i++) {
throttle = throttle + analogRead(A12);
}
throttle = throttle / 201;
duty = map(throttle, throttlel, throttleh, dutyl, dutyh);
if (duty > dutyl) {
duty = dutyl;
}
else if (duty < dutyh) {
duty = dutyh;
}
Serial.print("throttle read: ");
Serial.println(throttle);
Serial.print("Duty: ");
Serial.println(duty);
Serial.println(“Driving motor”);
motorAction();
changeThrottle();

// int step = 1;
// if (int dutyAdjust > duty) {
// int step = -1;
// }
}
else {
duty = dutyl;
clearstate();
}
}

void changeThrottle() {

}

void clearstate () {
//HIGHSIDE
analogWrite(qgateHApin, 255);//inverted logic prevents shoot through
analogWrite(qgateHBpin, 255);
analogWrite(qgateHCpin, 255);
//LOWSIDE
analogWrite(qgateLApin, 0);
analogWrite(qgateLBpin, 0);
analogWrite(qgateLCpin, 0);
}

void motorAction () {
//Commutation Block
if (cstate == 0) {//lookup table code 000
//detected as an error
//see error checker, nothing done here
}
else if (cstate == 1) {//OK
//lookup table code 001
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHApin, duty);
analogWrite(qgateLCpin, 255);
}
else if (cstate == 2) {//OK
//lookup table code 010
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHBpin, duty);
analogWrite(qgateLApin, 255);
}
else if (cstate == 3) {//OK
//lookup table code 011
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHCpin, duty);
analogWrite(qgateLApin, 255);
}
else if (cstate == 4) {//ok
//lookup table code 100
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHApin, duty);
analogWrite(qgateLBpin, 255);
}
else if (cstate == 5) {//ok
//lookup table code 101
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHApin, duty);
analogWrite(qgateLCpin, 255);
}
else if (cstate == 6) {//ok
//lookup table code 110
//what is equal to high and low according to arduino
clearstate();
analogWrite(qgateHApin, duty);
analogWrite(qgateLBpin, 255);
}
else if (cstate == 7) {//lookup table code 111
//detected as an error
//see error checker, nothing done here
}
}

Well firstly you should only have 6 states for the hall sensors, not 8, they go either:

001
011
010
110
100
101
001 (for a 120 degree hall placement)

or:

000
001
011
111
110
100
000 (for 60 degree placement)

Your code has to know whether its 60 or 120 to work. 120 seems the commonest scheme in
my experience.

The hall sensors are Hall switches, ie they have logic outputs. Well in every BLDC motor I've played
with, so no need for analog pins for them - calling analogRead 3 times is slowing everything down.

If you use a lookup table, use gate pins all on the same PORT, you can update the outputs with
a single instruction (though that forces the PWM to be done in software).

The main problem I think you are having is that you have to get the phasing right between the
outputs and the Hall inputs - there are many ways to get it wrong, which will give behaviour that
is unsatisfactory to various degrees.

What I've done for this sort of problem is add various variables in the code that allow reversal
and shifting of the mapping between inputs and outputs, and try the various conbinations till
I hit on the one that works. The 60/120 selection can be done similarly.

You use the variables to fill in the lookup table at startup, and then the actual loop is little more
than combine the hall values into a single variable to index the table.

Hello Mark,

I identify all 8 states but only drive the six. The states 000 and 111 I currently identify as an error.

The order you list, is that regardless of the amount of poles the motor has? I think this may be the issue I am having.

I currently have my hall sensors positioned in a way that will not give me 000 and 111. I thought this was enough. But it seems like you said, I need to have them either 120 deg or 60 deg. All three of my sensors currently sit within about 45 deg (close to each other).

Is there a particular way I can accurately place my sensors at 120deg? I can guess for sure but I'd rather something more concrete.

Thanks!

The pole count is not relevant. We only know about electrical phase/degrees, the actual rotor angles are
unknown to the driver, electrically there are 6 states per 360 electrical degrees.