fast encoder code

Good day,

I have a problem with encoder code, the problem is that the code is not detecting the rotation direction which leads to the counts increase when rotating the encoder at both directions.

the code is:

volatile unsigned int temp, counter = 0; //This variable will increase or decrease depending on the rotation of encoder
void setup()
{
  pinMode(2, INPUT_PULLUP); // internal pullup input pin 2 
  pinMode(3, INPUT_PULLUP); // internalเป็น pullup input pin 3
//Setting up interrupt
  //A rising pulse from encodenren activated ai0(). AttachInterrupt 0 is DigitalPin nr 2 on moust Arduino.
  attachInterrupt(0, ai0, RISING);
     //B rising pulse from encodenren activated ai1(). AttachInterrupt 1 is DigitalPin nr 3 on moust Arduino.
  attachInterrupt(1, ai1, RISING);
 if( counter != temp ){
  Serial.println (counter);
  temp = counter;
}
  void ai0() {
  // ai0 is activated if DigitalPin nr 2 is going from LOW to HIGH
  // Check pin 3 to determine the direction
  if(digitalRead(3)==LOW) {
  counter--;
  }else{
  counter++;
  }
  }
   
  void ai1() {
  // ai0 is activated if DigitalPin nr 3 is going from LOW to HIGH
  // Check with pin 2 to determine the direction
  if(digitalRead(2)==LOW) {
  counter--;
  }else{
  counter++;
  }
  }

Quadrature encoders derive their name from the four possible states that they can assume, if only momentarily. They have two outputs, typically named A and B each can be logic high or low thus giving us the four combinations (11, 10, 01, 00). Internally, these encoders are likely to have two disks, one for each A and B, that are slightly out of phase (or one disk and two sensors). Using mechanical or optical sensors, as the encoder is rotated so the signals vary on each output with a delay that represents the phase difference. In one direction, A change prior to B and the reverse in the opposite direction. The only stable state, which is commonly referred to as detente, is when the encoder is at one of its rest positions and both signals are at their respective rest values. For this discussion we'll assume signals at rest to be at a logic 1, hence the detente value would be 0b11 or 3. As the encoder leaves detente, first one signal will drop to zero, giving us 0b10 or 0b01 (2 or 1). Next we'll pass through a phase where both signals are zero and then through the reverse state from the start before returning to detente.

The two sequences can therefore be described (and tested for) as starting at detente 3, passing through 102 or 201 depending on direction and returning to detente 3. Your four interrupt hits are then 1-0-2-3 or 2-0-1-3.

Each time the interrupt fires (or you detect a change), it will reflect a state change on one of the two pins. If you read the port input pins, masking those that map to your A and B signals, you'll get 0, 1, 2 or 3, Keeping track of the sequence will tell you which direction the encoder is being rotated. If the current state is not consistent with a predicted state - one that you know should exist following the previous state - it is safe to assume that it is a false reading (double hit, bounce, etc.) and ignore it.

Your encoder algorithm is not correct. You are reading two of the four available quadrature states, but with both interrupts on RISING you are not reading a consistent amount of rotation from count to count.
Your logic is also incorrect for this algorithm, and one of them needs to have the count++ and count-- logic reversed.

Better, would be to place an interrupt on one encoder pin with CHANGE, and then in the ISR read both pins to detect the direction of rotation. If the digitalRead() values of the two pins are the same, you are going in one direction, if they are different you are going in the other.

This one works well and claims inherent debouncing.

YMMV

ARD_BH:
Good day,

I have a problem with encoder code, the problem is that the code is not detecting the rotation direction which leads to the counts increase when rotating the encoder at both directions.

I have a powerful and simple example code for tachometers

#define ClockPin 2 // Must be pin 2 
#define DataPin 3 // Must be pin 3
#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
volatile long count = 0;
long lastCtr;
unsigned long SpareCycles;

void Encoder(bool A) {
  (readB == A)  ? count++ : count--;
}

void setup() {
  Serial.begin(115200); //115200
  pinMode(ClockPin, INPUT);
  pinMode(DataPin, INPUT);
  /* 1 Step */
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( true);}, RISING);
  /**/

  /*  2 step count
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readB);}, CHANGE);
  */
  
  /* Full 4 Step Count
  attachInterrupt(digitalPinToInterrupt(ClockPin), [] {Encoder( readB);}, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DataPin ), [] {Encoder( !readA);}, CHANGE);
  */
}

