NOTE: This article is now obsolete, as the API has been incorporated into the newer versions of avr-libc.
Updated 19/11/06 for new ISR_ALIAS attribute and backwards compatibility
Updated 17/05/07 to fix ISR_ALIASOF definition
Freaks,
Most of you who use GCC are familiar with the method in which interrupt routines (ISRs) are defined and used. Before the latest AVRLibC major update, there existed two ways to declare an interrupt service routine:
SIGNAL(VectorName) { // Code goes here }
And:
INTERRUPT(VectorName) { // Code goes here }
While the two methods were easy to differentiate to the seasoned GCC user, these two macro names were a constant source of confusion to those new to the C language - or even just those new to GCC. The difference between the two was subtle, but important.
SIGNAL. This declares an ISR which keeps the global interrupt enable flag inside SREG disabled. It is by far the most commonly needed type of ISR, and sadly the least used by newbies.
INTERRUPT. Exactly the same as SIGNAL, except interrupts are re-enabled at the very start of the ISR code. Most newbies who did not study the AVRLibC manual properly chose this macro because of it's obvious name.
Knowing that this was a great source of confusion - mainly due to the large amount of problems posted in the GCC section of this site - the developers of AVRLibC deprecated both macros and replaced it with a single new one, functionally equivalent to the old SIGNAL macro:
ISR(VectorName) { // Code goes here }
Note: If you are going to use ISR(VectName) macro it may be worth to update to avr-libc version 1.4.4 due to some bugfixes concerning ISR() incompatibility with gcc 3.4.5.
This new name is more descriptive, and helped significantly. But the problem is now the opposite; newbies don't make mistakes, but seasoned GCC users no longer have an easy way to declare an ISR with the "interrupt" attribute. It's possible to create your own:
#define INT_ISR(vector) \ void vector (void) __attribute__((interrupt)); \ void vector (void)
But this solution seems rather hacked-together. It's harder to determine what the difference is between the INT_ISR and plain old ISR. I present my solution to the problem, a header file I made called "ISRMacro.h":
ISRMacro.h
/* Must be included after avr/interrupt.h. This file re-defines the new ISR macro to extend it to allow custom attributes. When the old ISR macros SIGNAL and INTERRUPT were depricated, no suitable replacement was specifed for interruptable ISR routine (and no macro at all exists for naked ISRs). This file avoids the clumsyness of declaring the ISR routines manually with custom attributes and thus gives code uniformity. As a bonus, the default vector (called when an interrupt fires which does not have an associated ISR routine) is aliased here to a more descriptive name - use the new name as you would a standard signal name. The new macro is backwards compatible with the original ISR macro. The avaliable attributes are: 1) ISR_BLOCK - ISR, interrupts disable until ISR completes. 2) ISR_NOBLOCK - ISR, interrupts enabled until ISR completes. 3) ISR_NAKED - ISR, no prologue or epilogue. 4) ISR_ALIASOF(vect) - ISR, alias to another interrupt vector's ISR. GCC 4.2+ only. For GCC 3.x vector aliases, you can use the ISR_ALIAS_COMPAT macro (instead of ISR macro). Works with GCC 3.x as well as GCC 4.x, but compat aliased vector ISR will contain a JMP instruction that the non-compat aliased vector does not have. */ #ifndef ISRMACRO_H #define ISRMACRO_H // If present, kill the current ISR macro: #if defined(ISR) #undef ISR #endif // The default vector is given a more descriptive alias here: #define BADISR_vect __vector_default // Return from interrupt command, defined for convenience in ISR_NAKED routines: #define reti() asm volatile ("RETI"::) // Internal macros: #define __replace_and_string(name) #name // Definition of the attributes here, GCC version specific: #if defined(__GNUC__) && (__GNUC__ > 3) #define ISR_NOBLOCK __attribute__((interrupt, used, externally_visible)) #define ISR_BLOCK __attribute__((signal, used, externally_visible)) #define ISR_NAKED __attribute__((signal, naked, used, externally_visible)) #define ISR_ALIASOF(v) __attribute__((alias(__replace_and_string(v)))) // GCC 4.2 and greater only! #else #define ISR_NOBLOCK __attribute__((interrupt)) #define ISR_BLOCK __attribute__((signal)) #define ISR_NAKED __attribute__((signal, naked)) #endif // GCC 3.x compatible alias macro. Works with GCC 4.1 also: #define ISR_ALIAS_COMPAT(vector, aliasof) \ void vector (void) ISR_NAKED; \ void vector (void) { asm volatile ("jmp " __replace_and_string(aliasof) ::); } // New ISR macro definition: #define ISR(vector, ...) \ void vector (void) ISR_BLOCK __VA_ARGS__; \ void vector (void) #endif
Instead of creating extra macros, this header file alters the existing one. The new format is:
ISR(VectorName, Attribute) { // Code goes here }
This leads to nicer-looking and more uniform ISR macros. If you want to create an ISR which blocks other ISRs from running until it has completed (signal attribute), you can use:
ISR(VectorName, ISR_BLOCK) { // Code goes here }
Or, conversely, an ISR which itself be interrupted can be specified by:
ISR(VectorName, ISR_NOBLOCK) { // Code goes here }
A third attribute (admittedly not commonly used) is ISR_NAKED, which creates an ISR which has no prologue or epilogue code. I've added it for completeness. Because the ISR_NAKED attribute also specifies that the routine is a signal (despite not having any prologue or epilogue code) the GCC extension to spell-check ISR vector names is still functional.
The final attribute (added 19/11/06) is in preparation for GCC4.2. It allows for the aliasing of one vector to another. An example of aliasing the PCINT0_vect vector to the PCINT1_vect vector:
ISR(PCINT0_vect, ISR_ALIASOF(PCINT1_vect));
If you are not using GCC 4.2 or greater, you can instead use the compatibility alias macro, which will work in all GCC versions, at the cost of a JMP instruction for the aliased vector:
ISR_ALIAS_COMPAT(PCINT0_vect, PCINT1_vect);
Finally, when debugging it can be desirable to create an ISR which runs if any vector without an accompanying ISR fires. This is usually done using the "__vector_default" signal name:
ISR(__vector_default) { // Bad interrupt code goes here }
However the name is clumsy and undescriptive at best. The ISRMacro.h gives an alternate and more descriptive name to this vector, that of BADISR_vect (conforms with the latest ISR naming scheme). With the new name, you can specify a bad-ISR catching ISR which blocks other ISRs from running until it completes via:
ISR(BADISR_vect, ISR_BLOCK) { // Bad interrupt code goes here }
The new macro is backwards compatible with the old - if no attributes are specified, the ISR will default to ISR_BLOCK, as is the norm without the new macros.
Just another suggestion and tool from a fellow lowly GCC C programmer :). Discuss and poke fun at at will.
- Dean :twisted: