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

Thread: Timerinterrupt for Teensy 4.1

  1. #1
    Junior Member
    Join Date
    Dec 2020
    Posts
    3

    Timerinterrupt for Teensy 4.1

    Hi there
    For the last few weeks I've been trying to find a good timer tutorial for my project.
    I'm not going to use the "IntervalTimer" library because I need a more frequent interrupt call and I am as efficient as possible.

    My goal with the Timer is:
    - Creating a I2C bus which may be faster than the standard Wire library.
    - Building a Special I2C parallel bus which can send data to multiple slaves at the same time:
    Signals:
    SCL ------> SCL slave1, slave2, slaveX
    SDA1 -----> SDA slave1
    SDA2 -----> SDA slave2
    SDAX -----> SDA slaveX

    Slaves are: 3x TLC59116 (LED Driver with 16 pwm outputs)

    I currently use 3 slaves and my bus is working fine (most of the time) -> sometimes the Teensy crashes because the timer works so fast I think.
    The crash won't occure on slower Timer interval settings.

    I currently have a Timer working. I copied it from a forum post but I don't know how the setup works.
    Current used Timer:
    Code:
    void interruptFunc();
    
    void setup()
    {
      ... // other stuff
      attachInterruptVector(IRQ_QTIMER1, interruptFunc);  // Arrach interrupt to my ISR function "interruptFunc"
      CCM_CCGR6        |= CCM_CCGR6_QTIMER1(CCM_CCGR_ON); //don't know
      TMR1_CTRL0        = 0x0000; // stop (don't know)
      TMR1_LOAD0        = 0x0000; // start val after compare (don't know)
      TMR1_COMP10      = 100;  // count up to this val, interrupt,  and start again
      TMR1_CMPLD10     = 100;  // (don't know) (Equal to TMR1_COMP10  ?)
      TMR1_CTRL0         = TMR_CTRL_CM(1) | TMR_CTRL_PCS(8) | TMR_CTRL_LENGTH ;  // prescale (don't know how this prescaler works)  
      TMR1_CSCTRL0 &   = ~(TMR_CSCTRL_TCF1); // clears the counter, I think ^^
      TMR1_CSCTRL0    |= TMR_CSCTRL_TCF1EN;  // enable interrupt or so
      NVIC_ENABLE_IRQ(IRQ_QTIMER1);            // (don't know)
     ... // other stuff
    }
    
    void interruptFunc()
    {
       // Highspeed I2C-parallelBus which currently works at 2Mhz clk
       // This Interrupt is executet at around 100ns interval
      ...
    }
    The question now is, is there a simple methode like descibed here?: https://www.pjrc.com/teensy/interrupts.html
    With these timers I found a example code but it won't compile because stuff is not defined:
    Code:
    #include <avr/io.h>
    #include <avr/pgmspace.h>
    #include <avr/interrupt.h>
    
    #define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
    #define OVERFLOW_INTERRUPT_ENABLE (TIMSK0 |= (1<<TOIE0))
    
    
    volatile bool value = 0; 
    
    ISR(TIMER0_OVF_vect) {
      // Toggle Pin 10
      if(value)
        GPIO7_DR |=   uint32_t(1<<CORE_PIN10_BIT); // Set Pin 10 = 1
      else
        GPIO7_DR &=  ~uint32_t(1<<CORE_PIN10_BIT); // Set Pin 10 = 0
    
      value = !value;
    }
    void setup()
    {
        cli();
        pinMode(10,OUTPUT);
        CPU_PRESCALE(0);
        OVERFLOW_INTERRUPT_ENABLE;
        sei(); //enable global interupts
    }
    void loop()
    {
        for(;;) {
          delay(10);
        }
    }
    Is the first code realy the right way of using Timers on a Teensy 4.1?
    Like I described above, my teensy crashes from time to time. Especialy when I recieve data over the serial Interface (BAUD = 115200).
    The crashes occures only then when I don't send data to my slaves.
    I have seen this with my logic analyzer.
    So I thougth, its May be possible to disable/enable a timer.
    So I can enable the Timer only then, when I need to send Data and disable it after the transmission.
    Currently I check at first in the Interrupt, if any data is in the sending buffer and if not, return the ISR.
    This is fast but it does need CPU time which can be eliminated with this disable/enable thing.

    I was wondering if anyone of you can explain to me how exactly I need to use this low level timers?
    I know there shuld be 4 of them available, are the setups of other timers equal/logical in naming of the registers? e.g. TMR1_CSCTRL0 (countervalue for timer 1) and TMR2_CSCTRL0 (for timer 2)?
    What are all these Registers doing and how do I implement these the right way?
    Is this enable/disable thing possible?
    Is it possible to disable the timer 1 in the ISR of the timer 1 it self? e.g if transmission is finished, the ISR can turn the Timer off.
    Why is the timer (IRQ_QTIMER1), from the first code example which is currently working, not in the Table?
    Is this Page only for other Teensies?
    Is dere a Page for the Teensy 4.1 Interrupt stuff?
    Why is there not many Tutorials out there for this Timer interrupt stuff? =)

    Thanks a lot for helping me =)

  2. #2
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,955
    Some questions...
    Why is Wire not fast enough? Which speed do you need? Which speed is supported by the chips you use?
    I guess you underestimate Teensy dramatically...

    There is no need to use low level code for i2c..not even on the "slowest" 3.x Teensy. I'd strongly recommend to try Wire first.
    For the registers, I'd recommend to read the reference manual (the 1000+ pages document)

    Then, Teensy 3.x and 4.x are ARM-Cortex processors. The AVR code does not work...

  3. #3
    Junior Member
    Join Date
    Dec 2020
    Posts
    3
    I need as much speed as possible ^^. Currently I can transmit 18bytes (including slave address) to each slave in around 80us With a I2C clock speed of 2MHz
    This must be done for each slave at the same time in order to work.
    With my self created bus, I can set a Port register with the data for all 12 slaves and therefore can be 12 times faster then with

    The TLC59116 supports up to 1MHz clock, but I tested it with up to 2MHz and it still works.
    Yes I didn't tested the Wire Library on the teensy but I havn't done that because I want to write to multiple slaves at the same time and thats not possible with the Wire library.
    Wire uses only one data line. Shure the teensy has multiple I2C interfaces but Thats not enough. I need 12 of them for my project.

    With the reference manual, do you meen the datasheet of the Provessor: IMXRT1060CEC wich is on the Teensy 4.1?
    Or is there a other manual I haven't found yet, because the Datasheet of IMXRT1060CEC only has 114 Pages.

  4. #4
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,479
    For Ref Manual see Technical section on this page : pjrc.com/store/teensy41.html

    Unfortunately the p#1 link for interrupt code is for the older AVR Teensy 1 and 2 family not the newer ARM family.

    @luni here @luni64 on github. Current library links as github.com/luni64/TeensyTimerTool
    >> It may solve the timing issue, or show code in use.

    How many interrupts/second are being processed? 2 MHz clock leaves 300 cycles per interrupt - any faster leaves fewer of course and T_4.x seems to top out at somewhere in 6-10 MHz interrupts the way the clocks run - and with the cycles it takes for the interrupt to get called and return.

  5. #5
    Junior Member
    Join Date
    May 2020
    Posts
    11
    Twelve i2C ports! And overclocked. Sounds like a recipe for disaster. There should be similar parts that are SPI to do what you want and these will probably go up to 30MHz. Then you'll need to worry about signal integrity. I'd suggest you reconsider your design.
    You might research the chips that are used in the commercial RGB modules that use the HUB75 interface.

  6. #6
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,955
    Sounds like a recipe for disaster
    Agree.

    If you _really_ want to try this, you might want to look at the FastLED library, and how it uses DMA.

    Or use one of these WSxyz LED stripes

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,746
    Quote Originally Posted by AlexKrieg View Post
    I'm not going to use the "IntervalTimer" library because I need a more frequent interrupt call and I am as efficient as possible.
    Maybe you should just copy the IntervalTimer code and edit the parts you feel aren't efficient enough? Or use Luni's timer tool library, either as-is or a starting point.

    Those PIT timers really are the right way to run an interrupt at regular intervals. (whether running at interrupt millions of times per second is an efficient want to do anything might be an open ended question.....)


    I currently have a Timer working. I copied it from a forum post but I don't know how the setup works.
    Current used Timer:
    Yup, that looks like code for one of the QuadTimers in Teensy 4. You can find the detailed documentation of those timers in the reference manual, chpater 54 starting on page 2987.

    https://www.pjrc.com/teensy/datasheets.html


    The question now is, is there a simple methode like descibed here?: https://www.pjrc.com/teensy/interrupts.html
    With these timers I found a example code but it won't compile because stuff is not defined:
    ...
    Is the first code realy the right way of using Timers on a Teensy 4.1?
    That page is about the timers on Teensy 2.0. It won't work on Teensy 4.


    Like I described above, my teensy crashes from time to time. Especialy when I recieve data over the serial Interface (BAUD = 115200).
    Please understand this is only a blind guess, since I can't see your code. The USB interrupt might be causing problems. Maybe your code is doing stuff with the Serial class or other things that aren't safe to do from interrupts, but the problems only manifest when that other code uses its interrupts? Or perhaps it's just a matter of the other interrupt adding response latency to your very rapidly triggered interrupt? (and your code not dealing with that situation well) Especially if your interrupt reoccurs very rapidly, it's easy to have a case where your code takes too much time and your interrupt ends up consuming 100% of the CPU, leaving everything else unable to run.

    A couple quick things to try are disabling the USB interrupt, or changing the relative priority of your interrupt and the USB and SysTick and other interrupts.


    I was wondering if anyone of you can explain to me how exactly I need to use this low level timers?
    There isn't any 1 simple answer. Libraries like IntervalTimer are meant to give you that answer in the form of a C++ class with specific functionality. Again, I would recommend you copy that code and make minimal edits to suit your needs (like removing the safety of configuring too short interval times).


    I know there shuld be 4 of them available, are the setups of other timers equal/logical in naming of the registers? e.g. TMR1_CSCTRL0 (countervalue for timer 1) and TMR2_CSCTRL0 (for timer 2)?
    What are all these Registers doing and how do I implement these the right way?
    All the registers are defined in imxrt.h and documented in the reference manual. But the reality is the QuadTimers and FlexPWM are so loaded with powerful features that it ends up being a mountain of documentation to digest. There isn't any 1 simple answer of how to use these.


    Is this enable/disable thing possible?
    Is it possible to disable the timer 1 in the ISR of the timer 1 it self? e.g if transmission is finished, the ISR can turn the Timer off.
    Yes. You can disable a timer within the interrupt code. But you should consider the case where the timer may already have triggered another interrupt before you managed to disable it. Unless you are 100% certain your code will always disable it soon enough, you should handle that case. You might do by just allowing it to run your interrupt function again, where your code is smart enough to recognize it was triggered inadvertently. Or you could explicitly clear the interrupt pending flag. If you go that route, remember there is a pending flag inside the NVIC in the ARM processor as well as (usually) a pending flag in the timer peripheral. Make sure you clear both. Best to clear the timer's flag first.


    Is this Page only for other Teensies?
    Is dere a Page for the Teensy 4.1 Interrupt stuff?
    Why is there not many Tutorials out there for this Timer interrupt stuff? =)
    To answer "why", the reason ultimately comes down to there are only so many hours in every day. Writing that sort of documentation takes a lot of time, and a ton of other things compete for that very limited time. If you look at other threads on this forum, you'll see a lot of dev time recently has gone into filesystem stuff, a File/FS abstraction layer, migrating to SdFat with Arduino SD compatibility, LittleFS and support for a variety of flash & other media, MTP, USB Host MSC, etc. I have also been working quite a lot on the Teensy 3.6 & 4.1 product pages, with plans to do the same on the other 6 models. Luni started a wiki recently, and it has (mostly empty) placeholders for people to write this sort of material. Eventually nice tutorials for this low level stuff may be written, but realistically that is likely years away. The situation with Teensy 2.0 was basically that all its hardware was very well supported by 2010, so nearly a full year of work in 2011 went into a lot of documentation... before focus shifted to developing Teensy 3.0 in 2012. Since the jump from 8 to 32 bits, the list of requested features and libraries has always grown, leaving relatively little time to write this sort of low-level documentation.

    So again, my recommendation is to reconsider your resistance to using the existing library code. If you feel it doesn't meet your needs, at least consider using it as a starting point. Maybe IntervalTimer can do everything you need if you just delete a couple lines?

    Or if you *really* want to craft everything from scratch, you certainly can. That's how all the code in the libraries we have now was created. But it's a steep learning curve. There aren't yet any really nice low-level tutorials like were written 9 years ago in the end of the 8 bit era, and to be honest & realistic, it's very unlikely that sort of tutorial material is going to be written anytime soon.

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    23,746
    Quote Originally Posted by AlexKrieg View Post
    I need as much speed as possible ^^.
    Maybe the FlexIO peripheral could help?

    There too, nice & easy to read documentation is sorely lacking. Kurt created a library that does serial and Ward created a WS2812 LED library, which shows it is possible and there is some code to read...


    With the reference manual, do you meen the datasheet of the Provessor: IMXRT1060CEC wich is on the Teensy 4.1?
    Or is there a other manual I haven't found yet, because the Datasheet of IMXRT1060CEC only has 114 Pages.
    The reference manual is 3437 pages. You can get it here:

    https://www.pjrc.com/teensy/datasheets.html

    It's also on NXP's website, but they make you go through a registration process which is silly for a non-confidential PDF. You can get it from that page by just clicking the "IMXRT1060 Manual" link.

  9. #9
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,955
    That Wiki isn't that small - it has many pages with useful information already. Many people (including me) already contributed to it, but of course we welcome any help..
    The lack of documentation is really a showstopper for many things, and that is why we users started the wiki.

  10. #10
    Junior Member
    Join Date
    Dec 2020
    Posts
    3
    Hey thanks guis

    This helps me a lot. I now found the right datasheet and with the help of that, I was able to create a PIT timer
    Code:
    #define TIE 0x02 // https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf#d12906e1712a1310 Page: 2985, 53.9.7.4Fields
    /*
     Timer Interrupt Enable
     When an interrupt is pending, or if TFLGn[TIF] is set, 
     enabling the interrupt causes an interrupt event. Toavoid this, 
     the associated TFLGn[TIF] must be cleared first.
       0b - Interrupt requests from Timer n are disabled.
       1b - Interrupt is requested whenever TIF is set.
     */
    
    #define TEN 0x01 // https://www.pjrc.com/teensy/IMXRT1060RM_rev2.pdf#d12906e1712a1310 Page: 2985, 53.9.7.4Fields
    /*
     Timer Enable
     Enables or disables the timer.
       0b - Timer n is disabled.
       1b - Timer n is enabled.
     */
    
    #define sda_0_Bit CORE_PIN12_BIT    // red 1
    const byte i2c_sda_0  = 12;
    bool value = true;
    
    static void pit_isr();
    void func();
    
    void setup() {
      pinMode(i2c_sda_0,OUTPUT);
    
     cli();
     // turn on PIT     
     PIT_MCR = 0x00;     
     PIT_LDVAL0 = 0x00000001;// setup timer 0 for 1 cycles   
     PIT_TCTRL0 = TIE; // enable Timer 0 interrupts      
     PIT_TCTRL0 |= TEN; // start Timer 0
     
     NVIC_ENABLE_IRQ(PIT_TFLG0);
     attachInterruptVector(IRQ_PIT, &pit_isr);
     NVIC_SET_PRIORITY(IRQ_PIT, 255); // Top priority
     NVIC_ENABLE_IRQ(IRQ_PIT);
     sei();
    }
    
    void loop() {
    }
    
    static void pit_isr()
    {
      if(PIT_TFLG0)
      {
        PIT_TFLG0 = 1;
        func();
      }
    }
    void func()
    {
      // Toggle pin 12
      if(value)
        GPIO7_DR |= 1<<sda_0_Bit;
      else
        GPIO7_DR &= ~(1<<sda_0_Bit);
    
      value = !value;
    }
    At the end I saw that the PIT timer only works with 50MHz and that brings me to an interrupt frequency of 2Mhz. XD
    But there are so many diffrent Timers wich I can use so I will read a little bit more about that.
    I also will study my current timer from the first poast to know what register is doing.

    Quote Originally Posted by PaulStoffregen View Post
    All the registers are defined in imxrt.h and documented in the reference manual. But the reality is the QuadTimers and FlexPWM are so loaded with powerful features that it ends up being a mountain of documentation to digest. There isn't any 1 simple answer of how to use these.
    To know where the registers are defined helps me a lot. I struggled to find the right words because the registersnames which are described in the datasheet werent exactly the same as in the code.
    Now i don't have to guess anymore. =)

    Quote Originally Posted by Frank B View Post
    That Wiki isn't that small - it has many pages with useful information already. Many people (including me) already contributed to it, but of course we welcome any help..
    The lack of documentation is really a showstopper for many things, and that is why we users started the wiki.
    Which Wiki do you mean? Do you mean this Forum?
    Maybe I can help filling up some pages =)


    Thanks for the quick response, I like this Forum

  11. #11
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    7,955

  12. #12
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,369
    At the end I saw that the PIT timer only works with 50MHz and that brings me to an interrupt frequency of 2Mhz. XD
    But there are so many diffrent Timers wich I can use so I will read a little bit more about that.
    Actually it runs with 24MHz but it can easily be switched to 150MHz. Here the TeensyTimerTool which Paul mentioned above: https://github.com/luni64/TeensyTimerTool It implements a generic access to PIT, TMR, GPR, FTM hardware timers and a software based timer. You can have a look at this folder https://github.com/luni64/TeensyTime...ter/src/Teensy to see the implementation without the generic interface stuff.

    Please note that your code:

    Code:
    static void pit_isr()
    {
      if(PIT_TFLG0)
      {
        PIT_TFLG0 = 1;
        func();
      }
    }
    may not work if func() is very fast. The timers and the ARM core run on different busses with different clocks. Thus, it will take some time until resetting the flag actually propagates to the timer. Best to use a DSB instruction or spin until the flag is definitely set. If you don't do that and you have a fast callback the interrupt will be called twice (at least).

    Also the syncing between the busses takes significant time. Thus you can not expect a interrupt call rate much more than some 3 (?) MHz. It will further decrease if you have more than one timer running. The bus syncing will eat up a considerable amount of clock cycles. There are a few posts in the forum presenting some measurements.

Posting Permissions

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