Timer 1 Quadrature Signal Generator and Encoder Reading Code

Following an approach suggested by Rugged Circuits a few years ago http://forum.arduino.cc/index.php?topic=105592.msg792269#msg792269
I implemented a timer 1, mode 12 , based quadrature signal generator to use for testing code written to read rotary encoders. Offset square wave outputs are on pins 9 and 10.

//Quadrature signal generator two outputs offset 90 degrees
//emulate rotary encoder
//Timer 1 CTC with ICR1 TOP and 

void setup() {

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

  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
  
  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 () {}

Attached is a capture from a scope showing the two offset square waves. One shot at 80khz and another at 500khz. With 4 edges per cycle, there should be 320k and 2m counts/second from the quadrature. It’s somewhat confusing in that the timer cycle is 2x the square wave frequency because of the output pin toggle on match, and the quadrature counts per second are 4x the square wave frequency (i.e. 2x the timer cycle).

This code, using external interrupts can read to 95K counts/second (ICR1 = 41, prescaler =8) with a few occasional errors. It locks up at 100K counts/second.

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;

void setup() {
  Serial.begin(115200);
  attachInterrupt(0, readEnc, CHANGE);
  attachInterrupt(1, readEnc, CHANGE);
}

void loop() {
  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);
  }
}

void readEnc() {
  encState = ((encState << 2) | ((PIND >> 2) & 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++;
}

I’d be interested in

  1. comments and feed back on the signal generating code.
  2. if anyone has any code which benchmarks faster than what I posted
  3. What the performance is on a Due.
  4. What can be observed with a hardware decoder.

80K square waves.jpg

500K square waves.jpg

80K square waves.jpg

500K square waves.jpg

Neat trick with the timer.

I believe the Due has hardware quadrature decoder.