I could use some assistance. So I have been working on this motor controller for different designs. The most recent attempt is using three timers. Based on design I made my own since I don't have an UNO, I have a mega. Speed wise I have no issue but the problem I am having are as follows:
-
The motor periodically spins the opposite direction and back again, this indicates an BEMF issue. I have a comparator for each phase that looks at the BEMF voltage and creates an external interrupt clamping the BEMF and pushing the motor to the next step, this doesn't seem to work.
-
I have tried modifying the timers namely T2A and T2B using OCR2A and OCR2B. This does not work.
-
There is a odd state where the code seems to be caught in a loop with BEMFR interrupt even when throttle is zero, after some time of waiting the motor picks up after not moving or doing anything. The signals for phase ABC show no voltage flow, yet I can see the output of the comparator concerning BEMF to be oscillating, which is what is causing the ripple. I think the input on both the positve and negative of the comparator are oscillating on each other, yet this is the circuit I see online.
-
I am uncertain of this since the instructable (below) I have been referring doesn't mention it but I think the motor operating frequency would be 32Hz (1900/60), would this be correct? Either way I am uncertain how to articulate the correct on times and how this might affect BEMF. As I have said, I have been working at this for a while.
The video I have been relying on is from Great Scott on youtube + his code found here, Part 2 (most of the troubleshooting is in part 2)
His instructable is here, BLDC Instructable
My Code
(You can start reviewing my code after "byte step = 0;")
(I can increase the speed of the motor by reducing the value in the "delayl" variable.)
int qgateHApin = 7; //Q1 pin 7 designation HS pwm
int qgateHBpin = 5; //Q3 pin 5 designation HS pwm
int qgateHCpin = 3; //Q5 pin 3 designation HS pwm
int qgateLApin = 6; //Q0 pin 6 designation LS on/off
int qgateLBpin = 4; //Q2 pin 4 designation LS on/off
int qgateLCpin = 2; //Q4 pin 2 designation LS on/off
int qgateHAd = 0;
int qgateHBd = 0;
int qgateHCd = 0;
int qgateLAd = 0;
int qgateLBd = 0;
int qgateLCd = 0;
int edgevector = -1; //0; //0 is falling, 1 is rising
int edgevectorpin = 3; //20;
//Coding variables
int McurrentA = 0; //current flowing in HA fet
int McurrentB = 0; //current flowing in HB fet
int McurrentC = 0; //current flowing in HC fet
int MTa = 0; //Temperature on HA fet
int MTb = 0; //Temperature on HB fet
int MTc = 0; //Temperature on HC Fet
int HA = 0; //hall1
int HB = 0; //hall2
int HC = 0; //hall3
int Hlimit = 389;
//normal = 300, advance = 250, recess = 340 : note 5V @ 1024bit resolution.
//300bit = 1.46484375V,
//410bit = 2.0V (logical 0), anything above value is 1
//int HBlimit = 389;
//int HClimit = 389;
int scheck_delay = 6; //delay in milliseconds for checking sensor values.
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, 8 used since state is undefined, motor has not been used since on
int pstate = 8; //previous state motor was last in, make default val not real state val, 8 used since state is undefined, motor has not been used since on
int sstate = 0; //sstate is the start state to move to nex position
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, can use this for regen control along with g sensor
double throttlel = 150; //Throttle min resolution
double throttleh = 1023; //1023;//438; //438;1023 //throttle max resolution, 2.74V throttle in and 10.7V avg to motor
double throttleavg = 0;
double driveDelay = 0;
double delayl = 100000; //5000; //300; //This directly reduces or increases RPM
double delayh = 120; //; //100; //was 100ms
unsigned long lpCmicros = 0;
unsigned long lpPmicros = 0;
unsigned long Cmicros = 0;
unsigned long Pmicros = 0;
unsigned long interval = 0;
//double dutyl = 255;
//double dutyh = 0;
int hipot = 0; //throttle hi detection on start only
int hipotlimit = 25; //100 = 0.489V, hipot detection maximum
int timerpreload = 24199;
byte step = 0;
bool wait = 0;
bool doonce = 0;
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);
const unsigned int maxcurrent = 482; // 1024, 0.2v/5v = 0.04*1024
void setup() {
Serial.begin(115200);
Serial1.begin(115200);
cstate = 0;
//timer0 will interrupt at 2khz
//timer1 will interrupt at 1hz
//timer2 will interrupt at 8khz
ADCSRA &= ~PS_128;
ADCSRA |= (1 << ADPS1);
//set timer1 interupt at Xhz
TCCR1A = 0; //set entire TCCR1A register to 0
TCCR1B = 0; //same for TCCR1B
TCNT1 = 0; //initialize counter value to 0, set compare match register for 1hz increments
//ICR1 = 0;
OCR1A = 31250; //set compare match register for Xhz increments
TCCR1B |= (1 << WGM12) | (1 << CS12); //turn on CTC mode, WGM12 = parallel interrupt mode
TIMSK1 |= (1 << OCIE1A); /// enable timer when interrupt reaches OCR1A
//set timer2 interrupt at Xhz
TCCR2A = 0; //set to 0
TCCR2B = 0; //set to 0
TCNT2 = 0; //initialize counter value to 0
OCR2A = 36; //A gate. on time, set compare match register for Xhz increments
OCR2B = 200; //B gate, BEMF time, wait = 1 on set compare match register for Xhz increments
TCCR2A |= (1 << WGM21); //turn on CTC mode, WGM21 = parallel interrupt mode
TCCR2B |= (1 << CS21); //set prescaler
TIMSK2 |= (1 << OCIE2A) | (1 << OCIE2B); // enable interrupt when timer reaches OCR2A or OCR2B
sei();
// EICRA = 0;
// EICRA |= (1 << ISC11) | (1 << ISC10);
// EIMSK = 0;
// EIMSK |= (1 << INT1);
// DDRD = B11111100;
// DDRB = B00000001;
// PORTD = B00000000;
// PORTB = B00000001;
attachInterrupt(edgevectorpin, BEMFR, RISING);
interrupts();
clearstate(); //Set up gates, braking mode
Serial.println("Starting...");
//Initial read of throttle on start, disables motor if high
throttle = 0;
for (int i = 0; i < 11; i++) { // exits loop at 200
throttle = throttle + analogRead(A12);
}
throttle = throttle / 10;
if (throttle > throttleh) {
throttle = throttleh;
} else if (throttle < 0) {
throttle = 0;
}
//Ask to press brake and shift to D then apply throttle
if (throttle >= hipotlimit) { //Check resolution on A4, if greater than high throttle limit, throttle is high
hipot = 1;
cerror = 1;
statecount = ustatecount;
//debug(); //throttle position high on start, throttle off for 2sec, try again
}
//digitalWrite(cerrorpin, HIGH); //turn on error light, default on
}
//Timer 2 has higher priority then Timer 1
ISR(TIMER1_COMPA_vect) { //frequency?
//Output compqare with OCIE1A
//Registers TCCR1A TCCR1A
Serial.println("WDT...");
throttle = 0;
for (int i = 0; i < 11; i++) { // exits loop at 200
throttle = throttle + analogRead(A12);
}
if (throttle > throttlel) {
throttle = throttle / 10;
if (throttle > throttleh) {
throttle = throttleh;
} else if (throttle < 0) {
throttle = 0;
}
// digitalWrite(49, HIGH);
// digitalWrite(49, LOW);
OCR1A = map(throttle, throttlel, throttleh, delayl, delayh);
driveDelay = OCR1A;
// Serial.println("Throttling...");
Serial.print("throttle read: ");
Serial.println(throttle);
Serial.print("OCR1A: ");
Serial.println(OCR1A);
// Serial.print("OCR1A: ");
// Serial.println(OCR1A);
// Serial.println("Driving Motor...");
// Serial.print("");
// Serial.println("");
// Serial.print("Duty: ");
// Serial.println(duty);
// Serial.print("Delay (ms) ");
// Serial.println(driveDelay);
// duty = (1 - (driveDelay / delayl));
// if (driveDelay > delayl) {
// driveDelay = delayl;
// } else if (driveDelay < delayh) {
// driveDelay = delayh;
// }
cstate++;
Serial.print("cstate: ");
Serial.println(cstate);
doonce = 0;
if (cstate == 6) {
cstate = 0;
}
Serial.println("WDT... done");
} else { //Throttle is off
//regen here?
clearstate();
OCR1A = 31250;
}
}
ISR(TIMER2_COMPB_vect) { //frequency?, B drives gate off time
//Output compqare with OCIE2B
//Registers TCCR2B
if (wait == 0) {
wait = 1;
doonce = 0;
}
}
ISR(TIMER2_COMPA_vect) { //frequency?, A drives gate on time
//Output compqare with OCIE2A
//Registers TCCR2A
if (wait == 1) {
wait = 0;
doonce = 0;
}
//Serial.println(wait);
}
// void loop() {
// int j = 5000;
// while (j > 100) {
// //delayMicroseconds(j);
// throttleControl();
// cstate++;
// cstate %= 6;
// j = j - 20;
// // }
// changeThrottleDelay();
// }
// }
void throttleControl() {
if (throttle > throttlel) { //Prevents forcing duty high even though throttle is not on
Serial.println("Throttling...");
//throttle is up
if (throttle > throttleh) {
throttle = throttleh;
}
Serial.println("Driving Motor...");
//motorAction(); //drive one phase
delay(driveDelay);
}
}
void changeThrottleDelay() {
//use map for upper throttle limit based on motor temp and current draw
//call motorphase current
for (int i = 0; i < 201; i++) { // exits loop at 200
throttle = throttle + analogRead(A12);
}
throttle = throttle / 200;
Serial.print("throttle read: ");
Serial.println(throttle);
driveDelay = map(throttle, throttlel, throttleh, delayl, delayh);
duty = (1 - (driveDelay / delayl));
if (driveDelay > delayl) {
driveDelay = delayl;
} else if (driveDelay < delayh) {
driveDelay = delayh;
}
Serial.print("");
Serial.println("");
Serial.print("Duty: ");
Serial.println(duty);
Serial.print("Delay (ms) ");
Serial.println(driveDelay);
}
void BEMFR() {
Serial.println(" >>Rising Edge 49, High");
digitalWrite(49, HIGH);
clearstate();
if (cstate == 0) {
GateLowerAHigh();
GateLowerBHigh();
} else if (cstate == 1) {
GateLowerAHigh();
GateLowerCHigh();
} else if (cstate == 2) {
GateLowerBHigh();
GateLowerCHigh();
} else if (cstate == 3) {
GateLowerBHigh();
GateLowerAHigh();
} else if (cstate == 4) {
GateLowerCHigh();
GateLowerAHigh();
} else if (cstate == 5) {
GateLowerCHigh();
GateLowerBHigh();
}
cstate++;
if (cstate == 6) {
cstate = 0;
}
clearstate();
digitalWrite(49, LOW);
Serial.println("49, Low");
doonce = 1;
wait = 1;
TCNT2 = 0;
cli();
}
void edgedetectF() {
// HA = digitalRead(A15);
// HB = digitalRead(A14);
// HC = digitalRead(A13);
// digitalWrite(49, HIGH);
// Serial.println("Falling Edge 49, High");
// digitalWrite(49, LOW);
// Serial.println("49, Low");
// //noInterrupts();
}
void clearstate() {
//Turns all fets off
//HIGHSIDE
analogWrite(qgateHApin, 0);
analogWrite(qgateHBpin, 0);
analogWrite(qgateHCpin, 0);
//documenting state change
qgateHAd = LOW;
qgateHBd = LOW;
qgateHCd = LOW;
//LOWSIDE
analogWrite(qgateLApin, 0);
analogWrite(qgateLBpin, 0);
analogWrite(qgateLCpin, 0);
//document state change
qgateLAd = LOW;
qgateLBd = LOW;
qgateLCd = LOW;
}
//void motorAction() {
void loop() {
Serial.println("Main loop");
Serial.print("Loop doonce: ");
Serial.println(doonce);
Serial.print("Loop wait: ");
Serial.println(wait);
//throttle = analogRead(A12);
Serial.print("throttle: ");
Serial.println(throttle);
Serial.print("throttlel: ");
Serial.println(throttlel);
Serial.print("OCR1A: ");
Serial.println(OCR1A);
//check gear position
//Serial.println("Motor Action");
//Serial.print(doonce);
if (cstate > 5) {
cstate = 5;
} else if (cstate < 0) {
cstate = 0;
}
if (doonce == 0) {
if (throttle > throttlel) {
Serial.println("Powering Gates - cstate: ");
Serial.println(cstate);
clearstate();
switch (cstate) {
case 0:
if (wait == 0) {
//PORTD = B10010000; //PIN 7,4 AB
GateUpperAHigh();
GateLowerBHigh();
doonce = 1;
} else {
GateLowerAHigh();
GateLowerBHigh();
doonce = 1;
TCNT2 = 0;
}
break;
case 1:
if (wait == 0) {
//PORTD = B10000100; //PIN 7,2 AC
GateUpperAHigh();
GateLowerCHigh();
doonce = 1;
} else {
GateLowerAHigh();
GateLowerCHigh();
doonce = 1;
TCNT2 = 0;
}
break;
case 2:
if (wait == 0) {
//PORTD = B00100100; //PIN 5,2 BC
GateUpperBHigh();
clearstate();
GateLowerCHigh();
doonce = 1;
} else {
GateLowerBHigh();
GateLowerCHigh();
doonce = 1;
TCNT2 = 0;
}
break;
case 3:
if (wait == 0) {
//PORTD = B01100000; //PIN 5,6 BA
GateUpperBHigh();
GateLowerAHigh();
doonce = 1;
} else {
GateLowerBHigh();
GateLowerAHigh();
doonce = 1;
TCNT2 = 0;
}
break;
case 4:
if (wait == 0) {
//PORTD = B01001000; //PIN 3,6 CA
GateUpperCHigh();
GateLowerAHigh();
doonce = 1;
} else {
GateLowerCHigh();
GateLowerAHigh();
doonce = 1;
TCNT2 = 0;
}
break;
case 5:
if (wait == 0) {
//PORTD = B00011000; //PIN 3,4 CB
GateUpperCHigh();
GateLowerBHigh();
doonce = 1;
} else {
GateLowerCHigh();
GateLowerBHigh();
doonce = 1;
TCNT2 = 0;
}
break;
}
clearstate();
}
}
}
void motor1phasecurrent() {
//phase current check and motor temperature check
//check motor or gate temperature?
//analog read for temperature if too high reduce current
//analog read for current check if too high reduce current
//change duty cycle
}
//Gate A Upper Lower
void GateUpperAHigh() {
qgateHAd = HIGH;
analogWrite(qgateHApin, 255);
//digitalWrite(qgateHApin, HIGH);
Serial.print(" GateHA ");
Serial.print(" ");
Serial.println(qgateHAd);
}
void GateUpperALow() {
qgateHAd = LOW;
analogWrite(qgateHApin, 0);
//digitalWrite(qgateHApin, LOW);
Serial.print(" GateHA ");
Serial.print(" ");
Serial.println(qgateHAd);
}
void GateLowerAHigh() {
qgateLAd = HIGH;
analogWrite(qgateLApin, 255);
//digitalWrite(qgateLApin, HIGH);
Serial.print(" GateLA ");
Serial.print(" ");
Serial.println(qgateLAd);
}
void GateLowerALow() {
qgateLAd = LOW;
analogWrite(qgateLApin, 0);
//digitalWrite(qgateLApin, LOW);
Serial.print(" GateLA ");
Serial.print(" ");
Serial.println(qgateLAd);
}
//Gate B Upper Lower
void GateUpperBHigh() {
qgateHBd = HIGH;
analogWrite(qgateHBpin, 255);
//digitalWrite(qgateHBpin, HIGH);
Serial.print(" GateHB ");
Serial.print(" ");
Serial.println(qgateHBd);
}
void GateUpperBLow() {
qgateHBd = LOW;
analogWrite(qgateHBpin, 0);
//digitalWrite(qgateHBpin, LOW);
Serial.print(" GateHB ");
Serial.print(" ");
Serial.println(qgateHBd);
}
void GateLowerBHigh() {
qgateLBd = HIGH;
analogWrite(qgateLBpin, 255);
//digitalWrite(qgateLBpin, HIGH);
Serial.print(" GateLB ");
Serial.print(" ");
Serial.println(qgateLBd);
}
void GateLowerBLow() {
qgateLBd = LOW;
analogWrite(qgateLBpin, 0);
//digitalWrite(qgateLBpin, LOW);
Serial.print(" GateLB ");
Serial.print(" ");
Serial.println(qgateLBd);
}
//Gate C Upper Lower
void GateUpperCHigh() {
qgateHCd = HIGH;
analogWrite(qgateHCpin, 255);
//digitalWrite(qgateHCpin, HIGH);
Serial.print(" GateHC ");
Serial.print(" ");
Serial.println(qgateHCd);
}
void GateUpperCLow() {
qgateHCd = LOW;
analogWrite(qgateHCpin, 0);
//digitalWrite(qgateHCpin, LOW);
Serial.print(" GateHC ");
Serial.print(" ");
Serial.println(qgateHCd);
}
void GateLowerCHigh() {
qgateLCd = HIGH;
analogWrite(qgateLCpin, 255);
//digitalWrite(qgateLCpin, HIGH);
Serial.print(" GateLC ");
Serial.print(" ");
Serial.println(qgateLCd);
}
void GateLowerCLow() {
qgateLCd = LOW;
analogWrite(qgateLCpin, 0);
//digitalWrite(qgateLCpin, LOW);
Serial.print(" GateLC ");
Serial.print(" ");
Serial.println(qgateLCd);
}
Thanks for any advice.