Incremental rotary encoders (Part 9)

Part 1234567, 8, 9, 10

Next diagrams show the signal patterns from the A and B switches. The “d” sign shows the rest position of the encoder after a detent (so as to say a click). As mentioned above, the diagrams deal with rotary encoders featuring one cycle per detent. Under the phase plots are the bit values for A and B switches. Just under is the horizontal time table (e.g. BA BA BA BA) and the corresponding binary ( e.g.  10 00 01 11) and decimal (e.g. 2 0 1 3) value coded by the A and B switches.

CLOCKWISE

    --> Time
B                d               d
    ---         -------         -------           
       |       |       |       |       |          
        -------         -------         -------   
A                d               d
            -------         -------         --- 
           |       |       |       |       |    
    -------         -------         -------     
    -------------d---------------d------------- 
B    1   0   0   1   1   0   0   1   1   0   0  
A    0   0   1   1   0   0   1   1   0   0   1  

--> Time
BA BA BA BA
10 00 01 11
2  0  1  3 


COUNTER-CLOCKWISE

    --> Time
B        d               d               d
    -------         -------         -------     
           |       |       |       |       |    
            -------         -------         --- 
A        d               d               d      
        -------         -------         ------- 
       |       |       |       |       |        
    ---         -------         -------         
    -----d---------------d---------------d----- 
B    1   1   0   0   1   1   0   0   1   1   0  
A    0   1   1   0   0   1   1   0   0   1   1  

--> Time
BA BA BA BA
01 00 10 11 
1  0  2  3

In the previous versions of PlainENCi, the successive states are buffered and the content of the buffer is compared to the reference pattern: e.g. in CW mode, the code was comparing the buffer content with 0x87 (10000111 in binary).

But !

In the real life, the readings from the encoder are more likely to look like… 10 00 10 00 10 00 01 00 01 00 01 11 01 11 ! The slower the rotational speed of the encoder the higher the probability of such bits stammer. This is the consequence of severe bouncing which prevents the code from achieving proper match.

Note: Adding RC filters (R=10 kΩ, C=10 nF) attached to the A and B outputs and ground as specified by some rotary encoder makers do not help .

alps_encoder_1

As I was unhappy with the lack of ruggedness from my last algorithm, I reworked the code. The major difference lies in the decoding of the successive encoder states. Taking the previous example, the code will expect 10 and then 00 and then 01 and ultimately 11 whatever comes in between each expected state. The CW/CCW decision is made while reading the first state: 10 means CW, 01 means CCW. Next lines of code constitute the heart of the decoding process.

	switch(encoderState) {
	case 0x00:
		if (stateCounts == 1) {
			stateCounts = 2;
		}
		break;
	case 0x01:
		if (stateCounts == 0) {
			step = -1;
			stateCounts = 1;
		} else if (stateCounts == 2) {
			stateCounts = 3;
		}
		break;
	case 0x02:
		if (stateCounts == 0) {
			step = 1;
			stateCounts = 1;
		} else if (stateCounts == 2) {
			stateCounts = 3;
		}
		break;
	case 0x03:
		if (stateCounts == 3) {
			stateCounts = 4; 
		} else {
			stateCounts = 0;
		}
		break;
	}

Once 4 consecutive valid states are read, the algorithm runs the boost function which increases the number of counts per detent depending upon the rotational speed of the encoder. This part also contains some code which prevent the counter to override the min and max limits.

	if (stateCounts == 4) {
		uint32_t now = millis();
		uint32_t elapsedTime = (now - lastTime);
		lastTime = now;
		if (elapsedTime < _encBoostCutOff) {
			step *= (1 + (((_encBoostFactor - 1) * (_encBoostCutOff - elapsedTime)) / _encBoostCutOff));
		}
		_encCounts += step;
		if (_encCounts > _encMaxCounts) {
			_encCounts = _encMaxCounts;
		} else if  (_encCounts < _encMinCounts) {
			_encCounts = _encMinCounts;
		}
		stateCounts = 0;		
	}

Next plot illustrates the counts per detent or clicks if you like while rotating the encoder using a x100 boosting factor. As you can see, it takes only 360 detents (12 turns) to raise the counter from 0 to 10000 while still being able to count 1 by 1 when turning the knob slowly. You can clearly see the acceleration/deceleration of the rotational speed from the encoder shaft.

alps_encoder_2

The result of this rework is fully satisfactory: whatever the rotational speed, the encoder gives the expected result. And the code remains pretty light and simple. The sad news is that I tried to transpose this principle to the “half cycle per detent” rotary encoders and I did not manage to achieve full reliability. So that I made the brutal however understandable decision to release this version which is only compatible with “one cycle per detent” encoders. This library was tested using Alps and Bourns encoders.

Next are some links to rotary encoder datasheets:

 Next post on same subject

Leave a Reply

You must be logged in to post a comment.