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

Thread: Teensy 4 Interval Timer

  1. #1
    Junior Member
    Join Date
    Oct 2019
    Location
    Germany
    Posts
    3

    Teensy 4 Interval Timer

    Hello,

    I am trying to use the IntervalTimer on a Teensy 4 to schedule two tasks periodically with different priority. It seems like even though I set two priorities for my two instances of the IntervalTimer the faster (and higher priority) one is not able to interrupt the other function. It seems like there is just a single priority for PIT_IRQ, even though the PIT has four channels so I guess my plan will not work.
    What are possible alternatives to solve the problem of two periodic tasks (40us and 1ms) where the faster one can interrupt the other on the Teensy 4?

    Thanks in advance for your help!

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,059
    priority levels?
    There are in steps of 16 (at least for T3.6, not checked yet for T4)
    e.g levels 16 to 31 are the same level

  3. #3
    Junior Member
    Join Date
    Oct 2019
    Location
    Germany
    Posts
    3
    I have tried to set the priorities to 0 and 128

  4. #4
    Junior Member
    Join Date
    Oct 2019
    Location
    Germany
    Posts
    3
    Looking further into the code of the IntervalTimer library and the documentation of the processor, I found out that all four channels of the IntervalTimer trigger the same interrupt and therefore must have the same priority.
    I solved my problem having two periodic tasks using one of the General Purpose Timers that are available on the Teensy 4. I looked into the reference manual of the processor and some code, that I found on this forum to produce the following:

    Code:
    void setup()
    {
        Serial.begin(1);
        uint32_t us = 1000;
    
        CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON); //enable clock for General Purpose Timer 1
        GPT1_CR = 0;
        GPT1_PR = 0;
        GPT1_SR = 0x3F;                         // clear all prior status
        GPT1_IR = GPT_IR_OF1IE;                 // enable GPT1 COMPARE 1 Interrupt
        GPT1_CR = GPT_CR_EN | GPT_CR_CLKSRC(1); // sets timer clock to 24 MHz (Teensy 4)
        GPT1_OCR1 = 24000000 / 1000000 * us;    // set compare value
    
        attachInterruptVector(IRQ_GPT1, timer_test);
        NVIC_SET_PRIORITY(IRQ_GPT1, 0);
        NVIC_ENABLE_IRQ(IRQ_GPT1);
    }
    
    void timer_test()
    {
        GPT1_SR = GPT_SR_OF1; //clear the compare flag, the timer resets automatically
        Serial.println(micros());
    }
    
    void loop()
    {
    }
    This way I have a different interrupt (GPT1_IRQ), that I can assign a different priority to. This allows the IntervalTimer to interrupt or be interrupted by the other task.

    Is this the way you would do it, or has the General Purpose Timer (GPT) some drawbacks in comparison to the Periodic Interrupt Timer (PIT) that is used in the IntervalTimer library? Should I use the PIT for the faster Task where timing needs to be more accurate or is there no difference?

  5. #5
    Junior Member dbuergin's Avatar
    Join Date
    Dec 2020
    Location
    Switzerland
    Posts
    8
    I had to invest a couple of hours to find out what is wrong with the above GPT example and my version :-) When toggling a pin in the ISR routine you see strange results. The pin doesn't stay up until it's toggled in the next interrupt. It goes down much faster. Reason seems to be a behaviour of the M7 (see the description of the macro __DSB()).

    First, add "__DSB();" at the end of the ISR routine.
    Second subtract one from GPT_OCR1, otherwise your duration is 1us to long

    My Makefile example:

    Code:
    #include "Arduino.h"
    #include "debug/printf.h"
    #include "arm_math.h"
    
    void GPT1_GPT_IRQHANDLER(void);
    
    int main(void)
    {
        pinMode(23, OUTPUT);
    
        uint32_t us = 1;
    
        CCM_CCGR1 |= CCM_CCGR1_GPT(CCM_CCGR_ON); //enable clock for General Purpose Timer 1
        GPT1_CR = 0;
        GPT1_PR = 0;
        GPT1_SR = 0x3F;                         // clear all prior status
        GPT1_IR = GPT_IR_OF1IE;                 // enable GPT1 COMPARE 1 Interrupt
        GPT1_CR = GPT_CR_EN | GPT_CR_CLKSRC(1); // sets timer clock to 24 MHz (Teensy 4)
        GPT1_OCR1 = 24000000 / 1000000 * us - 1 ;    // set compare value
    
        attachInterruptVector(IRQ_GPT1, GPT1_GPT_IRQHANDLER);
        NVIC_SET_PRIORITY(IRQ_GPT1, 0);
        NVIC_ENABLE_IRQ(IRQ_GPT1);
    
        printf_debug_init();
    
        for(;;)
        {
        }
    }
    
    void GPT1_GPT_IRQHANDLER(void)
    {
        GPT1_SR = GPT_SR_OF1;
        GPIO6_DR_TOGGLE = (1 << 25);
        __DSB();
    }

  6. #6
    dbuergin-Would you please help me understand why the _DSB() is needed here. I know it inserts and asm dsb but not sure why needed. I am not very assembly language savvy.

    I am also having trouble finding a reference for the use of printf_debug. Can you point me somewhere that explains this capability?

    Thanks

  7. #7
    Junior Member dbuergin's Avatar
    Join Date
    Dec 2020
    Location
    Switzerland
    Posts
    8
    Hi Neal,

    First of all, i'm also learning i'm coming from the STM32F407 and try to migrate my project to the M7 on the Teensy 4.1 Board. Actually i don't know if i want to work under fully Arduino control or Makefile based but with the Teensyduino libs from PRJC (great work by the way) or based on the SDK from MCUXpresso for the MIMXRT10162 CPU. So i'm experimenting around with everything.
    When i use the "IntervalTimer" lib, i don't need __DSB() simply because it works without. But this lib uses the PIT Timers and not the GPT Timers in the CPU, read the docu for the difference. If i used the generic GPT example from "jusak", i got the strange behaviour. Then i checked the GPT example from the MCUXpresso SDK and found this __DSB() in every example. So i added it to my example and voila it works.
    Then i google'd for __DSB() and found: https://developer.arm.com/documentation/dai0321/a/ , download the PDF and read it. My undestanding is, that it has something to do with the multiple execution pipelines the M7 has.

    printf_debug: If you work with the Arduino IDE or with the Makefile provided from PRJC ([your ardunio path]\hardware\teensy\avr\cores\teensy4), you can simple add "#define PRINT_DEBUG_STUFF" to your code or to the Makefile as a "-DPRINT_DEBUG_STUFF" and you have the debug output on Pin 17 (TX4), add a SerialTTL to USB Converter and connect it to your Computer and use Putty or HTerm or whatever tool you use for serial connections.
    In your code you can simply use "printf("");" to output on the debug connection. Don't use "Serial.print..." this will not use the debug Interface

    Hope that helps.

    Regards, Daniel

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,405
    @Neal : Need for _DSB() is a puzzle - but it makes sure the bus and I/O completes before the _isr exits. When that is not done each interrupt can complete twice. The interrupt flag needs to be unset to prevent it doubling, seeing it still set on exit it will re-enter. Perhaps that is handled in the IntervalTimer() code, and when you hand code the timer it must be in that code.

    That is the value in using the tested, refined and provided code versus hand coding timers.

    @luni - on github @luni64 has a more expansive Timer library set for Teensy good perhaps for reference or use as it exposes all types of timers AFAIK.

  9. #9
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,365
    The dsb is needed to make sure the reset of the interrupt flag propagated through the caches and busses to the actual timer hardware. Without dsb and a very short ISR (like the one you have) the ISR would be left before the interrupt flag is actually reset. Thus, the ISR would be called again, immediately after you left it.



    EDIT: Always those crossposts with defragster :-)

  10. #10
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    13,405
    Quote Originally Posted by luni View Post
    The dsb is needed to make sure the reset of the interrupt flag propagated through the caches and busses to the actual timer hardware. ...
    EDIT: Always those crossposts with defragster :-)
    And as usual I said it more quickly and you said it better

  11. #11
    Senior Member
    Join Date
    Apr 2014
    Location
    Germany
    Posts
    1,365
    ........

  12. #12
    Junior Member dbuergin's Avatar
    Join Date
    Dec 2020
    Location
    Switzerland
    Posts
    8
    @luni : Thanks for the explanation, that make sense. I'm actually checking your Lib, looks good.

  13. #13
    Junior Member dbuergin's Avatar
    Join Date
    Dec 2020
    Location
    Switzerland
    Posts
    8
    @luni and @defragster : Great effort from both of you

  14. #14
    Junior Member dbuergin's Avatar
    Join Date
    Dec 2020
    Location
    Switzerland
    Posts
    8
    Quote Originally Posted by luni View Post
    [*]And, there is a section about the hardware timers in the user WIKI https://github.com/TeensyUser/doc/wiki/Timing Unfortunately it is quite empty at the moment. Maybe you want to add your GPT code from above with some explanations?[/LIST]
    @luni : Done, hope it's ok, never did a Github wiki update :-)

Posting Permissions

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