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

EMBEDDED C - Volatile qualifier does not matter in my interrupt routine

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

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

我的问题是我的代码没有卡在下面的 whiletest(); 函数中 当我的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);
 }
}

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

注意:我用的是stm32h743zi (M7)

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 循环中调用了两个函数(Delay 和 printf)。编译器看不到这些函数的作用,因为它们出现在单独的编译器单元中。因此,编译器必须假定它们可能会更改全局变量 test 的值,因此无法优化对 test 中值的检查。删除函数调用,编译器可能会优化测试值的检查。