Using the Hardware Quadrature Encoder Channel on the DUE

For absolute position measurement, TC_CV register (32-bit) provides all you need, however care must be taken whenever TC_CV overflows or underflows. To deal with that, an int64_t (8 bytes) is handy, as well as interrupts on TC_CV overflow and underflow.

I tested the position measurement with a simulation of an 10000 PPR quadrature encoder, 42000000 edges per second and a reverse direction firstly after 5 seconds then every 10 seconds. You can see oscillations around the zero position such as a point above a linear rail by selecting tools>Serial Plotter and 250000 bauds.

/***************************************************************************************************/
/*                   QDEC1 test with an emulation of a 10000PPR Quadrature encoder                 */
/*                          QDEC position mode by polling in loop()                                */
/*               Connect TIOA0(pin 2) to TIOA6(pin 5), TIOB0(pin 13) to TIOB6(pin 4)               */
/*                     Select Serial Plotter to show scillations around the zero position          */
/***************************************************************************************************/

const uint32_t LOOP_DELAY_MS = 1 * 100;
volatile int64_t AbsPos , AbsPosBase;

/************************************************************************/
/*    This part before setup() is for a Quadrature encoder emulation    */
/************************************************************************/
#define PERIOD_VALUE   (1)           // 42000000 edges per second simulation by TC_SMMR

void setup() {

  Serial.begin(250000);

  PMC->PMC_PCER1 = PMC_PCER1_PID33                  // TC6 power ON ; Timer counter 2 channel 0 is TC6
                   | PMC_PCER1_PID34;               // TC7 power ON ; Timer counter 2 channel 1 is TC7

  // TC6 in capture mode
  TC2->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1   // capture mode, MCK/2, clk on rising edge
                              | TC_CMR_ABETRG              // TIOA6 is used as an external trigger.
                              | TC_CMR_LDRA_EDGE           // RA loading on each edge of TIOA6
                              | TC_CMR_ETRGEDG_RISING;     // External TC trigger by edge selection of TIOA

  TC2->TC_BMR = TC_BMR_QDEN                                // Enable QDEC (filter, edge detection and quadrature decoding)
                | TC_BMR_POSEN                             // Enable the position measure on channel 0
                | TC_BMR_EDGPHA                            // Edges are detected on both PHA and PHB
                // | TC_BMR_SWAP                              // Swap PHA and PHB if necessary
                | TC_BMR_MAXFILT(1);                       // Pulses with a period shorter than MAXFILT+1 peripheral clock cycles are discarded

  TC2->TC_CHANNEL[0].TC_IER = TC_IER_COVFS                 // Interruption enable on TC_CV overflow ( TC_CV = 0xFFFFFFFF)
                              | TC_IER_CPCS;               // Interruption enable on TC_CV underflow ( TC_CV = 0)

  // Enable Compare Fault Channel 0
  TC2->TC_FMR = TC_FMR_ENCF0;                              // To trigger an interrupt whenever TC_CV = TC_RC = 0

  NVIC_EnableIRQ(TC6_IRQn);

  TC2->TC_CHANNEL[0].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;
  TC2->TC_CHANNEL[1].TC_CCR = TC_CCR_CLKEN | TC_CCR_SWTRG;

  /*************************************************************************************/
  /*   This part of setup() simulates PHA and PHB of a quadrature encoder              */
  /*************************************************************************************/
  PMC->PMC_PCER0 |= PMC_PCER0_PID27;                      // TC0 power ON : Timer Counter 0 channel 0 IS TC0

  PIOB->PIO_PDR = PIO_PDR_P25
                  | PIO_PDR_P27;
  PIOB->PIO_ABSR = PIO_PB25B_TIOA0
                   | PIO_PB27B_TIOB0;

  TC0->TC_CHANNEL[0].TC_SMMR = TC_SMMR_GCEN;              // Gray Code divides TC frequency by 4

  TC0->TC_CHANNEL[0].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK1  // MCK/2, clk on rising edge
                              | TC_CMR_EEVT_XC0           // TIOB0 in output
                              | TC_CMR_WAVE;              // Waveform mode

  TC0->TC_CHANNEL[0].TC_RC = PERIOD_VALUE;  //<*********************  TIOA0 Frequency = (Mck/2/TC_RC/4
  TC0->TC_CHANNEL[0].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC0 counter and enable
}

void loop() {

  static uint32_t Oldmillis;
  static uint32_t Timestamp = 5000;

  // Reverse CW <--> CCW every 10 seconds
  if ((millis() - Oldmillis) > Timestamp) {
    Timestamp = 10000;
    Oldmillis = millis();
    TC0->TC_CHANNEL[0].TC_SMMR ^= TC_SMMR_DOWN;
  }
  AbsPos = AbsPosBase + (uint32_t)TC2->TC_CHANNEL[0].TC_CV;
  printf( // Print a 64-bit variable
    "%lld\n",
    AbsPos
  );
  delay(LOOP_DELAY_MS);
}

void TC6_Handler() {

  uint32_t status = TC2->TC_CHANNEL[0].TC_SR;

  if (status & TC_SR_COVFS) {
    // TC_CV overflow --> TC_CV = 0xFFFFFFFF
    AbsPosBase += 0xFFFFFFFFllu;
  }
  else  { // if (status & TC_SR_CPCS)
    // TC_CV underflow --> TC_CV = 0
    AbsPosBase -= 0xFFFFFFFFllu;
  }
}