Небольшая вариация на тему: процедура параллельно работает сразу с двумя энкодерами:
volatile char encoder_pos, inp_shift;
...
#define TR(x, y) (((x) << 4) | (y) )
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Place your code here
// Имеем 2 энкодера: первый подключен к PORTD.0 и PORTD.1. Второй - к PORTD.2 и PORTD.3.
char pins = (PIND & 0x0F) << 4;
// если состояние 0-го бита ни одного из энкодеров не изменилось с предыдущего вызова, ничего не делаем
if ((encoder_state & 0b01010000) == (pins & 0b01010000))
return;
// задвигаем новое состояние в переменную, забывая самое старое из
// 2 сохраненных состояний
encoder_state = ((encoder_state >> 4) | pins);// & 0xF0;
// проверяем условия, и изменяем количество щелчков, если нужно
if ((encoder_state&0b00110011) == TR(3,0)&&(encoder_pos&31)>0) encoder_pos--; //уменьшение громкости
if ((encoder_state&0b00110011) == TR(1,2)&&(encoder_pos&31)<31) encoder_pos++; //увеличение громкости
if ((encoder_state&0b11001100) == TR(12,0)&&inp_shift>0) inp_shift--; //к предыдущему входу
if ((encoder_state&0b11001100) == TR(4,8)&&inp_shift<2) inp_shift++; //к следующему входу
encoder_pos &= 31;
encoder_pos |= (1<<(inp_shift+5));
// В переменной encoder_state легко и непринуждённо размещаются: с нулевого по 4 бит - положение регулятора громкости.
// с 5-ого по 7-ой - реле переключения трёх входов усилителя. Очень удобно всё разом отправить в какой-нибудь из портов контроллера
}