PITimer: PIT (Periodic Interrupt Timer) library for Teensy 3.0

Status
Not open for further replies.

loglow

Well-known member
Following an active thread discussing interrupt timers, I've written and hosted a simple library that makes using them easier. It's been tested very lightly, so please report any problems you may encounter, or any improvements that you can think of.

Available here:
https://github.com/loglow/PITimer

Please note that only 3 of the 4 timers (0-2) are available for general use, as PIT3 is being used by the built-in tone() functions. Brief readme and example files have been included as well. -Dan

Updated:
- 2/1/2013, added support for custom callback functions
- 2/7/2013, cleanup, comments, and value range checking
- 2/7/2013, added counters and syntax highlight, updated readme and example
- 2/7/2013, simplified most functions by using dynamic register pointers
 
Last edited:
Thank you for this contribution - I am planning on using this in an active project.

Can you elaborate on PIT3 and it's conflict with the tone() function? Are you saying the PIT3 works, but if tone is used there will be a conflict?

What is the relationship with CPU speed? If one chooses one of the overclock CPU speeds - will the library compensate or must this be calculated in advance?

Thanks again for this very useful library.
 
You're welcome :D

Can you elaborate on PIT3 and it's conflict with the tone() function? Are you saying the PIT3 works, but if tone is used there will be a conflict?

At present, I've simply commented out the PITimer3 object in the library so it can't be used. The problem is that pit3_isr() is already defined in pins_teensy.c for use with tone(), which is using PIT3 for its operation. Even if you're not using tone(), one would have to go into the built-in includes and eliminate everything that might conflict. That's not something I'd like to do.

What is the relationship with CPU speed? If one chooses one of the overclock CPU speeds - will the library compensate or must this be calculated in advance?.

All timing values (period, frequency) are calculated based on F_BUS, which is relative to core speed. The default core is set to 96 MHz with a bus speed of 48 MHz. Changing the core clocking should have no effect on timings, since they should simply compensate (at compile time) based on the value of F_BUS.

I'm not sure if it's even possible to dynamically change the clock speeds, but in that case, I'm not entirely sure what would happen. Since the timer values are stored as a count of bus clock cycles, I suppose existing timers would probably change if the bus speed were modified during runtime (as their numerical values would remain unchanged). I don't think this is a likely situation.
 
At present, I've simply commented out the PITimer3 object in the library so it can't be used. The problem is that pit3_isr() is already defined in pins_teensy.c for use with tone(), which is using PIT3 for its operation. Even if you're not using tone(), one would have to go into the built-in includes and eliminate everything that might conflict. That's not something I'd like to do.

pit3_isr is already defined as 'weak' which allows its definition to be overridden --does that mitigate this ? Would we also need tone() to be defined as 'weak' too ? or perhaps using noTone() before using PIT3 and having noTone() release all resources ?
 
pit3_isr is already defined as 'weak' which allows its definition to be overridden --does that mitigate this ? Would we also need tone() to be defined as 'weak' too ? or perhaps using noTone() before using PIT3 and having noTone() release all resources ?

All good questions, and I'm not sure I know the answers.

I do know that when I try to define pit3_isr(), I get the following compile-time error:

Code:
pins_teensy.c.o: In function `pit3_isr':
<path>/pins_teensy.c:335: multiple definition of `pit3_isr'

I haven't encountered "weak" definitions before; does one need to do something special to overwrite them?

Also, the definition in pins_teensy.c seems to indicate nothing special:

Code:
void pit3_isr(void)
 
I hadn't come across it either (I do hardware !), but it is used when we redefine an isr in the 'main' code. I see now that pit3_isr's redefinition doesn't compile; I don't know why since ftm0_isr() looks similar and I override it successfully... {edit -- no, I didn't override it, I had bypassed that problem another way -- so why doesn't 'weak' actually work ? Am I using it incorrectly ?}

Here is the .h prototype:
mk20dx128.c:void pit3_isr(void) __attribute__ ((weak, alias("unused_isr")));
 
Last edited:
Hi, I've just updated the PITimer library with support for custom callback functions. In fact, the use of these is now mandatory.

Now, instead of defining pit0_isr() and calling PITimer0.start(), you'll define any function you want (timerCallback0(), for example) and call PITimer0.start(timerCallback0). You have to pass the name of the callback function every time you start a timer, but this also gives you the flexibility to change the callback function whenever you want, so you can reuse timers easily without resorting to awkward status flags. You also no longer have to manually call clear() anymore inside your callback, since this is now taken care of automatically; one less thing to remember. Below is a brief example of the change.

