Do I need a Mosfet Driver?

Since no one can or like to explain to me if my schematic will work or not and I still like the simplicity, I will give it a try and will order the pcb like this... :rolleyes::D

Unbenannt.PNG
 
Motor driver chips have internal logic...

Count me one of those who suggest a motor driver. Adafruit sells breakout boards for development (or production in case you decide to keep it in final design). The TB6612 and DRV8833 chips essentially answer many of the design questions internally.

The most important benefit deriving from using a motor driver is its internal push-pull logic driving the motor with clean consistent edges.

Larger capacity motor drivers are also available though I have not studied them.

I use LTSpice to improve my understanding of circuits, virtually probing interesting control points before committing to either soldered prototypes or PDB designs.
 
Last edited:
What voltage will you be considering for your lowest allowable / operational voltage. I see that you have a pull up to 3.3v. Where are you getting this 3.3v from?
Below is the voltage curve for the Li-Po technology.
Looking at the datasheet for the T4.0 voltage regulator, the minimum input voltage for 3.3v out is quoted as 3.6v.
If you are powering the teensy through the 5v input then you will not be able to use much of the LiPo cells capacity.
LiPo voltage curve.jpg
 
Are these schematics better?

The P-channel circuit has some chance of working, because 100 ohm pull-down will turn the N-channel mosfet off fairly quickly. But you should connect the P-channel source to 3.3V, not the battery, because you risk the transistor mistakenly turning on if the battery voltage is somewhat higher when it's freshly charged.

The other circuit might work too, if you replace the 10K resistor with a much lower value which turns the main mosfet on faster. Hopefully you can understand 10K doesn't deliver nearly enough current to charge the mosfet gate quickly enough.

Both will waste power in that 100 ohm resistor. Maybe that's ok?
 
3.3V comes from the teensy voltage regulator (TLV75733) yes. But it runs fine with a voltage much lower then 3.6V. I have tested this before. Maybe it needs a little more when booting, but that's not a problem because I start with a full battery.
 
Are you measuring battery voltage?
If not how will you know whether the Teensy is about to fall over?
If you are what voltage are you allowing to be your lowest operating voltage?
Using 3.5 volts as your min voltage will still leave 30% of the batteries voltage untapped.
 
You should never go below 3.5V with a lipo. I use a HV Lipo to be longer in the upper voltage range and to save current. Yes, I measure the voltage and am warned by a buzzer.
 
Thanks Paul, finally the clear answer I had hoped for :)
I also considered connecting the P Channel Mosfet to the 3.3V from the Teensy, but unfortunately I have at least 4 motors to control. That means I come to the limits of the TLV75733 when I have to provide the gate current for the Mosfets (each ~ 50mA). In addition, I am afraid that this will generate noise in the 3.3V. Even if I have two additional 47uF capacitors in the 3.3V circuit.
I am very curious what you say about it?
I'm sorry if these are all stupid questions from your point of view, but sometimes it takes me a little longer to understand things. I'm not that experienced yet, at least not in this area. I have the feeling that I can learn a lot here.

Regarding the power loss due to the low 100 ohm, I have to live with that if I want to switch the Mosfet fast enough. Even with decent driver I would have the same problem, or am I completely stupid? :D
At the moment I tend to use this circuit then, because I don't need an inverted PWM signal here either:

Unbenannt.PNG
 

Attachments

  • Unbenannt.PNG
    Unbenannt.PNG
    33.8 KB · Views: 44
I think I'll try the MIC4416 now.
The MIC4416 would like to have at least 4.5V. According to this characteristic, however, at a supply voltage of 3.5V (1S LIPO empty) at least ~ 100mA can be generated at the output. Do you think it's worth trying without a boot converter?
This current should be enough for the UT4404 gate to turn on and off quickly. Based on the internal circuitry of the MIC4416, I see no reason why it cannot be supplied with less voltage, apart from poorer efficiency.

206647f43101460e50763efb85fb70dfd3020372_2_727x750.jpeg

Unbenannt.PNG
 
Well the internal drivers in the chip are themselves MOSFETs, and they have a spread of gate voltage, so you can expect
considerable variation between devices in the minimum workable supply voltage - remember graphs in datasheets are
usually "typical" not "worst case", and for FETs there's considerable difference possible between these (+/-1V for gate
thresholds isn't unusual for instance). You'll probably be lucky, but don't rely on it being reliable in a product :)
 
I only tool a very brief look, but I think a LTC1157 has the right voltage specs and would drive the mosfets for two motors. I didn't check timing - but there might be similar 3.3V, dual gate drivers that are faster.
 
The T4 has some very nice deadtime insertion functionality on the eFlexPWM, but the pins are not broken out in pairs. Bummer, I will have to solder wires to the buttom side SD card footprint to use that function. At least I think so. One will need 6 pins on the same FlexPWM in pairs of A and B. Think it’s not possible without doing a custom design.

It might actually be possible on the T4.1 on FlexPWM module 0, 2 and 3


There are drivers for single PWM input, with integrated deadtime insertion. This guarantee, that no short happens inside the MOSFET.

The SimpleFoc lib works with 3 pwm pins for driving 3 phase brushless (BLDC). Would it still make sense to aim for the eFlexPWM or would the Quadtimer be better at that ?

Sorry for crashing the party, just realized this thread is about brushed motors.
 
Last edited:
The SimpleFoc lib works with 3 pwm pins for driving 3 phase brushless (BLDC). Would it still make sense to aim for the eFlexPWM or would the Quadtimer be better at that ?

FlexPWM definitely - you can do deadtime (2 pins per phase) and phase-correct PWM with these, allowing the duty factor to be updated twice per
PWM cycle for lower latency. Quadtimer isn't really designed for PWM, as the name might suggest :)

Here's a little test code I have for investigating PWM for FOC on the T4, hope its useful, not sure if its self-explanatory so ask questions if you want.

Code:
#ifndef IMXRT_FLEXPWM4
#error Needs Teensy 4.x, or compatible i.MXRT1062 board.
#endif

#define DEADTIME    3  // in half cycles (PWM clocked at 150MHz normally), so 3 = 24 cycles = 170ns
#define MIN_PWM    -444 // with the 115% PWM trick this permits upto 512 amplitude
#define MAX_PWM    +444
#define MID_PWM    (MAX_PWM + 2 * DEADTIME)
#define HALFCYCLE  (2 * MAX_PWM + 4 * DEADTIME)
#define TWOTO32    (65536.0 * 65536.0)  // 2^32
#define MAX_AMPLITUDE 512

// The i.MXRT1062 uses one config register per two XBAR outputs, so a helper
// function to make code more readable. 
bool xbar_connect (uint8_t input, uint8_t output)
{
  if (input >= 88 || output >= 132) 
    return false;
  volatile uint16_t * xbar_select_reg = &XBARA1_SEL0 + (output >> 1); // 1 reg per 2 outputs
  if (output & 1)  // high byte or low byte choice.
    *xbar_select_reg = (*xbar_select_reg & 0x00FF) | (input << 8);
  else
    *xbar_select_reg = (*xbar_select_reg & 0xFF00) | input;
  return true;
}

/* 3-phase PWM with deadtime
 * 
 *
 * Three A/B FlexPWM pairs, Teensy 4.x pins 2/3 (U-channel) and 6/9 (V-channel) and 8/7 (W-channel)
 *  
 */

IMXRT_FLEXPWM_t * flexpwm23 = &IMXRT_FLEXPWM4 ;
#define submodule23  2                         
IMXRT_FLEXPWM_t * flexpwm87 = &IMXRT_FLEXPWM1 ;
#define submodule87  3
IMXRT_FLEXPWM_t * flexpwm69 = &IMXRT_FLEXPWM2 ;
#define submodule69  2                         


