Reading a rotary encoder with ATmega328

There are a bewildering number of examples, libraries, and opinions about how to write routines to read values from a rotary encoder. I found one that that looked particularly elegant and easy to generalize, so adapted it to the Atmega328 that I’m using for my current project. I used a pretty popular rotary encoder, Adafruit #377, although I think the demo code would work with other quadrature encoders.

Most of the work in done in a relatively light interrupt service routine that receives information from both directional pins on the rotary encoder. I did not do any addition software or hardware debouncing and I didn’t notice any jitter or erratic behavior.


I’m sure my code isn’t as polished as Oleg’s, but in case other people might find it useful, here’s what I came up with:

/* Based on Oleg Mazurov's code for rotary encoder interrupt service
   routines for AVR micros, originally written for ATmega644p:
   https://www.circuitsathome.com/mcu/rotary-encoder-interrupt-service-routine-for-avr-micros
   Here, implemented on Atmega328.

Example using Adafruit 377 rotary encoder: 
With the three encoder tabs facing downward and the knob towards you
* leftmost pin - connect to digital pin 3
* middle pin - connect to ground
* rightmost pin - connect to digital pin 2

*/

#define ENC_RD	PIND	//encoder port read
#define A_PIN 2  // pdip 4, associated with INT0 vector; PD2
#define B_PIN 3  // pdip 5, associated with INT1 vector; PD3

long counter = 0;

void setup() {
  pinMode(A_PIN, INPUT_PULLUP);
  pinMode(B_PIN, INPUT_PULLUP);
  attachInterrupt(0, evaluateRotary, CHANGE);
  attachInterrupt(1, evaluateRotary, CHANGE);
  Serial.begin(9600); //make sure it's set to 9600 in the serial monitor on PC
  Serial.println("Start");
}

void loop() {
  static long lastCounter = 0;

  if(counter != lastCounter){
    Serial.print("Counter value: ");
    Serial.println(counter, DEC);
    lastCounter = counter;
  }
}

void evaluateRotary() {
/* encoder routine. Expects encoder with four state changes between detents */
/* and both pins open on detent */

  static uint8_t old_AB = 3;  //lookup table index
  static int8_t encval = 0;   //encoder value  
  static const int8_t enc_states [] PROGMEM = 
  {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};  //encoder lookup table
  /**/
  old_AB <<=2;  //remember previous state   
  old_AB |= (( ENC_RD >>2 ) & 0x03 );
  encval += pgm_read_byte(&(enc_states[( old_AB & 0x0f )]));

  if( encval > 3 ) {  //four steps forward
    counter++;
    encval = 0;
  }
  else if( encval < -3 ) {  //four steps backwards
   counter--;
   encval = 0;
  }
}

8 thoughts on “Reading a rotary encoder with ATmega328”

  1. I think there is a error in your listing. Line

    old_AB <>2 ) & 0x03 );

    should be 2 lines:

    old_AB <>2 ) & 0x03 );

  2. Hi George – Thanks for the comment, but I don’t see how the upper line differs from the lower – could part of your comment have been cut off? I have used this code in a practical project, so it seemed to work as expected. Regards – Jack

  3. Hi – off hand, I don’t know. I haven’t looked at the code in some time and am not familiar with the encoder that you mentioned. Sorry – Jack

  4. Hi Jack, Nice code. Thank you.

    In the above sketch, after “remember previous state” there needs to be a line break. One important line of code is being commented out.

    Lowell

  5. Thanks, Lowell. I’ve had some difficulty pasting code into wordpress, even using the “pre” or “code” HTML elements. I have occasionally fixed things like this only to have the line wrapping spontaneously change again after an update. Thanks for flagging this — I’ve reformatted it, and hopefully it will stick! Cheers – Jack

  6. Thanks so much for the code, it works just great for me, I had tried several peoples without much sucess. I have to say I don’t fully understand yours but that’s me being a dunce.

    Thanks again,
    Miles

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.