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

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

  1. #1
    Junior Member
    Join Date
    Jan 2020
    Posts
    17

    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
    Junior Member
    Join Date
    Jan 2020
    Posts
    17
    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:	50 
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
    2
    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() {
    }

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
  •