T4, FLEXPWM reload flag set independent of pwm module reload, dependent on SysClock

tomryan

Member
Aha! I posted in the General Discussion and Technical Support Boards before debugging lead me here. The IRQ_FLEXPWM2_0 (137) vector does not seem to be functioning properly.

I set the FLEXPWM2_SM0CTRL register so that reload flags should occur once per pwm cycle (ie the flag is set upon pwmCount = SM0Val1). I also added a debugging output to the valArampUpISR() by toggling a digital pin every time the ISR executed. D7 is the digital debugging output, <D0,D1,D2> are pwm channels <A,B,C>.

Here is a zoomed out shot of the scope showing the channelA ramp-up:
a.png

Here is the o-scope output when the Teensy runs at 150Mhz (reload ISR every ~1.5u):
b.png

Here is the o-scope output when the Teensy runs at 24MHz (reload ISR every ~3u):
c24.png
This is my source code:
Code:
#include <Arduino.h>
#include "imxrt.h"
#include "core_pins.h"
#define ChannelA 4
#define ChannelB 33
#define ChannelC 5
#define Maske 0x03

struct trapTest
{
  uint16_t dutyRamp;
  uint16_t period;
  volatile bool state;
  
};

trapTest trap;
trapTest* trap1 = &trap;

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_SM1CTRL = FLEXPWM_SMCTRL_FULL | FLEXPWM_SMCTRL_PRSC(Prescaler); //same prescaler for SM1
    FLEXPWM2_SM0VAL1 = CurrentCycles - 1; //Cycles f�r Periodendauer setzen!
    FLEXPWM2_SM1VAL1 = CurrentCycles - 1; //same period for SM1
    FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
    trap1->period = FLEXPWM2_SM0VAL1;
}

static inline void clearInterrupt()
{
  FLEXPWM2_SM0STS &= ~0x1000;
}

void valArampUpISR()
{
   
    if (FLEXPWM2_SM0VAL2 > 3)
    {
        FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
        FLEXPWM2_SM0VAL2 -= trap1->dutyRamp; 
        FLEXPWM2_SM0VAL3 += trap1->dutyRamp;
        FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
    }
    trap1->state = !trap1->state;
    digitalWrite(9,trap1->state);
    clearInterrupt();
    
}

void valBrampDownISR()
{
    if (FLEXPWM2_SM0VAL4 + 1 < FLEXPWM2_SM0VAL5)
    {
        FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);
        FLEXPWM2_SM0VAL4 += trap1->dutyRamp;
        FLEXPWM2_SM0VAL5 -= trap1->dutyRamp;
        FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
    }
    clearInterrupt();
}

void setOutputPWM(uint16_t valAL, uint16_t valAH, uint16_t valBL, uint16_t valBH, 
    uint16_t valCL, uint16_t valCH)
{
    FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);

    FLEXPWM2_SM0VAL2 = valAL;
    FLEXPWM2_SM0VAL3 = valAH;

    FLEXPWM2_SM0VAL4 = valBL;
    FLEXPWM2_SM0VAL5 = valBH;

    FLEXPWM2_SM1VAL2 = valCL;
    FLEXPWM2_SM1VAL3 = valCH;

    FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);

}

