Hello everyone,
I've been using Arduinos for quite a while now, but now stumled upon a very strange behaviour.
I'm using Arduino Uno or Nano (choose one...), to originally implement a control for a robot chassis with 2 drives (this explains the naming of "cnt_l" and "cnt_r" as being the left and right motor, originally.
Now, for demonstration, I replaced the external interrupts by just a timer interrupt every 2 msec's which should sufficiently represent the original interrupt frequency.
Now the interesting part is obvusly the "pulses" variable. This will be originally have values around ~1030 in the original application. By choice, I've set it to 1028 here.
The rest of the sketch should be quite easy to read; there are many Serial.println's for analysis, an ISR to just increment cnt_l and cnt_r, and some evaluation of cnt_l and cnt_r against "pulses".
Now this is the point where the "magic" happens: with "pulses" being 1028, you should see the board running into a wrong path after 4 loops (or so... can possibly vary?), as the "greater or equal" compare of "cnt_l" (being 1024, interestingly!) against "pulses" (being "1028" or whatever you enetered) gives a wrong "True" !!
Here is the code:
#include <MsTimer2.h>
// these are the questionable variables:
volatile unsigned int cnt_l=0, cnt_r=0, pulses=1028;
//volatile unsigned long cnt_l=0, cnt_r=0;
// these are for debugging only:
bool once[3] = {false, false, false};
unsigned int rot_cnt=0;
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
pinMode(0,INPUT_PULLUP);
pinMode(12,OUTPUT);
pinMode(13,OUTPUT);
// for testing - also happens the same with external interrupts:
MsTimer2::set(2,countOnTimer);
}
void loop() {
static int loopcnt=0;
static boolean started=false;
Serial.print("** Loop ");
Serial.print(++loopcnt);
Serial.println(" : **");
Serial.print("pulses=");
Serial.println(pulses);
MsTimer2::start();
started = true;
while ( started ) {
// this is where fault may happen -- variables cnt_l and cnt_r are incremented by interrupts: ...
if ( cnt_l >= pulses ) {
// it jumps in here at cnt_l=1024 instead of cnt_l >=1083 !!!
// sometimes also jumps in here at cnt_l=256 !!!
if (! (once[0]) ) {
Serial.print("LEFT Stop - cnt_l: ");
Serial.println(cnt_l);
Serial.print("- Req'd Pulses: ");
Serial.println(pulses);
Serial.println();
once[0]=true;
}
if ( (cnt_l == 1024) || (cnt_l == 256) ) {
Serial.println("FAIL!");
Serial.println(cnt_l);
while (true) {
delay(1);
}
}
}
// or also here fault may happen: ...
if (cnt_r >= pulses) {
// it jumps in here at cnt_r=1024 instead of cnt_r >=1083 !!!
// sometimes also jumps in here at cnt_r=256 !!!
if (! (once[1]) ) {
Serial.print("RIGHT Stop - cnt_r: ");
Serial.println(cnt_r);
Serial.print("- Req'd Pulses: ");
Serial.println(pulses);
Serial.println();
once[1]=true;
}
if ( (cnt_r == 1024) || (cnt_r ==256) ) {
Serial.println("FAIL!");
Serial.println(cnt_r);
while (true) {
delay(1);
}
}
}
if ( (cnt_l >= pulses) && (cnt_r >= pulses) ) {
// possibly also here the fault may happen, but should be covered by the upper 2 appearances already: ...
if (!(once[2]) ){
Serial.println("LEFT+RIGHT Stop ");
Serial.print("- cnt_r: ");
Serial.println(cnt_r);
Serial.print("- cnt_l: ");
Serial.println(cnt_l);
Serial.print("- Req'd Pulses: ");
Serial.println(pulses);
Serial.println();
}
digitalWrite(13,0);
digitalWrite(12,0);
if ( (cnt_r == 1024) || (cnt_r ==256) ) {
Serial.println("FAIL!");
Serial.println(cnt_r);
while (true) {
delay(1);
}
}
if ( (cnt_l == 1024) || (cnt_l == 256) ) {
Serial.println("FAIL!");
Serial.println(cnt_l);
while (true) {
delay(1);
}
}
Serial.print(++rot_cnt);
Serial.println(" - Rotation STOP!");
Serial.print("cnt_l=");
Serial.println(cnt_l);
Serial.print("cnt_r=");
Serial.println(cnt_r);
Serial.print("pulses=");
Serial.println(pulses);
// let motors run out:
delay(300);
Serial.println("Go on ...");
MsTimer2::stop();
cnt_l=0;
cnt_r=0;
started=false;
once[0]=false;
once[1]=false;
once[2]=false;
}
}
Serial.println("END OF TURN");
digitalWrite(12,0);
digitalWrite(13,0);
delay(500);
}
void countOnTimer() {
cnt_l++;
cnt_r++;
}
Here is the serial output I see:
** Loop 1 : **
pulses=1028
LEFT Stop - cnt_l: 1028
- Req'd Pulses: 1028
RIGHT Stop - cnt_r: 1028
- Req'd Pulses: 1028
LEFT+RIGHT Stop
- cnt_r: 1030
- cnt_l: 1031
- Req'd Pulses: 1028
1 - Rotation STOP!
cnt_l=1033
cnt_r=1034
pulses=1028
Go on ...
END OF TURN
** Loop 2 : **
pulses=1028
LEFT Stop - cnt_l: 1028
- Req'd Pulses: 1028
RIGHT Stop - cnt_r: 1028
- Req'd Pulses: 1028
LEFT+RIGHT Stop
- cnt_r: 1030
- cnt_l: 1031
- Req'd Pulses: 1028
2 - Rotation STOP!
cnt_l=1033
cnt_r=1034
pulses=1028
Go on ...
END OF TURN
** Loop 3 : **
pulses=1028
RIGHT Stop - cnt_r: 1028
- Req'd Pulses: 1028
LEFT+RIGHT Stop
- cnt_r: 1028
- cnt_l: 1029
- Req'd Pulses: 1028
3 - Rotation STOP!
cnt_l=1031
cnt_r=1032
pulses=1028
Go on ...
END OF TURN
** Loop 4 : **
pulses=1028
LEFT Stop - cnt_l: 1024
- Req'd Pulses: 1028
FAIL!
1024
- You can see, it runs for 3 turns correctly, and fails in round 4.
- Only changing "pulses" to "1029" instaed, will lead to abortion after 16 rounds.
Partial output here:
...
15 - Rotation STOP!
cnt_l=1034
cnt_r=1035
pulses=1029
Go on ...
END OF TURN
** Loop 16 : **
pulses=1029
LEFT Stop - cnt_l: 1024
- Req'd Pulses: 1029
FAIL!
1024
Please feel free to try other values of "pulses".
This behaviour is a total mystery to me - the common thing is, it always sees the respective counter at 1024!!! In the real application, this leads to the motor stopping prematurely.
I don't think the program has a logical error, since it runs for a varying number of turns.
I can also exclude it is HW (power supply) related, since it happens as well on an Arduino Uno, as on a bare-naked Arduino Nano.
More, I tried to put "pulses" definition in different places (local to "loop", or global), and also set the variables to different types, as well as making them "volatile" or not: No general change, it just changes the number of cycles after which the behaviour appears.
- In the real application, I also observed drop-outs at the counter value "256" . Thus the related "if" decision. In this test, I haven't seen faults with "256" counts occurring however.
I think it must be related to Atmega 328's internals of Interrupt handling, or maybe to something in the Ardino (1.8.x) IDE .
To me, it looks a bit as if the "Equal" bit in the condition register would toggle when too many bits somewhere else are toggling at once (sorry for the possibly inaccurate wording)...
Any help would be greatly appreciated - this haunts me for days and weeks now...
Kind regards
Ansgar