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