DIY ESC odd efficiency problem when BLDC drone motor is spinning under load

Hello everyone this is my second post in relation to an ESC project ive been working on for a while now I am done the basics of its design, designed a few PCBs and put in all the wanted features to the code now I have: the BEMF set up, 32khz square wave variable duty PWM and bootstrap drivers for the highside and lowside MOSFETs (PWM on highside) on all 3 phases so they all can be N channel, digital speed and reverse input, stall detection and a few basic beeps on start up and to indicate motor stall. The 2700 and 1000kv A2212 motors run in closed loop mode just fine with no load and I have attached the waveforms from the 1000kv motor which look pretty similar to a yellow commercial version so i think the closed loop is working fine, but my problem comes when I attach a small prop to act as a load to the motor.

In this case the motor works okay at low speeds but when the speed reaches roughly 50-60% power the motor and waveforms go from looking even, sounding even and maintaining a steady speed to making an almost grinding/radio static sound as it rotates, producing for lack of better words "wonky and erratic" waveforms (photos below), and oscillating between high speeds (think jumping from 70 to 60% power and then back to 70 for a second then up to 90 really quick and back down to a steady 80%) all while getting very hot, it also makes the static sound with no load when I break it with my finger only briefly though as when I move my finger it goes back to smooth closed loop spinning.

I have tried multiple fixes such as different PWM frequencies, Setting the code and driver inputs up so that its the lowsides getting hit with the PWM to manage the speed instead of it hitting the highsides, adding different decoupling capacitors on the power supply rails, pull down resistors on the MOSFETs gates and a bunch of little code tweaks in relation to the dead time between steps and how the motor starts off spinning from open to closed loop. So far none of this has worked and I have been unable to diagnose what's causing my ESC to have this sudden I assume forced switch to a sort of open loop from the closed loop control at high power under load if anyone has more experience with power electronics and has an input on what might be causing this I would love to hear from you.