// Helper to set up A/B pair on a FlexPWM submodule.
// can configure sync, prescale and B inversion.
void setup_pwm_pair (IMXRT_FLEXPWM_t * flexpwm, int submodule, bool ext_sync, int prescaler, bool invert_B)
{
  int submodule_mask = 1 << submodule ;
  flexpwm->MCTRL &= ~ FLEXPWM_MCTRL_RUN (submodule_mask) ;  // stop it if its already running
  flexpwm->MCTRL |= FLEXPWM_MCTRL_CLDOK (submodule_mask) ;  // clear load OK

  int sel = ext_sync ? 3 : 0;
  flexpwm->SM[submodule].CTRL2 = FLEXPWM_SMCTRL2_INDEP | FLEXPWM_SMCTRL2_WAITEN | FLEXPWM_SMCTRL2_DBGEN |
                                 FLEXPWM_SMCTRL2_FRCEN | FLEXPWM_SMCTRL2_INIT_SEL(sel) | FLEXPWM_SMCTRL2_FORCE_SEL(6) ;
  flexpwm->SM[submodule].CTRL  = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_HALF | FLEXPWM_SMCTRL_PRSC(prescaler) ;
  flexpwm->SM[submodule].OCTRL = invert_B ? FLEXPWM_SMOCTRL_POLB : 0 ;
  flexpwm->SM[submodule].DTCNT0 = 0 ;  // should try this out (deadtime control)
  flexpwm->SM[submodule].TCTRL = FLEXPWM_SMTCTRL_OUT_TRIG_EN (0b000010)  ; // sync trig out on VAL1 match.
  flexpwm->SM[submodule].INIT = -HALFCYCLE ;      // count from -HALFCYCLE to +HALFCYCLE
  flexpwm->SM[submodule].VAL0 = 0 ;
  flexpwm->SM[submodule].VAL1 = HALFCYCLE ;
  flexpwm->SM[submodule].VAL2 = -MID_PWM + DEADTIME ;
  flexpwm->SM[submodule].VAL3 = +MID_PWM - DEADTIME ;
  flexpwm->SM[submodule].VAL4 = -MID_PWM - DEADTIME ;
  flexpwm->SM[submodule].VAL5 = +MID_PWM + DEADTIME ;
  
  flexpwm->MCTRL |= FLEXPWM_MCTRL_LDOK (submodule_mask) ;  // loading reenabled
  flexpwm->MCTRL |= FLEXPWM_MCTRL_RUN (submodule_mask) ;   // start it running
}

void startup_pwm_pair (IMXRT_FLEXPWM_t * flexpwm, int submodule)
{
  int submodule_mask = 1 << submodule ;

  flexpwm->OUTEN |= FLEXPWM_OUTEN_PWMA_EN (submodule_mask); // enable A output
  flexpwm->OUTEN |= FLEXPWM_OUTEN_PWMB_EN (submodule_mask); // enable B output
}


volatile static uint32_t time0 = 0 ;   // store cycle count ARM_DWT_CYCCNT here when record phase
volatile static float freq = 0.0 ;     // used to advance the phase whenever its accessed by update_phase
volatile static uint32_t phase = 0.0 ;  // signed integer phase, MININT--MAXINT represents -pi to +pi.

void set_freq (float f)
{
  uint32_t now = ARM_DWT_CYCCNT ;  // reference timestamp for freq change
  uint64_t ph = phase ;
  int64_t inc = (int64_t) (freq * long(now - time0)) ;  // allow signed phase exceeding +/-pi range
  inc += ph ;
  ph = (uint32_t) (inc & 0xffffffffL) ;
  
  noInterrupts() ;   // critical section to update time0, phase and freq atomically
  time0 = now ;
  phase = ph ;
  freq = f ;
  interrupts() ;
}

// read the current phase, allowing for current value of freq, re-store it updated with new timestamp
inline uint32_t update_phase (void)
{
  uint32_t now = ARM_DWT_CYCCNT ;  // reference timestamp for freq change
  uint64_t ph = phase ;
  int64_t inc = (int64_t) (freq * long(now - time0)) ;  // allow signed phase exceeding +/-pi range
  inc += ph ;
  ph = (uint32_t) (inc & 0xffffffffL) ;
  
  //noInterrupts() ;   // critical section to update time0 and phase atomically
  time0 = now ;
  phase = ph ;
  //interrupts() ;
  
  return ph ;
}

void init_phase (float frequency)
{
  time0 = ARM_DWT_CYCCNT ;
  set_freq (TWOTO32 * frequency / 6e8) ;
}

// helpers
inline int max3 (int a, int b, int c)
{
  if (b > a) a = b ;
  if (c > a) a = c ;
  return a ;
}
inline int min3 (int a, int b, int c)
{
  if (b < a) a = b ;
  if (c < a) a = c ;
  return a ;
}


volatile static float amplitude = 0.0 ;

