Multi PWM with Phase Delay Teensy 4.1

robinvice

New member
Hi All,

I'm putting together a project that needs two PWM outputs.

The frequency will be fixed (non-changing) and the same frequency for both. I'm thinking it will end up around 500kHz to 1MHz.

The only strange thing about this is that the phase of the two outputs must be adjustable. The first PWM output can be fixed, with the second PWM changing phase. In other words the second PWM can have a delay compared to the first PWM. In the case of a 1MHz, 50% duty cycle output, the rising edge of the second PWM may be 100 to 900 nanoseconds after the rising edge of the first.

PWM_Waveforms.png


Is this possible?
Can it be done with a single group?
Like FlexPWM1.0 and FlexPWM1.1?
Or, would it have to be with two different groups, FlexPWM1.0 and FlexPWM2.0?

Thanks in advance for any responses.
robin
 
The program below will generate phase-shifted 1-MHz PWM on pins 2 and 3, which are channels A and B of the same SubModule on FlexPWM4. You can read about phase-shifted PWM in Chapter 55 of the iMXRT1062 reference manual. What the program does is use the standard Teensy/Arduino calls to set up the period and 50% PWM on both pins, then modifies the registers that control both pins to shift pin B relative to A, or A relative to B. If you think of B lagging A, with both having 50% duty cycle, only phase delays of +0 to +50% make sense. To get the other half of what you want, you have to change the phase of A relative to B, which would be like phase B relative to A of -0 to -50%. You can can experiment with different values to see what happens. Also, take a look at the code in cores\Teensy4\pwm.c to see how it sets up FlexPWM for the default setting of 50% PWM with no phase delay.

Code:
// pins 2 and 3 are channels A and B of FlexPWM 4, submodule 2
const int pinA = 2;    // pin 2 = flexpwm 4, submodule 2, channel A
const int pinB = 3;    // pin 3 = flexpwm 4, submodule 2, channel B

const int pwmFreq  = 1'000'000;
const int pwmRes   = 10;
const int pwmDuty  = (1<<pwmRes) / 2;   // 50% duty cycle
const int phasePct = 25;                              // valid phase = -49 to +49 %

void setup() {
  Serial.begin( 115200 );
  while (!Serial && millis() < 2000) {}

  // toggle LED to confirm program is running
  pinMode( LED_BUILTIN, OUTPUT );

  // set PWM resolution (bits)
  analogWriteResolution( pwmRes );

  // set frequency for both channels
  analogWriteFrequency( pinA, pwmFreq );

  // set duty cycle to 50% for both channels
  analogWrite( pinA, pwmDuty );
  analogWrite( pinB, pwmDuty );

  IMXRT_FLEXPWM_t *p = &IMXRT_FLEXPWM4;
  uint8_t submodule = 2;
  uint32_t period = p->SM[submodule].VAL1 + 1;  // period in clocks
  uint32_t high50 = period/2;                   // hightime for 50% duty cycle
  uint32_t riseA, fallA, riseB, fallB;
  if (phasePct >= 1 && phasePct <= 49) {
    riseA  = 0;                          // phase A rising edge always 0
    fallA  = high50;                     // phase A falling edge for 50%
    riseB  = (phasePct/100.0f)*period;   // phase B rising edge
    fallB  = riseB + high50;             // phase B falling edge
  }
  else if (phasePct <= -1 && phasePct >= -49) {
    riseB  = 0;                          // phase A rising edge always 0
    fallB  = high50;                     // phase A falling edge for 50%
    riseA  = (-phasePct/100.0f)*period;  // phase B rising edge
    fallA  = riseA + high50;             // phase B falling edge        
  }
 
  // shift PWM phase B to lag phase A by 90 deg
  p->MCTRL |= FLEXPWM_MCTRL_CLDOK(1<<submodule);  // do this before changes
  p->SM[submodule].VAL2 = riseA;                  // phase A rising edge
  p->SM[submodule].VAL3 = fallA;                  // phase A falling edge
  p->SM[submodule].VAL4 = riseB;                  // phase B rising edge
  p->SM[submodule].VAL5 = fallB;                  // phase B falling edge
  p->MCTRL |= FLEXPWM_MCTRL_LDOK(1<<submodule);   // do this after changes
 
  Serial.printf( "PWM phase     = %1ld %%\n",     phasePct );
  Serial.printf( "PWM frequency = %1lu Hz\n",     pwmFreq );
  Serial.printf( "PWM period    = %1lu clocks\n", period );
  Serial.printf( "PWM high time = %1lu clocks\n", high50 ); 
  Serial.printf( "PWM A rising  = %1lu clocks\n", riseA ); 
  Serial.printf( "PWM A falling = %1lu clocks\n", fallA ); 
  Serial.printf( "PWM B rising  = %1lu clocks\n", riseB ); 
  Serial.printf( "PWM B falling = %1lu clocks\n", fallB );
  Serial.println();
}

void loop() {
}
 
Last edited:
Thanks Joe!

Great answer to my question! I had to add a couple more PWMs to make my proto work, but you provided all the basics that I lacked.

I really appreciate your response!

Robin
 
Back
Top