Empty loop not compiling even with volatile variable iteration

Hello,

For a class project I need to simulate a delay using empty loops.

I've declared a volatile variable "int i" in a global scope.

My delay function looks like this

void myDelay(unsigned long delayVal) { for (i = delayVal; i > 0; i--) {

} }

In my main loop() I'm running commands like:

tft.print("A"); myDelay(10000000); tft.print("B"); myDelay(10000000);

...

No matter how large or how small I set the parameter, the letters print at the same rapid speed on the TFT display.

I have tried making myDelay an int function and returning i at the end of the loop. I have tried using a while loop instead of a for loop. Most puzzling to me, I've tried including a line in the loop that prints to the serial monitor:

for(i = delayVal; i > 0; i--) { Serial.println("test"); }

and nothing prints to the serial monitor. HOWEVER, if I call Serial.println("test") in myDelay() immediately before the loop, then the serial monitor does print.

The volatile keyword says that an interrupt could change the value, outside of the function that uses the value. You have no interrupts, and only one function uses the value,so the compiler probably dumps the whole useless loop.

What are you trying to accomplish with a delay function over which you have NO control over how long it takes?

You approach is never going to work (well). You must use "millis()" for timing your delay period.

@PaulS: It's home work; Make your own delay function..

If you put 10000000 into a 16-bit int you get -27008.
Make your counter unsigned long.

Because the whole function appears to be ‘optimised’ away,
You may get a little further with a simple assignment within the function {}

e.g. unsigned long x = delayVal;

The point being that if something different is done within each call of the function, the compiler won’t make it disappear.

Using millis(( would be the proper, long term solution in most applications.

The volatile trick works. The technique I've previously used to prevent the compiler from optimizing away an empty loop without any overhead is to add an empty inline assembly statement:

const byte LEDpin = LED_BUILTIN;

void setup()
{
  pinMode(LEDpin, OUTPUT);
}

void loop()
{
  digitalWrite(LEDpin, HIGH);
  myDelay(1000000);
  digitalWrite(LEDpin, LOW);
  myDelay(1000000);
}

void myDelay(unsigned long delayVal)
{
  for (unsigned long i = delayVal; i > 0; i--)
  {
    asm("");  // prevent the compiler from optimizing away the empty for loop
  }
}

It's interesting to compare the compiled sizes between the two techniques. The above code compiled for Uno with Arduino IDE 1.8.5/Arduino AVR Boards 1.6.21:

Sketch uses 766 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.

Compare to the volatile version:

const byte LEDpin = LED_BUILTIN;

void setup()
{
  pinMode(LEDpin, OUTPUT);
}

void loop()
{
  digitalWrite(LEDpin, HIGH);
  myDelay(1000000);
  digitalWrite(LEDpin, LOW);
  myDelay(1000000);
}

void myDelay(unsigned long delayVal)
{
  for (volatile unsigned long i = delayVal; i > 0; i--)
  {
  }
}
Sketch uses 866 bytes (2%) of program storage space. Maximum is 32256 bytes.
Global variables use 9 bytes (0%) of dynamic memory, leaving 2039 bytes for local variables. Maximum is 2048 bytes.

Note the former makes the for loop run significantly faster.

I expected you could also do this by setting the optimize attribute of the function but:

void myDelay(unsigned long delayVal) __attribute__((__optimize__("O0")));

const byte LEDpin = LED_BUILTIN;

void setup()
{
  pinMode(LEDpin, OUTPUT);
}

void loop()
{
  digitalWrite(LEDpin, HIGH);
  myDelay(1000000);
  digitalWrite(LEDpin, LOW);
  myDelay(1000000);
}

void myDelay(unsigned long delayVal)
{
  for (unsigned long i = delayVal; i > 0; i--)
  {
  }
}

Didn't result in any discernible blinking.