PWM Interrupts API?

DrM

Well-known member
Hi, is there an API that supports using the ability of the FlexPWM to generate interrupts?

I see there are at least two threads that describe doing this using register access, from about two years ago. The question is, has this capability been implemented in a library?

Thank you
 
See the eFlexPWM library referenced in the link below. This library is a wrapper to the NXP PWM driver, and at least one of the two example programs includes PWM interrupts and update of the duty cycle on each interrupt.


As an aside, I've used some of the other NXP iMXRT drivers on another platform. The drivers are all written in C and depend only on headers that are part of the driver package. The driver examples are C++ intended for the NXP tools, but they all follow the same pattern, and I found it easy to port them. I've successfully used the NXP drivers for PIT, PWM, GPT, ADC, QTMR, RTWDOG, and TEMPMON, and I've been meaning to port all of these examples to Teensy, but haven't yet found the time.
 
is there a high level document that explains all of that?

I see you have the usual oxygen sort of documentation. But to understand even that, I would still need to invest in a detailed study of the FlexPWM in the RM.
 
is there a high level document that explains all of that?

I see you have the usual oxygen sort of documentation. But to understand even that, I would still need to invest in a detailed study of the FlexPWM in the RM.

It's not my library, but I have used it, and like most open-source libraries, the documentation is scant, so there is some learning curve. The examples work right out of the box, which is not a given with Arduino, so I'd say the author is on the upper end of the spectrum in that regard. As always, the reference manual is the ultimate documentation, but there is documentation for all of the NXP MCUXpresso (link below). If you tell us what you want to do, we might be able to help you.

 
I want to output three clocks, A, B and C as follows

A on pin 4 (or 5), a simple 50% square wave, 2MHz​
B on pin 8, is a 1usec pulse, every "n" microseconds (in the range 100usecs to 100msecs).​
C on pin 7, is a 2usec pulse, every M-th B pulse, but rises 1/2 usec before that B pulse.​
A can be started in setup()

C and B are started (and later stopped) by a software command. I am thinking that the timing for B and C can be accomplished by starting C and then starting B after some interval measured in cpu cycles.

The interrupt is attached to the trailing edge of C.


Thank you
 
Are B and C independent of A, or should rising edges of B and C have a particular relationship to rising/falling edges of A?

Quick thoughts

1) B and C would better be done with output compare than with PWM
2) The large range of times between pulses (us to sec) will be a challenge because they imply at least 32-bit
3) If you want everything to be synchronized, you'll likely have to use pins that are all associated with one timer, or set of timers.
 
There is no timing requirement between A and the other two clocks.

For the 100msec interval between B pulses, the B pulse width can be a bit more than 1usec, for example 2usec or even a few usec. That brings it into the range of 15 bits

C needs to overlap B, starting 100ns to 1usec before B, and finishing 1usec to 5usec after B

How is output compare configured?
 
Most timers, including PWM timers, have input capture and output compare functions. Input capture reads and saves the timer's running counter on a specified input event. Output compare generates a specified output when the timer's running counter reaches a specified value, and generally provides for the possibility of an interrupt. To produce a pulse of N us, output compare could be configured to generate a rising edge (or simply toggle) at some time T, and in the ISR, a new output match could be configured for the falling edge to occur at T+H, where H is the desired high time in timer clocks. Each type of timer (GPT, QTMR, etc.) has its own details, i.e. register names, event types, etc.
 
I see. The challenge that I am trying to solve, is to keep A and B running accurately, while I am running a loop in software.

Here is the context a little more specifically. It is about a linear CCD.

The first clock A, is the master clock.

The second clock B, is the shift (SH). This moves charge from the array of photodiodes to a buffer, all the pixels at once.

The third clock C, is really a sort of enable and shift combined (ICG). When B happens with C high, the charge is moved into the readout register.

Then, A acts like a shift along the readout register. This is the actual CCD in CCD. The classic readout acts to shift charge to the right, in 4 clock cycles per shift. The charge comes off the end of the ccd into a charge to voltage amplifier and then appears on the output pin.

In other words, when C goes low, I need to start reading my ADC. Every 4th clock, I set the convert pin on the ADC, and then read the data back over SPI.

Until now, I have done all of this by running only A on a clock. use a timer for the the shiftt and the start of the readout. And running the readout as a loop that synchronizes itself to the clock pin (by tying it to another pin and looping for the leading or trailing edge).

Because I do that readout in software, that means I stop clocking the shift pin during the readout.

