在我的(个人(嵌入式项目中,全球变量正在堆积。我需要这些变量可以从ISR(中断服务例程(和/或菜单系统(以便用户可以由用户修改(中获得,但与此同时,我想避免使用太多的全球范围。
由于可以将它们分组为模块,因此我认为我可以将它们封装在他们自己的.c文件中,称为static
或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;
}
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;
}
此方案自然会扩展到其他模块。
现在我的问题是:
这是一种很好的方法吗?只有一堆全球群体也一样吗?有任何主要缺点吗?
我还认为我可以使用某种混音,例如仍然具有全局变量来允许ISR直接操作(因为ISR的函数调用有时会皱眉(以及在其他情况下的功能。这会更好吗?
其中有几个问题:
这是一个好方法吗?
是。也许有一个参数将ISR与访问函数和共享数据相同的模块放置在同一模块中,将其视为访问功能本身,允许其直接读取数据而无需访问包装器开销。
它更好/更糟/等于简单地有一堆全球群体吗?
更糟。有了全球,您将在哪里放置访问两个验证,数据验证或断点?它增加了模块耦合,应始终将其最小化。
有任何主要缺点?
不是真的。在ISR中可能至关重要。但是,如果这是一个问题,您将不会在ISR中调用printf()
!您可以减轻我以前有关将ISR放置在数据模块中或内部代码的建议的任何可能的绩效罚款 - 但是您的编译器可以随意忽略它,如果启用调试,则可以这样做,并且也可能会内联行列无论是否启用了优化。一些编译器具有"强制"内联延伸,不允许编译器忽略编译器。但这不是便携式。
一个重要的好处是,如果该变量是非原子的,则需要一些访问保护机制来确保稳定的访问 - 并且通过具有访问功能,最容易,安全地执行。在这种情况下,您可能会获取/设置功能,例如,可以禁用和重新启用访问中中断或使用旋转锁(适用于ISR写入的位置和正常上下文读取的位置 - 而不是相反的方式在这里 - 不要无限期地锁定ISR!(。
我还认为我可以使用某种混音,例如仍然具有全局变量来允许ISR直接操作(因为ISR的函数调用有时会皱眉(以及在其他情况下的功能。这会更好吗?
不是真的 - 请参阅上面关于功能调用开销和ISR放置的观点。ISR中调用函数的问题很少是一个时间范围的问题,而是该函数的实现者可能无法将其设计为从ISR安全有效地称为,并且可能使用过多的堆栈,无确定的执行时间,繁忙的等待或不重新进入或线程安全。例如,呼叫第三方或标准库功能可能会不明智地建议;您专门编写和设计的呼叫功能是为目的而设计的,并符合您的特定约束不一定是问题。
您需要了解为什么全球群体是一个坏主意和避免它们的适当方法,可以在杰克·甘斯尔(Jack Ganssle(的文章"全球诗歌"中找到 - 这也是一个有趣的阅读。