void loop() {
  long Counter;
  SpareCycles++;
  noInterrupts ();
  Counter = count;
  interrupts ();
  if ((lastCtr != Counter) & (SpareCycles > 10)) {
    Serial.println(Counter);
    SpareCycles = 0;
  }
  lastCtr = Counter;
}

the SpareCycles variable is there to prevent Serial.println from interfering with the counter during fast counts.
Also, some encoders need capacitors to provide proper debounce
Encoder.jpg
Z

Thanks for your reply
I try this code but same problem occur, the encoder could not recognize the direction rotation

zhomeslice:
I have a powerful and simple example code for tachometers

#define ClockPin 2 // Must be pin 2 

#define DataPin 3 // Must be pin 3
#define readA bitRead(PIND,2)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
#define readB bitRead(PIND,3)//faster than digitalRead()  (((value) >> (bit)) & 0x01)
volatile long count = 0;
long lastCtr;
unsigned long SpareCycles;

void Encoder(bool A) {
  (readB == A)  ? count++ : count–;
}

void setup() {
  Serial.begin(115200); //115200
  pinMode(ClockPin, INPUT);
  pinMode(DataPin, INPUT);
  /* 1 Step */
  attachInterrupt(digitalPinToInterrupt(ClockPin), {Encoder( true);}, RISING);
  /**/

/*  2 step count
  attachInterrupt(digitalPinToInterrupt(ClockPin), {Encoder( readB);}, CHANGE);
  /
 
  /
Full 4 Step Count
  attachInterrupt(digitalPinToInterrupt(ClockPin), {Encoder( readB);}, CHANGE);
  attachInterrupt(digitalPinToInterrupt(DataPin ), {Encoder( !readA);}, CHANGE);
  */
}

void loop() {
  long Counter;
  SpareCycles++;
  noInterrupts ();
  Counter = count;
  interrupts ();
  if ((lastCtr != Counter) & (SpareCycles > 10)) {
    Serial.println(Counter);
    SpareCycles = 0;
  }
  lastCtr = Counter;
}




the SpareCycles variable is there to prevent Serial.println from interfering with the counter during fast counts.
Also, some encoders need capacitors to provide proper debounce
![Encoder.jpg|477x378](upload://lzG9NjXvfAmbHZ77MSg3eRAkJnp.jpeg)
Z

ARD_BH:
Thanks for your reply
I try this code but same problem occur, the encoder could not recognize the direction rotation

I'm not familiar with your encoder. Your code has the internal pull-up resistor active. Change this in my code and see if this helps.

  pinMode(ClockPin, INPUT_PULLUP); // internal pullup input pin 2 
  pinMode(DataPin, INPUT_PULLUP); // internalเป็น pullup input pin 3

you are missing the pin 3 change. it is always low or high.
So how this code works:
Each time the clock pin changes from LOW to HIGH "RISING" the interrupt is triggered. because we now know the clock pin is high we just need to check the data pin. if the data pin is LOW we are moving in one direction and so we will subtract one and if the data pin is HIGH we are moving in the other so we add one.
Just that simple.

Z

Thanks for your help its now working

zhomeslice:
I'm not familiar with your encoder. Your code has the internal pull-up resistor active. Change this in my code and see if this helps.

  pinMode(ClockPin, INPUT_PULLUP); // internal pullup input pin 2 

pinMode(DataPin, INPUT_PULLUP); // internalเป็น pullup input pin 3




you are missing the pin 3 change. it is always low or high. 
So how this code works:
Each time the clock pin changes from LOW to HIGH "*RISING*" the interrupt is triggered. because we now know the clock pin is high we just need to check the data pin. if the data pin is LOW we are moving in one direction and so we will subtract one and if the data pin is HIGH we are moving in the other so we add one.
Just that simple.

Z