void pwm_handler (void)
{  
  FLEXPWM4_SM2STS |= FLEXPWM_SMSTS_RF; // Clear Reload

  // Use pin 1 for 'scope trigger.
  static volatile byte pin1_val = 0 ;
  digitalWrite (1, pin1_val) ;  
  pin1_val = 1-pin1_val ;
  
  int32_t intph = update_phase() ;
  // could improve this to use lookup rather than transcendental function calls
  float ph = 2*M_PI * intph / TWOTO32 ;
  int Uactual = int(amplitude * sin (ph)) ;
  int Vactual = int(amplitude * sin (ph+M_PI*2/3)) ;
  int Wactual = -(Uactual+Vactual) ;

  // Hack to allow 15% higher amplitude by shifting the phase triangle to the midpoint of its max and min values
  int midpoint = MID_PWM - (max3 (Uactual, Vactual, Wactual) +
                            min3 (Uactual, Vactual, Wactual)) / 2 ;
  // PWM requires swing between 0 and HALF_CYCLE, midpoint shifts the signed values up
  Uactual += midpoint ;
  Vactual += midpoint ;
  Wactual += midpoint ;

  // using phase-correct PWM with updates for half point as well as full for lowest latency
  // future work - figure out using the inbuilt deadtime options
  // U channel
  flexpwm23->SM[submodule23].VAL2 = -Uactual + DEADTIME ; // A on
  flexpwm23->SM[submodule23].VAL3 = +Uactual - DEADTIME ; // A off
  flexpwm23->SM[submodule23].VAL4 = -Uactual - DEADTIME ; // B off  (assuming B inverted)
  flexpwm23->SM[submodule23].VAL5 = +Uactual + DEADTIME ; // B on
  flexpwm23->MCTRL |= FLEXPWM_MCTRL_LDOK (1<<submodule23) ;  // signal new values
  // V channel
  flexpwm69->SM[submodule69].VAL2 = -Vactual + DEADTIME ;
  flexpwm69->SM[submodule69].VAL3 = +Vactual - DEADTIME ;
  flexpwm69->SM[submodule69].VAL4 = -Vactual - DEADTIME ;
  flexpwm69->SM[submodule69].VAL5 = +Vactual + DEADTIME ;
  flexpwm69->MCTRL |= FLEXPWM_MCTRL_LDOK (1<<submodule69) ;
  // W channel
  flexpwm87->SM[submodule87].VAL2 = -Wactual + DEADTIME ;
  flexpwm87->SM[submodule87].VAL3 = +Wactual - DEADTIME ;
  flexpwm87->SM[submodule87].VAL4 = -Wactual - DEADTIME ;
  flexpwm87->SM[submodule87].VAL5 = +Wactual + DEADTIME ;
  flexpwm87->MCTRL |= FLEXPWM_MCTRL_LDOK (1<<submodule87) ;
  
  digitalWrite (1, LOW) ;
  asm("dsb":::"memory");
 }


float frequency = 0.0;

void setup(void)
{
  Serial.begin (115200) ;
  // ensure pins inactive
  pinMode (2, INPUT) ;
  pinMode (3, INPUT) ;
  pinMode (6, INPUT) ;
  pinMode (7, INPUT) ;
  pinMode (8, INPUT) ;
  pinMode (9, INPUT) ;
  // pins used for 'scope observation.
  pinMode (1, OUTPUT) ;
  pinMode (4, OUTPUT) ;

  delay(20) ;


  // Configure FlexPWM units, each driving A/B pair, B inverted.
  // full speed about 80kHz, prescale 2 (div by 4) gives 20kHz
  setup_pwm_pair (flexpwm23, submodule23, false, 2, true) ;  // this is the master, internally synced
  //delayMicroseconds (5) ;
  setup_pwm_pair (flexpwm69, submodule69, true, 2, true) ;   // others externally synced
  //delayMicroseconds (5) ;
  setup_pwm_pair (flexpwm87, submodule87, true, 2, true) ;
  delayMicroseconds (100) ;

  // turn on XBAR1 clock for all but stop mode
  CCM_CCGR2 |= CCM_CCGR2_XBAR1(3);  

  // Connect trigger to synchronize all three units
  xbar_connect (XBARA1_IN_FLEXPWM4_PWM3_OUT_TRIG0, XBARA1_OUT_FLEXPWM1_PWM3_EXT_SYNC) ;
  xbar_connect (XBARA1_IN_FLEXPWM4_PWM3_OUT_TRIG0, XBARA1_OUT_FLEXPWM2_PWM2_EXT_SYNC) ;

  
  startup_pwm_pair (flexpwm23, submodule23) ;
  startup_pwm_pair (flexpwm69, submodule69) ;
  startup_pwm_pair (flexpwm87, submodule87) ;

  init_phase (frequency) ;
 
  delayMicroseconds(50) ;
  // config the pins 2/3/6/9/8/7 as their FLEXPWM alternates.
  CORE_PIN2_CONFIG = 1 ; // pwm_pin_info[2].muxval ;
  CORE_PIN3_CONFIG = 1 ; // pwm_pin_info[3].muxval ;

  CORE_PIN6_CONFIG = 2 ; // pwm_pin_info[6].muxval ;
  CORE_PIN9_CONFIG = 2 ; // pwm_pin_info[9].muxval ;

  CORE_PIN8_CONFIG = 6 ; // pwm_pin_info[8].muxval ;
  CORE_PIN7_CONFIG = 6 ; // pwm_pin_info[7].muxval ;
 
   // pin 4 observes out trigger line for 'scope
  xbar_connect (XBARA1_IN_FLEXPWM4_PWM3_OUT_TRIG0, XBARA1_OUT_IOMUX_XBAR_INOUT08) ;
  IOMUXC_GPR_GPR6 |= IOMUXC_GPR_GPR6_IOMUXC_XBAR_DIR_SEL_8 ;  // select output mode for INOUT8
  // Select alt 3 for  EMC_06 (XBAR), rather than original 5 (GPIO)
  CORE_PIN4_CONFIG = 3 ; // shorthand for  IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_06 =  3 ;
  // turn up drive & speed as very short pulse
  IOMUXC_SW_PAD_CTL_PAD_GPIO_EMC_06 = IOMUXC_PAD_DSE(7) | IOMUXC_PAD_SPEED(3) | IOMUXC_PAD_SRE ;

  //amplitude = MAX_AMPLITUDE * 0.8;
  
  //delay(4000);
  // Configure FlexPWM4 SM2 interrupt on reload
  attachInterruptVector (IRQ_FLEXPWM4_2, pwm_handler);
  NVIC_ENABLE_IRQ (IRQ_FLEXPWM4_2);
  FLEXPWM4_SM2INTEN |= FLEXPWM_SMINTEN_RIE ; // Reload Interrupt Enable
}

