Teensy4 PWM interrupt config

kdharbert

Well-known member
I am setting up PWM output on a digital pin. I want an interrupt to fire on the falling edge of the PWM output. At present, I can't get both the interrupt and the PWM configured simultaneously: If I call attachinterrupt, the square wave output stops. Was I crazy thinking you can throw an interrupt on an output pin or is this just a config error?

Here's the basic code:

analogWriteResolution(11);
pinMode(22,OUTPUT);
analogWrite(22,1024);
analogWriteFrequency(22,40000);
attachInterrupt(digitalPinToInterrupt(22),PIN_22_ISR,FALLING);
 
Sorry in advance for these ramblings :D But maybe something might work.

Both of these things are trying to use this pin in two different ways. Driven by Timer and trying to get interrupt like it was an IO pin.
Looking at the data associated with this pin: 23 4A1 A9 RX1 3:9 MCL1 1.25 AD-B1_09

There may be some straight forward ways of getting the interrupt like you tried not sure if that exact way would do it. You might try doing the attach before you do the AnalogWrite stuff to see if maybe the interrupt data will persist. I have my doubts.

If not and if I were playing, I would try things like:
Code:
22	[COLOR="#FF0000"]4A0	[/COLOR]A8			TX1			3:08		1.24	AD_B1_08

From my document P497 shows all of the things this pin does with the different MUX_MODES

and likewise sources in cores\teensy4\pwm.c, I See that this PWM is done by FLEXPWM4_PWMA01
Code:
{1, M(4, 0), 1, 1},  // FlexPWM4_0_A  22  // AD_B1_08
So this pin uses the FlexPWM timer system to do the PWM. So after the PWM is hooked up I might try experimenting with setting up an interrupt through the timer and see if they worked together.
Looking at imxrt.h I see there is an interrupt vector:
Code:
        IRQ_FLEXPWM4_0 =        147,
So wonder what would happen if I set up an interruptVector for this one, like a few libraries do, like: FreqMeasureMulti
You would need to do all of the normal things like enable enable the interrupt and the like.
The data that this is on Timer Module 4, SubModule 0, and Pin A

I would then look at the timers registers, probably, then look at the INTEN register associated with this pin.
Something like IMXRT_FLEXPWM4.SM[0].INTEN
And look to see if either the CAPTURE A0 or A1 work... Maybe not as I think these are mainly for Input Capture and not OUTPUT

AND if that did not work. (very possible ) I would wonder if maybe I could hack up something using XBAR and ???
Again I don't know what I would try to map the XBAR in of

Again I don't know if if it would work to map something like one of:
Code:
#define XBARA1_IN_FLEXPWM4_PWM1_OUT_TRIG0 52
#define XBARA1_IN_FLEXPWM4_PWM1_OUT_TRIG1 52
#define XBARA1_IN_FLEXPWM4_PWM2_OUT_TRIG0 53
#define XBARA1_IN_FLEXPWM4_PWM2_OUT_TRIG1 53
#define XBARA1_IN_FLEXPWM4_PWM3_OUT_TRIG0 54
#define XBARA1_IN_FLEXPWM4_PWM3_OUT_TRIG1 54
#define XBARA1_IN_FLEXPWM4_PWM4_OUT_TRIG0 55
#define XBARA1_IN_FLEXPWM4_PWM4_OUT_TRIG1 55
To something else that I can get an interrupt on. It could be possible...

But depending on my needs, I would probably simply try to connect up pin 22 to some unused IO pin and the set the interrupt on that pin... But I know that is cheating.

Again sorry for these ramblings (brain dump) of things I might try if I really needed this to work.
 
Setting up PWM on an unused IO pin and setting an interrupt on another is exactly what I'm doing presently. This is super lame since its using up an extra pin...but it works.

I'm primarily wanting interrupts off PWM output so I can easily match the exact frequency config of another Teensy 4. I only really use the output of the interrupt pin for a diagnostic...so its fine if I don't get output, but the interrupt still fires. I would prefer to just use a timer interrupt directly, but the configuration for that may not perfectly match what analogwritefrequency is doing...same gig with IntervalTimer.


It seems like there has to be a good way for me to just use the timers directly
 
It seems like there has to be a good way for me to just use the timers directly

