Teensy 4.1

Teensy 4.1 PWM phaseshift

Hi I like to have 4 PWM signals with 4 different starting points. So lets say 200khz 0.5µs on time 4µs off
first pwm starts at 0µs second at 0.6µs third at 1.6µs and the last one at 2.4µs

I attached my tiny example for 1pwm which is on for pretty much exactly 0.5µs and has a total period length of 5.12µs (to have even devision with the 512 resolution at that frequency)
Code:
void setup() {
  analogWriteResolution(9);
  analogWriteFrequency(7, 195312);
  analogWrite(7, 50);
}

void loop() {
}

Adding a second/third pwm is fairly easy: analogWrite(8, 50); analogWrite(25, 50); if all are supposed to be on the same timer. or different pins if the timers are supposed to be different. but how do I do the offset/phase shift
 
Last edited:
The good news is the timer hardware can do this sort of thing.

But the bad news is there is no simple & easy Arduino API. You will need to take a deep dive into the hardware registers. But the hardware is very powerful and packed with advanced features, which means there is a lot of info to read. Start with the FlexPWM chapter in the reference manual.
 
OK I found the manual: https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf
But from reading the chapter I basically got everything is possible but not really how to do it, or how I could get started so far I have never messed with registers Oo

From what I get from 55.4.1.3 Phase Shifted PWMs from the graph PWM_A (whatever that is) starts at val2 and ends at val3 PWM_B starts at val4 and ends at val5
That sounds basically perfect but I still have no clue how to get started. Isn't there a similar example?
 
Code:
#include "imxrt.h"
#include "core_pins.h"
#include "debug/printf.h"


struct pwm_pin_info_struct {
  uint8_t type;    // 0=no pwm, 1=flexpwm, 2=quad
  uint8_t module;  // 0-3, 0-3
  uint8_t channel; // 0=X, 1=A, 2=B
  uint8_t muxval;  //
};


#define M(a, b) ((((a) - 1) << 4) | (b))

#if defined(__IMXRT1062__)

const struct pwm_pin_info_struct pwm_pin_info[] = {
  {1, M(1, 1), 0, 4},  // FlexPWM1_1_X   0  // AD_B0_03
  {1, M(1, 0), 0, 4},  // FlexPWM1_0_X   1  // AD_B0_02
  {1, M(4, 2), 1, 1},  // FlexPWM4_2_A   2  // EMC_04
  {1, M(4, 2), 2, 1},  // FlexPWM4_2_B   3  // EMC_05
  {1, M(2, 0), 1, 1},  // FlexPWM2_0_A   4  // EMC_06
  {1, M(2, 1), 1, 1},  // FlexPWM2_1_A   5  // EMC_08
  {1, M(2, 2), 1, 2},  // FlexPWM2_2_A   6  // B0_10
  {1, M(1, 3), 2, 6},  // FlexPWM1_3_B   7  // B1_01
  {1, M(1, 3), 1, 6},  // FlexPWM1_3_A   8  // B1_00
  {1, M(2, 2), 2, 2},  // FlexPWM2_2_B   9  // B0_11
  {2, M(1, 0), 0, 1},  // QuadTimer1_0  10  // B0_00
  {2, M(1, 2), 0, 1},  // QuadTimer1_2  11  // B0_02
  {2, M(1, 1), 0, 1},  // QuadTimer1_1  12  // B0_01
  {2, M(2, 0), 0, 1},  // QuadTimer2_0  13  // B0_03
  {2, M(3, 2), 0, 1},  // QuadTimer3_2  14  // AD_B1_02
  {2, M(3, 3), 0, 1},  // QuadTimer3_3  15  // AD_B1_03
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {2, M(3, 1), 0, 1},  // QuadTimer3_1  18  // AD_B1_01
  {2, M(3, 0), 0, 1},  // QuadTimer3_0  19  // AD_B1_00
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {1, M(4, 0), 1, 1},  // FlexPWM4_0_A  22  // AD_B1_08
  {1, M(4, 1), 1, 1},  // FlexPWM4_1_A  23  // AD_B1_09
  {1, M(1, 2), 0, 4},  // FlexPWM1_2_X  24  // AD_B0_12
  {1, M(1, 3), 0, 4},  // FlexPWM1_3_X  25  // AD_B0_13
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {1, M(3, 1), 2, 1},  // FlexPWM3_1_B  28  // EMC_32
  {1, M(3, 1), 1, 1},  // FlexPWM3_1_A  29  // EMC_31
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {1, M(2, 0), 2, 1},  // FlexPWM2_0_B  33  // EMC_07
#ifdef ARDUINO_TEENSY41
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {1, M(2, 3), 1, 6},  // FlexPWM2_3_A  36  // B1_00
  {1, M(2, 3), 2, 6},  // FlexPWM2_3_B  37  // B1_01
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {0, M(1, 0), 0, 0},
  {1, M(1, 1), 2, 1},  // FlexPWM1_1_B  42  // SD_B0_03
  {1, M(1, 1), 1, 1},  // FlexPWM1_1_A  43  // SD_B0_02
  {1, M(1, 0), 2, 1},  // FlexPWM1_0_B  44  // SD_B0_01
  {1, M(1, 0), 1, 1},  // FlexPWM1_0_A  45  // SD_B0_00
  {1, M(1, 2), 2, 1},  // FlexPWM1_2_B  46  // SD_B0_05
  {1, M(1, 2), 1, 1},  // FlexPWM1_2_A  47  // SD_B0_04
  {0, M(1, 0), 0, 0},  // duplicate FlexPWM1_0_B
  {0, M(1, 0), 0, 0},  // duplicate FlexPWM1_2_A
  {0, M(1, 0), 0, 0},  // duplicate FlexPWM1_2_B
  {1, M(3, 3), 2, 1},  // FlexPWM3_3_B  51  // EMC_22
  {0, M(1, 0), 0, 0},  // duplicate FlexPWM1_1_B
  {0, M(1, 0), 0, 0},  // duplicate FlexPWM1_1_A
  {1, M(3, 0), 1, 1},  // FlexPWM3_0_A  54  // EMC_29
#endif
};

#endif // __IMXRT1062__

void setup()
{
  analogWriteResolution(9);
  analogWriteFrequency(6, 195312);
  analogWrite(6, 50);
  MyanalogWriteFrequencyPS(9, 195312,100);
  analogWrite(9, 50);
}

void MyanalogWriteFrequencyPS(uint8_t pin, float mfrequency, int phaseshift)
{
  const struct pwm_pin_info_struct *info;

  if (pin >= CORE_NUM_DIGITAL) return;
  //printf("analogWriteFrequency, pin %d, freq %d\n", pin, (int)frequency);
  info = pwm_pin_info + pin;
  if (info->type == 1) {
    // FlexPWM pin
    IMXRT_FLEXPWM_t *flexpwm;
    switch ((info->module >> 4) & 3) {
      case 0: flexpwm = &IMXRT_FLEXPWM1; break;
      case 1: flexpwm = &IMXRT_FLEXPWM2; break;
      case 2: flexpwm = &IMXRT_FLEXPWM3; break;
      default: flexpwm = &IMXRT_FLEXPWM4;
    }
    MyflexpwmFrequencyPS(flexpwm, info->module & 0x03, info->channel, mfrequency, phaseshift);
  } else if (info->type == 2) {
    // QuadTimer pin
    //ignore
  }
}

void MyflexpwmFrequencyPS(IMXRT_FLEXPWM_t *p, unsigned int submodule, uint8_t channel, float mfrequency, int phaseshift)
{
  uint16_t mask = 1 << submodule;
  uint32_t olddiv = p->SM[submodule].VAL1;
  uint32_t newdiv = (uint32_t)((float)F_BUS_ACTUAL / mfrequency + 0.5f);
  uint32_t prescale = 0;
  //printf(" div=%lu\n", newdiv);
  while (newdiv > 65535 && prescale < 7) {
    newdiv = newdiv >> 1;
    prescale = prescale + 1;
  }
  if (newdiv > 65535) {
    newdiv = 65535;
  } else if (newdiv < 2) {
    newdiv = 2;
  }
  //printf(" div=%lu, scale=%lu\n", newdiv, prescale);
  p->MCTRL |= FLEXPWM_MCTRL_CLDOK(mask);
  p->SM[submodule].CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(prescale);
  p->SM[submodule].VAL1 = newdiv - 1;
  p->SM[submodule].VAL2 = ((p->SM[submodule].VAL2 + phaseshift) * newdiv) / olddiv;  //offset from start at 0
  p->SM[submodule].VAL0 = (p->SM[submodule].VAL0 * newdiv) / olddiv;
  p->SM[submodule].VAL3 = ((p->SM[submodule].VAL3 + phaseshift) * newdiv) / olddiv;
  p->SM[submodule].VAL5 = (p->SM[submodule].VAL5 * newdiv) / olddiv;
  p->MCTRL |= FLEXPWM_MCTRL_LDOK(mask);
}


void loop()
{
}

Based on pauls sugguestion I forked the pwm code from https://github.com/PaulStoffregen/co.../teensy4/pwm.c
and added the val2 variable which should set the starting point of the pwm signal
So I basically hacked in a phaseshift and it seems to work. not the prettiest solution but hey for a first trial not bad. I can't tell you how happy I am.
My remaining two problems are that I need 4 signals so I guess I have to synchronise two pwm timers. And so far I haven't figured it out.
The last one is that I should probably scale the phaseshift to the resolution of the PWM. But that seems at least in my immagination to be a smaller problem.
 
Code:
#include "imxrt.h"
My remaining two problems are that I need 4 signals so I guess I have to synchronise two pwm timers. And so far I haven't figured it out.
The last one is that I should probably scale the phaseshift to the resolution of the PWM. But that seems at least in my immagination to be a smaller problem.[/QUOTE]

Did you ever figure out the 4 signals? Need to do something similar for an H-Bridge.
 
Back
Top