3

I'm writing a library which needs an ISR to turn off an LED some time after it was turned on. Since it's all about turning an LED on and off it doesn't need to be very precise. On the other hand I would like to use this library in a program where timer1 and timer2 are given out to other (more important) tasks, and I also need the millis() and micros() functions. Last, I'm using an ATmega328P microcontroller, which only has 3 timers (and I can't easily replace it with another one).


So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions, and – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...) and the disadvantages or side effects in using this timer in a non-Arduino library and the Arduino millis() and micros() functions.

Thanks in advance for any answer!

  • 2
    Why do you feel you need to use an interrupt for such a simplistic and not time-critical operation? – Majenko Jul 19 '17 at 22:08
  • I fact I'm currently using a 'checkLed()' function, and it works fine. But 1) the program main loop is sometimes quite long (e.g. sometimes it has to execute other loops), and it were easier for me if I didn't need to care about turning off the LED (which is not time-critical but quite important), 2) now I'm quite curious about this question in general ;) – noearchimede Jul 19 '17 at 22:17
  • Fair enough. TBH I have just been writing a routine that does exactly that, but on a different MCU that has a different timer structure (it controls the TX and RX LEDs to indicate USB activity - RX interrupt and TX functions turn the LEDs on, timer turns them off 50ms later), so I can understand the need. – Majenko Jul 19 '17 at 22:48
4

So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions,

Yes. a few ways, depending on your level of comfort:

1) you can declare the stock arduino Timer0 OVF "weak" and write your own where you can insert your ISR. But you have to handle the interaction between the millis() / micros() related variables.

2) you can modify the stock arduino Timer0 OVF to insert your own ISR.

and – if that can be done – what would be the restrictions of such an ISR (e.g. I suppose I could not use all the timer/interrupt modes...)

the stock ISR would look like this:

ISR(TIM0_OVF_vect) {
    ...
    timer0_fract = f;
    timer0_millis = m;
    timer0_overflow_count++;

    //insert your isr here
    _mytmr0_isrptr();
}

where _mytmr0_isrptr() is a function pointer to your own isr handler.

3) the simplest is probably to use the output compare interrupts. you can get it to behave like a virtual timer, and do lots of other stuff.

obviously, you can think of tons of other ways and this is just a starting point.

edit:

What are those interrupts? How can I use them? –

timer0 has three interrupts associated with it: overflow and compare ch A and ch B. the overflow interrupt is already being used by the timing functions millis() and micros(), as shown earlier.

the compare ch A/B interrupts are unused. and this discussion is about using them for timing purposes.

the general ideal is demonstrated here: https://dannyelectronics.wordpress.com/2017/06/23/using-spare-output-compare-channels-as-timers-collection/

the task there is more complicated than what you are trying to do here but the basic ideal is the same. to do what you wanted here it is much simpler than that.

if you still cannot figure it out, i can provide you with code pieces to get you started.

  • Sorry, i didn't understand the third option... What are those interrupts? How can I use them? – noearchimede Jul 19 '17 at 23:28
  • Thank you, now I got it! I've implemented it in my library and it works. – noearchimede Jul 20 '17 at 16:16
  • I'm glad you have a solution that works for you. The key here is to recognize that output compare is essentially a timer: it generates an interrupt on match, vs. Timers doing so on overflow or underlie. – dannyf Jul 20 '17 at 22:12
  • Once you recognize that you can make a lot of virtual timers out of output compare channels . for most mcus that means 2x to 8x virtual timers per hardware timer. – dannyf Jul 20 '17 at 22:14
1

So I was wondering if I could attach an ISR to timer0 without affecting the above Arduino functions ...

No, because those functions use an already-attached interrupt.

it doesn't need to be very precise ...

Just test for the current time (eg. using millis or micros) in your main loop.

the program main loop is sometimes quite long (e.g. sometimes it has to execute other loops) ...

Restructure so that it doesn't do that. You can use the state machine concept to do things from time to time, without getting stuck in inner loops for a long time.

  • It's not possible with TIMER0_OVF interrupt, but there are also TIMER0_COMPA/TIMER0_COMPB interrupts and these should be free to use. Only downside is that analogWrite on corresponding pins moves the time when the interrupt happens. – KIIV Jul 20 '17 at 8:27
  • @KIIV So using timer0_compx I couldn't use analogWrite on the corresponding pin any more? – noearchimede Jul 20 '17 at 8:54
  • @noearchimede You can. Just the interval will move according to that. But if you don't need precise timing, it won't be a problem. Just for sure you can test all PWM values just to be sure it's working everytime. – KIIV Jul 20 '17 at 8:59
  • @KIIV I've just written a new version of my library using TIMER0_COMPA and it works exactly as I wanted.May I ask you to write a complete answer with your suggestions, so that I can mark the question as answered? – noearchimede Jul 20 '17 at 15:02
  • @noearchimede It's already here in dannyf answer (third option) – KIIV Jul 20 '17 at 15:05
1

I think the first solution suggested by dannyf's answer is wrong as ISR in AVR GCC can't be defined as weak function (why).

I think the best solution is to add handler (AKA hook) functions using function pointer to the Timer0 ISR inside the core wiring.c source file shipped with Arduino core, and assign latter (attache) this function pointer to any desired function in the other library/class or in the main application code.

More about this trick in this article here.

0

An interrupt can only have one interrupt service routine. The only way you can do something else using an interrupt that is already handled elsewhere would be to modify that existing interrupt handler to also call your code. Not something that is practical for a library.

I can see no practical way of doing it short of modifying the entire millis() system in the main API to allow hooking of callback functions, which would be a really nice facility (chipKIT has a similar system with the Core Timer interrupt on the PIC32) and submitting it back to Arduino for inclusion in the main core - which would set a baseline version for compatibility of your library of course.

  • And there is no way to kind of override the ISR of the millis system without modifying the API files, right? – noearchimede Jul 19 '17 at 22:56
  • Correct. If you define a second ISR you will get vector conflicts. You will just have to decide on another interrupt to use. Some libraries provide #define macros etc to select which timer to run with. – Majenko Jul 19 '17 at 22:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.