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:
   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() {
  attachInterrupt(0, evaluateRotary, CHANGE);
  attachInterrupt(1, evaluateRotary, CHANGE);
  Serial.begin(9600); //make sure it's set to 9600 in the serial monitor on PC

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
    encval = 0;
  else if( encval < -3 ) {  //four steps backwards
   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.


  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,

Leave a Reply

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