使用中断处理程序时如何避免全局变量?

时间:2014-11-29 16:11:58

标签: c embedded global-variables microchip

我主要是用C语言自学。我编程嵌入式微控制器。 (例如dsPIC33fj128gp804) 我通常使用全局变量,我读过的所有内容都谴责使用全局变量,就像它们是瘟疫一样。我一直在努力减少使用,但有一种情况我不知道如何不使用全局变量。

微控制器配有中断。中断是硬件外部触发的事件。当触发中断时,主代码的执行停止,保存当前工作变量,执行预先分配的功能,然后主代码从中断处继续。因为中断是一个独立的功能,可以在任何时候触发任何无法传递进出功能的功能。

例如,当UART硬件接收到一个字节的数据时,该数据需要在被写入之前移出硬件缓冲区。

void __attribute__((interrupt, no_auto_psv)) _U2RXInterrupt(void)
{
    GlobalVariable = U2RXREG; // Move data to global variable
    IFS4bits.U2RXIF = 0; // Clear the UART2 Receive Interrupt Flag
}

有没有办法在没有全局变量的情况下执行此操作,或者这是一个例外吗?

2 个答案:

答案 0 :(得分:4)

您应该区分具有外部链接的全局变量和文件范围静态变量。你可以用后者来解决你的问题。

static volatile int shared_variable ;

int getShared(){ return shared_variable ; }

static void isr_handler()
{
    shared_variable++ ;
}

因此,在上面的示例中,对翻译单元外部的共享变量的唯一访问是通过访问函数getShared()。这种方法当然依赖于使用单独的编译,但由于许多原因,这不是坏事。

关于避免全局变量的其他技巧,以及为什么要这样做的解释,请参阅Jack Ganssle' A Pox on Globals

要考虑的另一件事,以及全局变量在这种情况下特别成问题的原因是共享变量必须是原子的或在关键部分中访问。例如,在16位dsPIC上,32位访问不是原子的,在这种情况下,可以在访问函数中放置必要的保护,而如果它是全局的,则每个访问都必须单独保护:

例如:

static volatile uint32_t shared_variable ;

int getShared()
{ 
    uint32_t ret ;

    _disable_interrupts() ;
    ret = shared_variable ;
    _enable_interrupts() ;

    return ret ;
}

答案 1 :(得分:3)

尽可能使用静态全局变量,以便变量仅在该特定源文件的范围内。使用在使用它们的函数中声明的静态变量来实现更好的隔离。

对中断例程和主代码循环中使用的所有变量使用volatile。

请注意,volatile是并不意味着您在ISR和主代码之间“安全地”共享此变量。它不能保证是原子访问,即使用单个CPU指令访问变量时。例如,8位微处理器上的16位变量将采用多个读取指令来读取该值。如果中断之间发生中断,则会损坏16位数据,因为只读取了一半的变量。 ISR之前的前8位和ISR之后的另外8位。这是糟糕的数据,如果涉及指针而不是仅仅传递ADC数据值,则会导致巨大的问题。这可能导致stackoverflow。

简单访问应该快速禁用中断,读取值并重新设置它们以确保序列化访问。

使用静态全局变量的小型嵌入式系统在我看来是一个很好的方法,只要你把它保持在最低限度并直截了当!使用结构也可以进一步将全局变量分解为更少。

当你有太多并且在许多文件上来回访问它们时,全局只是“邪恶的”。它变得非常混乱,您可以轻松创建另一个与另一个现有全局同名的变量。不好。