我正在尝试在TI MSP430 Launchpad板上闪烁LED。我有两段代码。一个工作,而另一个不工作。唯一的区别是在工作版本中包含volatile关键字。为什么程序执行需要此关键字?
此代码有效......
void main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure Port Directions
P1DIR |= 0x01; // 0000 0001
volatile unsigned int i;
for(;;)
{
P1OUT ^= 0x01; // Set P1.0 LED on
for (i = 20000; i > 0; i--); // Delay
}
}
虽然这段代码没有......
void main(void) {
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure Port Directions
P1DIR |= 0x01; // 0000 0001
unsigned int i;
for(;;)
{
P1OUT ^= 0x01; // Set P1.0 LED on
for (i = 20000; i > 0; i--); // Delay
}
}
答案 0 :(得分:4)
如果没有volatile
,编译器可以更好地优化代码,它确定什么都不做,以及重新排序内存访问。不使用volatile
时,您的延迟循环正在优化。
答案 1 :(得分:3)
这两个版本都没有任何好处,未来版本的编译器可能会生成截然不同的代码。
大多数MSP430开发工具提供了当您想要等待特定周期数时要使用的内部函数__delay_cycles()
。
例如:
#include <intrinsics.h>
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure Port Directions
P1DIR |= 0x01; // 0000 0001
for(;;)
{
P1OUT ^= 0x01; // Set P1.0 LED on
__delay_cycles(40000);
}
}
请注意,为此生成的代码将以完整的处理器速度执行。如果在功率受限的环境中需要更长的延迟,请考虑使用定时器并将处理器置于低功耗模式。
答案 2 :(得分:1)
如果您在循环中的第二个版本中添加NOP
:
for (i = 20000; i > 0; i--) {
asm volatile("nop");
}
它应该也可以。在这两种情况下,都需要volatile
来阻止优化。在第一个版本中,它阻止编译器完全删除循环。在使用asm
的第二个版本中,它告诉编译器将其保留在原来的位置(因此它不会移动到另一个位置)。
令人难过的是,这两个版本都不被认为是好的风格:考虑使用计时器来确保繁忙的延迟。如果更改核心频率,循环将无法执行您想要的操作。
答案 3 :(得分:1)
在检查IAR编译器的汇编输出(MSP430F5438的V4.21.9)时,总是编译无限循环,有或没有volatile关键字。 (达到中等优化设置。)因此,这可能是编译器依赖性。当然,请尝试关闭优化进行编译。
volatile关键字很重要的地方是告诉编译器不要指望一个值,因此重新读取它。例如,您可能正在读取接收外部字符的输入缓冲区。需要告诉编译器继续读取,因为缓冲区是由其知识范围之外的东西更新的。
答案 4 :(得分:0)
我更喜欢适用于每个编译器的解决方案,从另一个未优化或未通过循环优化的模块调用函数。 Asm就是一个很好的例子。一个只返回
的虚函数dummy:
ret
...
void dummy ( unsigned int );
unsigned int ra;
for(ra=0;ra<10000;ra++) dummy(ra);
编译器可以根据需要展开一些循环,但是必须以正确的顺序使用正确的参数调用dummy,您可以毫无顾虑地对C代码使用最大优化。
答案 5 :(得分:0)
如果你没有声明它是volatile,那么很多编译器都会执行运行时优化,因此你可能无法获取更改