Wierd error with while loop

Hello!

I'm doing a project creating an autonomous air hockey robot. I have written a program for the Arduino Mega using the Timers libraries to control 2 stepper motors. I have one weird error that i cannot understand why it behaves as it does. After the motors have been accelerated to the correct speed, a while loop starts which contains an if statement which checks if the deceleration of the motors are supposed to start.

The weird thing is that when monitoring the program through the serial monitor, you can see that boolean used in the if statement is set to true, but the program never enters it even if the term for it is true. If I comment one line(line 311 in the attached ino-file) it starts to work as it's supposed to.

I have tried to find a reason for this but I simply can't explain it. I have included the sketch for the program and the libraries used. To test it you can send the command B0500,0533T through the serial monitor. It only works on the Arduino Mega since it uses Timer5. When sending the command you will get some output along the lines of:

while loop begins
term is set to true

but it never enters the if statement which decelerates the motors.

The while loops is as follows:

while (m1_state || m2_state){
    
    if (info_available) {
      Serial.println("Break");
      return;
    }
    
    if (m_decel) {
      Serial.println("Decelerates");
      decel_stepper();
    }
    
  }

and if I comment out return, it works as it's supposed to, even though it should not affect it, and it isn't that it runs the return statement because it never prints "Break".

Most of the comments are in Swedish but I've change the important serial output to english so it should be understandable, if you have any ideas why it doesn't work I would greatly appreciate it.

libraries.zip (18.5 KB)

Motorprogram_3.ino (9.73 KB)

You never change m1_state or m2_state in the loop. how is it supposed to break?

int serial_period = 1000000 / 30;

What value do you see if you print serial_period?

   Serial.readBytes(target, 11);

This will read up to 11 bytes. It may read less than that. It does tell you how many it read. You ignore that useful information, and

   target[11] = '\0';

assume it read 11 bytes. Not a great idea.

If you change return to break, what happens?

KeithRB:
You never change m1_state or m2_state in the loop. how is it supposed to break?

They change in the interrupt handlers.

However, that brings up a good point. They are not declared volatile, so the code doesn't know that they can be changed by an interrupt handler.

@KeithRB:
It's changed using m1_stop() and m2_stop() which are called through decel_stepper.

@airhockey:
My experience is that if removing a single line of code makes 'it work', this is often caused by a memory issue. You still have plenty of dynamic memory available, so I can't quite explain it.

Looking further

Other point is the use of interrupts; to my knowledge, any variable that is set using an interrupt should be declared 'volatile'.

Hm, took too long to analyze this :wink:

Interesting, if you don't mark the variables as volatile would the compiler be within it's rights to generate code that amounts to:
skip the while if the condition is false.
create an infinite loop if the condition is true and never check the control variables at the end of the loop?

The compiler is allowed to optimize it if it sees that variables in the condition never change. So it can optimize to while(true).

Source: c++ - Why do we use volatile keyword? - Stack Overflow

would the compiler be within it's rights to

Yes, indeed. In fact, it would be remiss in it's job if it didn't.

The compiler is allowed to optimize

In general, it's required to.

That is if the variables are initialized and never modified. What if previous code modifies them based on an input so that the compiler cannot be sure of their state when the loop starts? It needs to use their state to decide whether to run the loop or not.

I just tried changing to use volatile variables and it works great now. Thanks for the help :slight_smile:

Have you tried writing a report instead of using a while loop? I mean, the alternative is great for several reasons:

  1. It is fun.
  2. Then David can relax.
  3. It has to be done.
  4. So that you can be home at saturday.
  5. Reports are awesome.

Thanks.