Location of ISRs

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In a multi file project. Should the ISR routines be in main.c, or in their respective files? I realize this might be a personal preference, but I'm a impressionable c coder.

Thanks,
Andy

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I like to keep initialization code in 1 file and out of the way, the same for ISRs regardless of C or ASM.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I put all the code for a particular module, including the ISR(s), in a file of that name so adc.c, timer.c and so on. I know that if I want to find anything to do with the timers it' bound to be in timer.c

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'd say it depends on the size of the code and they way it is organized. I have a very large (for AVR) application with lots of modules (over 108 files, roughly split evenly between source and headers). It is easier for me to place an ISR in the file associated with the function it is serving. For example, a servo fault ISR will be located in the code that controls that servo.

However, this can lead to problems, especially in two cases: timers and Pin Change interrupts. In my application, several timers are not used for just one purpose, but are shared. So, where do the ISRs for the timer reside? Usually I am able to separate a particular Timer ISR to a single function, and therefore a single module. For example, if function A uses the Timer1 OCRA interrupt and function B uses the OCRB interrupt, the split is easy.

The Pin Change interrupts, though, are a problem. After all, PCI covers 8 pins instead of just one. Here, I make a choice as to who the "primary" PCI use for that interrupt is and place the ISR there. This is not the best solution since I know that anyone else will have troubles figuring out the logic. Again, for example, I have one PCI ISR that covers both an important interrupt for my focus servo as well as end-point detection for the sled motor. This particular resides in the Sled module because, at the time, it seemed the primary function of the ISR. In fact, the ISR now works harder for the focus signal, so perhaps the ISR should reside under Focus, but the choice is made.

And this brings up my final point. In the end, each person will do what makes the most sense at the time. There are "best practices", but these can be (and are!) argued over.

If your application is small, then Cliff's recommendation makes sense. However, larger applications may require different organization.

Stu

Engineering seems to boil down to: Cheap. Fast. Good. Choose two. Sometimes choose only one.

Newbie? Be sure to read the thread Newbie? Start here!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Stu, in the case of multiple recipients as you describe, I tend to create a dispatch ISR, that resides in a file named after the resource it is tied to "PCINT.c" (and associated .h) for example. Then the associated tasks are pulled in through static-inline functions from headers that are maned after their associated function, like "focus.h" (there does not need to be an associated .c for this "friend" function, though I often use a single header, as such it exports stuff from an associated .c as well). [the "static inline" part is important, as then the task functions will reside in the ISR itself instead of being called]

Anyway, that's just my style, which seems similar to yours, just thought I'd share my "workaround" for the problem. I like it because it allows me to abstract the hardware a bit from the software.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

andygoessling wrote:
In a multi file project. Should the ISR routines be in main.c, or in their respective files? I realize this might be a personal preference, but I'm a impressionable c coder.
For the most part, I'd say don't make ISRs a special case.
Locate them the same way you would non-ISRs.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Whatever you do, document it! :)

I've been known to call it out in the code, like:

Thingy_Routine(void); // MasterThingy.c

This way you don't have to guess which file a given routine is in.. Of course you can just highlight the routine name and do a "find in files" too.

In my serial module, I have the init routine, the serial routines, and the UDRIE ISR. That makes sense to me because none of that is shared. My timer routine is shared, it affects serial and many other functions, so it sits at the bottom of the main project file.

Talking to the pros here, they say there are a lot of different styles, and the main thing is to pick one and be consistent.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dbvanhorn wrote:
In my serial module, I have the init routine, the serial routines, and the UDRIE ISR. That makes sense to me because none of that is shared. My timer routine is shared, it affects serial and many other functions, so it sits at the bottom of the main project file.

Talking to the pros here, they say there are a lot of different styles, and the main thing is to pick one and be consistent.

I'd like to plug a pet wrinkle of mine, and say that the initializing routine for such a packaged module should be performed automatically as a consequence of the mere fact of including the module in the application. Without needing to add an explicit call of "initSomething();" to a list of other such explicit calls at the beginning of the app's main() routine.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How would you know how to init it?

Also, order matters, so how could you do this without somewhere specifying it?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

How would you know how to init it?

Well on each function such as UART_sendchar() you could:

void UART_sendchar(char c) {
 if (!Uart_module_initialised) {
  UART_Init();
 }
 // existing code to send 'c'
}

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Quote:

How would you know how to init it?

Well on each function such as UART_sendchar() you could:

void UART_sendchar(char c) {
 if (!Uart_module_initialised) {
  UART_Init();
 }
 // existing code to send 'c'
}

but what should the baud rate be? Certainly a generalized usart library isn't going to be hard-wired to a specific baud and framing?

Unfortunately there is no standard mechanism in C for binding in routines and calling them automagically. Thus you will still need to code in the calls to the various init routines, when you include them into your design. Frankly I prefer it this way, as the programmer, I want to have full control over where and when something is initialized, and in some cases even how at runtime.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
but what should the baud rate be? Certainly a generalized usart library isn't going to be hard-wired to a specific baud and framing?

Unfortunately there is no standard mechanism in C for binding in routines and calling them automagically. Thus you will still need to code in the calls to the various init routines, when you include them into your design. Frankly I prefer it this way, as the programmer, I want to have full control over where and when something is initialized, and in some cases even how at runtime.

