嵌入式 C - 易失性限定符在我的中断例程中无关紧要

Posted

技术标签:

【中文标题】嵌入式 C - 易失性限定符在我的中断例程中无关紧要【英文标题】:EMBEDDED C - Volatile qualifier does not matter in my interrupt routine 【发布时间】:2021-06-13 14:58:40 【问题描述】:

我是嵌入式 C 的新手,最近看了一些关于 volatile 限定符的视频。他们都提到了同样的事情。 volatile 限定符的使用场景:

    在 ISR(中断服务例程)中读取或写入变量时 RTOS 应用程序或多线程(这不是我的情况) 内存映射 IO(这也不是我的情况)

我的问题是我的代码没有卡在下面的whiletest();function 当我的UART接收到数据然后触发void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)中断函数

int test;

int main(void)

  test = 0;
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch, 1);

  while (1)
   
        Delay(500);     
        printf("the main is runing\r\n");
        whiletest();
   


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)

    if(huart->Instance == USART1)
    
        test = 1;
        HAL_UART_Receive_IT(&huart1, (uint8_t *)&ch, 1);
    


void whiletest(void)

int count =0;
while(!test)
 count++;
 printf("%d\r\n",count);
 Delay(2000);
 

我使用 keil IDE 和 stm32cubeIDE。我了解到,如果您选择 o2 或 o3 优化级别,编译器会优化一些指令。因此,我为构建选项选择了 o2 级别,但它似乎对我的代码没有影响。编译器不会在 while 循环中优化加载指令,并在主函数中缓存测试值0,就像 youtube 上的视频教学一样。这令人困惑。在什么情况下我应该使用 volatile 限定符同时保持我的代码优化(o2 或 o3 级别)。

注意:我使用的是 stm32h743zi (M7)

【问题讨论】:

Testtest 不同。那么,这里还有更多代码没有显示吗? 并非所有资源都具有同等质量。我特别注意到,在 C 和 C++ 中,volatile 没有特定于多线程程序的有效用途,这与您检查的某些资源似乎告诉您的相反。这是一个有点普遍的误解。 至于“编译器不会优化加载指令”,建议您编写 C 语言定义所需语义的 C 代码,而不是依赖于编译器执行或避免特定优化,除非语言说明可以执行哪些类型的优化(这就是volatile 的来源)。如果您需要对二进制文件进行操作码级别的控制,则应使用汇编语言编写。 请不要标记无关的语言。 您赢得了编译器彩票——您的编译器更感兴趣的是有用,而不是在人工基准测试中取得好成绩。遗憾的是,您不应该依赖它,因为它可能会随着更新或构建选项而改变。 【参考方案1】:

volatile 通知编译器对象容易产生副作用。这意味着它可以被不在程序执行路径中的东西改变。

由于您从不直接调用中断例程,编译器假定test 变量永远不会是1。你需要告诉他(volatile 这样做)无论如何它可能会改变。

示例:

volatile int test;

void interruptHandler(void)

    test = 1;


void foo(void)

    while(!test);
    LED_On();

编译器知道test 可以以某种方式更改,并且总是while 循环中读取它

foo:
        push    r4, lr
        ldr     r2, .L10
.L6:
        ldr     r3, [r2]     //compiler reads the value of the test from the memory as it knows that it can change.
        cmp     r3, #0
        beq     .L6
        bl      LED_On
        pop     r4, lr
        bx      lr
.L10:
        .word   .LANCHOR0
test:

没有volatile 编译器将假定test 始终为零。

foo:
        ldr     r3, .L10
        ldr     r3, [r3]
        cmp     r3, #0
        bne     .L6
.L7:
        b       .L7     //dead loop here
.L6: 
        push    r4, lr
        bl      LED_On
        pop     r4, lr
        bx      lr
.L10:
        .word   .LANCHOR0
test:

在您的代码中,如果对象被不在程序路径中的东西更改,您必须使用 volatile。

【讨论】:

我可以添加为这些标志类型变量使用布尔值。 我认为你误解了这个问题,它说while循环确实退出【参考方案2】:

只有在优化后的代码表现得好像优化器什么都不做时,编译器才能优化(更改)代码。

在您的情况下,您在 while 循环中调用了两个函数(延迟和 printf)。编译器无法看到这些函数的作用,因为它们出现在单独的编译器单元中。因此,编译器必须假设它们可能会更改全局变量 test 的值,因此无法优化对 test 中值的检查。删除函数调用,编译器可能会优化测试值的检查。

【讨论】:

声明 static int test; 也会本地化共享变量并允许优化。 我试图找出问题的方式可能是这里的罪魁祸首!感谢您提供有见地的答案。我将查找删除了 delay() 和 printf() 的汇编代码。

以上是关于嵌入式 C - 易失性限定符在我的中断例程中无关紧要的主要内容,如果未能解决你的问题,请参考以下文章

将易失性数组转换为非易失性数组

EVERSPIN非易失性MRAM具吸引力的嵌入式技术

我应该何时以及如何在我的 simd 例程中执行浮点转换?

freeRTOS学习3--中断管理

C++ 易失性成员函数

易失性和缓存行为