The goal now, is to experiment with continuing to clock the shift pin during the readout. My conjecture or hope is that the pwm will keep it going at least reasonably on-schedule while I am in the readout loop.

The reason for this has to do with an aspect of the device physics not related to the readout, and at this point it is simply a curiosity on my part. If it proves interesting, I'll write it up for te github posting.
 
I forgot to mention something. In the application, the code that generates B and C (and the readout) is launched in one of three ways: (a) on user command, (b) on a timer interrupt (the user commanded to read the ccd every so many second) or (c) on a pin interrupt.

There are a lot of typos in the previous post, apologies for that too. I am resisting the urge to go back and fix it, but hesitance to generate the spurious extra email.
 
The challenge that I am trying to solve, is to keep A and B running accurately, while I am running a loop in software.

The T4 has 2 x GPT (general purpose timer), which are 32-bit, which will simplify your application a lot, I think. Each GPT has 3 x output compare registers, so I think what you would need to do is select 2 pins that can be assigned to 2 of the output compare functions of either GPT1 or GPT2. Both pins should be on the same GPT.

I think the GPT can be configured to use either the 24 MHz or 150 MHz clock. Either one is okay in your case because either would support 1 us pulses and long periods (seconds) between pulses. Generating a B or C pulse could be done something like this:

- start with the output low and set for toggle on match
- read the value of the timer
- set the output compare to generate the rising edge
- enable the match interrupt
- on the rising edge interrupt, set the output compare for the falling edge
- on the falling edge interrupt, set the output compare for the next rising edge

If you think of this is as describing the B pulse, the C pulse would be done the same way, with the rising edge of C set to 0.5 us sooner than the rising edge of B. Because both B and C are running on the same timer, it's easy to compute the value of the timer for:

- rising and falling edge of C
- rising and falling edge of B

All 3 output compare functions share one interrupt, so the ISR has to check flags to see which match has occurred and what to do next. It will be tricky to set up, but it will take almost no CPU time, so it won't get in the way of whatever else needs to be done.
 
The T4 has 2 x GPT (general purpose timer), which are 32-bit, which will simplify your application a lot, I think. Each GPT has 3 x output compare registers, so I think what you would need to do is select 2 pins that can be assigned to 2 of the output compare functions of either GPT1 or GPT2. Both pins should be on the same GPT.

I think the GPT can be configured to use either the 24 MHz or 150 MHz clock. Either one is okay in your case because either would support 1 us pulses and long periods (seconds) between pulses. Generating a B or C pulse could be done something like this:

- start with the output low and set for toggle on match
- read the value of the timer
- set the output compare to generate the rising edge
- enable the match interrupt
- on the rising edge interrupt, set the output compare for the falling edge
- on the falling edge interrupt, set the output compare for the next rising edge

If you think of this is as describing the B pulse, the C pulse would be done the same way, with the rising edge of C set to 0.5 us sooner than the rising edge of B. Because both B and C are running on the same timer, it's easy to compute the value of the timer for:

- rising and falling edge of C
- rising and falling edge of B

All 3 output compare functions share one interrupt, so the ISR has to check flags to see which match has occurred and what to do next. It will be tricky to set up, but it will take almost no CPU time, so it won't get in the way of whatever else needs to be done.
Thank you.

The problem is that I can't run an ISR during the readout. It will mess up the timing.

But now, after finally starting to grok the PWM section in the RM, a bit, it seems like a good solution might be to use three PWM modules.

Is it possible to reassign the external Teensy 4 pins among the PWM's?
 
Thank you.

The problem is that I can't run an ISR during the readout. It will mess up the timing.

But now, after finally starting to grok the PWM section in the RM, a bit, it seems like a good solution might be to use three PWM modules.

Is it possible to reassign the external Teensy 4 pins among the PWM's?

Because you want to control the relationship between A/B/C, or at least between B and C, you probably want 3 PWM channels from the same module. I'm guessing that you're asking about pin assignments to use whatever pins you chose for your existing hardware?

Why do you say you can't have interrupts during the readout? The nice thing about using input capture and output compare is that the signal timing is independent of the interrupts, so you only have to ensure that the ISR gets executed before the next event.

Are you disabling the 1-kHz time tick?
 
Because you want to control the relationship between A/B/C, or at least between B and C, you probably want 3 PWM channels from the same module. I'm guessing that you're asking about pin assignments to use whatever pins you chose for your existing hardware?

