PDA

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



loglow
01-31-2013, 09:07 PM
Following an active thread discussing interrupt timers (http://forum.pjrc.com/threads/14), 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() (http://arduino.cc/en/Reference/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

rbrockman
01-31-2013, 09:35 PM
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.

loglow
01-31-2013, 09:59 PM
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.

Jp3141
01-31-2013, 10:45 PM
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 ?

loglow
01-31-2013, 11:01 PM
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:


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:


void pit3_isr(void)

Jp3141
01-31-2013, 11:31 PM
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")));

loglow
02-01-2013, 06:59 AM
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:


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

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

New:


void timerCallback0() {
}

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

I've updated the code on github (https://github.com/loglow/PITimer) along with the readme (https://github.com/loglow/PITimer/blob/master/README.md) and the example (https://github.com/loglow/PITimer/blob/master/examples/Basic/Basic.ino). 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

jedix
02-06-2013, 05:42 PM
Looks pretty cool, thank you !

linuxgeek
02-07-2013, 01:30 AM
That's nice. I've already been using the library. Will update.

rbrockman
02-07-2013, 02:49 AM
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.

loglow
02-08-2013, 01:14 AM
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

loglow
02-08-2013, 01:21 AM
I am very interested in seeing PIT3 working, and have a need for 4 independent timers.

It'd be great to hear from a Teensy developer on the feasibility of this...

PaulStoffregen
02-08-2013, 02:39 AM
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.

loglow
02-08-2013, 02:47 AM
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

linuxgeek
02-08-2013, 05:41 AM
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.

loglow
02-08-2013, 06:18 AM
...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

PaulStoffregen
02-08-2013, 11:27 AM
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 (http://www.pjrc.com/teensy/td_timing.html), 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?

loglow
02-08-2013, 09:41 PM
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

PaulStoffregen
02-08-2013, 11:07 PM
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.

loglow
02-10-2013, 09:40 AM
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

Jp3141
02-10-2013, 06:00 PM
@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; }

PaulStoffregen
02-10-2013, 10:32 PM
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.

loglow
02-11-2013, 05:56 AM
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

ZTiK.nl
02-20-2013, 05:24 AM
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 (http://forum.pjrc.com/threads/18648-Sparkfun-Pulse-Sensor-(amplified)-on-Teensy-3-0) so far!

PaulStoffregen
02-20-2013, 02:03 PM
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.

ZTiK.nl
02-20-2013, 02:17 PM
Allright, thanks for the clarification.

In that case maybe it's wise to change the library link in the opening post to point to IntervalTimer instead of PITimer.

Dave X
02-27-2013, 04:47 PM
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.

If one needed non-periodic interrupts, like for coordinated-axis stepper motor control as in https://github.com/triffid/Teacup_Firmware/blob/Gen7/timer.c#L85, would trying to use FTM1 (and sacrificing PWM on pins 2&3) be reasonable?

PaulStoffregen
02-27-2013, 08:02 PM
If one needed non-periodic interrupts, like for coordinated-axis stepper motor control as in https://github.com/triffid/Teacup_Firmware/blob/Gen7/timer.c#L85, would trying to use FTM1 (and sacrificing PWM on pins 2&3) be reasonable?

Yes, using one of the FTM timers would be best.

If you do it carefully, by reading the MOD register to know what the maximum count is, you could probably design code that would use only 1 channel. If you avoid writing to the MOD register and other settings, it's a bit more work, but you could avoid interfering with the PWM running on the other channels.

sealyons
04-05-2013, 07:00 PM
This looks very interesting and something I need for an upcoming project. Sorry, lots of questions:

1) What is the status of IntervalTimer?
2) Is the plan still to incorporate this directly in the core?
3) What about tone() updates to release timer3? Once these changes are done, will tone() not lock-up a timer unless it's used?
4) Still assuming only 3 timers can currently be used with either PITimer or IntervalTimer, correct?
5) Are updates planned for the next release? If not, any idea when?
6) Is there a beta available?
7) Is it best to use PITimer or IntervalTimer for now?

Thanks.

linuxgeek
04-05-2013, 10:20 PM
Use IntervalTimer. If you want more details, please read all the posts for this thread. There are 2 pages of posts.

PaulStoffregen
04-05-2013, 11:38 PM
1) What is the status of IntervalTimer?
2) Is the plan still to incorporate this directly in the core?


