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

Thread: Teensy 4.1

  1. #1
    Junior Member
    Join Date
    Nov 2021
    Posts
    8

    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 by Etherflash; 11-02-2021 at 07:29 PM.

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,227
    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.

  3. #3
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    You mean the Datasheet itself or is there another reference manual?

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,227
    On the Teensy 4.1 page, scroll down to "Technical Information" near the end.

    https://www.pjrc.com/store/teensy41.html#tech

    The first link is "IMXRT1060 Manual - All the useful peripheral programing info". That is the reference manual.

    FlexPWM is chapter 55, starting on page 3029.

  5. #5
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    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?

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    25,227
    Maybe start with the analogWrite / analogWriteFrequency code.

    https://github.com/PaulStoffregen/co.../teensy4/pwm.c

  7. #7
    Junior Member
    Join Date
    Nov 2021
    Posts
    8
    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.

Posting Permissions

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