MOSFETs overheating even with no load on B-G431B-ESC1 board - influenced by dead time

I noticed the mosfets get quite hot on this board. Others have previously noticed this, however it’s happening even at very low and zero load. Even when the total current consumption of the board is only 90 milliamps, the mosfetsstill reach and sit at 65 degrees C or so, according to my infrared thermometer. I didn’t notice this before. The board has overheated several times on me, and stopped responding, but I thought it was due to something else. It was, in some cases, due to a small short circuit from a whisker of multi stranded wire, fortunately the board shut down and didn’t get fried.

I think there is a design flaw with this board, or maybe the simpleFOC doesn’t implement the PWM quite right.

When I set the dead time to 0.05 it dropped from 100 milliamps draw to 90 milliamps, when the board was not powering anything The motor.voltage_limit is set to zero in both cases, and the motor is not even connected.

Surely this must be some kind of problem with the mosfets turning on at slightly the wrong time, then, in the half h-bridge.

Good development boards have nice little measurement points where you can tap in to get info, but this board is so dense that is pretty hard to do.

Any ideas on how I can elucidate this phenomena? This is not just about this particular board, I think the future for the main reference board for the SimpleFOC project would sensibly be based on this MCU. So it makes sense to make sure it’s working.

Unfortunately I ordered 7 more of these boards already :frowning: . Again and again I am finding that anything except a board that is well tested and developed by the community is a bad bet.

edit: I tried changing the dead time to 0.1 and there is no change from 90 milliamps. Either I am not actually changing the dead time or it’s not the problem. IT may be the previous observation that it dropped from 100 to 90 was a measurement error. But as seen in the previous thread on dead time compensation, setting the dead time to a relatively low value did seem to have a noticeable effect on heating and current consumption, indicating the command was at least having some impact. There is definitely something wrong here though.

I never noticed these boards getting hot with low load, but never tried with zero load (at least, I don‘t remember).

Adding to what I wrote: I think on MOSFETS you always have switching losses proportional to the switching frequency, even if there is virtually no load. You are charging/discharging the capacitor which forms the gate.

1 Like

I didn’t notice this on mine. I check the mosfets frequently, and are barely above room temperature

Would you mind sharing sample code that shows the problem? I have a thermal camera and would like to measure what I see on my unit

1 Like

Here is the exact code I used, sorry if it is not elemental and as simple as possible, but you can run it the way it is and nothing bad should happen. If you actually use it to drive a motor, make sure you adjust the voltage in various ways, the acceleration etc. as well. It boots up with the voltage_override set to zero, you have to set it to 1 over the serial port for the motor to start moving, otherwise the voltage is zero. Even when it is zero, for me the mosfets get hot. You have to send enough characters over the serial port, 9 including the hard return, before it processes the command, for the commands with capital letters. The small letters read parameters, the capital letters set parameters.

#include <SimpleFOC.h>

