IntervalTimer library

There are some topics touching on using hardware timers on the Spark. However, I have not found any libraries for this. So,

  1. Which hardware timers are available to the user?
  2. How can they be setup to generate timed interrupts?
  3. Has anyone written a library to use these timers? Teensy has a library called IntervalTimer() that calls an ISR with user code at defined intervals.

I also know that PJRC is working on a timer “pool” that automatically allocates a hw timer to the user. It would be great to have a single library to go to. :smile:

1 Like

From what I can gather, the STM32 timers on the Spark are allocated as follows:

TIMER 1: system timer

TIMER2/CH1: A0 pwm
TIMER2/CH2: A1 pwm
TIMER2/CH3: A7 pwm
TIMER2/CH4: A6 pwm

TIMER3/CH1: A4 pwm
TIMER3/CH2: A5 pwm
TIMER3/CH3: ?? (used internally?)
TIMER3/CH4: ?? (used internally?)

TIMER4/CH1: D1 pwm
TIMER4/CH2: D0 pwm
TIMER4/CH3: not mapped
TIMER4/CH4: not mapped

Can anyone add to this?

Is anyone interested in a library for using (reallocating) hardware timers on the Spark?

The timers are currently not exposed to the user directly. You’ll have to fiddle with the firmware to get access. Timers 2,3 and 4 are also used by the Servo library to generate the necessary PWMs.

It maybe a little tricky to do this right away, since one would need to figure out which timers are available (in context to the user application) and then configure/use them.

Mohit, understood. For precise timing, having a hardware timer would be great. The folks doing the Teensy 3 are developing a library where you “check out” a timer resource. Of course, you give up certain functionality when you do so like servo and tone for example. But if you don’t use these, it makes sense to reallocate the resources. Ideally, the servo functionality can be “parked” so the timers are available. I’m going to look into what PJRC is doing and report back. I see this as a long term project as there are plenty of other priorities. :smile:

I’m also interested in future developments in this. For me hardware timers are a very valuable extra!

I just looked at PJRC’s IntervalTimer() library for allocating Programmable Interrupt Timers (PIT) on the teensy 3 platform. The library allows a user to use and release a timer from a pool of 4 timers. Several of PJRC’s libraries (tone, softPWM, etc) were modified to use the timer pool.

This library could be adapted to the Spark. In another topic, a member already reallocated timer 2 for his application.

  • The servo library will use Timers 2,3 & 4 depending on the pin specified in its attach() method.
  • analogWrite() will use Timers 2, 3 & 4 depending on the pin specified

So if you don’t use PWM or Servo, you could use timer 2, 3 and 4! Like PJRC, the servo and PWM code could be adapted to use the timer pool, at least to “lock out” a timer from the pool.

I have posted PJRC’s interval timer code on my github.

2 Likes

OK folks, it is DONE!! The SparkIntervalTimer library is now available for all your grubby little hands. I consider this a start but right now, you can grab an interval timer from a pool of three (TIM2, TIM3 or TIM4), pass it a callback and give it a period and it calls your code at that interval. When your done, the timer is returned to the pool. Due to the way the timers work, there are two timescales - 1-65535 microseconds and 1-65535 half milliseconds (or max of 32767 milliseconds). The code could be adapted to setup the timers for PWM also but that can come later. The code compiles both locally and on the web IDE.

If you are interested, the code is available on my github. Your feedback and ideas are greatly appreciated. :smiley:

8 Likes

@peekay123 this is super cool! I’m converting this post to a “Library” post and changing the title so this is more searchable in the future.

1 Like

I will probably give this a try this weekend. Is it possible to recycle an allocated timer?

Semi-pseudo-code example:

IntervalTimer myTimer;

void setup() {
    myTimer.begin(someFunc, 1000, hmSec);
}

void loop() {
    // Do nothing
}

void someFunc() {
    myTimer.end();
    myTimer.begin(someFunc2, 42, uSec);
}

void someFunc2() {
    myTimer.end();
}

wgbartley, if by recycling you mean release it back to the pool then YES. A call the myTimer.end() will do that. If you want to just reset the counter to a different time then I could modify the code so that it checks if the timer is allocated and if so, simply stops it and starts it again with the new settings. Makes sense! I’ll start working on it. :smile:

UPDATE: I just reread your pseudo-code and realized that what you want to do is already supported by IntervalTimer. I also added a begin(period, scale) that does not de-allocated the timer but just changes its timing settings. But since you want to change the ISR and the period, then that will work with the existing code. :smile:

I didn’t mean to restart the timer, but, yes, sure!

Does the term “interval” imply that it will continuously fire at a given interval frequency? If so, maybe add an extra argument to only fire once?

wgbartley, the interval timer is designed specifically to repeat. I have been thinking of a more generalized hardware timer library where you could specify interval (repeat), single shot or pwm functionality. Right now, I set the counter to auto-reload so a single shot is easy. The only catch is that the ISR provided by the user would have to use specific templates for each mode. This is not for beginners but it is definitely workable. Any thoughts?

For all practical purposes, I'm a beginner. My thoughts: huh? I'm may be reading it incorrectly, but maybe you could do a check before calling the callback that nukes the timer if the one-shot argument is passed and then calls the callback?

if(single_shot==true) code_that_nukes_this_timer();
IntervalTimer::SIT_CALLBACK[0]();

Also, I discovered a typo in your commenting on SparkIntervalTimer.cpp line 60. I'm pretty sure it should be "TIM4" instead of "TIM3". Yes, I read the comments. :slight_smile:

wgbartley, thanks for catching that! The reload function is a hardware feature and affects how the timer is configured so ideally, very little is done by the ISR and callback. Besides, on a one-shot, you may want to restart the one-shot from the ISR instead of nuking the timer and then re-allocating entirely. Food for thought though :wink:

@peekay123 this is RAD DUDE! Good job :wink:

I was looking at your example code and was thinking if you really couldn’t tolerate disabling interrupts for something really critical, then there is another way to do this:

https://github.com/pkourany/Spark-Interval-Timer/blob/master/SparkIntervalTimerDemo.cpp#L90-L99

// to read a variable which the interrupt code writes, we
// can read it twice and if it is the same, we know the 
// interrupt service routine has not changed it while
// we were reading it.  If you have a super fast interrupt
// interval, this will never work, however anything slower
// than 1us should work.
blinkCopy = blinkCount;
while(blinkCopy != blinkCount) blinkCopy = blinkCount;

Obviously this is just a suggestion for someone that can’t tolerate disabling interrupts, and should not be used by default without thinking about how all of the timing works.

Based on your example code, it’s conceivable that two of the interrupts could fire asynchronously within 1us of each other, but the third might not. Thus, the while loop can perform the read a 3rd or 4th time if needed.

BDub, thanks! I am seriously thinking of expanding the library to allow more generalized timer control so stay tuned. Great idea on the no-block-interrupts bit. Theoretically, I should limit the minimum period to more than 1 us to take overhead into account. Possibly a value between 10-20 us. Any thoughts on that?

Maybe just a warning not to set it below a certain value for that reason? Some users may want to generate a 120kHz squarewave/PWM and would have to modify the library to do so.

For one-shot mode, you may have to include a different set of ISRs because I think you’ll most likely need to disable that specific interrupt as the first instruction in your ISR to prevent nested IRQs from retriggering upon exiting your ISR. I haven’t really thought of it too much, just remembering the fun I had with the Pinewood Derby timer.

BDub, the warning is a good idea because they also have to know that the standard analogWrite() functions associated with the timer will no longer function when that timer is used.

I was considering writing ISRs that would detect the timer mode and run the appropriate code for that mode whether it is to do a callback or reload. The idea is to keep as much of the ISR part away from the use as possible. Disabling interrupts is another thing I have to think about and may be related to the timer mode as you pointed out. Fun, fun fun! :smile:

Some operations, depending upon the processor-compiler-language combo, are guaranteed to be atomic - cannot be interrupted. If a byte/char is flagged volatile then reading it seems to me to be one of those operations. Possibly ++incrementing it also. And maybe copying it to another volatile. If we knew of any one atomic operation then we could use that feature. Is there one?