Rotary Encoder Programming Code for Fast and Robust Usage

by RuiMonteiro in Circuits > Arduino

600 Views, 4 Favorites, 0 Comments

Rotary Encoder Programming Code for Fast and Robust Usage

rotary-encoder-module-pinout.png
Rotary-encoder-waveform.png

The typical Arduino sketches out there are prone to failure due to not considering the full cycle to count as a rotation. To solve the problem of stability and also to allow faster speeds here are two variations of a Fast and Robust code for the typical Rotary Encoder.

As you can see, there is a cycle of 5 steps for an entire movement, those steps are:

CLK(A) and DT(B) bit pairs for CCW (Moving Left):

  1. 11
  2. 01
  3. 00
  4. 10
  5. 11

CLK(A) and DT(B) bit pairs for CW (Moving Right):

  1. 11
  2. 10
  3. 00
  4. 01
  5. 11

This means we can reduce the entire cycle into a single byte (char) like so:

  • 01001011 for CCW
  • 10000111 for CW

You may visualize these A/B cycles in the picture above.

Supplies

m-dulo-encoder-rotativo-20imp-rot-com-knob-para-arduino.jpg

One Arduino Rotary Encoder.

wiring-rotary-encoder-with-arduino-uno.png

Wire the Rotary Encoder accordingly to the picture, like this:

  • CLK to Arduino pin 2
  • DT to Arduino pin 3
  • SW to Arduino pin 4

Robust and Simple Programming

Capturar_PinNumbers.PNG

For the Robust and Simple programming code use this one:

// Rotary Encoder Inputs
#define CLK 2
#define DT 3
#define SW 4

#define DPR 50    // Debounce timer for press and release
#define LPR 3000  // Long press/release in milliseconds

unsigned char readBits = 0b111;     // 8 bits
unsigned char rotationBits = 0b011; // 8 bits
unsigned char buttonBits = 0b111;   // 8 bits
int counter = 0;
String currentDir = "";
unsigned long lastActionButton = 0;

void setup() {

  // Set encoder pins as inputs
  pinMode(CLK,INPUT);
  pinMode(DT,INPUT);
  pinMode(SW, INPUT_PULLUP);
  
  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
  
}

void loop() {

  readBits = digitalRead(SW) << 2 | digitalRead(CLK) << 1 | digitalRead(DT);

  if ((rotationBits & 0b11) != (readBits & 0b11)) {

    rotationBits = rotationBits << 2 | readBits & 0b11;
    
    // Bit Pairs Cyclic Sequence (CLK DT Pair Bits):
    // 1.   2.   3.   4.   5.
    // 11 | 01 | 00 | 10 | 11 for CCW
    // 11 | 10 | 00 | 01 | 11 for CW
    if (rotationBits == 0b01001011 || rotationBits == 0b10000111) {
  
      if (rotationBits == 0b01001011) {
        currentDir = "CCW";
        counter--;
      } else {
        currentDir = "CW";
        counter++;    
      }

      Serial.print("Direction: ");
      Serial.print(currentDir);
      Serial.print(" | Counter: ");
      Serial.println(counter);
    }
  }

  if ((buttonBits & 0b001) != (readBits >> 2 & 0b001)) {

    // Bit Pairs Press Release Cyclic Sequence (OFF ON Pair Bits):
    // 1.    2.    3.    4.  (long press/release)
    // 000 | 001 | 011 | 111 for OFF
    // 111 | 110 | 100 | 000 for ON
    if ((buttonBits & 0b001) == 0b000) {
      buttonBits = 0b001;
    } else {
      buttonBits = 0b110;
    }

    // Starts counting time since button physically operated
    lastActionButton = millis();
    
  // LONG Press/Release Actions
  } else if (abs(millis() - lastActionButton) > (unsigned long)LPR) {
    if (buttonBits == 0b011) {
        buttonBits = 0b111;
        Serial.println("LONG released!");
    } else if (buttonBits == 0b100) {
        buttonBits = 0b000;
        Serial.println("LONG pressed!");
    } 

  // SIMPLE Press/Release Actions
  } else if (abs(millis() - lastActionButton) > (unsigned long)DPR) {
    if (buttonBits == 0b001) {
        buttonBits = 0b011;
        Serial.println("Button released!");
    } else if (buttonBits == 0b110) {
        buttonBits = 0b100;
        Serial.println("Button pressed!");
    }
  }

  // Put in a slight delay to help debounce the reading
  delay(1);
}

Robust and Fast Speeds Programming

CapturarPinsShift.PNG

For extremely high speeds you have to use bit operators and port registers. In this case use the following code for the Rotary Encoder:

int counter = 0;
String currentDir = "";
unsigned char currentPairBit = 0b11;  // 8 bits
unsigned char lastPairBit = 0b11;     // 8 bits

void setup() {

  Serial.begin(9600); // opens serial port, sets data rate to 9600 bps
  DDRD = 0b00000000;  // Sets all PD Arduino pins as inputs
  
}

void loop() {

  while(true) { // while cycle is faster than loop!

    // reads the PD digital pins for states!
    // PD2 >> 1 jumps to bit 0b0010 and PD3 >> 3 jumps to bit 0b0001
    currentPairBit = PIND >> 1 & 0b10 | PIND >> 3 & 0b01; // CLK(PD2) and DT(PD3) bits
    
    if ((lastPairBit & 0b11) != currentPairBit) {
  
      lastPairBit = lastPairBit << 2 | currentPairBit;
      
      // Bit Pairs Cyclic Sequence:
      //  1.   2.   3.   4.   5.
      //  11 | 01 | 00 | 10 | 11 for CCW
      //  11 | 10 | 00 | 01 | 11 for CW
      if (lastPairBit == 0b01001011 || lastPairBit == 0b10000111) {
    
        if (lastPairBit == 0b01001011) {
          currentDir = "CCW";
          counter--;
        } else {
          currentDir = "CW";
          counter++;    
        }
        
        Serial.print("Direction: ");
        Serial.print(currentDir);
        Serial.print(" | Counter: ");
        Serial.println(counter);
      }
    }
  }
}