【问题标题】:Using struct to protect globals inside ISR使用 struct 保护 ISR 中的全局变量
【发布时间】:2021-12-27 00:57:51
【问题描述】:

我多次听说,如果不谨慎使用全局变量可能是“危险的”。保护全局变量(或指示读者与哪些代码段相关)的一种方法是使用 struct 并将所有全局变量作为成员放入其中。

术语“全局保护”是指无法从某个 xxx 源文件中意外修改全局值。使用 struct,您可以向读者(或您自己)阐明这个全局结构的成员可能在哪里使用,因此变量功能的不确定性更少。

我尝试在我的项目中实现这一点,我认为这是一个好主意,因为我计划在未来使用更多的 ISR。为了防止意图不明或代码错误,使用了全局变量保护的上层方法。但得出的结论是,如果必须在 struct 内部访问 struct,那么新的变量组织看起来会更糟,因为代码可读性在很大程度上降低了(我的观点)。

这是我用于按钮去抖动的代码的 sn-p。异或两个成员看起来像一条讨厌的长“蛇”,但如果不需要某些变量组织(内部结构),则可以写为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 时,我是否应该倾向于使用指针和静态变量以避免全局变量?

【问题讨论】:

  • 我从来没有听说过让全局结构的全局成员来缓解与全局相关的问题。我想这样做可以解决大多数与全局变量相关的命名空间问题,但我从未认为这些是主要问题。
  • 在适当的时候使用全局变量并没有错。当然,如果您可以限制它们的范围(通过将它们标记为static)然后这样做,它将防止在其他翻译单元中“干扰”它们。
  • 我不会说全局变量是“危险的”。它们可能令人困惑,尤其是当你有很多它们时,尤其是当它们同时从代码的多个不同部分读取和写入时。没有针对全局变量的规则(在大多数人的风格指南中);只是建议谨慎使用它们,并且仅在需要时使用。
  • @Keno 如何使用指针... 你可以这样做。我已经做到了。这不是一个坏主意,但我也不会说这是一个超级好主意。从“穷人的命名空间”开始,它有点像“穷人的封装类”,尽管没有 getter 和 setter 的好处。它可能会为您买一点,也可能会花费您一点。
  • @Keno 最后,我的cmets都是从“高级”程序员的角度出发的。我的代码从来没有 ISR,所以我不必担心如何与它们通信,或者全局变量是否是这样做的好/坏方法。对于嵌入式编程,许多“规则”略有不同或显着不同。这里有很多嵌入式 C 程序员;您可能(可能?)通过添加 [embedded] 标签更有效地采纳他们的建议。

标签: c struct embedded global-variables isr


【解决方案1】:

阅读https://www.embedded.com/a-pox-on-globals

你的起始前提是一个谬误,你仍然有一个全局的,它恰好是一个结构。怀疑全局变量是“坏的”是不够的,你必须了解问题是什么以及如何解决它们。这不是一个单一的问题,它们不能用一种技术来解决,在这种情况下,你提出的技术根本不是解决方案。

ISR 共享和状态数据对象应在与 ISR 本身相同的源文件中声明为静态(如果您愿意,可以在一个结构中,这很有意义 - 即变量是相关的)。如果数据对象是共享的,那么同一个源应该包含访问器函数,并且是您在标头中声明的访问器,而不是数据对象。

然后访问器可以包含代码来处理数据一致性和原子访问,以及施加读/写限制和范围检查等。

此外,当您需要调试对任何共享数据对象的访问时,访问器会为您提供一个放置断点以捕获任何和所有访问的单一位置。

除了上面的 Globals 中的 A Pox 文章,您还可以阅读 https://www.embedded.com/introduction-to-the-volatile-keyword/。您的 ISR 共享数据必须至少声明为 volatile

【讨论】:

    猜你喜欢
    • 2017-06-11
    • 2013-02-07
    • 2017-08-10
    • 1970-01-01
    • 2016-07-14
    • 2014-01-25
    • 2015-03-19
    • 1970-01-01
    • 2013-12-10
    相关资源
    最近更新 更多