The T4 PWM timers can interrupt on period start and on "falling edge of the PWM output". Here is a simple sketch using flexpwm (ref ch 55) on T4 pin 8. Pin 13 is toggled on the falling PWM edge in the isr.
Code:
// PWM on pin8, flexpwm timer PWM1_A3
#define MMASK (1<<3)   // module mask 3 for PWM1_A3

volatile uint32_t ticks;
void pwm1_3_isr() {
  FLEXPWM1_SM3STS = FLEXPWM_SMSTS_CMPF(1 << 3); //clear VAL3 cmp
  GPIO7_DR_TOGGLE = 1 << 3; //fast GPIO 13 toggle
  ticks++;
  asm volatile ("dsb");
}
void setup() {
  pinMode(13, OUTPUT);
  analogWriteFrequency(8, 1000);
  analogWrite(8, 64);
  // set up ISR on falling edge, duty cycle compare match
  FLEXPWM1_MCTRL |= FLEXPWM_MCTRL_CLDOK(MMASK);  // stop timer
  attachInterruptVector(IRQ_FLEXPWM1_3, pwm1_3_isr);
  FLEXPWM1_SM3STS =  FLEXPWM_SMSTS_CMPF(1 << 3); // clear
  FLEXPWM1_SM3INTEN = FLEXPWM_SMINTEN_CMPIE(1 << 3) ; // enable compare interrupt
  NVIC_ENABLE_IRQ(IRQ_FLEXPWM1_3);
  analogWrite(8, 64); // restart
}

void loop() {
  static int prev = 0;
  Serial.printf("%d ticks %d\n", ticks - prev, ticks);
  prev = ticks;
  delay(1000);
}
Here is scope image of PWM on pin 8 (yellow, 25% duty) and pin 13 toggle (blue).
t4pwmcmp.png
You can see pin 13 toggles on the falling edge of the PWM. When you zoom in with the scope, you can measure the ISR latency at 56 ns. If you move the toggle to be first in the ISR, latency is 22.8 ns (13 cycles).
t4pwmisr.png
Using attachInterrupt() with pin 8 jumpered to pin 12, the ISR latency is 109 ns

see https://www.nxp.com/docs/en/application-note/AN12078.pdf measuring Teensy 4 ISR latency.
 
Last edited:
Thanks again for this code. How can I modify it to get an interrupt on both edges and rising edges only?
 
Thanks again for this code. How can I modify it to get an interrupt on both edges and rising edges only?

? "both" and "only" seem mutually exclusive?? any how, you can enable interrupt on both edges of the PWM. There is only one ISR for the interrupt so you will need to check the status flag (FLEXPWM1_SM3STS) to determine which interrupt fired. Read Ch 55 of reference manual.
Code:
...
void pwm1_3_isr() {
  if (FLEXPWM1_SM3STS & FLEXPWM_SMSTS_CMPF(1 << 3)) {
    FLEXPWM1_SM3STS = FLEXPWM_SMSTS_CMPF(1 << 3); //clear VAL3 cmp
    GPIO7_DR_TOGGLE = 1 << 3; //fast GPIO 13 toggle
  }
  if (FLEXPWM1_SM3STS & FLEXPWM_SMSTS_RF) {
    FLEXPWM1_SM3STS = FLEXPWM_SMSTS_RF; // reload clear
  }
  ticks++;
  asm volatile ("dsb");
}
...
  attachInterruptVector(IRQ_FLEXPWM1_3, pwm1_3_isr);
  FLEXPWM1_SM3STS =  FLEXPWM_SMSTS_CMPF(1 << 3) | FLEXPWM_SMSTS_RF; // clear
  FLEXPWM1_SM3INTEN = FLEXPWM_SMINTEN_CMPIE(1 << 3) | FLEXPWM_SMINTEN_RIE ; // enable interrupts
  NVIC_ENABLE_IRQ(IRQ_FLEXPWM1_3);
...
 
Bad sentence. I have code for FALLING. I need code for CHANGE and RISING configs individually, not in tandem.
 
Bad sentence. I have code for FALLING. I need code for CHANGE and RISING configs individually, not in tandem.
Hmmm, the ISR function in post #7 does all three. ISR is equivalent to CHANGE, then in the ISR you check the status flag to see if you have rising (RF) or falling (CMPF).
 
Sorry I am not very familiar with the these register settings. I did verify the code in Teensy 4 and it works.

My question is what if I want to get an interrupt on RISING edge of the PWM, where I should modify the code? Thanks.
 
Back
Top