这个策略,是为了避免C中的全局变量,对吧?

zak*_*kos 8 c embedded encapsulation global-variables

在我的(个人)嵌入式项目中,全局变量正在堆积.我需要从ISR(中断服务程序)和/或菜单系统(这样它们可以由用户修改)访问这些变量,但同时我想避免使用太多的全局变量.

因为它们可以按模块分组,所以我认为我可以将它们封装在它们自己的.c文件中,声明为staticor static volatile,并向外界展示一些处理它们的函数.

沿线的东西:

在module1.c中

#include module1.h

static volatile int module1_variable;

int getModule1Var(void){
    return module1_variable;
}

void setModule1Var(int i){
    //some code to disable interrupts for atomic operations
    module1_variable = i;
    //some other code to re-enable interrupts
    return;
}
Run Code Online (Sandbox Code Playgroud)

module1.h将包含函数原型,结构和所有其他元素,使模块工作除了当然的静态变量定义

在main.c中

#include module1.h

void main(){
    //setting the variable value, could be done from a menu 
    setModule1Var(15);
    //remaining application code
}

void generic_ISR(){
    //trivial usage example
    doSomething(getModule1Var());
    return;
}
Run Code Online (Sandbox Code Playgroud)

该方案自然会扩展到其他模块.

现在我的问题是:
这是一个好方法吗?简单地拥有一堆全局变量是一样的吗?有什么主要缺点吗?

我还认为我可以使用某种混合,例如仍然具有全局变量以允许ISR直接操作(因为来自ISR的函数调用有时不受欢迎)以及其他所有情况下的函数.这会更好吗?

Cli*_*ord 3

里面有几个问题:

这是一个好方法吗?

是的。也许有一个论点是将 ISR 放置在与其共享的访问函数和数据相同的模块中,将其视为访问函数本身,从而允许它直接读取数据而无需访问包装器开销。


简单地拥有一堆全局变量是更好/更差/等于吗?

更糟糕。对于全局,您将在哪里放置访问互斥锁、数据验证或断点?它增加了模块耦合,并且应该始终将其最小化。


有什么主要缺点吗?

并不真地。在 ISR 中,函数调用开销可能很关键;但如果这是一个问题,你就不会调用printf()ISR!您可以应用我之前关于将 ISR 放入数据模块中的建议,或者通过内联代码来减轻任何可能的性能损失 - 但您的编译器可以自由地忽略这一点,并且如果启用了调试,则可能会这样做,也可能内联无论是否启用优化。有些编译器有一个“强制”内联扩展,编译器不允许忽略它;但它不便于携带。

一个显着的好处是,如果变量是非原子的,则需要某种访问保护机制来确保一致的访问 - 而通过访问函数可以最轻松、最安全地执行此操作。在这种情况下,您可能有 get/set 函数来禁用和重新启用访问中断,或者使用自旋锁(适合 ISR 写入和正常上下文读取的位置 - 而不是像您那样相反)这里有 - 不要无限期地锁定 ISR!)。


我还认为我可以使用某种混合,例如仍然保留全局变量以允许 ISR 直接操作(因为来自 ISR 的函数调用有时会令人不悦)和其他情况下的函数。这样会更好吗?

并非如此 - 请参阅我上面关于函数调用开销以及 ISR 与数据的放置的观点。在 ISR 中调用函数的问题很少是时间开销问题,而是函数的实现者可能没有将其设计为从 ISR 安全有效地调用,并且可能会使用过多的堆栈、不确定的执行时间、忙等待或不可重入或线程安全。例如,调用第三方或标准库函数可能是不明智的;调用您专门为此目的编写和设计的函数并符合您的特定约束不一定是问题。


所有您需要了解的关于为什么全局变量是一个坏主意以及避免它们的适当方法都可以在 Jack Ganssle 的文章“全局变量上的痘痘”中找到- 这也是一本有趣的读物。