2
\$\begingroup\$

I have a question that I think the answer to is quite simple. But I've been unable to find a straight answer to.

If i have this service routine in some module foo.c

#pragma vector = SOME_VECTOR
interrupt void fooISR(){
    dosomething;
    IFG = 0;
}

if i have in my main.

#include "foo.h"

Then it appears that the fooISR() does indeed get loaded into the SOME_VECTOR and the ISR will trigger whenever the corresponding IFG is set.

I wanted to ask if this is the right way to write modules that utilize interrupts. Because with this approach the fooISR() will be in the SOME_VECTOR whenever I include foo.h this doesn't really allow for much configuration for the person working in main.

For example if in main.c I wanted to access some functionality of foo.c but wanted to write a separate ISR for SOME_VECTOR I'd be outta luck with this method right?

\$\endgroup\$
2
\$\begingroup\$

The concern that you express in the last paragraph is correct. It doesn't seem to be a good idea to bundle an ISR with other functionality that you might want to reuse separately from the ISR.

I usually do one of the 3 things:

  • Put the ISRs and main into the same file as main(). ISRs are short, so they don't clutter the main.c too much.
  • Put all ISRs together into a separate module. No other functionality in that module. No intention to make this ISRs module reusable.
  • Put each ISR into its own module. No other functionality in those modules.

You could put the ISR inside of an #ifdef block, if it makes sense in your particular situation.

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ All approaches are very reasonable. It also depends on which MCU you are using. For MSP430 first approach seems to be the best, as it usually has short ISRs, but for instance for C2000 it might be better to utilize third approach as you normally have most of the control functionality in ADC/PWM/CAP interrupts. \$\endgroup\$ – Andrejs Gasilovs Dec 4 '15 at 7:40
  • 2
    \$\begingroup\$ The normal way is otherwise to bundle the ISR with related functionality that you want to use together with the ISR. \$\endgroup\$ – Lundin Dec 4 '15 at 10:25
  • \$\begingroup\$ So ISRs typically do reside in the main.c? but with some #ifdef we could make it a bit more configurable? Yeah that makes a lot of sense thanks man that helped. \$\endgroup\$ – user2601592 Dec 4 '15 at 16:52
3
\$\begingroup\$

First of all, it is not meaningful to discuss these things unless you have some sort of program design. If you have a simplistic program of less than 1k LOC or so, then you might not need any design, it might be sufficient to just aimlessly hack away.

For larger, professional programs, you will need program design:

You should place ISRs in the same file as the rest of the driver controlling that particular piece of hardware, because an ISR is part of the hardware driver. They should not be in main(), they should not be in some unrelated file.

They should not be in some "here are all my ISRs" file unless you are forced to bundle them together because hardware or tool restrictions. You might have to keep the vector table in a file of its own though, unless its location is handled by the linker.

main() shouldn't fiddle around with ISRs. You will have an abstraction layer on top of the drivers and main will call the abstraction layer. This means you will have no tight coupling between the algorithm and the hardware, and also that you can easily replace the driver without having to rewrite the whole program from scratch.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ You are 100% right that it depends on program design. Completely decoupling ISRs from main.c can also have some drawbacks. You can have around 20 ISRs handlers inside your main.c with code amount less than 1k lines, with only flag setters + some ADC end of conversion results handling. This way another engineer can very quickly evaluate overall program architecture from just main file. Additionally, for some MCUs (MSP430 is a very good example) one might utilize same ISR (based on arguments) for different purposes (a good example is multiple time bases on a single MSP430 timer module). \$\endgroup\$ – Andrejs Gasilovs Dec 4 '15 at 10:58
  • 1
    \$\begingroup\$ @GAttuso The overall program architecture should be documented externally, a simple chart or graph is enough in most cases. You shouldn't change your program design in some attempt to dodge the need for documentation. As for general-purpose ISRs you write a driver as you would for any other hardware, responsible for allocation/deallocation of interrupts. General-purpose timers is the school book example of such hardware abstraction: your program could have 10 different timers all using the same ISR but with individual callback functions, all handled by the general-purpose timer driver. \$\endgroup\$ – Lundin Dec 4 '15 at 11:59
  • \$\begingroup\$ Well, I agree with you. Generally it should be like that. I personally find for myself more convenient/compact/readable to leave ISR's in the main for MSP430 family, but for more complex MCUs (C2000, ARMs) I go with your approach. Although, it's probably a very poor practice to change your code architecture depending on the MCU platform :) \$\endgroup\$ – Andrejs Gasilovs Dec 4 '15 at 12:34

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.