Teensy 4.0 PWM reversing H Bridge driver?

Status
Not open for further replies.

audiomath

Active member
Hello,

I have a DIY MOSFET H Bridge that requires opposite-phase signals to drive a motor "forward" or "reverse". That's easily accomplished with two IO pins on the Teensy 4.0 .

What I'm hoping to find is an easy way to generate PWM on two pins, exactly 180 degrees out of phase - and have that phase relationship be reversible.

I can implement that with a single PWM output and external logic, but since in this case the Teensy isn't highly loaded I'm again hoping there's something
implemented in firmware that can synchronize the two outputs and flip the phase relationship when required to reverse the motor.

I've done a few keyword searches on this forum and Google, but I haven't found anything suitable yet.
I could do something a bit clunky with (non-PWM) individual IO pins and timers, but if I have to go that far I'll just add the external logic.

Ideas?

Thanks!
 
You might meet that requirement with a modification of the simple multi-channel PWM code I posted in the General discussion forum:
https://forum.pjrc.com/threads/62857-Simple-multi-channel-PWM

Whether that software approach will work will depend on some things that you didn't specify: the period of the PWM and the resolution (#steps between minimum and maximum speed).
If a PWM period of 1millisecond and 100 steps of speed control is OK, then the the software approach should work and consume less than 1% of the CPU bandwidth of a T4.X


BUT---you said you wanted a hardware approach. I think that is possible, but may require using pins connected to two different timers. I need to write some test code to see if it will work.


Your control software will also have to manage a dead-time when switching directions--otherwise you may let the magic smoke out of some parts of your setup.

I assume that your DIY MOSFET H bridge has hardware to prevent the shoot-through you can get if you are driving the left and right sides of the H with a single pin each. If you don't have that, you could use 4 Teensy pins, one to control each MOSFET and adjust the software to make sure that you turn off the top of the H leg before you turn on the bottom. Integrated H-Bridge drivers do that in their hardware.
 
Thanks for the reply.

After giving it some more thought, please don't go to any additional effort on my account. I've decided to add additional logic required to make it a stand-alone solution. Not because I don't "trust" software, but because future applications might employ a much less capable MCU than the Teensy 4.0 .

FWIW, the original designers of the contraption I'm working on went to quite a bit of trouble to design a nice motor and gearbox, then ruined the whole thing with a "dumb" controller. The load can have quite a bit of mass and momentum, so hard stops and starts put premature wear on the system. My use for PWM has more to do with soft starts and stops than runtime speed control - the latter is just a side benefit.

Thanks for your comment on shoot-through. My "raw" H Bridge circuit is vulnerable to that, but adding the external logic should fix it. That configuration has inputs for enable, direction and "drive", for lack of a better word. "drive" can be either high or low for on/off operation or a PWM signal.

Or so says me. If it all works as expected I may submit it here as a project, since the prototype controller uses a Teensy.

Thanks!
 
The following code seems to work, according to my oscilloscope. It seems almost too simple to be real, so please test it carefully before running it on the robot you've spent the last 6 months of social-distancing time building in your garage ;-).

The code can easily be extended to 12-bit resolution and other PWM speed ranges. As my physics 101 professor would say, "The result should be intuitively obvious to the casual observer!". I can still remember the stifled groans as we struggled to copy the blackboard full of equations--and that happened in 1965!

If this algorithm works, you can get right to the fun of adding dead time on reversal and some acceleration profiles on speed changes.

Code:
/************************************************
   Test code for simple two-pin H-bridge driver on T4.X

   WARNING: this demo code does not implement a dead
            time between forward and reverse!

   mborgerson  9/6/2020
*************************************************/

const char compileTime [] = "\n\nH-Bridge driver compiled on " __DATE__ " " __TIME__;

// I specify two pins that are on different flexpwm timers
// However I don't know that it is necessary to do that
const int leftpin = 1;  // FlexPWM1_0
const int rightpin = 2; // FlexPWM4_2

bool forward;

#define PWMFREQUENCY 4096
#define PWMRES 8   // PWM resolution 8 bits = 256 steps
#define PWMSTEPS 256  // to match PWMRES
void SetSpeed(bool dir, uint8_t spd) {
  uint8_t righthi, lefthi;
  // if you use more than 8 bits resolution, you will
  // need to change the integer types of spd, righthi and lefthi
  // and add some safety  checks here.
  if (dir) {
    lefthi = spd;
    righthi = PWMSTEPS - spd;
  } else {
    lefthi = PWMSTEPS - spd;
    righthi = spd;
  }

  Serial.printf("Speed is %u ", spd);
  if (forward) Serial.println("forward"); else Serial.println("reverse");
  analogWrite(rightpin, righthi);
  analogWrite(leftpin, lefthi);
}

void setup() {
  Serial.begin(9600);
  delay(100); // wait for PC to respond
  Serial.println(compileTime);
  analogWriteRes(8);
  analogWriteFrequency(leftpin, PWMFREQUENCY);
  analogWriteFrequency(rightpin, PWMFREQUENCY);
  forward = true;
  SetSpeed(forward, 128);
}


