Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 8 of 8

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

  1. #1

    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 ...

  2. #2
    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.

    Click image for larger version. 

Name:	flexPWMPic.jpg 
Views:	184 
Size:	38.6 KB 
ID:	18801

    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() {
    }

  3. #3
    Junior Member
    Join Date
    Dec 2019
    Posts
    7
    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() {
    }

  4. #4
    Junior Member
    Join Date
    Nov 2015
    Posts
    9
    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?

  5. #5
    Junior Member
    Join Date
    Sep 2019
    Posts
    8
    Quote Originally Posted by Durch_Razor View Post
    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() {
    }

  6. #6
    Junior Member
    Join Date
    Jun 2022
    Location
    Poland
    Posts
    2
    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 by megagame; 06-09-2022 at 12:00 PM.

  7. #7
    Senior Member+ KurtE's Avatar
    Join Date
    Jan 2014
    Posts
    10,792
    Quote Originally Posted by lbeckner View Post
    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...

  8. #8
    Junior Member
    Join Date
    Jun 2022
    Location
    Poland
    Posts
    2
    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?

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •