嵌入式 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)
【问题讨论】:
Test
与 test
不同。那么,这里还有更多代码没有显示吗?
并非所有资源都具有同等质量。我特别注意到,在 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 - 易失性限定符在我的中断例程中无关紧要的主要内容,如果未能解决你的问题,请参考以下文章