// NUMBER OF POLE PAIRS, NOT POLES, specific to the motor being used!
BLDCMotor motor = BLDCMotor(7); 
//this line must be changed for each board
BLDCDriver6PWM driver = BLDCDriver6PWM(A_PHASE_UH, A_PHASE_UL, A_PHASE_VH, A_PHASE_VL, A_PHASE_WH, A_PHASE_WL);
LowsideCurrentSense currentSense = LowsideCurrentSense(0.003, -64.0/7.0, A_OP1_OUT, A_OP2_OUT, A_OP3_OUT);
LowPassFilter diff_filter = LowPassFilter(0.05);
float goal_speed =0;
float v=2;
float v_diff=1;
float accel = 92;// in rads per second per second
float v_per_radsPS = 0.0232;
float accel_v_boost = 0.5;// voltage is increased during acceleration and deacceleration by this amount
bool voltage_override = 0;
float power_figure = 1.5;
float power_coeff = 0.00043;// the serial communicator could actually use an extra digit for this one.
float A, B, C;
float currentlf_now =0;
float prop_V= 0;
float min_V = 1;
float v_limit = 19;
float current_limit_slope = 1.6;// this is in milliamps pre rad per second
float current_limit_o_term = 200;//this is the current limit at zero rps, it may not trip with stall 
float maybe_o = 1;
void SerialComm(){ 
  if (Serial.available() > 0){
  switch(Serial.peek()){
      case 't': Serial.read(); Serial.print("t"); Serial.println(goal_speed); break;
      case 'c': Serial.read(); Serial.print("c"); Serial.println(accel); break;
      case 'v': Serial.read(); Serial.print("v"); Serial.println(motor.voltage_limit, 4); break;
      case 'n': Serial.read(); Serial.print("n"); Serial.println(v_diff); break;
      case 'p': Serial.read(); Serial.print("p"); Serial.println(v_per_radsPS, 4); break;
      case 'b': Serial.read(); Serial.print("b"); Serial.println(accel_v_boost); break;
      case 'o': Serial.read(); Serial.print("o"); Serial.println(voltage_override); break;
      case 's': Serial.read(); Serial.print("s"); Serial.println(motor.target); break;
      case 'f': Serial.read(); Serial.print("f"); Serial.println(power_coeff, 6); break;
      case 'g': Serial.read(); Serial.print("g"); Serial.println(currentSense.getDCCurrent(), 5); break;
      case 'i': Serial.read(); Serial.print("i"); Serial.println(get_mA(), 4); break;
      case 'j': Serial.read(); Serial.print("j"); Serial.println(min_V); break;
      case 'w': Serial.read(); Serial.print("w"); Serial.println(driver.voltage_power_supply); break;
      case 'k': Serial.read(); Serial.print("k"); Serial.println(v_limit); break;
      case 'y': Serial.read(); Serial.print("y"); Serial.println(current_limit_slope); break;
      case 'u': Serial.read(); Serial.print("u"); Serial.println(current_limit_o_term); break;
      case 'e': Serial.read(); Serial.print("e"); if (motor.shaft_angle >= 0){
           Serial.println(motor.shaft_angle, 3);
           }
           if (motor.shaft_angle < 0){
           Serial.println((_2PI-(-1*motor.shaft_angle)), 3);
           }
           break;
  case 'T': break;
  case 'C': break;
  case 'V':  break;
  case 'P':  break;
  case 'B':  break;
  case 'Y':  break;
  case 'U':  break;
  case 'O': break;
  case 'F':  break;
  case 'J':  break;
  case 'W': ;break;
  case 'K': ;break;
  default: Serial.read(); break; //if anything we don't recognize got in the buffer, clear it out or it will mess things up.
       
  }
}
  if (Serial.available() >= 9){
  switch(Serial.read())
  { 
    
  case 'T': goal_speed = Serial.parseFloat();break;
  case 'C': accel = Serial.parseFloat();break;
  case 'V': v_diff = Serial.parseFloat(); break;
  case 'P': v_per_radsPS = Serial.parseFloat(); break;
  case 'K': v_limit = Serial.parseFloat(); break;
  case 'B': accel_v_boost = Serial.parseFloat(); break;
  case 'Y': current_limit_slope = Serial.parseFloat(); break;
  case 'U': current_limit_o_term = Serial.parseFloat(); break;
  case 'O': 
    maybe_o = Serial.parseFloat(); // just in case the wrong data gets in somehow we don't want the voltage going crazy
    if (maybe_o < 1){
      voltage_override = 0;
      } 
    if (maybe_o >= 0.999){ 
      voltage_override = 1;
    }
    break;// if it's not one of these, ignore it.
  case 'F': power_coeff = Serial.parseFloat();break;
 // case 'W': driver.voltage_power_supply = Serial.parseFloat();break;
 // case 'J': min_V = Serial.parseFloat();break;
  
  }
  }
}
void overcurrent_trip(){// if it stalls this won't help except at higher powers, probably. Just helps prevent disaster
  float current_cap = current_limit_o_term + fabs(motor.target)*current_limit_slope;
  if (get_mA() > current_cap){
    voltage_override = 0;
  }
}

void setup() {
  Serial.begin(1000000);
  Serial.println("test serial2");
  // driver config
  // power supply voltage [V]
  
  driver.voltage_power_supply = 24;
 // driver.dead_zone = 0.1;
  driver.init();
 // driver.dead_zone = 0.1;
  // link the motor and the driver
  motor.linkDriver(&driver);
  currentSense.linkDriver(&driver);
  currentSense.init();
  currentSense.skip_align = true;
  FOCModulationType::SinePWM;
  motor.voltage_limit = 1;   // [V]
  motor.velocity_limit = 255; // [rad/s]
 
  motor.controller = MotionControlType::velocity_openloop;

  // init motor hardware
  motor.init();
  motor.voltage_limit = 2;
  goal_speed = 2;
}

