Using FlexPWM on Teensy 4.0 for Interleaved PWM Signals with deadtime in between...

Hello :)

for a project I need to generate two interleaved pwm signals with a deadtime of 2uS in between.
The frequency and duty cycle have to be adjustable at any moment.
the frequency's max value is about 100kHz

My Problem is, that i don't know how to initialize the timers and set the corresponding registers.

Perhaps anyone of you is a expert on this field and can help me out here ;)

All my notes until now are these:
(I probably have to use FlexPWM2)


IO Muxing:
IOMUXC_FLEXPWM2_PWMA0_SELECT_INPUT = 0 => GPIO_EMC_06_ALT1 (Pin 4) => available pin on Teensy 4
IOMUXC_FLEXPWM2_PWMB0_SELECT_INPUT = 0 => GPIO_EMC_07_ALT1 (Pin 33) = available pin on Teensy 4

Clocking:
CCM_CCGR4_PWM2 (clock enable) ??? set to 1 ???

Clocksource:
FLEXPWM_SMCTRL2_CLK_SEL = 0; // 0=UPbus, extclock=1, aux_clk =2
Prescaler:
FLEXPWM_SMCTRL_PRSC(n);

FLEXPWM2_MCTRL2 => 0 = complementary Channels


Register Reloading:
CTRL[FULL] = every Cycle reload => used to get the ability to load new frequency and duty cycle values?

Definitions from the imxrt.h file:
(I don't know which register is for what... don't find clear information either in the reference manual)

#define FLEXPWM2_SM0CNT (IMXRT_FLEXPWM2.SM[0].CNT)
#define FLEXPWM2_SM0INIT (IMXRT_FLEXPWM2.SM[0].INIT)
#define FLEXPWM2_SM0CTRL2 (IMXRT_FLEXPWM2.SM[0].CTRL2)
#define FLEXPWM2_SM0CTRL (IMXRT_FLEXPWM2.SM[0].CTRL)
#define FLEXPWM2_SM0VAL0 (IMXRT_FLEXPWM2.SM[0].VAL0)
... and many more definitions of course ...
 
I got It working thanks to the PWM.c file in the teensy 4 core. Two interleaved PWM signals are now beeing generated with flexible duty cycle and frequency and both signals obviously centered.

flexPWMPic.jpg

Here is my code, if anyone is interested:

#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"
#define ChannelA 4
#define ChannelB 33
#define Maske 1
#define ResolutionPWM 12 //12-Bit für PWM

void setOutputPWM(uint16_t val) {
uint32_t PeriodendauerCycles = FLEXPWM2_SM0VAL1; //Periodendauer
uint32_t Cycles = ((uint32_t)val * (PeriodendauerCycles + 1)) >> ResolutionPWM;
if (Cycles > PeriodendauerCycles) Cycles = PeriodendauerCycles;
FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM2_SM0VAL3 = Cycles;
FLEXPWM2_SM0VAL4 = FLEXPWM2_SM0VAL3 + (((FLEXPWM2_SM0VAL1 - 2 * Cycles) / 2)); //PauseOffset anpassen!
FLEXPWM2_SM0VAL5 = FLEXPWM2_SM0VAL4 + (Cycles); //tOn für Channel B anpassen
FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(Maske); //ChannelA aktivieren
FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Maske); //ChannelB aktivieren
FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
*(portConfigRegister(ChannelA)) = Maske;
*(portConfigRegister(ChannelB)) = Maske;
}

void setOutputFrequency(float frequency)
{
uint32_t CurrentCycles = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5);
uint32_t Prescaler = 0;

//Falls Frequenz zu gering Prescaler switchen!
while (CurrentCycles > 65535 && Prescaler < 7) {
CurrentCycles = CurrentCycles >> 1;
Prescaler = Prescaler + 1;
}
if (CurrentCycles > 65535) {
CurrentCycles = 65535;
} else if (CurrentCycles < 2) {
CurrentCycles = 2; //minimale Cycles --> 10nS oder so
}

FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM2_SM0CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(Prescaler); //Erst nach vollem Cycle updaten und Prescaler setzen!
FLEXPWM2_SM0VAL1 = CurrentCycles - 1; //Cycles für Periodendauer setzen!
FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
}

void setup() {
setOutputFrequency(100000); //100kHz
setOutputPWM(410); //10% PWM
}

void loop() {
}
 
Thanks for the example. I ported it to pin 2 and 3 which use FlexPWM4 submodule 2. Am I correct to assume that the "maske" should be 1 << submodule, and the portConfigRegister() function should set the pads to 1? In any case the code below does work.


#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"
#define ChannelA 2
#define ChannelB 3
#define Maske 4
#define ResolutionPWM 12 //12-Bit für PWM

void setOutputPWM(uint16_t val) {
uint32_t PeriodendauerCycles = FLEXPWM4_SM2VAL1; //Periodendauer
uint32_t Cycles = ((uint32_t)val * (PeriodendauerCycles + 1)) >> ResolutionPWM;
if (Cycles > PeriodendauerCycles) Cycles = PeriodendauerCycles;
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM4_SM2VAL3 = Cycles;
FLEXPWM4_SM2VAL4 = FLEXPWM4_SM2VAL3 + (((FLEXPWM4_SM2VAL1 - 2 * Cycles) / 2)); //PauseOffset anpassen!
FLEXPWM4_SM2VAL5 = FLEXPWM4_SM2VAL4 + (Cycles); //tOn für Channel B anpassen
FLEXPWM4_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(Maske); //ChannelA aktivieren
FLEXPWM4_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Maske); //ChannelB aktivieren
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
*(portConfigRegister(ChannelA)) = 1;//Maske;
*(portConfigRegister(ChannelB)) = 1;//Maske;
}

void setOutputFrequency(float frequency)
{
uint32_t CurrentCycles = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5);
uint32_t Prescaler = 0;

//Falls Frequenz zu gering Prescaler switchen!
while (CurrentCycles > 65535 && Prescaler < 7) {
CurrentCycles = CurrentCycles >> 1;
Prescaler = Prescaler + 1;
}
if (CurrentCycles > 65535) {
CurrentCycles = 65535;
} else if (CurrentCycles < 2) {
CurrentCycles = 2; //minimale Cycles --> 10nS oder so
}

FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM4_SM2CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(Prescaler); //Erst nach vollem Cycle updaten und Prescaler setzen!
FLEXPWM4_SM2VAL1 = CurrentCycles - 1; //Cycles für Periodendauer setzen!
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
}

void setup() {
setOutputFrequency(5000000); //100kHz
setOutputPWM(820); //10% PWM
}

void loop() {
}
 
Thanks for you examples its helping me to understand how the PWM works. Reading through the MCU manual I don't see where GPIO_B0_00 is PWM capable. It is tied to pin 10 on the T4 and labeled as PWM. What am I missing?
 
Thanks for the example. I ported it to pin 2 and 3 which use FlexPWM4 submodule 2. Am I correct to assume that the "maske" should be 1 << submodule, and the portConfigRegister() function should set the pads to 1? In any case the code below does work.


#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"
#define ChannelA 2
#define ChannelB 3
#define Maske 4
#define ResolutionPWM 12 //12-Bit für PWM

void setOutputPWM(uint16_t val) {
uint32_t PeriodendauerCycles = FLEXPWM4_SM2VAL1; //Periodendauer
uint32_t Cycles = ((uint32_t)val * (PeriodendauerCycles + 1)) >> ResolutionPWM;
if (Cycles > PeriodendauerCycles) Cycles = PeriodendauerCycles;
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM4_SM2VAL3 = Cycles;
FLEXPWM4_SM2VAL4 = FLEXPWM4_SM2VAL3 + (((FLEXPWM4_SM2VAL1 - 2 * Cycles) / 2)); //PauseOffset anpassen!
FLEXPWM4_SM2VAL5 = FLEXPWM4_SM2VAL4 + (Cycles); //tOn für Channel B anpassen
FLEXPWM4_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(Maske); //ChannelA aktivieren
FLEXPWM4_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Maske); //ChannelB aktivieren
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
*(portConfigRegister(ChannelA)) = 1;//Maske;
*(portConfigRegister(ChannelB)) = 1;//Maske;
}

