简体   繁体   English

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

[英]How to avoid global variables when using interrupt handlers?

I'm mostly self taught in C. I program embedded micro controllers. 我主要是用C语言自学。我编程嵌入式微控制器。 (dsPIC33fj128gp804 for example) I generally use global variable and everything I've ever read denounces using global variables like they are a plague. (例如,dsPIC33fj128gp804)我通常使用全局变量,我读过的所有内容都谴责使用全局变量,就像它们是瘟疫一样。 I've been working on using less but there is a scenario that i don't know how not to use global variables. 我一直在努力减少使用,但有一种情况我不知道如何不使用全局变量。

The micro controller is equipped with interrupts. 微控制器配有中断。 An interrupt is an event triggered externally in hardware. 中断是硬件外部触发的事件。 When the interrupts is triggered the execution of the main code stops, the current working variables are saved, a preassigned function is executed and then the main code picks back up where it left off. 当触发中断时,主代码的执行停止,保存当前工作变量,执行预先分配的功能,然后主代码从中断处继续。 Because the interrupt is a stand alone function that can trigger at any time nothing can be passed into or out of the function. 因为中断是一个独立的功能,可以在任何时候触发任何无法传递进出功能的功能。

For example when the UART hardware receives a byte of data, that data needs moved out of the hardware buffer before it gets over written. 例如,当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
}

Is there a way to do this without global variables or is this an exception? 有没有办法在没有全局变量的情况下执行此操作,或者这是一个例外吗?

You should distinguish between a global variable with external linkage, and a file scope static variable. 您应该区分具有外部链接的全局变量和文件范围静态变量。 You can solve your problem with the latter. 你可以用后者来解决你的问题。

static volatile int shared_variable ;

int getShared(){ return shared_variable ; }

static void isr_handler()
{
    shared_variable++ ;
}

So in the above example, the only access to the shared variable external to the translation unit is via the access function getShared() . 因此,在上面的示例中,对转换单元外部的共享变量的唯一访问是通过访问函数getShared() This method of course relies on using separate compilation, but that is no bad thing for many reasons. 这种方法当然依赖于使用单独的编译,但由于许多原因,这不是坏事。

For other techniques for avoiding globals, and explanations on why you should do so, see Jack Ganssle's A Pox on Globals 对于其他避免全局变量的技巧,以及为什么要这样做的解释,请参阅Jack Ganssle的全球化A Pox

Another thing to consider, and a reason why globals are particularly problematic in this instance is that the shared variable must be either atomic or accessed in a critical section. 要考虑的另一件事,以及全局变量在这种情况下特别成问题的原因是共享变量必须是原子的或在关键部分中访问。 For example, on a 16 bit dsPIC, 32 bit accesses are not atomic, in which case the necessary protection can be placed in the access function, whereas if it were global, every access would have to be individually protected: 例如,在16位dsPIC上,32位访问不是原子的,在这种情况下,必要的保护可以放在访问函数中,而如果它是全局的,则每个访问都必须单独保护:

For example: 例如:

static volatile uint32_t shared_variable ;

int getShared()
{ 
    uint32_t ret ;

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

    return ret ;
}

Use static globals as much as possible so that the variable is only in scope in that particular source file. 尽可能使用静态全局变量,以便变量仅在该特定源文件的范围内。 Use static variables declared in functions that use them for even better isolation. 使用在使用它们的函数中声明的静态变量来实现更好的隔离。

Use volatile for all variables used in both Interrupt routines and main code loop. 对中断例程和主代码循环中使用的所有变量使用volatile。

Note that being volatile does NOT mean that you are "safely" sharing this variable between the ISR and main code. 请注意,volatile是并不意味着您在ISR和主代码之间“安全地”共享此变量。 It is NOT guaranteed to be Atomic access which is when the variable is accessed with a single CPU instruction. 它不能保证是原子访问,即使用单个CPU指令访问变量时。 For instance a 16-bit variable on an 8 bit micro will take multiple read instructions to read the value. 例如,8位微处理器上的16位变量将采用多个读取指令来读取该值。 If the interrupt fires in between you will have corrupted 16-bit data because only half of the variable has been read. 如果中断之间发生中断,则会损坏16位数据,因为只读取了一半的变量。 The first 8bit before the ISR and the other 8bit after the ISR returns. ISR之前的前8位和ISR之后的另外8位。 This is bad data which can cause huge problems if pointers are involved rather than just passing a ADC data value for example. 这是糟糕的数据,如果涉及指针而不是仅仅传递ADC数据值,则会导致巨大的问题。 This can lead to stackoverflow. 这可能导致stackoverflow。

A simple access should quickly disable interrupts, read the value, and renable them to ensure serialized access. 简单访问应该快速禁用中断,读取值并重新设置它们以确保序列化访问。

With small embedded systems using static globals is in my opinion a great way to go as long as you keep it to a minimum and straight to the point! 使用静态全局变量的小型嵌入式系统在我看来是一个很好的方法,只要你把它保持在最低限度并直截了当! Use structures also to further break down globals into less. 使用结构也可以进一步将全局变量分解为更少。

Globals are only "evil" when you have too many and are accessing them back and forth over many files. 当你有太多并且在许多文件中来回访问它们时,全局只是“邪恶的”。 It just gets very messy and you can easily create another variable that has the same name as another existing global. 它变得非常混乱,您可以轻松创建另一个与另一个现有全局同名的变量。 Not good. 不好。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM