过滤中断的最佳方法

Best way to filter interrupts

这里有点基于意见的问题...

我目前正在使用一个 uController(cc1310),它在单个回调中实现所有 32 个引脚的变化中断。

通过为给定的引脚启用中断,将调用所述回调,我们需要检查哪个引脚产生了中断,方法是:

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    if (pinMask & 0x00000001)
    {
        /* pin_0 generated interrupt */
    }

    ...
}

此时pinMask中的每一位都是对应的pin中断标志

直到现在,每个应用程序只需要一个 pin/process 来使用 ioc 事件,所以每次我们需要它时,我们只需注册我们的回调就可以了。

最后一个应用程序将使用更多,所以我正在考虑制作一个 GPIO_interrupt 模块,我可以在其中为给定的 pin 注册一个回调,该模块将通过 pinMask 和调用正确的回调。

所以...

大师们建议哪个选项?


a)

我只能为我使用的中断添加 if's

typedef void (*pin_interrupt_callback_t)( void );

pin_interrupt_callback_t pin0_callback;

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );


    if (pinMask & 0x00000001)
    {
        HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 ) |= 0x00000001;

        /* pin_0 generated interrupt */
        if(NULL != pin0_callback)
            pin0_callback();
    }
    else if (pinMask & 0x00000002)
    {
        ...
    }
    ...
}

b)

这样更动态一些?,我可以在其中添加或删除回调 to/from table (A table 也可以用在 a 中) 不过)

typedef void (*pin_interrupt_callback_t)( void );

pin_interrupt_callback_t pin_callback[32] = {
   pin0_callback,
   pin1_callback,
   ...
}

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    for(uint8_t i = 0; i < 32; i++)
    {
        if(pinMask & (1 << i))
            if(NULL != pin_callback[i])
                pin_callback[i]();
    }
}

c)

这种方式是最复杂的一种,但我只会循环现有的中断,我可以为几个引脚注册相同的回调(也可以在a中这样做)b),但这里如果同时发生多个中断只会调用一次)

我们甚至可以为每个引脚注册多个回调

typedef void (*pin_interrupt_callback_t)( uint32_t mask );

typedef struct pin_callback_element
{
    uint32_t callbackMask;
    pin_callback_element *next;
    pin_interrupt_callback_t callback;
} pin_callback_element_t;

pin_callback_element_t *pin_callbacks = NULL;

/* elem must be declared as `static pin_callback_table_t elem;` */
uint8_t register_callback(
            pin_callback_element_t *elem,
            uint32_t pinMask,
            pin_interrupt_callback_t callback )
{
    /* Make sure no element is registered for a NULL callback */

    elem->next = NULL;
    elem->callbackMask = pinMask;
    elem->callback = callback;

    /* Add element to the pin_callbacks list */
}

/* gpio_interrupt_callback is registered in the interrupt vector */
void gpio_interrupt_callback( void )
{
    uint32_t pinMask = HWREG( GPIO_BASE + GPIO_O_EVFLAGS31_0 );

    pin_callback_element_t *p_cp = pin_callbacks;
    while(NULL != p_cp)
    {
        if(0 != (pinMask & p_cp->callbackMask))
            p_cp->callback(pinMask);

        p_cb = p_cb->next;
    }
}

d)

以上

None


我知道中断回调必须尽可能快和小,但是我们现在制作的模块可以被其他应用程序使用,它们可能会或可能不会自己使用其他引脚中断,所以我认为根据其他应用程序更改通用代码是个坏主意

抱歉这么久 post 感谢您的反馈

我会选择 b),因为您 can/should 在 read-only look-up table 中注册了回调。这个 table 最好设置在 compile-time 并放置在你拥有所有其他 GPIO-related 常量的地方。

这样做的原因是 非常 烦人的是让项目的每个分散部分在内部与 GPIO 混在一起。您需要一个集中列表,您可以在其中概览哪些引脚是输入、输出、浮动、拉动等。这样您就可以轻松 review/verify 软件对照原理图,反之亦然。还要避免程序的不同部分意外使用相同引脚的冲突。

理论上,您可以创建一个动态read/write列表,如c),一个链表,不同的调用者可以在其中注册回调。我经常为 general-purpose 定时器创建这样的定时器,但在与硬件非常接近的 GPIO 的特定情况下,出于已经提到的原因,我建议不要这样做。

所以也许是这样的:

typedef void callback_t (void);

#define CALLBACK_N 32u

static callback_t* const callback [CALLBACK_N] =  // function ptr table in read-only flash
{
  [1] = callback_1,
  [22] = callback_22
};

这意味着我们没有显式初始化的所有索引都将隐式设置为 null。

不幸的是,奇怪的 TI 库 return 是位掩码而不是位号,或者 ISR 中的 for 循环可能已被删除并替换为直接访问 look-up table.

一些未成年人nit-picks:

  • 在 ISR 中可能会更改 if 语句的顺序以稍微加快执行速度(微优化):

    if(pin_callback[i] != NULL) // check vs zero will be single instruction
      if(pinMask & (1u << i))
    
  • 始终u后缀整型常量。永远做 1u <<,永远不要做 1 <<。如果您将数据转移到 1 创建的带符号 int 的符号位中,后者可能会调用未定义的行为。

  • 不需要 1980 年代的“尤达条件”NULL != pin_callback[i] 废话。只需使用 half-decent 编译器,例如 1989 年或之后的 Turbo C - 它们会针对条件中的错误赋值发出警告。通过我上面的修复,函数指针 table 无论如何都是 read-only。