【问题标题】:volatile variables or volatile structure problems in CC 中的 volatile 变量或 volatile 结构问题
【发布时间】:2023-05-24 21:53:01
【问题描述】:

这是我的头文件(Header.h)

#include <stdio.h>
#include <string.h>
void Process(void);

和“Header.C”

#include <stdio.h>
#include <string.h>
#include "Header.h"

struct St{
    unsigned long int volatile Var1;
    unsigned long int volatile Var2;
    unsigned char volatile Flag;
};

extern struct ST Variable;

void Process(void){
Variable.Var1=Variable.Var2;
}

和主文件:

#include <stdio.h>
#include <string.h>
#include "Header.h"

struct St{
    unsigned long int volatile Var1;
    unsigned long int volatile Var2;
    unsigned char volatile Flag;
};

struct ST Variable;

//Interrupt each 10us

void TIM_IRQHandler(){

//Do something

    if(something==True)
    {
    Variable.Flag=1;
    Variable.Var2=AVariable; //AVariable is between 30-40
    }

}

int main(){

    while(1)
    {
    //wait 10ms
    if(Variable.Flag==1)
        {
        Process();
        Variable.Flag=0;
        }

    }

}

正如您所见,每 10us 发生一次中断,如果它正确执行某些代码,它会将 Var2 更改为 30-40 之间的值并设置一个标志变量。在主代码中,如果已设置标志,则应调用处理函数并将 Var1 更改为 Var2 值。 但是有时我会收到带有其他不正确值的奇怪值的 var1。我测试了我的中断,发现我从来没有用奇怪的值填充我的 Var2。

void TIM_IRQHandler(){

//Do something


    if(something==True)
    {
    Variable.Flag=1;
    Variable.Var2= < 30-40>;
    }
   if(Variable.Var2>40 || Variable.Var2<30){
      printf("ERROR");
     } 

}

所有中断功能都可以正常工作,但在处理功能中它让我很生气。

我会感谢我没有注意的任何技巧。

【问题讨论】:

  • 这是什么意思? Variable.Var2= &lt; 30-40&gt;; 它应该如何编译?
  • 这不是我的确切代码,但这意味着它的值可以在 30 到 40 之间
  • @Ehsan Zakeri:你有一个潜在的竞争,第二个中断触发和Var2,而之前的结果仍在处理中。如果读取不是原子的,例如在典型的 8 位 MCU 上,那么您最终可能会得到一些来自先前值的字节和一些来自新值的字节。最简单的解决方法是在Process 之前重置Variable.Flag 并测试以查看它是否在之后再次升起,在这种情况下您旋转并重试。相反,标志应该是sig_atomic_t 类型,这在实践中不太可能成为问题。还有其他需要注意的问题。
  • @doynax 感谢您的解释和贡献,我有同样的想法,它想要处理变量,但在其他过程中它想要填充它。我正在使用 Stm32f MCU(32 位)不幸的是,这个技巧不起作用。它快把我逼疯了。
  • @Ehsan Zakeri:很公平,极短的中断间隔往往会打击任何可能潜伏的微妙比赛。不幸的是,说明性代码中有太多空白,无法有效地分析它。我建议您尝试缩小一个最小但完整的可重现示例。

标签: c volatile


【解决方案1】:

在任何类型的 typedef 中使用关键字“volatile”时都不要期望任何东西。您正在声明类型“struct St”,包括关键字。您的描述暗示您期望变量“Variable”具有易变行为,该变量是在没有关键字的情况下定义和声明的。 以我的经验,关键字仅有时(取决于平台和编译器)在类型内部产生影响。如果在变量的定义和声明中都使用它,它似乎会产生可靠的效果。

尝试改变

struct ST Variable;

volatile struct ST Variable;

extern struct ST Variable;

extern volatile struct ST Variable;

另外,“St”!=“ST”周围是否有错字,在其他地方声明了“struct ST”类型?
另外,作为旁注,您可能希望将类型声明移动到标题中。

我目前无法访问 C 环境,所以请原谅我没有测试我的答案。

【讨论】:

  • 这个答案是指volatile的有效性,考虑到它是发布问题的相关部分;因为标题。正如其他人所评论的那样,彻底保护赛车条件对于使其坚固也非常重要。
  • 你还记得失败的编译器吗?我从来没有对特定于成员的volatile(或const)限定符或嵌入到typedef中的限定符有任何问题,并且我可以说它们的使用受到标准的支持,尽管我可能是错的,我当然不不要争论那里有损坏的编译器。
  • 很抱歉不能为我的陈述提供一些基础。我使用过几种编译器,其中大多数是用于嵌入式设备的交叉编译器。关于嵌入式设备上的“const”:
  • On "const" on embedded:它经常将常量对象链接到非易失性内存中,将非常量对象链接到易失性内存中。这使得在 typedef 中声明具有不同“const/non-const”属性的结构成员(在内存中非常接近,如果不是连续的)对于链接器来说很难实现或效率低下。我不能将此作为无效 volatile 关键字的论据,但如果不太可能的话,它使之成为可能。
  • 我当然同意在结构中使用const 存储ROM 是徒劳的。 volatile/restrict 限定符仅描述访问语义,而不是为数据存储提供双重职责,因此我不确定该论点是否通用(我不知道有任何编译器将 volatile 改组到特殊内存)。顺便说一句,我过去使用const 结构字段的原因是动态数据和只读描述符,并且出于性能原因(以避免追逐指针)混合在 RAM 中,同时记录不修改字段的意图。