float frequency_inc = 0.08 ; //0.000002 ;

void loop ()
{
  set_freq (TWOTO32 * frequency / 6e8) ;  
  // If I got things right frequency is in units of Hz if running at 600MHz

  // play with frequency and amplitude to show off waveforms on 'scope.
  frequency = constrain (frequency + frequency_inc, -300.0, 300.0) ;
  if (frequency == 300.0)
    frequency = -300.0 ;
  amplitude = constrain (amplitude + 0.04, 0.0, MAX_AMPLITUDE) ;
  //if (amplitude == MAX_AMPLITUDE)
   // amplitude = 0.0 ;
  delay (1) ;
}
The top three traces are one of the outputs for each phase.
The pwm_handler() ISR function scales/shifts the values to get full use of the available PWM range, a trick that gets about 16% more amplitude.

3phase_pwm.png
 
/* 3-phase PWM with deadtime
*
*
* Three A/B FlexPWM pairs, Teensy 4.x pins 2/3 (U-channel) and 6/9 (V-channel) and 8/7 (W-channel)
*
*/

Are you saying the T4.0 does have this option and that the product page showing the FlexPMW pins does not tell the whole truth!? Awesome !

Edit: I think I must have mistaken something in the deadtime concept. Thought we had to use all 6 pins within the same FlexPWM. From your post, im guessing it´s ok to mix the FlexPMW´s as long as each phase has a pair attached.

Eg. Pins 2 and 3 are on FLEXPWM4 and pin 6 and 9 are on FLEXPWM2. Thanks for that. So if I need pin nr. 9 for a FlexIOSPI integration, can I swap that pair for another?

At the moment im just researching the pin matching side of things. I might get back to you about optimization. Better yet... could you merge your code with SimpleFoc Teensy MCU specific. Pretty plz
 
Last edited:
Ok

I do have a question for you

Have you, or someone else paired the T4.0/T4.1, used for BLDC commutation, with a motionsensor eg. 9 axis? Somehow I have this inclination towards doing a ebike motor controller coupled with a fast motionsensor and a external ADC.

Since the new 1176 MCU will probably come in the 4.1 form factor or larger, I have started to stall a bit with regards to assigning pins. It would be quite nice to be able to see a pinout for the upcoming Teensy. The thing is, the 1176 MCU has a 4.2 MSPS ADC, so 4x the sampling rate, which will make the external ADC for 1Mhz current sense amplifier a thing of the past. Those external ADC´s are not cheap and the T4 (eFlexPWM) does not support external ADC. So waiting for the upcoming one might just be the right thing, right now.

Hmmm... Teensy 4X

Does not support external ADC.png
 
Last edited:
Nope, not an IMU, just 3-phase bridge and ADCS7477's + ACS711's for current sensing - been experimenting with 3-phase induction motors.
 
Back
Top