unsigned long int ticks_diff(unsigned long int t2,unsigned long int t1){ //t2 should be after t1, this is for calculating clock times.
  if (t2<t1){//t2 must have wrapped around after t1 was taken
     return (4294967295-(t1-t2));
  }
     return (t2-t1);
} 
float get_mA(){// this is the estimated current being drawn from the power supply, not the actual motor current which is a bit different
   float x =0;
   x = currentlf_now*motor.voltage_limit/24;
   return  1000*((4.0384440932900223e-002)+3.4514090071108776e-002*x*30);// this is off by like 12 percent in some cases a polynomial of third order fits the data better but might flake out at higher than 500 mA so I didn't try it.
}
void loop() {
     static unsigned long int loop_clock_in = millis();
     unsigned long int loop_time = 0;
     float loop_time_s = 0;
//     unsigned long int inner_loop_time = 0;
     loop_time = ticks_diff(millis(), loop_clock_in);
     loop_clock_in=millis();
     loop_time_s = float(loop_time)/1000;
     if (motor.target < goal_speed-(accel*loop_time_s*1.5)){//rps not positive enough
           if (motor.target < 0){//counterclockwise rotation, deaccelerating
      motor.target = motor.target+accel*loop_time_s*0.7;
      motor.move();
      prop_V = (v_diff+accel_v_boost+fabs((motor.target*v_per_radsPS))+(power_coeff*pow(fabs(motor.target),power_figure)))*voltage_override;
     }
          if (motor.target >= 0){ //clockwise rotation, accelerating
      motor.target = motor.target+accel*loop_time_s;
      motor.move();
      prop_V = (v_diff+accel_v_boost+fabs((motor.target*v_per_radsPS))+(power_coeff*pow(fabs(motor.target),power_figure)))*voltage_override;
     }
     }
     
     if (motor.target>=goal_speed-(accel*loop_time_s*1.5)){//steady run phase
      if (motor.target<=goal_speed+(accel*loop_time_s*1.5)){ 
        motor.move();
      prop_V = (v_diff+fabs((motor.target*v_per_radsPS))+(power_coeff*pow(fabs(motor.target),power_figure)))*voltage_override; //constant run
      }
     }
     
     
     if (motor.target > goal_speed + (accel*loop_time_s*1.5)){ //rps too positive
           if (motor.target > 0){ //clockwise rotation, deaccelerating
      motor.target = motor.target-accel*loop_time_s*0.7; 
      motor.move();
      prop_V = (v_diff+accel_v_boost+fabs((motor.target*v_per_radsPS))+(power_coeff*pow(fabs(motor.target),power_figure)))*voltage_override;
     } 
          if (motor.target <= 0){
      motor.target = motor.target-accel*loop_time_s; //counterclockwise rotation, accelerating
      motor.move();
      prop_V = (v_diff+accel_v_boost+fabs((motor.target*v_per_radsPS))+(power_coeff*pow(fabs(motor.target),power_figure)))*voltage_override;
     }

     }
     if (prop_V < min_V){
      motor.voltage_limit = min_V*voltage_override;
     }
     else {
      motor.voltage_limit = prop_V;
     }
          if (prop_V > v_limit){
      motor.voltage_limit = v_limit;
     }
     
     for (int i=0;i<10;i++){ // shouldloop at about 37 khz on b-g431 board
     for (int q=0;q<5;q++){
     motor.move();
     motor.move();
     motor.move();
     motor.move();
     motor.move();
     }     
//     Serial.println(micros()-inner_loop_time);
//     inner_loop_time = micros();
     SerialComm();
     }
     currentlf_now = currentSense.getDCCurrent();
     currentlf_now = diff_filter(currentlf_now);
     overcurrent_trip();
}

I think if you just run it with open loop at 2 radians per second with elemental code that should show the problem. Might give you more confidence that it’s not my code in some way. I’m pretty sure it can’t be my code.

I noticed there is a short period after power up when the program is still initializing that the current is more like 50 milliamps. I could probably remove lines of code or stop the program with a delay() to see when the current first starts, but presumably it is when the pwm signal starts going to the mosfet drivers. What is really needed is to poke in there with an oscilloscope with multiple channels. I have a suitable scope but it would be very hard to access the pins esp multiple pins with only two hands… hm maybe I can do it somehow.

I did a test and the mosfets get to about 90 degrees C when the whole ssystem is consuming 350 mA, which implies about 250 mA to the actual motor. It seems to be stable there though and the mosfets should be good for higher temperatures, so my main concern is that this problem be inhereted by the lepton 3.0, rather than fixing it with this board, but really it should be elucidated as there is definitely something borked here.

Will try it tomorrow. Just to save time, can you please provide a sample serial string I can use? I got a bit confused by all the possible parameters…

I did try on my board, with a 12V power supply, sending O2V2 and enough return to make the changes. Power supply shows a 240mA draw. I’m using an RcTimer 5010 360kV motor with 0.28 Ohm phase resistance and 7 pole pairs (like yours). The motor turns just fine.

I get only <50C. Not low, but also not high by any stretch of imagination for an ESC supposed to be used in a drone with air cooling from the propellers.

1 Like

to set the speed to 100 rads per second you would submit the following through the arduino serial interface for instance : T100.000 . That’s 8 characters including the decimal point, and so it’s 9 when you press enter, it also sends a newline character. But it’s ok, you can add extra zeros on the end or whatever and it will just chop them off. I just enter more zeros than I need and it’s fine.

To make the motor go on boot enter O1.00000 and it will start rotating slowly. Bit clunky to use a floating point to send an on or off but it uses the known goo dsub system…

Don’t send V2 or O2, that won’t work, it will confuse you because if you send more characters the commands will be processed after enough characters accumulate in some cases and some times not… sorry it’s not really suitable for your purposes. You could chop things out and simplify it.

I think the best approach is to just take the open loop example and modify it to match the board. That way it just boots and does one thing. To change what it does just change the program and recompile. takes a couple minutes but it’s not going to be any faster to make a nice test program…

Edit: ok so your board is running cooler than my board for some reason. I have some new boards, we will see when I get around to using them what’s up/

edit: there’s still something out of whack, though even if the heating is manageable. Not a big deal I guess, everything seems to work, but with the low resistance these mosfets present there shouldn’t be significant heating at these currents. Obviously the timing is off and some current is flowing straigth across the h-bridges for some reason. I don’t think it’s the 25 kHz switching speed of the mosfets and the actual power consumption of driving the gates, a quick calculation of gate-drain capacitance and the voltage and stuff would tell the maximal amount of power involved there but it’s usually very small.