Hello everybody,
I'm doing this on Arduino UNO. I've written a small program that takes input from a quadrature rotary hall effect encoder via pins 2 and 3. It uses two interrupt routines that are invoked on input change to keep track of the state of the encoder and the rotary position by means of the three volatile int variables posi ( shaft position ), ai (state of encoder output A) and bi (state of encoder output B). Using ATOMIC_BLOCK, these three variables are copied to the three loop()-variables pos, a and b, which in turn are written to the serial port. Here is the complete code:
#include <util/atomic.h> // For the ATOMIC_BLOCK macro
#define ENCA 2 // Encoder green wire
#define ENCB 3 // Encoder yellow wire
volatile int posi = 0; // specify posi as volatile. Represents shaft position
volatile int ai, bi; // encoder state, also volatile
int a, b; // used to display encoder state
int pos = 0; // used to display shaft position
void setup() {
Serial.begin(115200);
pinMode(ENCA,INPUT);
//pinMode(ENCA,INPUT_PULLUP); //it does not seem to matter if I use INPUT_PULLUP
pinMode(ENCB,INPUT);
//pinMode(ENCB,INPUT_PULLUP);
ai = digitalRead(ENCA);
bi = digitalRead(ENCB);
attachInterrupt(digitalPinToInterrupt(ENCA),readEncoder1,CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCB),readEncoder2,CHANGE);
}
void loop() {
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
pos = posi;
a = ai;
b = bi;
}
Serial.print(pos);
Serial.print(" ");
Serial.print(a);
Serial.print(" ");
Serial.println(b);
}
void readEncoder1(){
int ai = digitalRead(ENCA);
int bi = digitalRead(ENCB);
if (ai != bi){
posi++;
} else {
posi--;
}
}
void readEncoder2(){
int ai = digitalRead(ENCA);
int bi = digitalRead(ENCB);
if (ai == bi){
posi++;
} else {
posi--;
}
}
When turning the shaft, the output is expected to show how the position variable goes up or down (depending on the turning direction), and how the state variables a and b cycle through the different states according to the hamming code sequence. So, assuming that I am starting with state a = 0, b = 0 and pos = 0, and that I am turning the shaft in the positive direction, I should see the following: 000, 101, 211, 310, 400...
But this is not what happens. Instead I am seeing the following: 000, 100, 200, 300, 400...
I have observed that the pos variable consistently shows exactly the expected value after turning the shaft for one full rotation. It is consistently and precisely the right number every time, in each direction. So the hardware side of my setup seems to be ok.
Next I tried the following: instead of printing out the loop-variables a and b, I directly used ai and bi. Again, the output was like 000, 100, 200...
Next I tried the following code:
#include <util/atomic.h> // For the ATOMIC_BLOCK macro
#define ENCA 2 // Encoder green wire
#define ENCB 3 // Encoder yellow wire
volatile int posi = 0; // specify posi as volatile. Represents shaft position
volatile int ai, bi; // encoder state, also volatile
void setup() {
Serial.begin(115200);
pinMode(ENCA,INPUT);
//pinMode(ENCA,INPUT_PULLUP); //it does not seem to matter if I use INPUT_PULLUP
pinMode(ENCB,INPUT);
//pinMode(ENCB,INPUT_PULLUP);
ai = digitalRead(ENCA);
bi = digitalRead(ENCB);
attachInterrupt(digitalPinToInterrupt(ENCA),readEncoder1,CHANGE);
attachInterrupt(digitalPinToInterrupt(ENCB),readEncoder2,CHANGE);
}
void loop() {
}
void readEncoder1(){
int ai = digitalRead(ENCA);
int bi = digitalRead(ENCB);
if (ai != bi){
posi++;
} else {
posi--;
}
Serial.print(posi);
Serial.print(ai);
Serial.println(bi);
}
void readEncoder2(){
int ai = digitalRead(ENCA);
int bi = digitalRead(ENCB);
if (ai == bi){
posi++;
} else {
posi--;
}
Serial.print(posi);
Serial.print(ai);
Serial.println(bi);
}
This time, the output was exactly as expected: 000, 101, 211, 310, 400...
This conclusively proves to me that everything is wired correctly, and that the interrupt-routines trigger when they should and do exactly what they are supposed to do. But it leaves me without any idea why my original program is not behaving as it should.
Does anybody have any idea what's going on?