views:

167

answers:

4

I have two interrupt service routines (ISR) which basically do the exact same thing but each handles an interrupt from a different device (although the same type of device). Therefore, the logic is the same but they access different CPU registers and memory locations.

As a simple example consider the following code:

extern volatile unsigned int dev1_rx_buffer;
extern volatile unsigned int dev2_rx_buffer;

volatile unsigned char data;

void __attribute__((__interrupt__)) _dev1_interrupt(void)
{
    /* Clear interrupt flag */
    dev1.IF = 0;

    if (dev1.IS_FULL) {
         /* Read data from device */
         data = dev1_rx_buffer;
    } else {
         /* do something else using registers of device 1 */
    }
    /* More stuff using registers of device 1 */
}

void __attribute__((__interrupt__)) _dev2_interrupt(void)
{
    /* Clear interrupt flag */
    dev2.IF = 0;

    if (dev2.IS_FULL) {
         /* Read data from device */
         data = dev2_rx_buffer;
    } else {
         /* do something else using registers of device 2 */
    }
    /* More stuff using registers of device 2 */
}

How can I avoid the code duplication with the restrictions that apply to ISRs (i.e. I cannot pass parameters to the ISRs and function calls should be avoided because of their overhead).

I have thought of writing a template from which to generate the two ISRs using a higher level scripting language, but I'd prefer a solution using only C or C preprocessor macros.

+2  A: 

If they are handling the same type of device it's quite reasonable to have just one interrupt handler handling multiple interrupts. You could check which flag was set at the top and continue on from there. However, I wouldn't recommend this if the two interrupt handlers are for different types of devices and just have the same logic flow.

Trent
+2  A: 

Why don't you use an inline helper function which gets pointers to the device and the buffer?

I would check the generated assembly to make sure the compiler does what I expect, though.

You could also use a macro, but IMHO it is not good to do this for functions this long.

starblue
+6  A: 

In cases like this I usually have the front-end of the ISR (vector entry point) set up a pointer to a device specific block, and then call the common code with a pointer to that block.

Roughly (not worrying about ISR syntax etc.)

void __attribute__((__interrupt__)) Isr1(void)
{
   CommonISR(&dev1info);
}

void __attribute__((__interrupt__)) Isr2(void)
{
   CommonISR(&dev2info);
}

void CommonISR(Foo *devptr)
{
    devptr->count = 0;
    devptr->reset();
    etc...
}

dev1info and dev2info are configured/initialized at startup; they might have pointers to HW registers, etc...

Dan
With the compiler I am using I would have to declare CommonISR as inline explicitly to avoid a function call. Otherwise this is probably what I will do. Thanks.
davitenio
+1  A: 

are you sure that your compiler will not optimize a function calls ?
You definitely can use a macros to generate this code automatically, but it will be a bit ugly :

#define __CONCAT(a,b)    a ## b

#define ISR_DECLARE(name) \
\
void __attribute__((__interrupt__)) _CONCAT(name,_interrupt)(void) \
{ \         
    /* Clear interrupt flag */ \
    name.IF = 0; \
    \
    if (name.IS_FULL) \
    { \
        /* Read data from device */ \
        data = _CONCAT(name, _rx_buffer); \
    } \
    else \
    { \
        /* do something else using registers of device 1 */ \
    }\ 
    /* More stuff using registers of device 1 */ \

}

and then:

ISR_DECLARE(dev_1)

ISR_DECLARE(dev_2)

But i would strongly suggest to check first if your compiler will optimize the code using inline, as suggested in previous posts.

Ilya
Apparently I have to declare functions explicitly as inline and moreover add an "-finline" flag to the compiler to make sure that the compiler optimizes function calls away. thanks
davitenio
as i sad this is preferred options, but you explicitly wanted macro so i gave an example.
Ilya