I'm planning to include it in Teensyduino 1.14 or 1.15.

The timing depends somewhat on when Arduino 1.05 is released. Seven days ago, they upped the version to 1.0.5 on github, so 1.0.5 might release soon. If it does, I'll publish 1.14 quickly without IntervalTimer, and add it later in 1.15. If they delay several more days, I'll delay 1.14 and probably get a few more things in. IntervalTimer is on my short list.....

When I include IntervalTimer, of course I'll change tone() to use it.

Linuxgeek is correct, you should use IntervalTimer. Some of your questions are probably answered in the messages on this thread.

cord
04-06-2013, 12:27 AM
Great work, but the reset function did not work for me.
This modification did fix the problem:

void PITimer::reset() {
*PIT_TCTRL = 0;
// writeValue();
*PIT_TCTRL = 3;
}

PaulStoffregen
04-19-2013, 12:58 AM
I've been working on integrating IntervalTimer into the core library. One change I made was adding back the ability to use floating point to specify the interval. It's currently implement as inline functions, so the float math is done at compile time if the input is a constant.

Soooo... here's the main thing I'd like to discuss. I'm contemplating whether the interval ought to be expressed in microseconds or milliseconds?

Changing to milliseconds will of course break compatibility with all programs using the library today. If there were ever a time to make this change, it would be going from a separate library to built in. Once 1.14 is released, I really do not want to consider this type of change.... but I'm thinking about it now and asking here.

The advantage of milliseconds is many people will tend to use integers, and any integer number of milliseconds has the opportunity to someday be implemented by the systick interrupt, which frees the PIT timers for more demanding uses. A secondary advantage is a human perception, that using something like mytimer.begin(funct, 0.02) just "feels" like requesting something really small, compared to mytimer.begin(funct, 20).

If anyone is still following this thread and using IntervalTimer, please let me know your thoughts? I'm seriously considering this change, but I am also sensitive to how much code it might break. After 1.14 is released, the API will be frozen.

Wozzy
04-19-2013, 01:58 AM
I'd prefer the precision of being able to use microseconds.
I like the idea of looking to the future where faster clock speeds, ever smaller timing intervals and increased resolution are the norm.
I'm currently using IntervalTimer but it's in an active project, so It'll be easy for me to make the modifications when the 1.14 update is released.

Oh and many THANKS to Dan (Loglow) for his excellent efforts on PITtimer and IntervalTimer.

Zeph
04-19-2013, 02:44 AM
I've been working on integrating IntervalTimer into the core library. One change I made was adding back the ability to use floating point to specify the interval. It's currently implement as inline functions, so the float math is done at compile time if the input is a constant.

Is the idea that internally it will use integer microseconds, but that the user can specify time in that format OR using a floating point interface which converts to microseconds (via an inline function which the compiler can optimize)? Or would floating point times be required in order to use the library?

adrianfreed
04-19-2013, 03:36 AM
Microseconds please!
I am looking at what it will take for Arduino (especially the Teensy) to be able to do PTP IEEE1588 and API's
with more resolution will be required.

linuxgeek
04-19-2013, 05:41 AM
I would prefer microseconds as integers. I would think that there's quite a few people like me who will use the interrupt over long periods of time, and are concerned with drift. Using a float that's converted to a microsecond would seem to add to an unexpected drift.

Couldn't systick still be used for microseconds that are based evenly on 1000's? I'm wondering how systick would work. Is it almost like a free interrupt?

I'm personally not concerned with breaking compatibility.

adrianfreed
04-19-2013, 04:30 PM
Professional Time Engineers (yes, this is a real sub-discipline) don't like floating point representations of time because the resolution goes down as the time values go up. They usually use multi-word integers, e.g. PTP time stamp. I am hoping the Teensy 3.0++ uses a K60 w/ethernet core because they have an IEEE1588 timer which you can use
even if you don't use the ethernet MAC on the chip.

PaulStoffregen
04-19-2013, 06:51 PM
Internally, IntervalTimer works in resolution of bus clock cycles, not microseconds. The bus clock is 48 MHz.

In another thread, the point was raised that even integer microseconds don't offer enough resolution to get specific interrupt rates, like exactly 7 kHz, or as close as the hardware can generate, which is 7.000149 kHz. That got me thinking about the IntervalTimer API. I generally like keeping things as simple as possible, but adding the option to express the number as a float or integer provides access to the full native clock-cycle resolution without increasing the number of functions or parameters. I think it's worth doing.

Regardless of the API using microseconds or milliseconds, you still get full access the cycle-level resolution. For 7 kHz, with milliseconds you'd write myTimer.begin(funct, 0.142857). With microseconds, you'd write myTimer.begin(funct, 142.857). It's the same result either way.

So far, there's 4 votes for microseconds, and slightly over half a vote (me) for milliseconds.

I just to make sure everyone who's so strongly in favor of microseconds understands it does NOT give you any extra precision. You still get 20.83 ns timing resolution (yes, nanoseconds). There is the issue of floating point precision, but for constant inputs the calculation is done at compile time using double precision, which is far better precision than this hardware actually offers. Even with single precision float, the rounding errors only start to become a factor (a 20.8 ns factor) at intervals of approx 0.25 seconds or longer.

My main reason for favoring milliseconds is the likelihood of many people just using integer constants for relatively undemanding applications. Someday I want to extend IntervalTimer to also use systick. Today, you get only 3 timers. With Teensyduino 1.14, you'll get 4 timers (but calling tone will consume a timer for as long as the tone plays). A future version may allow 8 or 12 timers, where 4 can be higher precision and the rest only millisecond resolution.

rbrockman
04-22-2013, 07:08 PM
I have a project which is using IntervalTimer extensively. Regarding micro vs milli seconds my vote would be for microseconds if we have to choose a single unit of measure. A suggestion would be to choose one of the units as a default, and provide an optional way to specify another unit if desired. Example: myTimer.begin(funct, 142.0, MICRO) selects the unit of microseconds and myTimer.begin(funct, 0.142) selects the default unit of milliseconds.

In my case, if milliseconds is selected, it will mean that I have to do a converting calculation 1000 times a second for each of the 4 timers I'd like to use. If I can use microseconds directly, I can avoid all of this.

recri
05-05-2013, 09:40 PM
I'd say implement both interfaces:

timer.begin(cb0, 1); // milliseconds
timer.beginMicroseconds(cb0, 1000); // microseconds

This matches the convention on delay() and extends obviously to Nanoseconds in the future.

Allowing both integer and floating point intervals should be matched with the same change to delay() and delayMicroseconds().

linuxgeek
05-05-2013, 10:20 PM
How do you propose to deal with the problem of rounding?

I mean if it is rounded, how do you know it is rounded? It might not be a big deal that the resolution is imprecise to the interval that was asked, but I think we'd want to know exactly what the interval is that will be used. To avoid compounded time drift from what one would expect. So do you return a value? If so, I think it's better to return the # of clock cycles used as the interval. It makes me uneasy working with floats for something that is clearly integer math, like dollars and cents.

recri
05-06-2013, 03:09 AM
It's getting rounded even when you give it an integer number of milliseconds or microseconds. What you get is integer numbers of whatever clocks the processor has to count, with whatever jitter or drift there might be to that number. If the interval you want is a whole number of clock ticks, but not a whole number of microseconds, you should be able to specify the correct fraction of a microsecond.

-- rec --

linuxgeek
05-06-2013, 06:30 AM
It's getting rounded even when you give it an integer number of milliseconds or microseconds.
-- rec --

I realize that's a possibility (after Paul described it), which is why I was wondering if it might be better to have the actual # of clock cycles as the return value. If you don't care, you wouldn't do anything with it.
And it's only done during setup so it's not like there's any overhead to worry about. But if you did care, you could store that and use that for other calculations, when accuracy counted. And if I had to calculate the fraction so that it matched a clock cycle, I'd feel more comfortable just passing the actual clock cycle.

SISKO
10-03-2013, 11:41 PM
Hello, sorry for the thread hijack, if im wrong let me know and ill make a new post

