Teensyduino 1.48 Beta #3

Status
Not open for further replies.
PulsePosition hasn't been ported yet. It's filled with FTM/TPM timer code. Only Teensy 3.x & LC have those timers. NXP put different timers in this IMXRT new chip.

I will eventually port PulsePosition, maybe to the FlexPWM timers, maybe to the quad timers. But that's not going to happen for the 1.48 release.

I have a proof of concept sketch for PPM input portion of PulsePosition for the T4 using quad timer, see
https://github.com/manitou48/teensy4/blob/master/ppminq.ino
This was based on earlier sketch/discussion that used T4 quad timer. As with that sketch, I was never able to get overflow (TOF) interrupt to work on T4 quad timer, so I used timer compare (0xffff) to count timer overflows. In the ppminq.ino sketch the T4 quad timer is clocked at 37.5 MHz. I tested with a T3.2 running PulsePosition LoopBack example with PPM output on T3.2 pin 9 jumpered to T4 capture pin 11. The printed output looks good.
Code:
1086 :  600.03  1500.03  759.23  1500.03  1500.00  1234.56  
1087 :  600.03  1500.00  759.25  1500.00  1500.03  1234.53  
1088 :  600.03  1500.03  759.23  1500.03  1500.00  1234.56  
1089 :  600.03  1500.00  759.25  1500.00  1500.03  1234.53
You can flip polarity with TMR_SCTRL_IPS.

Now to see how to get the T4 to generate PPM out pulses...
 
Last edited:
I have a proof of concept sketch for PPM input portion of PulsePosition for the T4 using quad timer, see
https://github.com/manitou48/teensy4/blob/master/ppminq.ino

Now to see how to get the T4 to generate PPM out pulses...

Here is a T4 quadtimer sketch that emits PPM pulses like the example in PulsePosition lib.
https://github.com/manitou48/teensy4/blob/master/ppmoutq.ino
Much of the logic is lifted from PulsePostion lib. Polarity can be changed with TMR_SCTRL_OPS. I verified with scope and jumpered T4 PPM output to T3.2 running PulsePostion lib LoopBack example. Here is the output from T3.2
Code:
21538 :  600.08  1500.04  759.27  1500.04  1500.04  1234.60  
21539 :  600.08  1500.04  759.27  1500.04  1500.04  1234.60  
21540 :  600.08  1500.04  759.27  1500.04  1500.04  1234.60  
21541 :  600.08  1500.04  759.27  1500.04  1500.04  1234.60
 
Last edited:
Sketch in previous post is working now with T3.2 PulsePosition example. I updated github. I'll let others test and weave logic in to PulsePosition lib ...
 
@manitou
Nice. I just put your input sketch on one T4 and the output on another T4. Jumped pins 10 (output) to pin 11 (input) and got:
Code:
201 :  600.08  1500.05  759.28  1500.03  1500.05  1234.61  
202 :  600.08  1500.05  759.28  1500.03  1500.05  1234.61  
203 :  600.08  1500.05  759.25  1500.05  1500.05  1234.61  
204 :  600.08  1500.03  759.28  1500.05  1500.05  1234.61  
205 :  600.08  1500.03  759.28  1500.05  1500.05  1234.61
Out of curiosity I hooked up a scope to the output and wanted to see what happened when I reversed polarity. Yep and it worked, polarity was reversed as advertised.

Have to read up on PPM signals a bit then guess I will give it a go to update the lib unless someone beats me to it. That weaving you mentioned will probably confuse me but that's nothing new.
 
Excellent. There are 10 quadtimer pins on T4. One problem requiring additional logic with both quadtimer PPM out and in in the same lib is that I never could get quadtimer Overflow interrupt to work (??). Counter in overflow interrupt is used to generate 32-bit counter for capture values. I had to use compare-value interrupt (0xffff) to count overflows, BUT compare-value interrupt is used in PPM out. So you'd need to know which pin is in capture mode and which pin is in compare mode to follow the proper path in the ISR -- maybe just look at timer's compare value register, if it has 0xffff then it is just counting overflows. For PPM out, lib's logic never allows value > 60000 in compare register.
 