// A simple user interface to change speed and direction
void loop() {
  char ch;
  static uint8_t spd = 128;
  if (Serial.available()) {
    ch = Serial.read();
    if (ch == 'f' ) {
      forward = true;
      SetSpeed(forward, spd); // SetSpeed needed to update waveforms
    }
    if (ch == 'r' ) {
      forward = false;
      SetSpeed(forward, spd); // SetSpeed needed to update waveforms
    }
    // keys '1' to '8' set the speed
    if ((ch > '0') && (ch < '9')) {
      spd = (ch - 48) * 30;  // spd from 30 to 240
      SetSpeed(forward, spd);
    }
  }

}
 
UPDATE:

* If I pick pins 6 and 9, which are on the same FlexPWM timer, the rising edges occur at the same time----and you lose the phase inversion.

* If I mix up FlexPWM and QuadTimer pins, you also lose the phase inversion and get a phase delay whose value is a mystery to me.

On closer examination, the rising edge on Pin1 precedes the falling edge of Pin2 by about 4.2 uSec. That value doesn't change with the PWM speed.

If I change to pins 1 and 4, which are on FlexPWM1_0 and FlexPWM2_0, the difference decreases to 1.4uSec. If there's a pattern here, it is not intuitively obvious to me. Of course, two examples is not much data for discerning patterns.


If anyone knows why two pins on different FlexPWM channels just happen to be 180 degrees out of phase, please enlighten me. I don't know the internal workings of the FlexPWM timers well enough to figure this out.

P.S. There's a good list of the hardware that does the PWM on the various T4.X pins in

Arduino-1.8.13/hardware/teensy/avr/cores/teensy4/pwm.c
 
UPDATE 2:

After a few interesting hours with the IMXRT1062 manual and the PWM.c source, I figured out that you can do the generation of the complementary outputs in the FlexPWM hardware with a bit of bit twiddling. The FlexPWM timers are very versatile and were designed with motor control in mind. You can control four outputs with shoot-through control and lots of other neat features. Just skimming through the 120 pages of the manual dedicated to the FlexPWM hardware brought up several points about motor control that I hadn't thought of.

If you're just going to build one or a few of your gadgets, you can add the safety features in the hardware and use digital logic to control the speed and do the phase inversion. Then all you need is a single PWM pulse train and a FWD/REVERSE output bit. You can do that with just about any Arduino system or other low-cost hardware. All that FlexPWM capability is handy when you're going to build tens of thousands of units and a few extra bits of hardware affect the profit margin.\

With the latest version of my demo program the output bit inversion is done in the T4 hardware and the software to control it reduces to about 5 lines of code to initialize, and 6 lines to control the speed and direction. When I used to do this kind of thing for a living, I'd charge several hundred bucks for this afternoon's programming entertainment. Just about anyone on this forum can write a hundred lines of code to get a particular task done in an afternoon. A smaller number can do the job in a few hours with 30 lines of well-commented code. When you can get to that level, it may be time to consider a career in embedded software. Then you can retire after 40 years and write software for fun--when you can't fly to Nice for a few weeks of French food and wine!

Code:
/************************************************
   Test code for simple two-pin H-bridge driver
   Uses internal pin inversion hardware to generate 
   complementary outputs

   WARNING: this demo code does not implement a dead
            time between forward and reverse!

   mborgerson  9/6/2020
**********************************************************/

const char compileTime [] = "\n\nH-Bridge driver compiled on " __DATE__ " " __TIME__;

//  Setting the invert bit on channel B  only works for a limited set of pin pairs
//  where the two outputs are the A and B channels of the same PWM module.
//  use these if you don't want small phase delays between signals.
//  The possible pairs are:
//  pins 2 and 3    FlexPWM4_2A and FlexPWM4_2B
//  pins 6 and 9    FlexPWM2_2A and FlexPWM2_2B
//  pins 7 and 8    FlexPWM1_3B and FlexPWM1_3A
//  pins 22 and 23  FlexPWM1_3B and FlexPWM1_3A

//  pads 28 and 29  FlexPWM3_1A and FlexPWM3_1B 
//  pin4 and pad 33 FlexPWM2_0A and FlexPWM2_0B

// For the T4.0, pins 34 and 35, 36 and 37, 38 and 39 can also be used but are on
// the bottom and used for the SD card interface.


// For the T4.1, pins 34 to 39 are available as normal pins and there are more pairs on the 
// bottom at pins 42 to 47,  but I'm not sure where they are.


const int leftpin = 6;  // FlexPWM2_2A
const int rightpin = 9; // FlexPWM2_2B

#define INVERTB 0x0200;   // bit pattern to invert channel B

bool forward;

