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;
}
}