c-指向volatile结构成员的Const指针



我正在使用微控制器进行一些ADC测量。当我尝试使用-O2优化编译以下代码时,我遇到了一个问题,当代码中存在PrintVal((函数时,MCU会冻结。我做了一些调试,结果发现,当我添加-fno-inline编译器标志时,即使使用PrintVal((函数,代码也会运行良好。

以下是一些背景:

AdcIsr.c包含ADC完成作业时执行的中断。该文件还包含ISRInit((函数,该函数初始化转换后将保持值的变量。主循环将等待中断,然后才访问AdcMeas.value。

AdcIsr.c
static volatile uin16_t* isrVarPtr = NULL;
ISR()
{
uint8_t tmp = readAdc();
*isrVarPtr = tmp;
}
void ISRInit(volatile uint16_t *var)
{
isrVarPtr = var;
}
AdcMeas.c
typedef struct{
uint8_t id;
volatile uint16_t value;
}AdcMeas_t;
static AdcMeas_t AdcMeas = {0};
const AdcMeas_t* AdcMeasGetStructPtr()
{
return &AdcMeas;
}
main.c
void PrintVal(const AdcMeas_t* data)
{
printf("AdcMeas %d value: %drn", data->id, data->value);
}
void StartMeasurement()
{
...
AdcOn();
...
}
int main()
{
ISRInit(AdcMeasGetStructPtr()->value);
while(1)
{
StartMeasurement();
WaitForISR();
PrintVal(AdcMeasGetStructPtr());
DelayMs(1000);
}
}

问题:

  1. 使用const AdcMeas_t*数据作为PrintVal((函数的参数有问题吗?我知道AdcMeas.value可能在interrupt内部发生更改,PrintVal((可能已经过时。

  2. AdcMeas包含一个"通用getter"。使用这种函数允许对静态结构进行只读访问,这是一种好的做法吗?或者我应该实现AdcMeasGetId((和AdcMeas GetValue函数(注意这个结构只有2个成员,如果它有8个成员怎么办(?

我知道这段代码有点笨(在while循环中等待中断(,这只是一个例子。

一些错误:

  • 您没有头文件,也没有库包含或您自己的头文件。这意味着所有都被彻底破坏,直到你修复为止。如果没有头文件,就不能在C中执行多个文件项目。

  • *isrVarPtr = tmp;在这里,您在没有竞争条件保护的情况下写入变量。如果主程序分几个步骤读取此变量,则可能会得到不正确的数据。您需要针对种族条件进行保护或保证原子访问。

  • const AdcMeasGetStructPtr()是胡言乱语,它里面的return &AdcMeas;不可能用兼容的C编译器编译。

    如果您有一个旧的但兼容的C90编译器,则返回类型将被视为int。否则,如果你有一个现代的C编译器,甚至连函数定义都不会编译。因此,您的编译器似乎出了很大的问题,这比这个bug更令人担忧。

  • 在C文件中声明typedef struct,然后返回指向它的指针是没有任何意义的。您需要重新设计此模块。如果只有一个私有结构的实例(singleton(,那么您可以使用getter函数将一个实例返回到该结构。然而,如前所述,它需要处理比赛条件。

风格问题:

函数声明中的空括号()在C中几乎总是错误的;接受任何参数";。C++在这里不同。
  • CCD_ 8在微控制器系统中根本没有任何意义。您应该使用一些适用于独立程序的实现定义的形式。最常用的支持形式是void main (void)

  • DelayMs(1000);在任何嵌入式系统中都是非常可疑的代码。你永远不应该有理由挂断你的MCU,因为它是无用的,电流消耗最大,整整一秒钟。

  • 总的来说;连续转换";ADC。支持连续转换的ADC只需将其最新读取的数据转储到数据寄存器中,您可以随时通过轮询获取。捕获所有ADC中断实际上只适用于硬实时系统、信号处理等。

    最新更新