Polarity Inversion for Teensy 4.0 FlexPwm

jannst

New member
Hello everybody,

i am currently working on a project which includes driving a MOSFET driver chip at a frequency of 40 kHz with 2 complementary signals.
So pin 1 should be LOW when pin 2 is HIGH and pin 1 should be HIGH when pin 2 is LOW.
Because of the high frequency in wanted to do this in hardware using the PWM module of Teensy.
Unfortunately I could not find any resources on how to accomplish this using FlexPwm.
After some digging I found FlexPwm driver examples in the MCUXpresso SDK from NXP. After some more digging I was able to get this running on the Teensy.

Because it took me a good amount of time, I wanted to share my findings and maybe help somebody with the same issue.

I added the following function to pwm.c file in cores/teensy4.
This function should allow to invert the polarity of any FlexPwm pin on Teensy 4. (see pin definitions in pwm.c to find out which pins are FlexPwm pins)
Also see https://github.com/jannst/cores/commit/ee23db74963caabe8fc84caee9458a24a559038e
Code:
void flexPwmInvertPolarity(uint8_t pin, bool inversePolarity)
{
	const struct pwm_pin_info_struct *info;

	if (pin >= CORE_NUM_DIGITAL) return;
	info = pwm_pin_info + pin;

	//return if not a FlexPWM pin
	if (info->type != 1) return;

	// FlexPWM pin
	IMXRT_FLEXPWM_t *flexpwm;
	switch ((info->module >> 4) & 3) {
		case 0: flexpwm = &IMXRT_FLEXPWM1; break;
		case 1: flexpwm = &IMXRT_FLEXPWM2; break;
		case 2: flexpwm = &IMXRT_FLEXPWM3; break;
		default: flexpwm = &IMXRT_FLEXPWM4;
	}

	unsigned int submodule = info->module & 0x03;
	uint8_t channel = info->channel;
	uint8_t polarityShift = 0;

	//find out offset for the channel
	//TODO: move magic numbers to declarations 
	switch (channel) {
	  case 0: // X
	  	polarityShift              = 8U;  //PWM_OCTRL_POLX_SHIFT
		break;
	  case 1: // A
		polarityShift              = 10U; //PWM_OCTRL_POLA_SHIFT
		break;
	  case 2: // B
	    polarityShift              = 9U;  //PWM_OCTRL_POLB_SHIFT
	}

	//if polarityShift was not initialized skip
	if(!polarityShift) return;

	//update polarity
	if(inversePolarity) {
		flexpwm->SM[submodule].OCTRL |= ((uint16_t)1U << (uint16_t)polarityShift);
	} else {
		flexpwm->SM[submodule].OCTRL &= ~((uint16_t)1U << (uint16_t)polarityShift);
	}
}



As a proof of concept I wrote the following sketch.
You need to clone my fork of the cores repository and checkout the add_pwm_polarity_inversion branch to get this working.
You should then be able to change the duty cycle of all outputs with keys "S" and "D". With the keys "F", "G", "H", "J" you can invert the polarity of the different outputs.
Code:
#include "Arduino.h"
#include <stdbool.h>

#define FlexPWM1_1_X   0
#define FlexPWM1_0_X   1
#define FlexPWM4_2_A   2
#define FlexPWM4_2_B   3

bool invertFlexPWM1_1_X = false;
bool invertFlexPWM1_0_X = false;
bool invertFlexPWM4_2_A = false;
bool invertFlexPWM4_2_B = false;
int duty = 128; // 50%

void setup()
{
  Serial.begin(9600);
  analogWriteFrequency(FlexPWM1_1_X, 40000);
  analogWriteFrequency(FlexPWM1_0_X, 40000);
  analogWriteFrequency(FlexPWM4_2_A, 40000);
  //no need to setup frequency for FlexPWM4_2_B as it is on the same timer ans same submodule as FlexPWM4_2_A

  analogWrite(FlexPWM1_1_X, duty);
  analogWrite(FlexPWM1_0_X, duty);
  analogWrite(FlexPWM4_2_A, duty);
  analogWrite(FlexPWM4_2_B, duty);
}

void loop()
{
  if (Serial.available()) {
    char incoming = Serial.read();
    if(incoming == 'a') {
      duty--;
      Serial.printf("duty=%d\n", duty);
      analogWrite(FlexPWM1_1_X, duty);
      analogWrite(FlexPWM1_0_X, duty);
      analogWrite(FlexPWM4_2_A, duty);
      analogWrite(FlexPWM4_2_B, duty);
    }else if(incoming == 'd') {
      duty++;
      Serial.printf("duty=%d\n", duty);
      analogWrite(FlexPWM1_1_X, duty);
      analogWrite(FlexPWM1_0_X, duty);
      analogWrite(FlexPWM4_2_A, duty);
      analogWrite(FlexPWM4_2_B, duty);
    }else if(incoming == 'f') {
      invertFlexPWM1_1_X = !invertFlexPWM1_1_X; 
      flexPwmInvertPolarity(FlexPWM1_1_X, invertFlexPWM1_1_X);
      Serial.printf("invertd FlexPWM1_1_X (pin %d): %d\n", FlexPWM1_1_X, invertFlexPWM1_1_X);
    }else if(incoming == 'g') {
      invertFlexPWM1_0_X = !invertFlexPWM1_0_X; 
      flexPwmInvertPolarity(FlexPWM1_0_X, invertFlexPWM1_0_X);
      Serial.printf("invertd FlexPWM1_0_X (pin %d): %d\n", FlexPWM1_0_X, invertFlexPWM1_0_X);
    }else if(incoming == 'h') {
      invertFlexPWM4_2_A = !invertFlexPWM4_2_A; 
      flexPwmInvertPolarity(FlexPWM4_2_A, invertFlexPWM4_2_A);
      Serial.printf("invertd FlexPWM4_2_A (pin %d): %d\n", FlexPWM4_2_A, invertFlexPWM4_2_A);
    }else if(incoming == 'j') {
      invertFlexPWM4_2_B = !invertFlexPWM4_2_B; 
      flexPwmInvertPolarity(FlexPWM4_2_B, invertFlexPWM4_2_B);
      Serial.printf("invertd FlexPWM4_2_B (pin %d): %d\n", FlexPWM4_2_B, invertFlexPWM4_2_B);
    }
  }
}

As I am pretty much a beginner to Teensy and Microcontrollers in general, I am very curious what you think about this solution and if there is an easier way to do this.

Greetings,
Jannik
 
Wow, this is exactly what I was looking for !

I would prefer to not use a branch of cores/teensy4, but add this as a library.
Do you see a reason why that would be difficult to achieve ?

Thank you very much for sharing this !
Jeremy
 
Might as well just put the code into your program. Maybe add another file / tab if you want to keep it "out of sight, out of mind".

You could go to the trouble to turn it into a library, but probably not a lot of point to all that extra effort.
 
Back
Top