#define PWMFREQUENCY 16000
#define PWMRES 12   // PWM resolution 12 bits = 4096 steps
#define PWMSTEPS 4096  // to match PWMRES
void SetSpeed(bool dir, uint16_t spd) {
  uint16_t hightime;
  // if you use more than 8 bits resolution, you will
  // need to change the type of spd, righthi and lefthi
  //and add some safety  checks here.
  if (dir) {
    hightime = spd;
  } else {
    hightime = PWMSTEPS - spd;
  }

  Serial.printf("\nSpeed is %u ", spd);
  if (forward) Serial.println("forward"); else Serial.println("reverse");
  analogWrite(rightpin, hightime);
  analogWrite(leftpin, hightime);
  // we may not need to do this at every change---
  // but what's a few extra nanoseconds gonna hurt?
  FLEXPWM2_SM2OCTRL |= 0x0200;
  //ShowFlexPWMInit(); // OK, this takes more than a few nanoseconds ;-)
}


// show the values of the output control register
// the channel B output polarity bit is pin 9
void ShowFlexPWMInit(void) {

  Serial.println("\nFlexPWM1:" );
  Serial.printf("  SM0OCTL: %04X ",FLEXPWM1_SM0OCTRL);
  Serial.printf("  SM1OCTL: %04X ",FLEXPWM1_SM1OCTRL);
  Serial.printf("  SM2OCTL: %04X ",FLEXPWM1_SM2OCTRL);
  Serial.printf("  SM3OCTL: %04X \n",FLEXPWM1_SM2OCTRL); 
  
  Serial.println("FlexPWM2:" );
  Serial.printf("  SM0OCTL: %04X ",FLEXPWM2_SM0OCTRL); 
  Serial.printf("  SM1OCTL: %04X ",FLEXPWM2_SM1OCTRL);
  Serial.printf("  SM2OCTL: %04X ",FLEXPWM2_SM2OCTRL);
  Serial.printf("  SM3OCTL: %04X \n",FLEXPWM2_SM3OCTRL);
     
  Serial.println("FlexPWM3:" );
  Serial.printf("  SM0OCTL: %04X ",FLEXPWM3_SM0OCTRL);
  Serial.printf("  SM1OCTL: %04X ",FLEXPWM3_SM1OCTRL);
  Serial.printf("  SM2OCTL: %04X ",FLEXPWM3_SM2OCTRL);
  Serial.printf("  SM3OCTL: %04X \n",FLEXPWM3_SM3OCTRL);
  
  Serial.println("FlexPWM4:" );
  Serial.printf("  SM0OCTL: %04X ",FLEXPWM4_SM0OCTRL);
  Serial.printf("  SM1OCTL: %04X ",FLEXPWM4_SM1OCTRL);
  Serial.printf("  SM2OCTL: %04X ",FLEXPWM4_SM2OCTRL);
  Serial.printf("  SM3OCTL: %04X\n ",FLEXPWM4_SM3OCTRL);
  
}


void setup() {
  Serial.begin(9600);
  delay(100); // wait for PC to respond
  Serial.println(compileTime);
  analogWriteRes(PWMRES);
  Serial.print("\nBefore setting speed");
  ShowFlexPWMInit();
  analogWriteFrequency(leftpin, PWMFREQUENCY);
  analogWriteFrequency(rightpin, PWMFREQUENCY);
  forward = true;
  SetSpeed(forward, PWMSTEPS / 2);
  Serial.print("\nAfter Setting speed.");
  ShowFlexPWMInit();
}

void loop() {
  char ch;
  static uint16_t spd = PWMSTEPS / 2;
  if (Serial.available()) {
    ch = Serial.read();
    if (ch == 'f' ) {
      forward = true;
      SetSpeed(forward, spd); // SetSpeed needed to update waveforms
    }
    if (ch == 'r' ) {
      forward = false;
      SetSpeed(forward, spd); // SetSpeed needed to update waveforms
    }
    // keys '1' to '8' set the speed
    if ((ch > '0') && (ch < '9')) {
      spd = (ch - 48) * 500;  // spd from 500 to 4000
      SetSpeed(forward, spd);
    }
  }

}
 
Hey

Since the SimpeFOC lib is under heavy Development i would like to revive this thread. (https://simplefoc.com/)

I have been doing work on a 10kw (300amp) EVnode w. CAN FD. The SimpleFOC lib. does not support 6pin pwm w. dead timeinsertion. This is also often hardware specific like stated above. But now the pre-work is done for the T4, it would be cool to put it to the test. The board is intended to be 105µm copper with some 2mm thick plain copper busbars. Those wire lugs, should be able to handle the current. Im thinking to doo some sort of cooling surface potting w. 6mm aluminium. (thats what i got).

Would you guys help out to implement the above for SimpleFOC lib? There is some real potential for that library when current control and CAN FD is implemented. My ultimate goal is to make 4 nodes (one for each wheel) talk and sync on the CAN bus. Do you think the CAN bus is fast enough to sync 4 spinning motors and how would you implement it?

Edit: Using the modular approach of the Teensy platform is a huge advantage in this regard, since you can only do 0,25 mm gabs doing 105µm copper. And there is the fact that T4 is a freaking awesome MCU w. USB & LDO. The controller i´m making is more or less just a powerstage w. 3 phases and current sensors for the T4. Having the power caps as a add on, like the fuse, you can change the caps when they have done max life hours or maybe one is blown.

EVNode.PNG
TOP.PNG
 
Last edited:
Status
Not open for further replies.
Back
Top