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

Thread: T4.0 FlexPWM Documentation?

  1. #1
    Junior Member
    Join Date
    Aug 2022
    Posts
    4

    T4.0 FlexPWM Documentation?

    Hey everyone, I am trying to get 4 PWM channels (2 sets of complimentary signals, with one pair phase shifted dynamically.

    Below is my current code, it is built from code I found here, as I cannot find any documentation on how to use the FlexPWM registers. This code works for Channels A & B (FlexPWM 4.2 - pins 2/3), but does not work for Channels C & D (FlexPWM 2.2 - pins 6/9).

    I have two questions:
    1. How do I get channels C & D working? I assumed I could simply copy over the A & B code and change from timer FlexPWM 4 to FlexPWM 2 because they are both X.2, however this does not appear to work.
    2. What is the best way to add a shift angle to this? I have found shift angle examples here, but they use analogWrite()


    Thanks for any help!

    Code:
    #include "imxrt.h"
    #include "core_pins.h"
    #include "debug/printf.h"
    
    #define Mask 4
    #define ResolutionPWM 12 //12-Bit PWM
    
    #define ChannelA 2
    #define ChannelB 3
    #define ChannelC 6
    #define ChannelD 9
    
    #define SET_AB	0
    #define SET_CD	1
    
    void hbridge_setFreq(uint8_t setIdx, float freqHz) {
    	if(setIdx == SET_AB) {
    		//	Set AB
    		uint32_t CurrentCycles = (uint32_t)((float)F_BUS_ACTUAL / freqHz + 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(Mask);
    		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(Mask);
    	}
    	else if(setIdx == SET_CD) {
    		//	Set CD
    		uint32_t CurrentCycles = (uint32_t)((float)F_BUS_ACTUAL / freqHz + 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(Mask);
    		FLEXPWM2_SM2CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(Prescaler); //Erst nach vollem Cycle updaten und Prescaler setzen!
    		FLEXPWM2_SM2VAL1 = CurrentCycles - 1; //Cycles für Periodendauer setzen!
    		FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Mask);
    	}
    }
    
    void hbridge_setDuty(uint8_t setIdx, uint16_t val) {
    	if(setIdx == SET_AB) {
    		//	Set AB
    		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(Mask);
    		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(Mask); //ChannelA aktivieren
    		FLEXPWM4_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Mask); //ChannelB aktivieren
    		FLEXPWM4_MCTRL |= FLEXPWM_MCTRL_LDOK(Mask);
    		*(portConfigRegister(ChannelA)) = 1;//Maske;
    		*(portConfigRegister(ChannelB)) = 1;//Maske;
    	}
    	else if(setIdx == SET_CD) {
    		//	Set CD
    		uint32_t PeriodendauerCycles = FLEXPWM2_SM2VAL1; //Periodendauer
    		uint32_t Cycles = ((uint32_t)val * (PeriodendauerCycles + 1)) >> ResolutionPWM;
    		if (Cycles > PeriodendauerCycles) Cycles = PeriodendauerCycles;
    		FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Mask);
    		FLEXPWM2_SM2VAL3 = Cycles;
    		FLEXPWM2_SM2VAL4 = FLEXPWM2_SM2VAL3 + (((FLEXPWM2_SM2VAL1 - 2 * Cycles) / 2)); //PauseOffset anpassen!
    		FLEXPWM2_SM2VAL5 = FLEXPWM2_SM2VAL4 + (Cycles); //tOn für Channel B anpassen
    		FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(Mask); //ChannelA aktivieren
    		FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Mask); //ChannelB aktivieren
    		FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Mask);
    		*(portConfigRegister(ChannelC)) = 1;//Maske;
    		*(portConfigRegister(ChannelD)) = 1;//Maske;
    	}
    }
    
    #define HBRIDGE_FREQ_HZ	100000
    #define HBRIDGE_DUTY	0	//	todo - make definition for allocating dead time
    
    //	Start H-Bridge Timing H-Bridge
    void hbridge_on() {
    	hbridge_setFreq(SET_AB, HBRIDGE_FREQ_HZ);
    	hbridge_setDuty(SET_AB, 2000);
    	hbridge_setFreq(SET_CD, HBRIDGE_FREQ_HZ);
    	hbridge_setDuty(SET_CD, 2000);
    	//hbridge_setDuty(SET_AB, 820);	//	10% PWM
    }
    
    //	Turn off H-Bridge
    void hbridge_off() {
    	*(portConfigRegister(ChannelA)) = 0;
    	*(portConfigRegister(ChannelB)) = 0;
    	*(portConfigRegister(ChannelC)) = 0;
    	*(portConfigRegister(ChannelD)) = 0;
    }
    
    void setup() {
    	hbridge_on();
    }
    
    void loop() {
    	
    }

  2. #2
    Senior Member+ manitou's Avatar
    Join Date
    Jan 2013
    Posts
    2,719
    The mask for the C/D FLEXPWM timers is ALT 2 (ref page 515)
    Code:
    		*(portConfigRegister(ChannelC)) = 2;//Maske;
    		*(portConfigRegister(ChannelD)) = 2;//Maske;
    See https://www.nxp.com/docs/en/application-note/AN4485.pdf eFlexPWM

    Here is some Teensy 3 (FTM) phase shift info https://www.nxp.com/docs/en/application-note/AN5142.pdf
    and https://forum.pjrc.com/threads/42158...ase-shift-help (Teensy 3* FTM)
    Last edited by manitou; 08-09-2022 at 03:35 PM.

  3. #3
    Junior Member
    Join Date
    Aug 2022
    Posts
    4
    Thanks a ton! The mask fixed the issue for channels C & D! I knew it was some dumb mistake on my part.

    As for the phase shifting, I did see those resources, but it was my understanding that the Teensy 4.X boards dropped FTM for FlexPWM and QuadTimer. I'm looking for documentation on those - specifically the definitions of the appropriate registers defined in the Teensy imrtx.h file.

  4. #4
    Junior Member
    Join Date
    Aug 2022
    Posts
    4
    I have been looking at the technical manual, but I still can't tell the best way to dynamically apply a phase shift angle (with constant frequency)

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    26,801
    I would imagine you'd write the channels mask to the MCTRL CLDOK bits to zero the LDOK bits for all the timers you wish to alter. Then write your new settings to some or all of the 6 timing registers. Repeat for each timer. Then write the channels to MCTRL LDOK bits to cause all of your new settings to any combination of the 4 timers to all take effect at the same moment.

    The trick with FlexPWM is it's so flexible that you really have to think carefully about what to do with those 6 registers.

Posting Permissions

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