void initTest()
{
  *(portConfigRegister(ChannelA)) = 1; //trap->pin1, initializes muxval in pwm.c pwm_pin_info struct
  *(portConfigRegister(ChannelB)) = 1; //trap->pin2
  *(portConfigRegister(ChannelC)) = 1; //trap->pin3

    FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_CLDOK(Maske);

    FLEXPWM2_SM0CTRL |= (uint16_t)0x0400; //Full cycle reloads, enable LDMOD for instant reloads
    FLEXPWM2_SM1CTRL |= (uint16_t)0x0400;

    FLEXPWM2_SM0CTRL &= (uint16_t)~0x0800; //disable half cycle reloads
    FLEXPWM2_SM1CTRL &= (uint16_t)~0x0800;

    FLEXPWM2_SM1CTRL2 |= FLEXPWM_SMCTRL2_CLK_SEL(0x02); //SM1 relies on SM0 clock

    FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMA_EN(Maske);
    FLEXPWM2_OUTEN |= FLEXPWM_OUTEN_PWMB_EN(Maske);

    //FLEXPWM2_SM0CTRL2 |= (uint16_t)0x00C0; //FRCEN, FRCSEL !ACHTUNG!, STILL need to add FRCOUT to pwmOutput,
    //FLEXPWM2_SM1CTRL2 |= (uint16_t)0x00C8; //SM1 relies on SM0 FRC          //... setup sel45 for initial values

    FLEXPWM2_SM0INTEN |= 0x1000;

    FLEXPWM2_MCTRL |= FLEXPWM_MCTRL_LDOK(Maske);
}

void printInitState()
{
  Serial.println("initTest");
Serial.println("SM0INTEN: ");
    for(int x = 15; x >= 0; --x)
    {
        Serial.print(bitRead(FLEXPWM2_SM0INTEN, x));
    }
   Serial.println();

Serial.println("SM0CTRL: ");
    for(int x = 15; x >= 0; --x)
    {
        Serial.print(bitRead(FLEXPWM2_SM0CTRL, x));
    }
   Serial.println();
}
void setup() {
  pinMode(9,OUTPUT);
  digitalWrite(9,LOW);
  Serial.begin(115200);
  Serial.println("Begin");
  
setOutputFrequency(40000.0);
setOutputPWM(trap1->period/2,trap1->period/2,0,trap1->period,trap1->period/4,trap1->period*3/4);
initTest();
printInitState();
delay(500);

trap1->dutyRamp = 1;
attachInterruptVector(IRQ_FLEXPWM2_0, valArampUpISR);
NVIC_ENABLE_IRQ(IRQ_FLEXPWM2_0);

delay(500);
FLEXPWM2_SM0INTEN &= ~0x1000;
NVIC_DISABLE_IRQ(IRQ_FLEXPWM2_0);
clearInterrupt();
    
attachInterruptVector(IRQ_FLEXPWM2_0,valBrampDownISR);
NVIC_ENABLE_IRQ(IRQ_FLEXPWM2_0);
FLEXPWM2_SM0INTEN |= 0x1000;
}


void loop() {
  // put your main code here, to run repeatedly:

}


As you can see, the RF flag is not only set many times a pwm period, but the frequency at which it is set is dependent on the Clock rate. The program sets the Prescaler such that the pwm period is ~40 kHz regardless of the clock frequency. Even when I set a 1kHz pwm period, the reload flag is set at the same frequency (resulting in hundreds of ISRs every pwm period).

Thank you for any help, I would love to find out that this is my own doing and not a problem with the Teensy Platform.

For reference the links to my previous posts can be found here:

https://forum.pjrc.com/threads/70640-trouble-attaching-consecutive-ISRs-to-the-same-FLEXPWM-IRQ

https://forum.pjrc.com/threads/70644-T4-1-attachInterruptVector()-and-NVIC-Controller
 
Sorry, I have not gone through all of the code, the one thing I do see is:
Code:
static inline void clearInterrupt()
{
  FLEXPWM2_SM0STS &= ~0x1000;
}
I believe this register all of the Writes are marked W1C (Write one Clear)
So probably want.
FLEXPWM2_SM0STS = 0x1000;
 
Thank you so very much! I won't be near my Teensy again until tomorrow, but I will try that first thing. If the flag hasn't been clearing properly it would make sense why the ISR frequency is related to the Bus frequency of the pwm module. Also, if the program is stuck in the ISR it makes sense that the next interrupt would never get attached. I'll post a status update tomorrow morning EST. You're nothing short of a saint for digging through someone else's register code.
 
To keep a T4 ISR from firing twice, it is recommended to add asm volatile ("dsb"); to the ISR
 
Back
Top