Domanda

I'm trying to flash an LED on a TI MSP430 Launchpad board. I have two pieces of code. One works, while the other doesn't. The only difference is the inclusion of the volatile keyword in working version. Why is this keyword needed for the program to execute?

This code works...

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
    }
}

While this code does not...

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
    }
}
È stato utile?

Soluzione

Without volatile, the compiler has a lot more liberty in optimizing out code which it determines does nothing, as well as reordering memory access. Your delay loop is being optimized out when not using volatile.

Altri suggerimenti

Neither version is any good, future versions of the compiler may generate vastly different code.

Most MSP430 development tools provide the intrinsic functions __delay_cycles() intended to be used when you want to wait a specific number of cycles.

For example:

#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);
  }
}

Note that the code generated for this will execute at full processor speed. If you need a longer delay in a power-restricted environment, please consider using timers and put the processor in low-power mode.

If you add a NOP in your second version in the loop:

for (i = 20000; i > 0; i--) {
    asm volatile("nop");
}

it should work as well. In both cases the volatile is needed to prevent optimization. In the first version, it prevents the compiler from completely removing the loop. In the second version With asm it tells the compiler to leave it where it is (so it's not moved to another location).

That being sad, both versions are not considered to be good style: Consider using a timer for exact busy delays. The loops will not do what you want, if the core frequency is changed.

In examining the assembly output of IAR compiler (V4.21.9 for MSP430F5438) the infinite loop is always compiled in, with or without the volatile keyword. (Up to Medium optimization setting.) So this may be a compiler dependency. For sure try compiling with optimizations off.

Where the volatile keyword is important is to tell the compiler not to count on a value and hence reread it. For instance, you could be reading an input buffer receiving an external character. The compiler needs to be told to keep reading, since the buffer is updated by something out of its scope of knowledge.

I prefer a solution that works on every compiler, call a function from another module that is not optimized or not optimized with the loop. Asm is a good example. a dummy function that just returns

dummy:
   ret

...

void dummy ( unsigned int );

unsigned int ra;
for(ra=0;ra<10000;ra++) dummy(ra);

The compiler can unroll the loop some if it wants but will have to call dummy with the right arguments in the right order, you can use maximum optimization on the C code without worry.

if you don't declare it volatile, then a lot of the compiler will perform run time optimization, hence you may not pick up the changes

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top