Im having some trouble using this library (first time im including an extern library)
What ive done is to download the PITimer.h and .ccp to a folder named PITimer in the libraries section of Arduino.
Then created a new sketch, pasted the example (https://github.com/loglow/PITimer) and verify the code.

The problem is that i get all kind of errors:

Arduino: 1.0.5 (Windows XP), Board: "Teensy 3.0"
C:\Archivos de programa\Arduino\hardware\tools\arm-none-eabi\bin\arm-none-eabi-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mcpu=cortex-m4 -DF_CPU=48000000 -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 -mthumb -nostdlib -D__MK20DX128__ -fno-rtti -felide-constructors -std=gnu++0x -DUSB_SERIAL -DLAYOUT_US_ENGLISH -IC:\Archivos de programa\Arduino\hardware\teensy\cores\teensy3 -IC:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer C:\DOCUME~1\Fran\CONFIG~1\Temp\build13994399269598 5522.tmp\sketch_oct03a.cpp -o C:\DOCUME~1\Fran\CONFIG~1\Temp\build13994399269598 5522.tmp\sketch_oct03a.cpp.o

In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:7:19: warning: character constant too long for its type [enabled by default]
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:9:9: error: stray '\302' in program
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:9:9: error: stray '\267' in program
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:9:9: error: stray '\302' in program
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:9:9: error: stray '\267' in program
In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:50:39: warning: character constant too long for its type [enabled by default]
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:50:56: warning: character constant too long for its type [enabled by default]
In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:215:25: warning: character constant too long for its type [enabled by default]
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:221:25: warning: character constant too long for its type [enabled by default]
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:392:17: warning: character constant too long for its type [enabled by default]
In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:523:60: warning: multi-character character constant [-Wmultichar]
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:523:70: warning: multi-character character constant [-Wmultichar]
In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:4:1: error: expected unqualified-id before '<' token
In file included from sketch_oct03a.ino:1:0:
C:\Documents and Settings\Fran\Mis documentos\Arduino\libraries\PITimer/PITimer.h:567:18: error: expected unqualified-id before numeric constant
sketch_oct03a.ino: In function 'void timerCallback0()':
sketch_oct03a:6: error: 'PITimer0' was not declared in this scope
sketch_oct03a:9: error: 'PITimer1' was not declared in this scope
sketch_oct03a:12: error: 'PITimer2' was not declared in this scope
sketch_oct03a.ino: In function 'void timerCallback1()':
sketch_oct03a:17: error: 'PITimer2' was not declared in this scope
sketch_oct03a.ino: In function 'void setup()':
sketch_oct03a:26: error: 'PITimer1' was not declared in this scope
sketch_oct03a:27: error: 'PITimer2' was not declared in this scope
sketch_oct03a:30: error: 'PITimer0' was not declared in this scope

I dont like large errors messages in forum post, but i want to give as much information as i can.
Can anyone spot my mystake?

PaulStoffregen
10-04-2013, 08:15 PM
The PITimer library was replaced by IntervalTimer, which is now part of the built-in core library. You can no longer use PITimer, because it conflicts with IntervalTimer.

SISKO
10-04-2013, 08:26 PM
Thank you Paul

GuitarHack
10-18-2013, 04:57 AM
Hi Paul -
You might want to add notes/examples about IntervalTimer to your page on Delay and Timing. I was looking to embark on adding timer interrupts and happened to run into this - was going down a much longer path before a fortuitous google search took me to this thread.

And thanks so much for pulling this into Teensy - as an old embedded guy I was going nuts without timer interrupts but wasn't quite ready to dig deep into the chip.

zaggo
02-16-2014, 12:11 AM
Hi all,
I try to trigger an ISR with high frequency (< 1 microsecond). Currently, the IntervalTimer library limits the timer period to minimum 40 F_BUS cycles, so the minimum period with F_BUS=48MHz is limited to 0.822916667 Ás.
Is this a hardware limit of the cpu/timer?
I checked and my ISR needs far less than 1Ás for execution (around 300ns). Is there a significant overhead for calling the ISR?

If so, is there another way to periodically fire a small function (with kind of timing control) on a Teensy 3.1, allowing periods <1Ás?
Thanks.

stevech
02-16-2014, 12:28 AM
Hi all,
I try to trigger an ISR with high frequency (< 1 microsecond). Currently, the IntervalTimer library limits the timer period to minimum 40 F_BUS cycles, so the minimum period with F_BUS=48MHz is limited to 0.822916667 Ás.
Is this a hardware limit of the cpu/timer?
I checked and my ISR needs far less than 1Ás for execution (around 300ns). Is there a significant overhead for calling the ISR?

If so, is there another way to periodically fire a small function (with kind of timing control) on a Teensy 3.1, allowing periods <1Ás?
Thanks.
Recurring interrupt with interval < 1 microsecond is too frequent to be viable or useful. Anything on the order of 100 microseconds or less, recurring, would cost too much CPU time, and have too much latency.

PaulStoffregen
02-16-2014, 06:52 PM
You could always try editing the code to simply remove that 40 cycle limit.

I'm curious to hear how fast you can get the interrupt to run and still have even a little CPU left over for the main program? 40 cycles was merely a guess on my part, which has never been experimentally verified. If you do investigate this, please share your findings?

zaggo
02-17-2014, 11:04 AM
Recurring interrupt with interval < 1 microsecond is too frequent to be viable or useful. Anything on the order of 100 microseconds or less, recurring, would cost too much CPU time, and have too much latency.

If so, I might use the wrong approach in my code. But what's the right way to do it then?
The problem I have to solve is to switch a Laser on/off according to data in a bitfield buffer (i.e. not simple PWM but an arbitrary pattern). The switch timing is crucial.
During the switching the main program does virtually nothing, so it isn't an issue when the CPU doesn't have any time left to do much else.
Given this, what would be the best approach to switch a digital pin on/off (according to a bit value in a memory buffer) *exactly* each -say- 700ns?
In my current approach, I use IntervalTimer wich works fine so far down until ~950ns (I attached the source below).
If there's a different/better approach to do such thing (especially if it enables me to do shorter timing), please let me know.
Thanks!

---

This is my test program. It fills a buffer with sample data and then "outputs" the data once on a digitalPin:



// Create an IntervalTimer object
IntervalTimer myTimer;

const int laserPin = 5; // the pin with a LED

uint8_t scanBuffer[150];
const int kBufferSize = 150;

volatile uint8_t scanTemplate;
volatile uint8_t scanIndex;
volatile uint8_t nextPixelState = LOW;
volatile uint8_t nextBitIndex = 0;

void setup(void) {
pinMode(laserPin, OUTPUT);

// Fill scanBuffer with sample data
for(int i=0;i<kBufferSize;i++)
scanBuffer[i] = 0b10101010;

// Start output
startLaserLine();
myTimer.begin(switchLaser, 0.822916667);
}

void startLaserLine() {
noInterrupts();
scanIndex = 0;
scanTemplate = scanBuffer[scanIndex];

nextBitIndex = 7;
nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;

interrupts();
}

// ISR
void switchLaser(void) {
// Output prepared bit on digital pin as first thing after the timer fired
digitalWriteFast(laserPin, nextPixelState);

// Prepare the next bit
if(nextBitIndex>0) {
nextBitIndex--;
nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;
} else if(scanIndex<kBufferSize) {
scanTemplate = scanBuffer[scanIndex++];
nextBitIndex = 7;
nextPixelState = (scanTemplate & (1<<nextBitIndex))?HIGH:LOW;
} else {
nextPixelState = LOW;
}
}

// Do nothing in the main loop
void loop(void) {
}

DaleGribble
11-22-2014, 10:34 PM
I was wondering what the best way to change the PITimer interval period would be.

Per the datasheet, on page 904, it says

Timer Load Register (PIT_LDVALn) sets the timer start value. The timer will count down until it reaches 0, then it will generate an interrupt and load this register value again. Writing a new value to this register will not restart the timer; instead the value will be loaded after the timer expires. To abort the current cycle and start a timer period with the new value, the timer must be disabled and enabled.

On page 907,

The timers generate triggers at periodic intervals, when enabled. The timers load the start values as specified in their LDVAL registers, count down to 0 and then load the respective start value again. Each time a timer reaches 0, it will generate a trigger pulse and set the interrupt flag.

On page 909,

LDVAL trigger = (period / clock period) - 1


In their example they write to the register in hexadecimal.



50 MHz clock = 2 ns clock period
Interrupt @ 5.12 ms = 5.12 ms / 20 ns = 256,000 = 0x0003E7FF
PIT_LDVAL1 = 0x0003E7FF;

My first question is, can I write to the register with a decimal number and have it be the same as writing to the register in hexadecimal?

Is PIT_LDVAL1 = 0x0003E7FF the same as PIT_LDVAL1 = 256000 ?

Because I have tried to write the register with a decimal formatted number and have received very odd results. I am wonder if I am writing to the register correctly and if not, how do I write to the register?

mjhilger
12-22-2014, 06:52 PM
@zaggo
I see I'm rather late in adding to this discussion, but if you truly need consistent timing, you need to use hardware. You can use something like a small FPGA to set up fixed timer/counters that increment/decrement on the smallest boundary of your switch timing and then set the CPU to use DMA to load the FPGA timer. You should double buffer the FPGA timer buffer so you stay ahead. You have probably solved this issue, but I thought I might throw in my $0.02.

sbfreddie
12-30-2014, 01:31 AM
Paul:
I am having a teensy problem with the IntervalTimer on Teensy3.1.
I have set up three instances of "IntervalTimer":


IntervalTimer calculateFuncTimer;
IntervalTimer printDataTimer;
IntervalTimer updateGPSTimeTimer;

status = calculateFuncTimer.begin(calculateFuncISR, 200000); // Calculate Wind Data ISR to run every 200 milliseconds.
if (status == false)
{
if (DEBUG) Serial.println("calculateFuncTimer initialization failed");
}
status = printDataTimer.begin(printDataISR, 500000); // Print Data ISR to run every 500 milliseconds.
if (status == false)
{
if (DEBUG) Serial.println("printDataTimer initialization failed");
}
status = updateGPSTimeTimer.begin(updateGPSTimeISR, 10000000); // Update GPS Time to run every 10 seconds.
if (status == false)
{
if (DEBUG) Serial.println("updateGPSTimeTimer initialization failed");
}

One with period set to 200000, one with period set to 500000, and one with 10000000 (10 million or 10 seconds). The first two work great, doing their thing at the proper time, I originally set this third one to 1000000 (1 million or 1 second) and it worked just fine.
But when I set the third for a 10 second period it does not work properly. It does not produce any error when the functions are set up. It fires continually every time thru the main loop.
I checked with the code in IntervalTimer.h to look a little closer at the Maximum number in microseconds allow and I have come up with this number as the Maximum allowed: 89,478,485 or 89 seconds.
The formula: MAX_PERIOD = UINT32_MAX / (F_BUS / 1000000.0) is from IntervalTimer.h, and when I plug these values (UINT32_MAX = 4,294,967,295) (F_BUS = 48000000) / 1000000 into it I come up with the above value.
Could you help me understand what I have done wrong?

Thanks,
Freddie

PaulStoffregen
12-30-2014, 01:12 PM
Yes, it does seem like up to 89 seconds should be allowed. I do not know why it's failing for you. It could be a bug in the code, or some other unexpected limitation.

As a general rule, I only investigate problems when a complete program is posted.

Jp3141
03-13-2016, 10:50 PM
I was wondering what the best way to change the PITimer interval period would be.

Per the datasheet, on page 904, it says


On page 907,


On page 909,


In their example they write to the register in hexadecimal.


My first question is, can I write to the register with a decimal number and have it be the same as writing to the register in hexadecimal?

Is PIT_LDVAL1 = 0x0003E7FF the same as PIT_LDVAL1 = 256000 ?

Because I have tried to write the register with a decimal formatted number and have received very odd results. I am wonder if I am writing to the register correctly and if not, how do I write to the register?

Yes, you can write to it in decimal. Note that this value takes effect on the next timer cycle (from the F1RM: "Sets the timer start value. The timer will count down until it reaches 0, then it will generate an interrupt and load this register value again. Writing a new value to this register will not restart the timer; instead the value will be loaded after the timer expires. To abort the current cycle and start a timer period with the new value, the timer must be disabled and enabled again.");

I'm working on a very similar thing: I am trying to use the FrequencyCounter library, and have a suggestion to @loglow for an enhancement:

I'd like to calibrate the frequency counter (for variations in the 72 MHz Teensy crystal). My plan is to adjust this PIT_LDVALx value on just the 1st interrupt each cycle -- this gives me fine resolution (1 part in 36M, and a range of +/- 1000 ppm). I cloned the FreqCount.cpp files and inserted this:

index = gate_index + 1;
length = gate_length;
// 1st PIT interrupt can be adjusted ==> have option to finely calibrate by adjusting length
if (index == 1) PIT_LDVAL0=36000-1+20; else PIT_LDVAL0=36000-1;
if (index >= length) {
gate_index = 0;

In practice, the 36000 constant is variable depending on the Teensy clock frequency, (it's F_BUS); the '20' I have is just to show the impact of a change. In addition, I assume that FreqCount uses just the 1st PIT, but this is not generally true.

At present in the IntervalTimer lib, there is no supported way to return the index of the actual PIT used for the timer in FreqCount. It would be convenient for IntervalTimer() to return the index of the PIT used (now it just returns true or false); that would make my technique more general.