void setOutputFrequency(float frequency)
{
uint32_t CurrentCycles = (uint32_t)((float)F_BUS_ACTUAL / frequency + 0.5);
uint32_t Prescaler = 0;

//Falls Frequenz zu gering Prescaler switchen!
while (CurrentCycles > 65535 && Prescaler < 7) {
CurrentCycles = CurrentCycles >> 1;
Prescaler = Prescaler + 1;
}
if (CurrentCycles > 65535) {
CurrentCycles = 65535;
} else if (CurrentCycles < 2) {
CurrentCycles = 2; //minimale Cycles --> 10nS oder so
}

FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
FLEXPWM4_SM2CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(Prescaler); //Erst nach vollem Cycle updaten und Prescaler setzen!
FLEXPWM4_SM2VAL1 = CurrentCycles - 1; //Cycles für Periodendauer setzen!
FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
}

void setup() {
setOutputFrequency(5000000); //100kHz
setOutputPWM(820); //10% PWM
}

void loop() {
}


I can't find the definitions of *(portConfigRegister(ChannelA)) and *(portConfigRegister(ChannelB)), but I figured out that you can do this to find out what they should be:


Code:
#define ChannelA 2
#define ChannelB 3

void setup() {
  // put your setup code here, to run once:

  Serial.print("*(portConfigRegister(ChannelA)) "); Serial.println(*(portConfigRegister(ChannelA)));
  Serial.print("*(portConfigRegister(ChannelA)) "); Serial.println(*(portConfigRegister(ChannelB)));
  analogWrite(ChannelA, 10);
  analogWrite(ChannelB, 10);
  Serial.print("*(portConfigRegister(ChannelA)) "); Serial.println(*(portConfigRegister(ChannelA)));
  Serial.print("*(portConfigRegister(ChannelA)) "); Serial.println(*(portConfigRegister(ChannelB)));
}
void loop() {
}
 
Mux should be configured for pin which will be used as PWM.

Code:
extern "C" {
  #include "pwm.c"
}

void pwm_config_pin(int _pin)
{  
  *(portConfigRegister(_pin)) = pwm_pin_info[_pin].muxval;
}

This is basically the same as:

Code:
  IOMUXC_SW_MUX_CTL_PAD_GPIO_EMC_04 &= !B10111 | B1; // set pad to PWM4_A2

There is also another sutup which I'm not currently sure what is doing:

Code:
 // IOMUXC_FLEXPWM4_PWMA2_SELECT_INPUT = B0; // EMC_04
 
Last edited:
Thanks for you examples its helping me to understand how the PWM works. Reading through the MCU manual I don't see where GPIO_B0_00 is PWM capable. It is tied to pin 10 on the T4 and labeled as PWM. What am I missing?

On the T4.x boards, if you look at pwm.c, there are two different ways that PWM is generated.

a) FlexPWM timer (Chapter 55 in RM) - which is what most of this thread is talking about
b) Quad Timers (Chapter 54 in RM) which is what PIN 10 on T4 uses. {2, M(1, 0), 0, 1}, // QuadTimer1_0 10 // B0_00

If you look through the pwm.c code you will see where it uses the table to choose which code path...
 
If I would like to change frequency of the signal to precisely 8 MHz where should I look?

My understanding is that peripheral clock is used for driving pwm/quad timer and other modules. Unfortunately it uses 150MHz or 132MHz (in case of 528MHz CPU clock). Both of them are not divided by 8MHz.
Can I change source clock (PLL?) for only specific module like ex. PWM?
 
Back
Top