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; } }
I think there is a error in your listing. Line
old_AB <>2 ) & 0x03 );
should be 2 lines:
old_AB <>2 ) & 0x03 );
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
Will it work with high resolution encoder like 1440 PPR?
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
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
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
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