Incremental rotary encoders (Part 1)

Part 1234567, 8, 9, 10

Note: This post is the starting point for a whole series of post dedicated to rotary encoders. Please read through thme all in order to benefit from all the improvements brought to the original code.You may also want to read about this  heat control application which features encoders.

An absolute encoder has a number of binary outputs that indicate the switch shaft’s absolute rotational position referenced to some spot on the switch’s body. The direction is derived from the difference between previous and actual BCD counting.

An incremental rotary encoder, will not tell where the shaft is positioned. It will only tell which direction the shaft is being turned and how fast. Incremental encoders have only two outputs called phases. Each outputs a square wave. Turning the shaft one direction causes one phase to lead the other by 90 degrees. Reversing the direction will cause the other phase to lead. The frequency of the output is proportional to the rotational speed of the shaft. Incremental encoder may incorporate an additional switch which is activated by pressing down the encoder shaft. In this way the incremental rotary encoder can be used as a simplistic user interface.

Warning: The pinout may differ from one encoder to the other

The switching pattern looks like:

  ---     ---     ---            ---     ---     ---
 |   |   |   |   |   |     A    |   |   |   |   |   |  
-     ---     ---     -        -     ---     ---     --
---     ---     ---                ---     ---     ---
   |   |   |   |   |       B      |   |   |   |   |   | 
    ---     ---     ---        ---     ---    ---  
       Turn Left                       Turn right
A 1 1 0 0 1                    A 1 1 1 0 1
B 1 0 0 1 1                    B 0 1 0 0 0

Note: This is the ideal case. In the real life, we will have to care about the bouncing effect!

The most obvious way to analyze the signal consists in attaching an externally driven interrupt to one output, and to measure the signal from the other output.

An alternative consists in sampling periodically both signals. Comparing previous and actual outputs combination gives the direction indication: no change, cw rotation ccw rotation. This principle does not require interrupts and simplifies the code, its debugging and its maintenance.

This is the minimal set up content:

void setup() {
	// Setup encoder pins as inputs
	DDRB  &= ~(1 << ENC_PIN_B);
	DDRB  &= ~(1 << ENC_PIN_A);
	// Bias inputs
	PORTB |=  (1 << ENC_PIN_B); 
	PORTB |=  (1 << ENC_PIN_A);
	// Set global variable
	int counts = 0;
}

The periodical encoder test can be part of the loop() routine

void loop() {
	enc_test();
}

void enc_test(void) {
// Test encoder outputs
	static int lastCount = 0;
	int encoder = enc_read();
	if (encoder) {
		counts += encoder;
		lastCount = counts;
	} 
}

And this is the encoder reader

Note: Most encoders will experience multiple state changes per step. The encoder reader buffers these changes up to a predefined value (stored in the pulsesPerStep variable) before incrementing or decrementing the counter.

int enc_read(void) {
// returns change in encoder state (-1: ccw, 0: no change, 1: cw) 
  int result = 0;
	static byte prevState = 0;
	static int bufferedCounts = 0;
	byte startState = State(); // Get current state
  delayMicroseconds(samplingTime); // Wait safety bounce time
	byte stopState = State(); // Get current state
	if ((startState == stopState) && (stopState != prevState)) { // check if the previous state was stable
		if (stopState == nextEncoderState[prevState]) {
			bufferedCounts++; 
		}
		else if (state == prevEncoderState[prevState]) {
			bufferedCounts--; 
		}
		prevState = state; // Record state for next pulse interpretation
		if (abs(bufferedCounts) == pulsesPerStep) {
			result = int(bufferedCounts / pulsesPerStep);
			bufferedCounts = 0;
		}
	}
	return(result);
}

byte State (void) {
	return(((PINB >> ENC_PIN_B) & 0x01) | (((PINB >> ENC_PIN_A) & 0x01) << 1));
}

The bouncing filter is pretty simple (not to say simplistic) but it works great.

Next post on same subject

Leave a Reply

You must be logged in to post a comment.