Why do you say you can't have interrupts during the readout? The nice thing about using input capture and output compare is that the signal timing is independent of the interrupts, so you only have to ensure that the ISR gets executed before the next event.

Are you disabling the 1-kHz time tick?

As presently coded, I can't have any code run whether from an interrupt or anything else, during the readout.

Following is the readout. This gets luanched on the falling edge of the "C" output described above. Notice that it syncs itself to the 1MHZ clock. At each iteration it toggles the convert pin in the ADC and then reads the result over SPI.

I could conceivably run the readout from a fourth PWM if I could access one, and if it could be used to trigger the SPI at a specified time during each cycle.

C-like:
#define CLOCKREAD (CORE_PIN5_PINREG & CORE_PIN5_BITMASK)
#define SETCNVST (CORE_PIN9_PORTSET = CORE_PIN9_BITMASK)
#define CLEARCNVST (CORE_PIN9_PORTCLEAR = CORE_PIN9_BITMASK)

inline void SensorReadOutLoop_() {

  uint16_t *p16 = bufferp;
  int i;

  // Read on each rising edge
  p16 = bufferp;
  for (i=0; i<NREADOUT; i++){

    while ( CLOCKREAD ) {}   // wait while high
    while ( !CLOCKREAD ) {}  // wait while low

    SETCNVST;
    //delayNanoseconds( 710 );
    delayNanoseconds( 670 );
    CLEARCNVST;
    *p16++ = SPI.transfer16(0xFFFF);     
  }
}
 
Okay, you can't have interrupts now because you are polling the CLOCK input for a rising edge, and then generating your conversion signal by polling ARM_DWT_CYCCNT via delayNanoseconds(). I don't see why it's necessary to do it that way. Generally speaking, this is what timers are for.
 
I don't see why it's necessary to do it that way. Generally speaking, this is what timers are for.

Yes, I agree, generally. The CNVST pin happens to be the CS (pin 10) from the SPI interface. And according to this, it is not a flexPWM pin.

Hence the question about rerouting the pins. Is that something the XBAR can do? (Is there a simple example to demonstrate how to do it?)

The other thing is that if the flexPWM is going to launch the SPI transfer, it needs to do it at the hardware level. I recall that the RM says the flexPWM can initiate a DMA. If either of those can run the SPI transfer, we're in business.
 
You've lost me in terms of what you want to do with the PWM outputs, and I don't understand your system well enough to do any more than make the type of general suggestions I have so far. I'm not the one to answer questions on XBAR or DMA.
 
Well naively, the PWM could clock the readout, the output pulse would be the assertion of the CS (CNVST) for 700nsec, and then on the trailing edge it could start the SPI. But it would have to be a hardware mechanism (rather than an ISR) that starts the SPI transfer, because the interrupt latency has too much varation comared to the nsec scale response that we need for this.

I recall some discussion about doing SPI transfers by DMA, and I recall in the RM that it talks about the PWM being able to launch a DMA. So, I wonder if that provides a way to do this?

The pin remapping questions is about driving the CS pin from a PWM. That would be a convenience, provided the above could be worked out.
 
Thank you for the discussion so far though, it has been helpful. With that and re-reading the PWM a few times, I am starting to get it.
 
Thank you for the discussion so far though, it has been helpful. With that and re-reading the PWM a few times, I am starting to get it.
You're welcome. I'll just make one more comment. You can get less jitter with output compare than you have now with polling. Input capture and output compare are done in hardware, so there is no interrupt response latency.
 
Yes, that is one of the things I like about it. If I can figure out, or someone can offer, how to run the SPI from it, it would be fantastic.

This how a lot of ADC's work nowadays. So it would be a hugely useful capability and very worthy of a library.
 
So... I'm getting ready to actually try this. Re-reading the PWM, I think it might actually be possible to setup the clocks I need and start them with sufficiently low latency. So I am eager to try that.

Re-reading the LPSPI, there seems to be an input trigger. That might be just the thing to clock an external ADC. Assuming I am reading it correctly; Do we have an API for using that? Is there a pin that input triggers comes in on?

What I am thinking of is to assert the CNVST on a clock, PWM for example, and modulo an inverter perhaps, jumper it into whatever pin that LPSPI reads its trigger from.

If we can do this, than we could problably use a similar method to run a photon tagger, another very important capability that normally costs tens of thousands of dollars and could be implemented in a teensy for around 100, So, I am excited at the possibility,
 
Back
Top