【问题标题】:How to avoid global variables when using interrupt handlers?使用中断处理程序时如何避免全局变量?
【发布时间】:2014-11-29 16:11:58
【问题描述】:

我主要是自学 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
}

有没有办法在没有全局变量的情况下做到这一点,或者这是一个例外?

【问题讨论】:

  • 对于不同的线程和ISR(中断服务例程),您确实必须使用全局数据。但您有时也需要确保在访问这些数据时互斥。
  • 全球不是瘟疫,很多人说实际上不知道为什么或为什么不知道,除非有人告诉他们。根据经验而不是猜测从那里慢慢改变你所知道的......
  • 如果您采纳@dwelch 的建议“慢慢改变”,您可能会发现您会“慢慢”减少错误数量!虽然我们可能同意很多,但我不确定我是否同意。然而,确实许多人盲目地遵循“没有全局变量”的口头禅,而没有费心去理解为什么或更重要的是也许是适当的替代方案。理解为什么以及如何是不被诱惑假设你有一个可以或必要的特殊情况的关键。然而在瘟疫上:A Pox on Globals.

标签: c embedded global-variables microchip


【解决方案1】:

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

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 ;
}

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

    【讨论】:

    • 我认为没有理由在纯 C 嵌入式程序中避免使用全局变量,尤其是在中断处理程序中,除非您有充分的理由(如 Clifford 很好地说明)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-04
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    相关资源
    最近更新 更多