使用 struct 保护 ISR 中的全局变量
Using struct to protect globals inside ISR
我多次听说全局变量如果不小心使用会“危险”。保护全局变量(或指示 reader 与代码的哪一部分相关)的一种方法是使用 struct 并将所有全局变量作为成员放入其中。
术语“全局保护”指的是不能意外修改某些 xxx 源文件中的全局值。使用 struct,您可以向 reader(或您自己)澄清可能使用此全局结构的成员的位置,因此变量功能的不确定性较小。
我试图在我的项目中实现这个,我认为这是一个好主意,因为我计划在未来使用更多的 ISR。为了防止意图不明或错误的代码,使用了全局变量保护的上层方法。但是得出的结论是,如果必须在结构内部访问结构,那么新的变量组织看起来会更糟,因为代码的可读性在很大程度上降低了(我的意见)。
这是我用于按钮去抖动的代码片段。异或两个成员看起来像一条讨厌的长“蛇”,但如果不需要某些变量组织(内部结构),可以写成 change = new ^ old
。
/*** HEADER FILE ***/
/** Button state structure **/
typedef struct {
uint16_t old;
uint16_t new;
uint16_t change;
} state;
/** Protect ISR globals within structure **/
typedef struct {
state buttonState;
uint16_t isPortStable;
uint16_t countStableStates[BTN_COUNT];
uint32_t tick;
} debounceISR;
debounceISR DB; // !! Keep struct name short to affect readability as little as possible !!
/*** SOURCE FILE ***/
/* Read keyboard and check for transitions */
DB.buttonState.new = BTN_PORT;
DB.buttonState.change = DB.buttonState.new ^ DB.buttonState.old;
DB.buttonState.old = DB.buttonState.new;
从长远来看运行我想防止全局变量在用户(编码人员)不知情的情况下进行干扰,并随着一个项目中 ISR(以及全局变量)数量的增加而导致复杂的问题。
是否有任何替代方案可以满足此类需求?在使用 ISR 时,我是否应该倾向于使用指针和静态变量以避免全局变量?
阅读https://www.embedded.com/a-pox-on-globals。
你的起始前提是一个谬论,你还有一个全局,它只是一个结构。怀疑全局变量是“坏的”是不够的,你必须了解问题是什么以及如何解决它们。这不是一个单一的问题,它们不能通过一种单一的技术来解决,在这种情况下,你提出的技术根本不是解决方案。
ISR 共享和状态数据对象应在与 ISR 本身相同的源文件中声明为静态(如果您愿意,在一个结构中,这是有意义的——即变量是相关的)。如果数据对象是共享的,那么同一个源应该包含访问器函数,而且是你在头文件中声明的访问器,而不是数据对象。
然后访问器可以包含代码来处理数据一致性和原子访问,以及施加 read/write 限制和范围检查等。
此外,当您需要调试对任何共享数据对象的访问时,访问器会为您提供一个放置断点的位置以捕获所有访问。
除了上面的 A Pox in Globals 文章,您还可以阅读 https://www.embedded.com/introduction-to-the-volatile-keyword/。您的 ISR 共享数据必须至少声明 volatile
。
我多次听说全局变量如果不小心使用会“危险”。保护全局变量(或指示 reader 与代码的哪一部分相关)的一种方法是使用 struct 并将所有全局变量作为成员放入其中。
术语“全局保护”指的是不能意外修改某些 xxx 源文件中的全局值。使用 struct,您可以向 reader(或您自己)澄清可能使用此全局结构的成员的位置,因此变量功能的不确定性较小。
我试图在我的项目中实现这个,我认为这是一个好主意,因为我计划在未来使用更多的 ISR。为了防止意图不明或错误的代码,使用了全局变量保护的上层方法。但是得出的结论是,如果必须在结构内部访问结构,那么新的变量组织看起来会更糟,因为代码的可读性在很大程度上降低了(我的意见)。
这是我用于按钮去抖动的代码片段。异或两个成员看起来像一条讨厌的长“蛇”,但如果不需要某些变量组织(内部结构),可以写成 change = new ^ old
。
/*** HEADER FILE ***/
/** Button state structure **/
typedef struct {
uint16_t old;
uint16_t new;
uint16_t change;
} state;
/** Protect ISR globals within structure **/
typedef struct {
state buttonState;
uint16_t isPortStable;
uint16_t countStableStates[BTN_COUNT];
uint32_t tick;
} debounceISR;
debounceISR DB; // !! Keep struct name short to affect readability as little as possible !!
/*** SOURCE FILE ***/
/* Read keyboard and check for transitions */
DB.buttonState.new = BTN_PORT;
DB.buttonState.change = DB.buttonState.new ^ DB.buttonState.old;
DB.buttonState.old = DB.buttonState.new;
从长远来看运行我想防止全局变量在用户(编码人员)不知情的情况下进行干扰,并随着一个项目中 ISR(以及全局变量)数量的增加而导致复杂的问题。
是否有任何替代方案可以满足此类需求?在使用 ISR 时,我是否应该倾向于使用指针和静态变量以避免全局变量?
阅读https://www.embedded.com/a-pox-on-globals。
你的起始前提是一个谬论,你还有一个全局,它只是一个结构。怀疑全局变量是“坏的”是不够的,你必须了解问题是什么以及如何解决它们。这不是一个单一的问题,它们不能通过一种单一的技术来解决,在这种情况下,你提出的技术根本不是解决方案。
ISR 共享和状态数据对象应在与 ISR 本身相同的源文件中声明为静态(如果您愿意,在一个结构中,这是有意义的——即变量是相关的)。如果数据对象是共享的,那么同一个源应该包含访问器函数,而且是你在头文件中声明的访问器,而不是数据对象。
然后访问器可以包含代码来处理数据一致性和原子访问,以及施加 read/write 限制和范围检查等。
此外,当您需要调试对任何共享数据对象的访问时,访问器会为您提供一个放置断点的位置以捕获所有访问。
除了上面的 A Pox in Globals 文章,您还可以阅读 https://www.embedded.com/introduction-to-the-volatile-keyword/。您的 ISR 共享数据必须至少声明 volatile
。