Old:

Code:
void pit0_isr() {
  PITimer0.clear();
}

void setup() {
  PITimer0.start();
}

New:

Code:
void timerCallback0() {
}

void setup() {
  PITimer0.start(timerCallback0);
}

I've updated the code on github along with the readme and the example. If you're already using PITimer, please note that you'll have to modify your code a little.

Thanks for the feedback, and I'm still interested in the possibility of enabling PIT3 for general use, but my guess is that'll require some changes on the Teensy side first.

Sleep well. -Dan
 
Last edited:
This library is working great. Really simplified my code and is a great alternative to polling the clock often to do repetitive functions.

I am very interested in seeing PIT3 working, and have a need for 4 independent timers.
 
Just wanted to make note of an update!

  • changed variable types to absolute lengths (stdint)
  • added round() function for code readability
  • improved the formatting of code blocks
  • added comments to all the functions
  • added basic range checking for bad values
  • updated the example to provide some output

Please note, the range checking will silently fix invalid values. For example, a period of 100 (seconds) will be changed to 89.478485312 which is the absolute maximum period of the timers. Likewise, because of the resolution of the timer, only specific higher frequencies are available. For example, a frequency of 74000 (74 kHz) will be changed to 73959.938366718 because of the granularity between the cycle counts of 648 and 649. Use period() and frequency() to check the actual values of your timers if you're setting them to anywhere near the extremes. Also keep in mind that value() returns/requires 1 less than the actual bus cycle count.

Take care, -Dan

Edit: An update to my update...

  • renamed the round() function to roundFloat() due to a macro conflict with another library
  • added a counter function with count() and zero()
  • updated example to use the new built-in counters
  • added a keywords.txt file for syntax highlighting

Edit: Final update for the day...

  • simplified most functions by using dynamic register pointers
 
Last edited:
It'd be great to hear from a Teensy developer on the feasibility of this...

Perhaps the Teensy core library should provide this functionality, rather than a separate library? Maybe it could dynamically assign the next available timer, rather than having to request a specific one. Then the tone() function could request a timer the same way other users would. You could get all 4 timers, but if all 4 are in use, obviously tone() would not be able to work.
 
Perhaps the Teensy core library should provide this functionality, rather than a separate library?

I'm not sure about the logistics of this since I haven't been using Teensy for long. Are the core libraries hosted somewhere for collaborative development or are they developed internally by PJRC? How are they licensed?

In any case, you're more than welcome to use any of this library's code for built-in support. -Dan
 
