【问题标题】:How to avoid code duplication between similar ISRs?如何避免类似 ISR 之间的代码重复?
【发布时间】:2009-03-24 15:48:50
【问题描述】:

我有两个中断服务例程 (ISR),它们的功能基本相同 事情,但每个处理来自不同设备的中断(尽管设备类型相同)。因此,逻辑相同,但它们访问的 CPU 寄存器和内存位置不同。

作为一个简单的例子,考虑以下代码:

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 */
}

如何避免代码重复以及适用于 ISR 的限制 (即我不能将参数传递给 ISR,应该避免函数调用 因为他们的开销)。

我曾考虑编写一个模板,以使用更高级别的脚本语言生成两个 ISR,但我更喜欢仅使用 C 或 C 预处理器宏的解决方案。

【问题讨论】:

    标签: c refactoring embedded


    【解决方案1】:

    在这种情况下,我通常让 ISR(向量入口点)的前端设置一个指向设备特定块的指针,然后使用指向该块的指针调用公共代码。

    大致(不用担心 ISR 语法等)

    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 和 dev2info 在启动时配置/初始化;他们可能有指向硬件寄存器等的指针......

    【讨论】:

    • 使用我正在使用的编译器,我必须将 CommonISR 显式声明为内联以避免函数调用。否则这可能是我会做的。谢谢。
    【解决方案2】:

    为什么不使用内联辅助函数来获取指向设备和缓冲区的指针?

    不过,我会检查生成的程序集以确保编译器符合我的预期。

    您也可以使用宏,但恕我直言,对于这么长的函数这样做并不好。

    【讨论】:

      【解决方案3】:

      如果他们正在处理相同类型的设备,那么只有一个中断处理程序处理多个中断是非常合理的。您可以检查在顶部设置了哪个标志并从那里继续。但是,如果两个中断处理程序用于不同类型的设备并且具有相同的逻辑流程,我不建议这样做。

      【讨论】:

        【解决方案4】:

        您确定您的编译器不会优化函数调用吗?
        你当然可以使用宏来自动生成这段代码,但是会有点难看:

        #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 */ \
        

        }

        然后:

        ISR_DECLARE(dev_1)
        
        ISR_DECLARE(dev_2)
        

        但我强烈建议首先检查您的编译器是否会使用内联优化代码,如之前的帖子中所建议的那样。

        【讨论】:

        • 显然我必须将函数显式声明为内联,此外还要向编译器添加一个“-finline”标志,以确保编译器优化函数调用。谢谢
        • 我很难过这是首选选项,但你明确想要宏,所以我举了一个例子。