I started to take a look at the library. Not going to be as easy to integrate as the FreqCount lib was. Why - everything is so integrated to the FTM timers that I would be having ifdefs all over the place. But that's just an initial reaction :)

As for which pin is Rx and which is Tx, don't think that will be a problem. In the lib there are 2 classes, one for input and one for output. In each class you have to specify which pin is used for capture and which is used for compare.

So this is going to be project - unless I just create a pulseposition_t4 lib and just use the same function calls - would be cleaner.
 
@manitou and others

SOS :)

Started working on incorporating your POC into a pulse position lib for the T4 with the ultimate goal of then incorporating it back into a master branch but running into an issue with using "attachInterruptVector" from within a class. And this pretty much will show my ignorance with using interrupts like this :( --- but I am learning.

Since I like doing things in steps I started with the PulsePositionOutputClass. In part in have in the begin:
Code:
	  attachInterruptVector(IRQ_QTIMER1, isrTimer1);
	  TMR1_CSCTRL0 &= ~(TMR_CSCTRL_TCF1);  // clear
	  TMR1_CSCTRL0 |= TMR_CSCTRL_TCF1EN;  // enable interrupt
	  NVIC_SET_PRIORITY(IRQ_QTIMER1, 32);
	  NVIC_ENABLE_IRQ(IRQ_QTIMER1);

On initial testing you will get an error that you can declare "isrTimer1" as a non-static class member. Ok so did a little searching and came across this which didn't work either:
Code:
https://stackoverflow.com/questions/41443720/how-to-create-an-isr-in-an-arduino-class

I figured I would have to eventually address the issue of multiple instances of the isr but this was just an initial test, and would have to do what @Paul did in the current pulse position library. Then I decided to follow the link on his trick:
// some explanation regarding this C to C++ trickery can be found here:
// http://forum.pjrc.com/threads/25278...cture-brainstorm?p=43496&viewfull=1#post43496

But I guess the real question I have is if I am not attaching an interrupt using attachInterruptVector how does the timer know to use the interrupt function as in the case of current lib and then port that over to the T4 Quad timer use case.

Right now this is the only thing that is holding me up.

EDIT: just did a kludge and set the isr up like was done in intervaltimer.cpp and it seemed to work. But still have to do more on this one. Just using pin 10:
Code:
278 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61  
279 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61  
280 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61  
281 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61  
282 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61  
283 :  600.08  1500.05  759.28  1500.05  1500.05  1234.61
so far so good more to follow.
 
Last edited:
I started to take a look at the library. Not going to be as easy to integrate as the FreqCount lib was. Why - everything is so integrated to the FTM timers that I would be having ifdefs all over the place. But that's just an initial reaction :)

As for which pin is Rx and which is Tx, don't think that will be a problem. In the lib there are 2 classes, one for input and one for output. In each class you have to specify which pin is used for capture and which is used for compare.

So this is going to be project - unless I just create a pulseposition_t4 lib and just use the same function calls - would be cleaner.

When I cruised the FreqMeasure code it had simple looking top class - that has #ifdef Spaghetti underlying it. Here is a glance toward the ARM half ...
Code:
void FreqMeasureClass::begin(void)
uint8_t FreqMeasureClass::available(void)
uint32_t FreqMeasureClass::read(void)

[B]
#if defined(__AVR__)
 // ...
#elif defined(__arm__) && defined(TEENSYDUINO)
void FTM_ISR_NAME (void)[/B]
It uses a generic name for _ISR that maps out, and the various Teensy Specific setup, read and the _isr was all blanketed in #ifdefs so it fell out clean on the top - but a bit of a cat herding underneath.


...\hardware\teensy\avr\libraries\FreqMeasure\util\FreqMeasureCapture.h
Code:
#if defined(CAPTURE_USE_FTM1_CH0)
#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm1_isr
// …

#elif defined(CAPTURE_USE_FTM1_CH1)
#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm1_isr
// …

#elif defined(CAPTURE_USE_FTM2_CH0)
#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm2_isr
// …

#elif defined(CAPTURE_USE_FTM2_CH1)
#define FTM_SC_VALUE (FTM_SC_TOIE | FTM_SC_CLKS(1) | FTM_SC_PS(0))
#define FTM_ISR_NAME ftm2_isr
// …

#elif defined(CAPTURE_USE_FLEXPWM4_CH0A)
#define FTM_ISR_NAME flexpwm_4_0_isr
void flexpwm_4_0_isr(void);
// …
 
@defragster - @manitou

Ok spent most of the day playing with the output portion to make it work on all channels and all timers. Also made it generic enough so if we get more timer pins can be easily added. Right now I have it working on pins 10,11,12,13,14,15,18 and 19. Got something wrong with using pins and 6and 9 but will eventually figure that one out. Here is what I have so far in terms of a library:
View attachment PulsePosition_t4.zip

I have this sketch on one T4:
Code:
#define PRREG(x) Serial.print(#x" 0x"); Serial.println(x,HEX)
#include <PulsePosition_t4.h>

PulsePositionOutput myOut;

void setup()   
{
  Serial.begin(115200);
  delay(4000);
  myOut.begin(9);
  
  myOut.write(1, 600.03);
  myOut.write(2, 1500);
  myOut.write(3, 759.24);
  // slots 4 and 5 will default to 1500 us
  myOut.write(6, 1234.56);
}

void loop() {

}
and @manitou's pulseinput example sketch :) Ok enough for now.
 
@defragster

Am I reading it right that I can only transmit on 1 Tx at a time? So I can't really do:
Code:
myout.write(11, 1400.01);
myout1.write(6, 600.00);
Don't think the code supports it?
 
@defragster

Am I reading it right that I can only transmit on 1 Tx at a time? So I can't really do:
Code:
myout.write(11, 1400.01);
myout1.write(6, 600.00);
Don't think the code supports it?

Not sure where you are reading:: "Teensy 3.1 supports up to 8 simulaneous PPM streams with up to 16 signals per stream. " and "Teensy-LC supports up to 6 simulaneous PPM streams with up to 16 signals per stream. "
 
@defragster - @manitou

Got something wrong with using pins and 6and 9 but will eventually figure that one out.

OK, confirmed with scope your zip file works on pin 12 and does NOT work on pin 6.
i modified another quadtimer test sketch i had to use pin 6, i got PWM working on pin 6 using QT4_1 -- so hardware works.
quick visual inspection of zip files didn't reveal anything broken about pin 6 and 9 ... yet
 
this fixes pin 6 and 9, change <= to == :) so it reads
Code:
} else if(txPin == 14 || txPin == 15 || txPin == 18 || txPin == 19) {
 
this fixes pin 6 and 9, change <= to == :) so it reads
Code:
} else if(txPin == 14 || txPin == 15 || txPin == 18 || txPin == 19) {

@manitou - thanks for the sanity check. I caught that last night before I crashed for the night. Started work on it again this morning but running into a problem with getting more than 1 pin sending data but I think I know whats causing that - have to figure out Paul's trickery with the ISRs so that the pulse buffers aren't conflicting. Can fix it but then would be limiting it to 4 transmit and 4 receive.

EDIT: ok think I figured it out enough to try and implement it. Had to read on the FTM timers though. FTM0 is one timer module with 8 channels as opposed to what we are dealing with - 4 timers with 4 channels max so just a bit more complicated - so we are going to have to deal with 4 ISRs - ok. Not sure I will get it implemented today - will be tied up with family most of the day.
 
Last edited:
@mjs513, @defragster, @manitou - Sorry I am late to the game ;)

Not sure if makes sense for another person to jump in as I already probably have enough diversions :D
Will be interested when we get to PulsePosition Input as I will be at some point wanting to adapt a robot to using a RC setup, and I have one that outputs a single PPM output...

One of the issues with multiple writes, I think is that all of them are being driven by by one or more global variables, where instead maybe the need to be instance variables? and/or per channel variables?

Example:
Code:
static void isrTimer1()
{
  IMXRT_TMR1.CH[[COLOR="#FF0000"]timer_channel[/COLOR]].CSCTRL &= ~(TMR_CSCTRL_TCF1);  // clear compare interrupt
  [COLOR="#FF0000"]ticks[/COLOR]++;

  if ([COLOR="#FF0000"]state [/COLOR]== 0) {
    // pin was just set high, schedule it to go low
I marked a couple that I think may have issue with multiple things active, there may be others, or they may be fine?

But I am guessing, that the ISRS need to maybe do something like either look at some Timer status register to see which timer(s) are triggered (which I did not see) or look through the channel registers (SCTRL?), see which ones are triggered, and compare it to which ones you have active objects on, and maybe figure out which object this corresponds to, and then maybe call off to a member ISR method, which does the work and have all of the state information , like lists of pulses... as member variables? But again I am just guessing as I have not done much at all with the timer code.
 
@mjs513, @defragster, @manitou - Sorry I am late to the game ;)

Not sure if makes sense for another person to jump in as I already probably have enough diversions :D
Will be interested when we get to PulsePosition Input as I will be at some point wanting to adapt a robot to using a RC setup, and I have one that outputs a single PPM output...

One of the issues with multiple writes, I think is that all of them are being driven by by one or more global variables, where instead maybe the need to be instance variables? and/or per channel variables?

Example:
Code:
static void isrTimer1()
{
  IMXRT_TMR1.CH[[COLOR="#FF0000"]timer_channel[/COLOR]].CSCTRL &= ~(TMR_CSCTRL_TCF1);  // clear compare interrupt
  [COLOR="#FF0000"]ticks[/COLOR]++;

  if ([COLOR="#FF0000"]state [/COLOR]== 0) {
    // pin was just set high, schedule it to go low
I marked a couple that I think may have issue with multiple things active, there may be others, or they may be fine?

But I am guessing, that the ISRS need to maybe do something like either look at some Timer status register to see which timer(s) are triggered (which I did not see) or look through the channel registers (SCTRL?), see which ones are triggered, and compare it to which ones you have active objects on, and maybe figure out which object this corresponds to, and then maybe call off to a member ISR method, which does the work and have all of the state information , like lists of pulses... as member variables? But again I am just guessing as I have not done much at all with the timer code.

Morning @KurtE :)

Your guess is exactly right about the ISRs. The current implementation was just so I could get something working that I could play with the setup structure that I wanted to use. In the process of converting the whole thing to what Paul used and explained in: https://forum.pjrc.com/threads/2527...cture-brainstorm?p=43496&viewfull=1#post43496. Think the register you have to check is the CSCTRL register for each timer and channel.

Glad you decided to play along - you know me - I can do but it usually takes me a lot longer to understand whats going on and to get it right.

EDIT: just a status. Used this for the Timer base addresses:
Code:
IMXRT_TMR_t* TMR[] = {&IMXRT_TMR4,&IMXRT_TMR4,&IMXRT_TMR1,&IMXRT_TMR1,&IMXRT_TMR1,&IMXRT_TMR2, &IMXRT_TMR3,&IMXRT_TMR3,&IMXRT_TMR3,&IMXRT_TMR3};
and made the changes in the lib and it worked. All keyed off of idx_channel. Let me know if you want me to post the latest and greatest that I have when you want to start playing :)
 
Last edited:
@mjs513 - You do great stuff! Sometimes at least for myself it has a great benefit to be able to bounce ideas and like off of others as it helps figures things out quicker...

My guess is to start off with, you could probably have your init code fill in: PulsePositionOutput * PulsePositionOutput::list[10];
With a pointer to this for the channel that you are setting up.

You could then simply do something in each of your ISRs, like:
Code:
static void isrTimer1()
{
  if ((IMXRT_TMR1.CH[0].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[2])  { 
        PulsePositionOutput::list[2]->isr();   
        IMXRT_TMR1.CH[0].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
  if ((IMXRT_TMR1.CH[1].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[4]) {
        PulsePositionOutput::list[4]->isr();   
        IMXRT_TMR1.CH[1].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
  if ((IMXRT_TMR1.CH[2].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[3]) {
        PulsePositionOutput::list[3]->isr();   
        IMXRT_TMR1.CH[2].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
   asm volatile ("dsb");  // wait for clear  memory barrier
}
Could be more table driven but might work...
 
@mjs513 - You do great stuff! Sometimes at least for myself it has a great benefit to be able to bounce ideas and like off of others as it helps figures things out quicker...

My guess is to start off with, you could probably have your init code fill in: PulsePositionOutput * PulsePositionOutput::list[10];
With a pointer to this for the channel that you are setting up.

You could then simply do something in each of your ISRs, like:
Code:
static void isrTimer1()
{
  if ((IMXRT_TMR1.CH[0].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[2])  { 
        PulsePositionOutput::list[2]->isr();   
        IMXRT_TMR1.CH[0].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
  if ((IMXRT_TMR1.CH[1].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[4]) {
        PulsePositionOutput::list[4]->isr();   
        IMXRT_TMR1.CH[1].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
  if ((IMXRT_TMR1.CH[2].CSCTRL & TMR_CSCTRL_TCF1)) && PulsePositionOutput::list[3]) {
        PulsePositionOutput::list[3]->isr();   
        IMXRT_TMR1.CH[2].CSCTRL &= ~(TMR_CSCTRL_TCF1);
  }
   asm volatile ("dsb");  // wait for clear  memory barrier
}
Could be more table driven but might work...

Thanks. Cool - will give it a try. Learned a long time ago that you learn a lot more by trying things to see if they work or not.
 
Thanks. Cool - will give it a try. Learned a long time ago that you learn a lot more by trying things to see if they work or not.

FYI - I am not sure if it will help or not, but another sort of example of array of this pointers and ISRs and using 'this' is in our st7735_t3 code, with the setup to do DMA operations, as we have an array of this pointers associated one per each SPI buss.
 
FYI - I am not sure if it will help or not, but another sort of example of array of this pointers and ISRs and using 'this' is in our st7735_t3 code, with the setup to do DMA operations, as we have an array of this pointers associated one per each SPI buss.

Will take a look - don't believe in reinventing the wheel - maybe just rotating them :)

Here is what I have in case you want to play, still have a lot to do on it - but once ISRs is fixed then we are off to the races and the Input section will go easier:
 

Attachments

  • PulsePosition_t4.zip
    5.9 KB · Views: 181
Hi @mjs513 still a WIP but what do you think of these types of changes, using hardware structure... I have not tried running it yet.... Game starting ;)View attachment PulsePosition_t4.zip

I tried to get everything in begin to work with new table of hardware structure...

FYI your arrays I believe are off, i.e. I don't think pin 16 belongs in the first list, as it has 1 more item then your other lists.

My structure/array is sort of setup to replace all of them... But again I have not completed, was interested in your first read to see what you think
 
Hi @mjs513 still a WIP but what do you think of these types of changes, using hardware structure... I have not tried running it yet.... Game starting ;)View attachment 18025

I tried to get everything in begin to work with new table of hardware structure...

FYI your arrays I believe are off, i.e. I don't think pin 16 belongs in the first list, as it has 1 more item then your other lists.

My structure/array is sort of setup to replace all of them... But again I have not completed, was interested in your first read to see what you think

Just sat down when I got your message but beside home distractions my little one doesn't want to leave me alone today :). So will be off in a few.

Took a quick look and it looks great. That's exactly what I was shooting for eventually. Will have to give it a try later, but it should work.

BTW. You are correct pin 16 shouldn't have been in the list. I used your pin spreadsheet to get the quad timer pins so the only allowable pins are: 6,9,10,11,12,13,14,15,18,and 19 for now :)

Thanks Kurt - really appreciate the effort.
 
Hi @mjs513 still a WIP but what do you think of these types of changes, using hardware structure... I have not tried running it yet.... Game starting ;)View attachment 18025

Re: PulsePosition.h

Unless you changed additional logic, based on Paul's PulsePosition.h, you need +1 in your pulse vector dimensions
Code:
	uint32_t pulse_width[PULSEPOSITION_MAXCHANNELS+1];
        uint32_t pulse_buffer[PULSEPOSITION_MAXCHANNELS+1];
element [0] contains long trailing space duration.
 
Status
Not open for further replies.
Back
Top