Incremental rotary encoders (Part 6)

Part 1234567, 8, 9, 10

Some may say: why bother so much about rotary encoders?
The answer is that I plan to use them extensively in combination with LCDs as multi purpose User Interface for my MicroXXX projects.

In spite of its smooth operation, my latest production is not fully complying with the Good Interrupts Management Pratices (GIMP !?!). So that I decided to flip back to normal peridiocal sensing of switches within the loop() routine. This is an extract from my PlainENC library:

int16_t PlainENC::getCounter(void) {
// Returns the value of the global variable counter after constraining it to the user defined limits
// Interpret switches position
	// Get current state
	uint8_t currentState = ((((PIN(*_encPort) >> _swA) & 0x01) << 1) | ((PIN(*_encPort) >> _swB) & 0x01)); 
	// Check if the previous state was stable and different from the previous one
	if (currentState != prevState) { 
		if (currentState == 0x03) { // If in idle state
			uint32_t now = millis();
		  uint8_t stepValue = 1;
			// Serial.println((now - prevStateTime), DEC);
			if ((now - prevStateTime) < _boostCutOff) {
				stepValue = 1 + ((_boostFactor * (_boostCutOff - (now - prevStateTime))) / _boostCutOff) ;
			}
			// Decode steps pattern
			if (patternBuffer == patternCW) {
				_counts += stepValue;
				if (_counts > _max)	_counts = _max;
			}
		  else if (patternBuffer == patternCCW) {
				_counts -= stepValue; 		
				if (_counts < _min)	_counts = _min;				
			}
			patternBuffer = 0x00; // reset pattern buffer
			prevStateTime = millis();
		}
		else {
			// Append state to the buffer
			(patternBuffer <

Comments:

  • Any Pin can be used as far as they belong to the same PORT. Check this thread about the PIN() macro
  • There is an optional “counter booster”: rotating a knob may be boring for incrementing large values. Thus the booster: the quicker the shift, the more the counts. Recommended default value are 50 ms cutoff and 10 to 20 for the boost factor.

Please check this page if you are interested in the code.

Next post on same subject

4 Comments

  1. Andrey67 says:

    Sorry for my English!
    I have the question about the encoder. I use Atmega32 and encoder ALPS SRGPHJ3200 and have the problem. This connection scheme: http://imageshack.us/photo/my-images/600/encodero.jpg/
    At some point, the output of the encoder makes a move in the opposite direction.
    http://imageshack.us/photo/my-images/821/testjt.jpg/
    In the test we see the rotation in the right but there are a few steps back!
    This is part of the source code:
    ISR(INT1_vect)
    {
    unsigned char tmp;
    if (DialChange==0)
    {

    tmp=PINC;
    _delay_us(30);
    if ((tmp&0x80)==0x00)
    {
    dial++;
    DialChange =1;
    }
    else
    {
    dial–;
    DialChange =1;
    }
    }
    }

    Help me! What is my mistake?

    • Didier says:

      Hi Andrey,
      I am afraid neither your schematics nor your code mean much to me :-s …
      I suggest you start from my code or you send more detailed information
      Regards
      Didier

  2. ifranco says:

    Dear Didier,

    I’m an artist and I am developing a new body performance that will use some electronics
    The setup will consist of 7 retractable cables tied the different joints of a dancer that will control synths and lights, mapped by the traveling of the cable.

    In terms of electronics my goal is to have 7 rotary encoders connected to an Arduino Mega that will send OSC to computers.
    I have some difficulties with doing the bitwise logic but I think I almost understand your technique. Still I can’t get my setup to work.

    1) How do you derive byte enc_patternCW =  B010010 and byte enc_patternCCW = B100001 from the map bellow? Isn’t it supposed to be a bitwise &  operation of the two channels?

     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

    1) I’ve tried to adapt your code to no avail. If I understand right I don’t need the button (in your exampleto reset the counter?).

    Can you spare a couple of minute with the code bellow?…
    🙂

    Thanks in advance!
    Ivan

    ————————-
    #define ENC_PIN_A PCINT23
    #define ENC_PIN_B PCINT22
    #define ENC_PIN_BUTTON PCINT21

    byte enc_patternCW =  B010010;
    byte enc_patternCCW = B100001;
    static int enc_counter = 0;
    static int enc_buttonState = 1;

    void setup() {

    /* Pin to interrupt map:
     * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2
     * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
     * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
     */
    // Setup encoder pins as inputs
    DDRD&= ~((1<<  ENC_PIN_B) | (1<<  ENC_PIN_A) | (1<<  ENC_PIN_BUTTON));
    // Bias pins
    PORTD |=  ((1<<  ENC_PIN_B) | (1<<  ENC_PIN_A) | (1<<  ENC_PIN_BUTTON));
    // Attach interrupt to PIND5, PIND6
    // ADC15, ADC14 e ADC13 on Mega
    PCMSK2 = ((1<<  PCINT23) | (1<<  PCINT22) | (1<<  PCINT21)); // Pin Change Mask Register 2
    PCICR = (1<<  PCIE2); // Pin Change Interrupt Enable 2

    Serial.begin (115200);
    Serial.println("Ready");

    }

    ISR(PCINT2_vect) {
    // Interpret switch

    static byte prevState = B11;
    byte startState = state(); // Get current state
    for (int i=0; i>  1) == B11) { // If in idle state

     if (enc_patternBuffer == enc_patternCW)
    enc_counter++;
     if (enc_patternBuffer == enc_patternCCW)
    enc_counter–;
    enc_patternBuffer = 0x00; // reset buffer
    }
    else {
    enc_patternBuffer<>  1) ;
    }
    prevState = stopState; // Record state for next change
    }
    }

    void loop() {
    static int lastCount = 0;
    if (lastCount != enc_counter) {
    Serial.print(enc_counter, DEC);
    Serial.println();
    lastCount = enc_counter;
    }
    delay(10);
    }

    byte state(void) {
    // Analyze input lines state
    byte statusA = ((PIND>>  ENC_PIN_A)&  0x01); // Read input line A state
            byte statusB = ((PIND>>  ENC_PIN_B)&  0x01); // Read input line B state
            byte statusC = ((PIND>>  ENC_PIN_BUTTON)&  0x01); // Read input line B state
    return((statusA<<  2) | (statusB<<  1) | statusC);
    }

    ————————-

Leave a Reply

You must be logged in to post a comment.