With gnu C, there is the constructor attribute that
will automatically call a function before main.
That still leaves the problem of what it should do.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

skeeve wrote:
glitch wrote:
but what should the baud rate be? Certainly a generalized usart library isn't going to be hard-wired to a specific baud and framing?

Unfortunately there is no standard mechanism in C for binding in routines and calling them automagically. Thus you will still need to code in the calls to the various init routines, when you include them into your design. Frankly I prefer it this way, as the programmer, I want to have full control over where and when something is initialized, and in some cases even how at runtime.

With gnu C, there is the constructor attribute that
will automatically call a function before main.
That still leaves the problem of what it should do.
Also with gnu C, and even better than the constructor attribute is this little mechanism:
// initMacro.h - Defines an "Initialize()" macro
//
// The "initialize" macro inserts inline statements into the "init5" section,
// which is part of the sequence of instructions that the startup code will
// execute before calling main().
//
// Typical use:    Initialize(Nectarine) { PORTA |= 1; }

#ifndef Initialize_H
#define Initialize_H

#define Initialize(something) \
void Init_ ## something (void) __attribute((naked)) __attribute__ ((section (".init5"))); \
void Init_ ## something (void)

#endif

The code goes in with zero overhead; not even a call/return. Admittedly, it can't take a parameter, so the case of initializing a UART, complete with baudrate, can't be done this way, other than requiring that the baudrate be set up in advance with a #define. (But what's next? requiring that the clock rate of the CPU be provided in a global #define just so that other timing-related stuff can work? ) Still, I find that *most* subsystems can be set up very nicely this way: configuring fields of port pins as outputs, turning on pullups, enabling individual interrupt sources, and so on. True, there is essentially no control over the execution order among these sorts of initializers, but that also seldom seems to matter, so long as all of them get run before main(). And if it turns out that it's useful to RE-run such an initialization, then of course implement it as a subroutine, and stick a call to it in the initializer sequence (same as the constructor attribute). But I seldom find myself doing that, either.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I tend to put all code related to a UART, including initialization, ISRs, and management routines, in one module. Ditto TWI, SPI, timers (if warranted), etc.

Jim

Jim Wagner Oregon Research Electronics, Consulting Div. Tangent, OR, USA http://www.orelectronics.net

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Levenkay wrote:
Also with gnu C, and even better than the constructor attribute is this little mechanism
It is nice that it is possible. Would I use it? No.

I prefer to keep my code "obvious" instead of "magic".

Initialization code needs to be run. I think we can agree on that. But I want to see an explicit call to the xyz_init() function in my code so I see that initialization is called, and when it is called. And I can easily put a debugger on it. The best of it, a few month later, when I have to maintain the code, I don't have to scratch my head about the magically happening initialization. And I can port the code to other compilers that aren't descendants of gcc.

Stealing Proteus doesn't make you an engineer.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

glitch wrote:
Unfortunately there is no standard mechanism in C for binding in [construction and/or initialisation] routines and calling them automagically.

No there isn't, but I think gcc provides a __attribute__(constructor) or something, or you can manually place code in an init section which gets magically linked without an explicit call. But both of these are non-portable and non-standard, and hence non-obvious.

Getting this sort of stuff automatically in a standardised way is, of course, one of the advantages of a language with object constructors. Sometimes you do have to play tricks to get things constructed in the right order, but with good software architecture a lot of that comes free (eg I have a C_BufferedUART class which has a pair of circular buffers as members. The C++ construction order rules ensure that the buffers get constructed and initialised before the UART hardware itself is initialised, and the opposite sequence happens in the destructor).

It's not always appropriate, but when it is it often works very neatly and efficiently.

Christopher Hicks
==

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ArnoldB wrote:
Levenkay wrote:
Also with gnu C, and even better than the constructor attribute is this little mechanism
It is nice that it is possible. Would I use it? No.
I don't use it either.
I don't have a lot of initialization issues that would be helped by it.
Were I to use it, I would be inclined to put the constructor
function near the thing constructed.
That should deal with the magic issue.
Quote:
I prefer to keep my code "obvious" instead of "magic".

Initialization code needs to be run. I think we can agree on that. But I want to see an explicit call to the xyz_init() function in my code so I see that initialization is called, and when it is called. And I can easily put a debugger on it. The best of it, a few month later, when I have to maintain the code, I don't have to scratch my head about the magically happening initialization. And I can port the code to other compilers that aren't descendants of gcc.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

cmhicks wrote:
glitch wrote:
Unfortunately there is no standard mechanism in C for binding in [construction and/or initialisation] routines and calling them automagically.

No there isn't, but I think gcc provides a __attribute__(constructor) or something, or you can manually place code in an init section which gets magically linked without an explicit call. But both of these are non-portable and non-standard, and hence non-obvious.

Of course compilers like GCC may provide mechanisms for doing this, but my comment was about a standard/portable way. I tend to not endorse extensions, unless absolutely necessary. Frankly I cannot think of a single application where it would be beneficial to have the compiler automatically init a module before main as opposed to manually calling an init routine from within my own code.

Writing code is like having sex.... make one little mistake, and you're supporting it for life.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I agree, clarity is most important.

Nothing prevents you from creating an init fuction as a single call before you enter your main loop, and "hiding" the various init calls in there.

I would definitely NOT want something that made it more difficult for me to step through the inits in sim or emulation.