Here is some sample code I tried and it worked very well with the cheap encoders I got from Amazon. I never detected any errors while turning as fast as I could. YMMV depending on the encoder. You can try both the state based or table driven code by commenting out the unused section of code. Of course the code would have to be modified for multiple encoders, but this will give you as base to start from.
As for processing the interrupts you should process all changed bits (all encoders on the port) before exiting the interrupt routine. View the interrupt routing as a asynchronous polling loop only call when actually needed instead of wasting cycles polling when most of the time nothing has changed.
/*
* Use PORTB bits 0 and 1 as S1 and S2 of rotory encoder. We use change if pin interrupt to
* find out which direction the encoder is moving.
*
*/
#include <avr/interrupt.h>
typedef signed char S8;
typedef signed int S16;
typedef signed long S32;
typedef unsigned char U8;
typedef unsigned int U16;
typedef unsigned long U32;
typedef struct // Rotory_En
{
U32 etime;
S16 counts;
S16 errors;
U8 button;
}
Rotory_En;
#define SV 2 // starting value, fudge to get divide by 4 to work
//static volatile int button = 0, count = 0, errors = 0;
static volatile Rotory_En encoder = {0,SV,0,0};
ISR(PCINT0_vect)
{
unsigned char pb;
static volatile U8 lc = 0b1100; //fudge init number to get correct value most of the time for the 1st int after startup
unsigned long startt;
static S8 delta[] = {
0, // b0000 button or error
1, // b0001
-1, // b0010
0, // b0011 error
-1, // b0100
0, // b0101 button or error
0, // b0110 error
1, // b0111
1, // b1000
0, // b1001 error
0, // b1010 button or error
-1, // b1011
0, // b1100 error
-1, // b1101
1, // b1110 error
0 // b1111 button or error
};
startt = micros();
pb = PINB; //read port b
encoder.button = (pb & 0B100) / 4;
lc = ((lc * 4) | (pb & 0x3)) & 0xF;
encoder.counts += delta[lc];
/*
switch (lc)
{
case 0b1110: // clockwise
case 0b1000:
case 0b0001:
case 0b0111:
encoder.counts++;
break;
case 0b0100: // counter clockwise
case 0b0010:
case 0b1011:
case 0b1101:
encoder.counts--;
break;
case 0b1111: // if this then just button pressed
case 0b1010:
case 0b0101:
case 0b0000:
break;
default:
encoder.errors++;
}
*/
encoder.etime = micros() - startt;
}
void setup()
{
Serial.begin(115200);
//delay(100);
Serial.print("Starting ");
Serial.println();
cli();
DDRB &= B11111000; // set port B bits 0, 1 and 2 as input
PCICR |= 0x01; // turn on port B change interrupts.
PCMSK0 |= 0x07; // setup port b pins 0, 1 and 2 to intterupt on change
sei();
}
void loop()
{
static int c = SV;
if (c != encoder.counts)
{
Serial.print(" Counts = ");
Serial.print(encoder.counts / 4);
Serial.print(" Errors = ");
Serial.print(encoder.errors);
Serial.print(" Button = ");
Serial.print((S8)encoder.button);
Serial.print(" Time = ");
Serial.print(encoder.etime);
Serial.println();
c = encoder.counts;
}
}