Quadrature Enocder Simulation

Here is the Timer1 Quadrature Generator(quadrature output on D9 and D10) with Pin Change Interrupt encoder readings on those same pins.

The quadrature output triggers a pin change interrupt. No need to jumper pins or provide external input to interrupt pins.

//Quadrature signal generator two outputs offset 90 degrees
//emulate rotary encoder
//Timer 1 CTC with ICR1 TOP
//Switch direction with Serial input F/R/S Enter Capital F S or R with not line ending
//Pin Change Interrupts on Timer Output Pins

const char encTable[16] = {0, 1, -1, 0, -1, 0, -0, 1, 1, 0, 0, -1, 0, -1, 1, 0}; //gives -1, 0 or 1 depending on encoder movement
volatile long encoderCount;
volatile long errorCount;
volatile byte encState;
unsigned long prevDisplay;
unsigned long interval = 1000;

char Command = 'F'; //default state is Forward

void setup() {

  Serial.begin(115200);

  pinMode(9, OUTPUT); //output A
  pinMode(10, OUTPUT); //output B

  //Enable pin change interrupts on pins 9 and 10 PB 2 and 3
  PCICR |= (1 << PCIE0); //enable group interrupts on PORTB PCINT[7:0]
  PCMSK0 |= (1 << PCINT2); //enable interrupt pin 10
  PCMSK0 |= (1 << PCINT1); //enable interrupt pin 9

  TCCR1A = 0; //clear timer registers
  TCCR1B = 0;
  TCNT1 = 0;
  GTCCR |= 1 << PSRASY; //reset prescaler

  //ICR1 and Prescaler sets frequency
  //no prescaler .0625 us per count @ 16mhz
  //prescaler 8 .5 us per count

  TCCR1B |=  _BV(CS11); // prescaler 8
  //TCCR1B |= _BV(CS10); //no prescaler

  //counts are zero indexed 2edges per ICR1 period
  //numerical values for prescaler 8.
  //e.g. 10k period give 20k encoder counts

  ICR1 = 199;//10k ICR1 period  20k encoder counts
  //ICR1 = 99; //20k ICR1 period 40k encoder counts
  //ICR1 = 49; //40K ICR1 period 80k encoder counts
  //ICR1 = 46; //42.5K ICR1 period 85k encoder counts
  //ICR1 = 41; //47.5k ICR1 period 95K encoder counts
  //ICR1 = 39; //50k ICR1 period 100k encoder counts
  //ICR1 = 29; //66.6K ICR1 period 133k encoder counts
  //ICR1 = 19; //100k ICR1 period 200k encoder counts
  //ICR1 = 17;
  //ICR1 = 14;
  //ICR1 = 9;

  OCR1A = ICR1 - 1; //two different pulse widths almost 100% duty cycle
  OCR1B = OCR1A / 2; //offset by half period

  TCCR1B |= _BV(WGM13) | _BV(WGM12); //CTC mode with ICR1
  TCCR1A = _BV(COM1A0) | _BV(COM1B0); //Toggle OC1A/OC1B on compare match
}
void loop () {

  readCommand();

  if (Command == 'F')
  {
    OCR1A = ICR1 - 1; //two different pulse widths almost 100% duty cycle
    OCR1B = OCR1A / 2; //offset by half period
    TCCR1B |= _BV(CS11);//start timer
  }

  if (Command == 'R')
  {
    OCR1B = ICR1 - 1; //two different pulse widths almost 100% duty cycle
    OCR1A = OCR1B / 2; //offset by half period
    TCCR1B |=  _BV(CS11);//start timer
  }

  if (Command == 'S')
  {
    TCCR1B &= ~(_BV(CS11));//stop timer
  }

  if (millis() - prevDisplay >= interval)
  {
    prevDisplay += interval;
    noInterrupts();
    long copyEncoderCount = encoderCount;
    encoderCount = 0;
    long copyErrorCount = errorCount;
    errorCount = 0;
    interrupts();

    Serial.print(copyEncoderCount);
    Serial.print('\t');
    Serial.println(copyErrorCount);
  }
}

ISR (PCINT0_vect)
{
  encState = ((encState << 2) | ((PINB >> 1) & 3)) & 15; //use encoder bits and last state to form index
  encoderCount += encTable[encState];//update actual position on encoder movement
  if (encTable[encState] == 0)
    errorCount++;
}

void readCommand() {

  if (Serial.available() > 0)
  {
    Command = Serial.read();
    Serial.println(Command);
  }
}