That sounds like a pretty good idea since it seems all the timers are the same. If the timers varied in the future (I thought they sometimes do, but maybe I'm misremembering) would this pose a problem tho? Is that a concern for this codebase? I do like the idea of the user getting to use all of the timers. And if they fill up, they'll have to know enough to recycle them temporarily if they want to get tone() to work, or any other function that uses a timer.
 
...recycle them temporarily...

Just in case you or anyone else hadn't realized, the current PITimer lib does allow you to recycle the 3 available timers, all you have to do is stop one and start it up again with a different callback function. I'm also into the idea of having all 4 available for use, especially since I'm probably more likely to use all 4 at once than I am to use tone(), but that's just me. -Dan
 
Dan, I'd like to include your library or similar functionality into the core library, which is the built-in support that comes with Teensy. But doing this would involve changes... which might amount to rewriting much of it? I don't want to give you the impression I don't like what you've done. In fact, it's really nice. So please don't take these many suggestions the wrong way.

To be included in the core library, it needs to be MIT license. If this becomes part of the built-in support, of course your name will remain in the code as required by the license. I'll also mention you in 2 places on the website, the release notes on the download page, and on the Timing page, where I'll add documentation about using these functions. I want to give you full credit for your work!

The core library is maintained by PJRC. Once included, there will be limited opportunity to change it. So we really need to get this to a final state before including it. There are a lot of changes I'd like to consider.....

First, I'd like to see the dynamic assignment work a little differently. Arduino has 2 conventions for objects, pre-instantiated (PITimer0, PITimer1, PITimer2) and user defined (myTimer, myOtherTimer, etc). Normally pre-instantiated objects represent fixed hardware resources, which is the model you're using now. I'd like to give users a more abstract model, where they create instances without specifying which hardware resource will be used.

Another strong Arduino convention is the use of begin() and end() functions, to start and stop actually using the hardware. I'd really like to see the object constructor do very little. The begin() function would dynamically assign available hardware to the object, either by inspecting the hardware registers for a timer not in use, or by using a static variable, probably a bitmask of which timers are in use, or by any other method you think is appropriate. Likewise, the end() function would release the hardware and mark it as available for other objects.

As a longer term goal, when a slow period that's an integer number of milliseconds is used, rather than using a PIT timer, the library could integrate with the main systick timer interrupt using a small table of callback pointers. Or it could possibly be written to share a PIT between multiple objects if they have the same period or periods that are integer multiples. A more abstract object which doesn't expose hardware details like PIT0, PIT1, PIT2, etc can facilitate all these things. The user just creates an interval timer, gives it a period and callback function, and how the library actually achieves the functionality is not something they need worry about. It can be ported to other chips with different types of timers, and has the flexibility to be implemented in many different ways.

A more generic name might be good. There's a strong convention on Arduino to use longer, plain English names. Perhaps "IntervalTimer" might be good? I'm open to ideas. Staying with PITimer might be ok too? But I'd really to give the user a model of an abstract interval timer, rather than a specific hardware resource.

Another design goal I'd like to see would be a minimalistic API. Currently it looks like there are 15 public functions. I know it's painful to take away functionality, but I really do believe a minimal API is much simpler to learn and use. It's also much easier to document. A really minimal approach might be only 2 functions: begin(period, function) and end(). A way to temporarily prevent/delay the function call without fully disabling interrupts (and without disrupting the interval) might be worthwhile? But I'd like to avoid providing access to the actual counter and multiple functions to access or change the period. However, it might make sense to have some of that stuff as protected variable or functions, which would allow a class that inherits from the simpler class to provide a more fully featured API.

I realize this is a LOT of stuff, perhaps changing almost everything in the library. Please let me know your thoughts?
 
Last edited:
Dan, I'd like to include your library or similar functionality into the core library, which is the built-in support that comes with Teensy. But doing this would involve changes... which might amount to rewriting much of it? I don't want to give you the impression I don't like what you've done. In fact, it's really nice. So please don't take these many suggestions the wrong way.

Cool and thanks! No worries at all.

To be included in the core library, it needs to be MIT license.

Not a problem. I've forked PITimer to IntervalTimer (https://github.com/loglow/IntervalTimer) on GitHub and the new library is MIT licensed.

First, I'd like to see the dynamic assignment work a little differently. Arduino has 2 conventions for objects, pre-instantiated (PITimer0, PITimer1, PITimer2) and user defined (myTimer, myOtherTimer, etc). Normally pre-instantiated objects represent fixed hardware resources, which is the model you're using now. I'd like to give users a more abstract model, where they create instances without specifying which hardware resource will be used.

How do you envision the creation of a timer failing gracefully if all hardware resources are currently in use?

Another strong Arduino convention is the use of begin() and end() functions, to start and stop actually using the hardware. I'd really like to see the object constructor do very little. The begin() function would dynamically assign available hardware to the object, either by inspecting the hardware registers for a timer not in use, or by using a static variable, probably a bitmask of which timers are in use, or by any other method you think is appropriate. Likewise, the end() function would release the hardware and mark it as available for other objects.

This makes sense. I've stripped down and changed the interface naming for IntervalTimer. Dynamic allocation of hardware will be the next, more involved step. I just wanted to setup a base to start from, and the new fork (while much more minimal) remains fully functional. The readme and example have also been updated.

As a longer term goal, when a slow period that's an integer number of milliseconds is used, rather than using a PIT timer, the library could integrate with the main systick timer interrupt using a small table of callback pointers. Or it could possibly be written to share a PIT between multiple objects if they have the same period or periods that are integer multiples. A more abstract object which doesn't expose hardware details like PIT0, PIT1, PIT2, etc can facilitate all these things. The user just creates an interval timer, gives it a period and callback function, and how the library actually achieves the functionality is not something they need worry about. It can be ported to other chips with different types of timers, and has the flexibility to be implemented in many different ways.

I think this is a great idea, and an interesting challenge for me.

A more generic name might be good. There's a strong convention on Arduino to use longer, plain English names. Perhaps "IntervalTimer" might be good? I'm open to ideas. Staying with PITimer might be ok too? But I'd really to give the user a model of an abstract interval timer, rather than a specific hardware resource.

I like the name IntervalTimer too.

Another design goal I'd like to see would be a minimalistic API. Currently it looks like there are 15 public functions. I know it's painful to take away functionality, but I really do believe a minimal API is much simpler to learn and use. It's also much easier to document. A really minimal approach might be only 2 functions: begin(period, function) and end(). A way to temporarily prevent/delay the function call without fully disabling interrupts (and without disrupting the interval) might be worthwhile? But I'd like to avoid providing access to the actual counter and multiple functions to access or change the period. However, it might make sense to have some of that stuff as protected variable or functions, which would allow a class that inherits from the simpler class to provide a more fully featured API.

The new fork is pretty severely stripped down. No counters, no reset, no frequency option, etc. I think the pause/delay/reset functionality might be good to include. How about checking the remaining time on a counter? Important?

I realize this is a LOT of stuff, perhaps changing almost everything in the library. Please let me know your thoughts?

It all sounds good to me. Looking forward to participating more, and I still have a lot to learn. Best, -Dan
 
How do you envision the creation of a timer failing gracefully if all hardware resources are currently in use?

begin() could return an integer or boolean, perhaps true for success, false for failure? I suspect very few people will bother to check the return, but it's simply and adds very little code. The main failure mechanism will simply be no calls to the user's function.


The new fork is pretty severely stripped down. No counters, no reset, no frequency option, etc. I think the pause/delay/reset functionality might be good to include. How about checking the remaining time on a counter? Important?

I'd like to keep it conceptually very simple: a function gets called at a regular interval.

There are at least 3 reasons why I'd really like to see a very minimal API.

First and foremost, people find things they perceive to be simple to be easy to use. The best way to make something seem simple is with a clear concept of what it does, and as few things to learn as possible.

Second, more functionality could complicate implementing multiple objects on the same PIT timer or from PIT and systick, or whatever types of timers are available on some future boards. I'd like to see those options explored with only the absolutely minimum API, and then consider more features after gaining some experience with alternate implementation.

Third, this library exposes users to the perils of interrupt context execution. Any documentation and examples will have to explain the need for "volatile" on variables and also disabling interrupts while accessing them. Just sharing data between in interrupt context function and the rest of a program is difficult to get right, but at least it can be explained. Adding functions to manipulate the way the timer works risks difficult-to-understand race conditions, both in the library itself (what happens if the functions are called from interrupt context), and even worse, in the types of programs users will attempt to craft with those functions. Perhaps I'm overly cautious, but I've been bit by interrupt context race condition problems so many times over the last 20 years that I always take a very cautious approach to try preventing them from being possible in the first place.


However, there may be true needs for other functions. I think it's best to listen to feedback from people actually using the library. There may be really useful things that just can't be done with only begin() and end(). Or doing them may be too inefficient? It's easy to add stuff later... but once a piece of software is widely used, it's almost impossible to take away functions. I know it's tempting to add features. Features make people happy in the short term. But in the long term, simplicity and ease of learning & use are what people really value, and keeping things simple is the best way.
 
Cool, your rationale makes sense to me. You've been doing this for a lot longer than I have.

I've retooled the library, tested it a bunch, and updated it: https://github.com/loglow/IntervalTimer

It still only supports the three (unused) PIT modules, however it no longer pre-defines class objects for them. Individual IntervalTimer objects can be created, and each one is allocated to a PIT when it's started. In creating the private functions, I tried to keep in mind that other resources will be added (aside from PITs) for the timers to use. Additionally, the PIT clock is now dynamically enabled and disabled in the background based on whether any PITs are being used; this obviously creates a problematic situation for tone() until it can be made compatible with IntervalTimer. There is no constructor (needed) at the moment. There's a new status flag which indicates whether a timer is off, or what kind of resource it's using if it's on.

My goal in this update was to get the framework more in-line with the dynamic functionality you've been describing, while leaving the actual resource updates (systick, multiple timers per PIT, etc.) to be tackled next. Another thing I changed was the use of some new register bit defines that aren't present (yet) in mk20dx128.h, as opposed to their representation as bare integer numbers. I think starting from a solid base is almost always the right way to go. Take a look and let me know what you think. -Dan
 
@loglow -- I notice you have set the max PIT speed at the equivalent of 640 clocks == 75 kHz. In my current applications, I need a higher frequency -- is there a reason for this limitation ? I haven't checked if it is possible to run all 3 PITs at this rate.

Also, in your code for the isrs, is it necessary to wrap in cli & sei ? (thinking about speed)

Also, perhaps (depending on how you would want stacked PITs to interrupt each other (perhaps not a desirable situation), wouldn't you generally move the clearing flag (PIT_TFLG0 etc.) to after the isr ?. Also, since the rest of this register is unused, a plan write would work (or a bitband write, but I don't know how to do that yet) and be 1 cycle faster.

So, could this:
void pit0_isr() { cli(); PIT_TFLG0 |= PIT_TFLG_TIF; IntervalTimer::pIT_ISR[0](); sei(); }

become
void pit0_isr() {IntervalTimer::pIT_ISR[0](); PIT_TFLG0=PIT_TFLG_TIF; }
 
Once this library is more developed, I'll convert tone() to use it. Then it can have all 4 interrupts, and there won't be any concern about conflicting with tone's use of the hardware.

Inside the interrupt, cli and sei probably should not be used. The NVIC handles interrupt nesting issues.

At the moment, I'm mostly concerned with the API. There are lots of implementation details too, but those are easy to change later. The API is always the most challenging part of a good library, because it's so very hard to change later.

The one other API issue that's been on my mind is the period parameter. I have mixed feelings about using float. On one hand, it is very nice and convenient to express the period as actual seconds. But I have a few concerns about float. Virtually all Arduino APIs use unsigned integers, either milliseconds or microseconds. There are some finer points about float that many novice users do not know, like the difference between "1.0" and "1". In the example, it has "timer0.begin(timerCallback0, 1.0 / 2000)". If someone uses "1 / 2000", does the compiler perform an integer division and return zero before converting to float? My other concern about float is the imprecise conversion to integer, which could complicate efforts to identify intervals that are exact integer multiples and schedule them from the same PIT or from systick if they're multiplies of 1ms.
 
Updated: https://github.com/loglow/IntervalTimer

  • changed back to direct register writes instead of read-modify-write operations (probably faster)
  • removed cli() and sei() from the PIT ISRs
  • changed period specification from float (seconds) to integer (microseconds)
  • in light of above, changed the range check (invalid for value of 0 or value greater than overflow condition, which is based on the bus clock speed)
  • invalid period now returns false instead of silent adjustment
  • updated readme to reflect these changes
  • updated example sketch to reflect these changes

@loglow -- I notice you have set the max PIT speed at the equivalent of 640 clocks == 75 kHz. In my current applications, I need a higher frequency -- is there a reason for this limitation ? I haven't checked if it is possible to run all 3 PITs at this rate.

I was limiting it based on some performance tests, but it's no longer being limited; 1 microsecond is the new minimum, though it doesn't appear to be very accurate/reliable at that speed, and more than one running that fast doesn't seem to work at all.

Also, perhaps (depending on how you would want stacked PITs to interrupt each other (perhaps not a desirable situation), wouldn't you generally move the clearing flag (PIT_TFLG0 etc.) to after the isr ?.

I'd appreciate more input on this, but some earlier testing seemed to indicate instability if the flag was cleared later. Clearing the flag (by writing 1 to it) says "ok, this interrupt can occur again" so I don't see how doing it at the end of the ISR would result in a speedup. Am I misunderstanding?

Also, since the rest of this register is unused, a plan write would work (or a bitband write, but I don't know how to do that yet) and be 1 cycle faster.

I've abandoned the read-modify-write operations almost entirely. I hadn't considered the fact that they'd be slower. I looked into bit-banding a little bit (har har), and it seems kinda cool. Is there a benefit to using it over a direct write, or is it more geared for regs with multiple single-bit flags/switches?

Once this library is more developed, I'll convert tone() to use it. Then it can have all 4 interrupts, and there won't be any concern about conflicting with tone's use of the hardware.

Cool. Right now even though the 4th PIT isn't being made available, there will be a conflict with tone() if someone enables one or more timers and then ends all of them, because this will trigger the PIT clock to be disabled. Just stating that in case anyone's using it now. When it's time, I think enabling the 4th PIT will be as simple as changing NUM_PIT from 3 to 4 and uncommenting pit3_isr().

Inside the interrupt, cli and sei probably should not be used. The NVIC handles interrupt nesting issues.

Good to know, I removed 'em.

I have mixed feelings about using float.

I don't think the benefits outweigh the drawbacks you mentioned, so I've changed it to an integer (microseconds) and adjusted the math and range checks accordingly.

-Dan
 
Last edited:
Just to make sure that I'm on the right track here.
IntervalTimer is the library to use now, PITimer is to be considered replaced?

Thanks for your work on this library, it works great for me so far!
 
The plan is to include IntervalTimer into the built-in core library.

If you use IntervalTimer, your code will probably work with future versions of Teensyduino without needing to install the library separately.

PITimer will stop working when IntervalTimer is built in, so you probably should use IntervalTimer rather than PITimer for any new projects.
 
Status
Not open for further replies.
Back
Top