I have attached my code, waveform photos, THT schematic and a photo of the THT pcb ive designed (although both smd and tht versions run into basically the same issue tht is easier to reprogram so I'm trying to solve it on the tht version first). Thanks in advance Harry.

My code for the Atmega328: ( I annotate as I code so sorry for some of the spelling :smile: )

#define DELAY_CYCLES(n) __builtin_avr_delay_cycles(n) //clock cycle based delay (1 = 62.5nS @16MHZ)
volatile long DutyMicros = 0;
volatile long pulseTime = 0;
volatile long triggerPoint = 0;
volatile long risePoint = 0;
volatile long fallPoint = 0;
volatile long FirstHigh = 0;
volatile long SecondHigh = 0;
volatile int PulseStarted = 0;
volatile int PrevState = 0;
volatile int FilteredDuty = 0;
volatile int SetDutyVariable = 0;
volatile int UpdateDuty = 0;
volatile int StepCount = 0;
volatile int spinDirection = 0;
volatile int StartRevving = 0;
volatile int ChngDir = 0;
volatile int UpdateStallWatchdog = 0;
volatile int RisingSteps = 0;
volatile int FallingSteps = 0;
long HoldUpnumber = 0;
int DutyPercent = 0;
int motorSpinning = 0;
int motorPrevOff = 1;
int lastSetDuty = 0;
int ElecCycles = 0;
int BPCnt = 0;
int ChangingSteps = 0;
int motorStalled = 0;
int StallCheckCycles = 0;
int StallCheckTimeout = 0;
int AsetPWM = B01100001;
int BsetPWM = B10000010;
int CsetPWM = B00100010;
int ALow = B10100000;
int BLow = B10000100;
int CLow = B00100100;

void setup() {
  
  DDRD = (1<<PD2) + (1<<PD5) + (1<<PD7) + (1<<PD3); //Sets all outputs
  DDRB = (1<<PB1) + (1<<PB2); 

  //enables pin changes for speed input
  PCICR = B00000101; //sets portD and B to have interput inputs
  PCMSK2 = B00010000; //sets portD4 to trigger the speed interupt 
  PCMSK0 = B00100000; //sets portB5 trigger the direction change interupt 

  //timer1 PWM pins D9 and D10 setup
  TCCR1A = 0;
  TCCR1B = 0;
  TCCR1A = (1<<COM1A1) + (1<<COM1B1) + (1<<WGM11); //phase correct pwm
  TCCR1B = (1<<WGM13) + (1<<CS10);
  ICR1 = 250; //set 32 kHz
  OCR1A = 0; //set % duty
  OCR1B = 0; //set % duty

  //timer2 PWM pin D3 setup
  TCCR2A = 0;
  TCCR2B = 0;
  TCCR2A = (1<<COM2A0) + (1<<COM2B1) + (1<<WGM20); //phase correct pwm
  TCCR2B = (1<<CS20) + (1<<WGM22);
  OCR2A = 250; //set 32 kHz
  OCR2B = 0; //set % duty

  //internal comparator setup
  ACSR = B00011000; //enables the ISR
  ADCSRA = (0<<ADEN); // disable the ADC module
  ADCSRB = (1<<ACME); // enable the MUX selector for negative input of comparator
  ACSR |= B00000011; //sets rising edge interupt
  ADMUX = 001; // Select A1 as comparator negative input  

  ICR1 = 25; //set 320 kHz for startup tone
  OCR2A = 25; //set 320 kHz for startup tone
  StartUpSound(); //Plays a tone through the motor showing esc has started succesfully and is waiting in standby
  ICR1 = 250; //set back to 32 kHz
  OCR2A = 250; //set back to 32 kHz
  
  //sets all phases to floating to stand by
  PORTD = B10100100;
  TCCR2A = 0;
  TCCR1A = 0;
  OCR1A = 0; //sets PWM to 0% duty
  OCR1B = 0;
  OCR2B = 0;
}

void loop() { //CLOSED MIAN CONTROL LOOP WITH BEMF, REVERSE AND HIGH SPEED STALL DETECT IMPLEMENTED
    
  if (UpdateDuty == 1){ //check to see if the duty is in need of an update
    DutyPercent = FilteredDuty; //pulls duty from the interupt generating it into a stable variable for the loops use
    UpdateDuty = 0; //ensures loop only runs once
  }

  if ((UpdateStallWatchdog == 1) && (motorStalled == 0)){ //Checking the flag set by the stall Watchdog if the motor is running
    ChangingSteps = 1; //saving said flag thats telling the loop the watchdog says the motor is still spinning
    UpdateStallWatchdog = 0;
  }
  
  if ((StallCheckCycles <= 150) && (motorStalled == 0)){ //if it hasnt been long enough to check for a stall this runs
    StallCheckCycles++; //increase the counter 
    StallCheckTimeout = 0; //set stall check flag off
    DELAY_CYCLES(1);
  }
  else if ((StallCheckCycles > 150) && (motorStalled == 0)){ //if it has been long enough to check for a stall this runs
    StallCheckTimeout = 1; //set stall check flag on
    StallCheckCycles = 0;  //reset the stall timer
  }

  if ((lastSetDuty >= 170) && (motorStalled == 0) && (StallCheckTimeout == 1) && (ChangingSteps == 1)){ //this runs if the motor is still running
    ChangingSteps = 0; //resets the watchdog output to see if it stays reset or the watchdog fixes it meaning the motor is still spinning
    StallCheckTimeout = 0; //resets the timeout flag
  }
  else if ((lastSetDuty >= 170) && (motorStalled == 0) && (StallCheckTimeout == 1) && (ChangingSteps == 0)){ //this runs if the motor has stalled
    cli();
    PORTD = B10100100; //set motor to floatig
    TCCR2A = 0;
    TCCR1A = 0;
    OCR1A = 0; //sets PWM to 0% duty
    OCR1B = 0;
    OCR2B = 0;
    motorStalled = 1; //ensures nothing runs until the speed input drops to be off and the motor can restart safely
    StallCheckTimeout = 0;
    StallSound(); //plays stall sound 
    sei();
  }

  if ((StartRevving == 1) && (lastSetDuty < 50) && (ChngDir == 1)){ //sets the spin direction
    spinDirection = 1;
    ChngDir = 0;
  }
  else if ((StartRevving == 0) && (lastSetDuty < 50) && (ChngDir == 1)){ //sets the spin direction
    spinDirection = 0;
    ChngDir = 0;
  }

  if ((DutyPercent >= 50) && (motorStalled == 0)){ //if the duty is above 0 the motor needs to be spinning so duty cycle is set

    OCR1A = DutyPercent; //sets PWM duty
    OCR1B = DutyPercent;
    OCR2B = DutyPercent;
    lastSetDuty = DutyPercent;
    motorSpinning = 1;
    if (motorPrevOff == 1){ //for if the motor was just off and needs to start
      OCR1A = 55; //sets PWM to low duty
      OCR1B = 55;
      OCR2B = 55;
      cli(); // disables interupts so it can start in open-loop
      ElecCycles = 0; //forces the motor electrical cycles variable to 0
      while (ElecCycles < 4){ //runs while the motor has done less than 8 full step 1-6 cycles 
        ElecCycles++; //goes up every full loop
        for (StepCount = 1; StepCount <= 6; StepCount++){ // for loop to make push the motor through all 6 steps sequentially
          if (spinDirection == 0) { TakeStep(StepCount); } //take the next step of the sequence
          else if (spinDirection == 1) { TakeRevStep(StepCount); } //take the next reverse step of the sequence
          if ((ElecCycles == 4) && (StepCount == 6)) { 
            StepCount = 2;
            sei(); 
            if (spinDirection == 0) { TakeStep(StepCount); } //take the next step of the sequence
            else if (spinDirection == 1) { TakeRevStep(StepCount); } //take the next reverse step of the sequence
          }
          else { DELAY_CYCLES(40000); }
        }
      }
      motorPrevOff = 0; // tells the code the motor is now running
      StallCheckCycles = 0; //resets stall timer as motor starts
      StallCheckTimeout = 0;
    }
  }
  
  if ((motorSpinning == 1) && (DutyPercent < 50)) { //if motor needs to be turned off
    
    //sets all phases to floating
    PORTD = B10100100;
    TCCR2A = 0;
    TCCR1A = 0;
    OCR1A = 0; //sets PWM to 0% duty
    OCR1B = 0;
    OCR2B = 0;
    lastSetDuty = 0; //resets variables for next start
    motorSpinning = 0;
    motorPrevOff = 1;
    motorStalled = 0;
    
  }
  
}

ISR (ANALOG_COMP_vect) {//analog comparator interput for step change
 
  if ((StepCount & 1) && (ACSR & B00100000)){ // if step is odd and rising edge was just triggered

    StepCount++; //increment step by 1
    RisingSteps = 1;
    if (StepCount > 6) {StepCount = 1;} //If step is larger than 6 set step back to 1
    if (spinDirection == 0) { TakeStep(StepCount); } //take the next step of the sequence
    else if (spinDirection == 1) { TakeRevStep(StepCount); } //take the next reverse step of the sequence          
  }
  
  else if ((!(StepCount & 1)) && (!(ACSR & B00100000))) { //if not odd step must be even and falling edge was just triggered

    StepCount++; //increment step by 1
    FallingSteps = 1;
    if (StepCount > 6) {StepCount = 1;} //If step is larger than 6 set step back to 1
    if (spinDirection == 0) { TakeStep(StepCount); } //take the next step of the sequence
    else if (spinDirection == 1) { TakeRevStep(StepCount); } //take the next reverse step of the sequence
  }

  if ((RisingSteps == 1) && (FallingSteps == 1)){
    UpdateStallWatchdog = 1;
    RisingSteps = 0;
    FallingSteps = 0;
  }
  
}

ISR(PCINT2_vect){ //interput vector that does all the speed input signal detecting
  
  triggerPoint = micros(); //stores the time of interput start as micros
  //this part calcs duty cycle pulse
  if ((PIND & B00010000) && (PrevState == 0)){ //if input pin just went high and was just low
    PrevState = 1; //records that the input pin is currently high
  }
  else if ((!(PIND & B00010000)) && (PrevState == 1)){ //if the input pin just went low and was just high
    PrevState = 0; //records that the input pin is currently low
    fallPoint = triggerPoint; //sets the duty fall point
  }

  //this part calcs frequency using the time of a full pulse (high to low and back to high again)
  if ((PrevState == 1) && (PulseStarted == 0)){ //if input is high and its the start of a new full pulse
    FirstHigh = triggerPoint; //saves the micros time
    risePoint = triggerPoint; //sets the duty risepoint
    PulseStarted = 1; //records that the new full pulse has started
  }
  else if ((PrevState == 1) && (PulseStarted == 1)){ //if the input is high again and a full pulse was started on prev high
    SecondHigh = triggerPoint; //saves the micros time
    DutyMicros = (fallPoint - risePoint); //calcs the duty in microseconds
    pulseTime = (SecondHigh - FirstHigh); //calcs the total time it took to pulse from high (start of new pulse) to low and back to high again giving the time in micros of the full pulse
    PulseStarted = 0; //records that the next high will be a new full pulse
    SetDutyVariable = 1; //tells interput to check and calucate the duty to be pushed to main loop for setting
  }
  

  if ((SetDutyVariable == 1) && (pulseTime >= 100) && (pulseTime <= 10000) && (DutyMicros < pulseTime) && (DutyMicros > 1)){ //Filter to make sure the raw signal can be mapped properly
    
    FilteredDuty = map(DutyMicros, 1, pulseTime, 45, 250); //maps the duty cycle using micros freq as top into a rough percentage 
    if (FilteredDuty < 50){ //constrains duty to ensure its within the correct range
      FilteredDuty = 0;
    }
    else if (FilteredDuty > 245){
      FilteredDuty = 245;
    }
    UpdateDuty = 1; //sets an update flag to be seen by the main loop
    SetDutyVariable = 0; //ensures this loop only runs when told to
    }
  else if (SetDutyVariable == 1){
    SetDutyVariable = 0; //ensures this loop only runs when told to
    }  
}

ISR (PCINT0_vect) { //pin change interupt vector for direction change

  if ((PINB & B00100000)){ //checks if pin 13 went high rev
    StartRevving = 1; //sets flag for the main loop
    ChngDir = 1;
  }
  else if (!(PINB & B00100000)){ //checks if pin 13 went low fwrd
    StartRevving = 0; //sets flag for the main loop
    ChngDir = 1;
  }
   
}

void TakeStep(int stepNum){ //Forward steps setting function

  switch(stepNum){

    case 1://step1: A, PWM. B, low. C, floating/bemfsense.
    TCCR1A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = BLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR2A = AsetPWM; //set the wanted highside pulsing
    ADMUX = 3; //set A3 as negative side comparator input (phase C)
    ACSR |= 0x03; //sets rising edge interupt
    break;

    case 2: //step2: A, PWM. C, low. B, floating/bemfsense.
    TCCR1A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = CLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR2A = AsetPWM; //set the wanted highside pulsing
    ADMUX = 2; //set A2 as negative side comparator input (phase B)
    ACSR &= ~0x01; //sets falling edge interupt
    break;

    case 3: //step3: B, PWM. C, low. A, floating/bemfsense.
    TCCR2A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = CLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = BsetPWM; //set the wanted highside pulsing
    ADMUX = 1; //set A1 as negative side comparator input (phase A)
    ACSR |= 0x03; //sets rising edge interupt
    break;

    case 4: //step4: B, PWM. A, low. C, floating/bemfsense.
    TCCR2A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = ALow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = BsetPWM; //set the wanted highside pulsing 
    ADMUX = 3; //set A3 as negative side comparator input (phase C)
    ACSR &= ~0x01; //sets falling edge interupt
    break;

    case 5: //step5: C, PWM. A, low. B, floating/bemfsense.
    TCCR2A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = ALow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = CsetPWM; //set the wanted highside pulsing 
    ADMUX = 2; //set A2 as negative side comparator input (phase B)
    ACSR |= 0x03; //sets rising edge interupt
    break;

    case 6: //step6: C, PWM. B, low. A, floating/bemfsense.
    TCCR2A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = BLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = CsetPWM; //set the wanted highside pulsing  
    ADMUX = 1; //set A1 as negative side comparator input (phase A)
    ACSR &= ~0x01; //sets falling edge interupt
    break;
    
  }
} 

void TakeRevStep (int revstepNum){ //reverse steps setting function

  switch(revstepNum){

    case 1: //Step 1 C, PWM. B, low. A, floating/bemfsense.
    TCCR2A = 0; //turn off unwanted highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = BLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = CsetPWM; //set the wanted highside pulsing  
    ADMUX = 1; //set A1 as negative side comparator input (phase A)
    ACSR |= 0x03; //sets rising edge interupt
    break; 

    case 2: //step5: C, PWM. A, low. B, floating/bemfsense.
    TCCR2A = 0; //turn off unwated highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = ALow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = CsetPWM; //set the wanted highside pulsing 
    ADMUX = 2; //set A2 as negative side comparator input (phase B)
    ACSR &= ~0x01; //sets falling edge interupt
    break;

    case 3: //step4: B, PWM. A, low. C, floating/bemfsense.
    TCCR2A = 0; //turn off unwated highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = ALow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR1A = BsetPWM; //set the wanted highside pulsing  
    ADMUX = 3; //set A3 as negative side comparator input (phase C)
    ACSR |= 0x03; //sets rising edge interupt
    break;

    case 4: //step3: B, PWM. C, low. A, floating/bemfsense.
    TCCR2A = 0; //turn off unwated highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = CLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through 
    TCCR1A = BsetPWM; //set the wanted highside pulsing 
    ADMUX = 1; //set A1 as negative side comparator input (phase A)
    ACSR &= ~0x01; //sets falling edge interupt
    break;

    case 5: //step2: A, PWM. C, low. B, floating/bemfsense.
    TCCR1A = 0; //turn off highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = CLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR2A = AsetPWM; //set the highside pulsing
    ADMUX = 2; //set A2 as negative side comparator input (phase B)
    ACSR |= 0x03; //sets rising edge interupt
    break;

    case 6://step1: A, PWM. B, low. C, floating/bemfsense.
    TCCR1A = 0; //turn off highsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    PORTD = BLow; //set the lowsides
    DELAY_CYCLES(3); //delay for deadtime to prevent shoot through
    TCCR2A = AsetPWM; //set the highside pulsing
    ADMUX = 3; //set A3 as negative side comparator input (phase C)
    ACSR &= ~0x01; //sets falling edge interupt
    break;
    
  }
}

void StartUpSound(){ //program to play 3 short higher tone beeps to indicate the ESC has completed its startup and is ready for use

  cli(); //disables interupts
  TCCR1A = 0; //turn off highsides
  TCCR2A = 0;
  OCR1A = 0; //sets PWM to no duty
  OCR1B = 20; //sets PWM duty
  OCR2B = 15; //sets PWM duty
  PORTD = BLow; //sets B low with A & C floating.
  
  for (BPCnt = 0; BPCnt <= 10000; BPCnt++){ //for loop to generate a beep
    TCCR2A = AsetPWM; //set phase A to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
    TCCR1A = CsetPWM; //set phase C to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
  }

  HoldUpnumber = 0;
  while (HoldUpnumber < 10000){ //while loop to introduce some rough (~ 7 millisecond) delay.
    HoldUpnumber++;
    DELAY_CYCLES(5); 
  }

  for (BPCnt = 0; BPCnt <= 10000; BPCnt++){ //for loop to generate a beep
    TCCR2A = AsetPWM; //set phase A to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
    TCCR1A = CsetPWM; //set phase C to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
  }

  HoldUpnumber = 0; 
  while (HoldUpnumber < 125000){ //while loop to introduce some rough (~ 7 millisecond) delay.
    HoldUpnumber++;
    DELAY_CYCLES(5); 
  }

  for (BPCnt = 0; BPCnt <= 12500; BPCnt++){ //for loop to generate a beep
    TCCR2A = AsetPWM; //set phase A to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
    TCCR1A = CsetPWM; //set phase C to be pulsing
    DELAY_CYCLES(90); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(100); //small delay
  }
  sei(); //re-enables interupts
}

void StallSound(){ //program to make the motor play a short low tone beep to indicate the motor has stalled

  TCCR1A = 0; //turn off highsides
  TCCR2A = 0;
  PORTD = B10100100; //turn off lowsides
  OCR1A = 0; //sets PWM to 0% duty
  OCR1B = 75; //sets PWM to medium duty
  OCR2B = 75; //sets PWM to medium duty
  PORTD = BLow; //sets B low with A & C floating 
  for (BPCnt = 0; BPCnt <= 12500; BPCnt++){ //for loop to generate a beep
    TCCR2A = AsetPWM; //set phase A to be pulsing
    DELAY_CYCLES(180); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(200); //small delay
    TCCR1A = CsetPWM; //set phase C to be pulsing
    DELAY_CYCLES(180); //small delay
    TCCR1A = 0; //turn off highsides
    TCCR2A = 0;
    DELAY_CYCLES(200); //small delay
  }
  PORTD = B10100100; //set motor to floatig
  TCCR2A = 0;
  TCCR1A = 0;
  OCR1A = 0; //sets PWM to 0% duty
  OCR1B = 0;
  OCR2B = 0;
  
}

Schematic:


PCB photos:

Waveform Photos: (1: low speed no load, 2: med speed no load, 3: high speed no load, 4: low speed load, 5: med speed load, 6: high speed load, (where it really gets "wonky")).






If anyone needs more information or for me to do more tests and get more waveforms, Etc. Please let me know and thanks for reading this far, I hope to hear your input soon :slight_smile:

Please edit your post and break that slab of text into smaller sentences and paragraphs to make it easier to read

Thank you for your input

The decoupling caps on the MOSFET drivers need to be at least 10 times the value of the bootstrap capacitors - you've used 100nF for both which is pretty low anyway.

Try 330nF or larger for the bootstrap caps and more like 10uF for the decoupling caps. The IRF1405 has massive gate capacitance, so you cannot scrimp on bootstrap capacitor value, it needs to be much larger than the gate capacitance.

I'd also worry about the UF4004 diodes - try changing for Schottky diodes which don't have reverse recovery.

Do you have a 'scope with enough bandwidth to see the detail of the switching waveforms ? Like 50MHz or above?

Ah - I've just spotted another thing - these capacitors for the bootstrap circuit must be MLCC ceramic chip capacitors - they have to be very low inductance - you seem to be using wound film caps which are a no-no - that immediately explains the circuit malfunction.

And you know 3-phase drivers like the FAN7388 and HIP4086 are available to reduce the BOM?

Hello @MarkT thanks for your response, I am going to change the bootstrap and decoupling caps as you said, il probably be going with 470nF for bootstrap and 22uF decoupling, also sorry but I only have my little red scope to use for now but I hope to change that in the future :crossed_fingers:.

Now when you say ceramic capacitor, I know my smd version has the 1206 ceramic chip caps on it but I assume your meaning the type in Figure1 or will the type in Figure2 work just as well? since I think the monolithic figure2 ones are all I can quickly get my hands on See here.

More importantly when it comes to changing the diode, is being Schottky and having no reverse recovery and the right current and voltage ratings my only concern? As I have some 1N5819 1amp Schottky diodes, do you think they will be good enough or should I look for something different? regardless thanks for your help so far and I am keen to hear more from you :slight_smile:

Figure1:

Figure2:

Those look like MLCC, which is what you need - SMT is always better (for low inductance), but if its not possible through will do, keep the leads are short as possible.

Hello @MarkT so i have added the 470nF bootstrap caps, and gone with a 100uF electrolytic and 50nF ceramic decoupling caps so all my bootstrapping and decoupling should be okay now, i also upgraded the gate and BEMF resistors to be 1/2 watt instead of the original 1/4 watt ones.

This seems to have helped as the waveforms when there is no load look very similar to the waveforms from a commercial HW30A esc. Now when i break the motor with my hand or put the prop on the waveforms get a little bit shakey at low and med speed but they are better than what they were.

My new problem and what I’m thinking may be the root cause of all my issues is that when i go above 70% ish speed with the prop on, the main supply voltage sags down from 12 volts to 9-10 volts i also looked at it with my scope and it was rather noisey. Now I thought maybe i just needed more capacitors across the main supply rails so i added another 1000uF electrolytic and a 2uF film for higher frequency. This may have slightly helped with how noisy the VCC rail was but didnt impact it sagging down and causing the motors speed to oscillate and im assuming that causes it to have trouble staying in the closed loop properly which leads to my original inefficiency problem described earlier.

Now i am using a small A2212 2700kv motor and a fully charged 12V 6.5Ah lead acid, and i also used a fully charged 12.8V 7Ah LiFe battery and got the same voltage sag to around 10volts at over 70% power result both times. Now is there something I’m missing here with maintaining a steady voltage even and high speed under heavy load? (around 9 amps on my multimeter) or do you all think my issue is still probably related to the BEMF and bootstrapping? I am really stumped on this one and would love anyones input. No idea is a bad idea il try anything to make this work! Thanks in advance for your help :slight_smile:

I'd advise against locked rotor operation for an RC motor, that usually pops MOSFETs instantly due to the massive stall currents possible. Use the prop as load only, and wear eye-goggles (they can break, fly off etc)... Never attempt to stop a spinning prop on an RC motor like this, they will easily cut through your hand.

Low speed operation isn't good with BEMF sensing, due to the back EMF being lower in the noise.

You aren't using a LiPo pack? These motors pull astonishing currents, they are specifically designed for heavy current sources such as high performance LiPo.

Have you checked if the sag is due to the SLA or the wiring (or a bit of both)?

Yeah @MarkT i will only test with a prop and yes whenever I’m testing i have safety goggles on and usually have a piece of plywood between me and the spinning prop to ensure if something breaks il be protected

I also thought the SLA might have been an issue but I tested with a LiFePo4 battery and got a similar voltage sag. I am using 10amp automotive wire to connect the battery to the pcb so i doubt its the problem.

I will try find a 4s LiPo to test with as this motor is 2700kv and will definitely draw lots of current, and ive also thought maybe the prop itself is the issue ive 3d printed one and its actually mounted around the rotor not at the bolt on point on top, would this cause any issues or does it not matter? (i know its not the best safety wise) Photo attached. As always thanks for your help and i look forward to hearing from you :slight_smile:

I'd worry that prop will stretch at speed and fly off - why not use a spinner? That also allows the prop airflow to help cool the motor.

Hey @MarkT the prop definitely isnt ideal but it fits very tightly, never the less ive printed a new one that fits onto the bolt on screw top that comes with the motors. Now i have actually killed all my a2212 motors (i think because the heat causes the windings enamel to melt enough for it to short) and the new ones will be coming from china so my plan is to rewind one of them using some high quality enamelled wire to ensure it isn't the motors themselves causing my problems. Now do you still think the type of battery I’m using would most likely be responsible for my voltage sag at high speed? Or does my LiFePO4 battery test point towards it still being a problem with my circuitry, perhaps 0.5watt is stills too low for my BEMF resistors? As always thanks for devoting your time to helping me and I eagerly await your response. Kind regards Harry.

These motors rely on strong air flow to cool them, they dissipate a lot of heat (RC electric motors are about 10 times as power-dense and other sorts, typically), so I can understand why you've cooked them - this is what the holes are about, to let prop-wash airflow through the motor.

Once windings get hot their resistance increases exacerbating the issue - using higher temperature rated wire isn't going help much with this, you need airflow, lots of it.

Normally motor manufacturer's will recommend a size of prop for each motor to load it properly (get the most from the motor without cooking it). Too heavy a load for a motor will slow it down, reduce its cooling, and pull more current, bingo in 20s its cooked.

Ahh @MarkT i understand that completely and it makes lots of sense as to why i havnt seen a prop design like mine before. I have moved to using a prop mounted on top of the motor but i am still running into a heat issue although it has gotten better.

Now i tested with a commercial HW30A esc and my own esc, and the results showed that although my esc when using a top mounted prop as you suggested worked well it still had a voltage sag at over 70% speed and the motor got rather hot especially at the base.

The commercial version however also encountered the voltage sag issue although not quite as bad roughly 0.6 volts less sag than my esc did now the interesting part is that the commercial version caused the motor to heat up alot less even with the same load (it’s important to note before both tests i put the motor in the fridge for an hour and then let it sit at room temp for 20 mins to ensure an even temp starting point).

Now another note is that when i stress tested the commercial esc it actually blew its heat sink attached mosfets after running for about 10ish mins while my version although its fets got very hot (no heat sink) survived cooled down and still works fine.

This motor heating result leads me to believe either 1) there is something about my circuit that is inefficient and causing the problem, or 2) my circuit has larger mosfets that can supply a much greater continuous current meaning the motor is able to draw more power thus heating itself more. And for why the commercial version failed all i can think is the mosfets must have been closer to 15amp rated not 30.

Now in your opinion do you think I’m on the right track with idea 2 or is idea 1 still more likely to be the case?

Oh and @MarkT i just want to say thank you again for sticking with me in regards to this thread and continuing to spend your time helping me try find a solution to these issue :slightly_smiling_face:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.