Go Down

Topic: Arduino Zero TCC Capture (Read 45368 times) previous topic - next topic

trampas

I was not sending the resync command before reading counter.

Now that this fixed I am counting but the direction is always counting positive.

MartinL

#166
Feb 05, 2019, 10:25 am Last Edit: Feb 05, 2019, 10:47 am by MartinL
Hi trampas,

The easiest way to get this working, is to set-up the event channels for asynchronous operation and the interrupt inputs to work on level rather than edge detection. This allows the input signals to pass through the event channels to the TCC2 counter unhindered and for the timer to clock off the event edges instead.

Setting up the event channels for asynchronous operation also means that you don't have clock them with a generic clock.

Here's an example that sets up TCC2 to count input events on D12, with D10 determining timer count direction. The TCC2 counter value is output to the console:

Code: [Select]
// Setup TCC2 to count input events on D12, with D10 determining timer count direction
void setup()
{
  SerialUSB.begin(115200);                   // Send data back on the Zero's native port
  while(!SerialUSB);                         // Wait for the SerialUSB port to be ready
 
  PM->APBCMASK.reg |= PM_APBCMASK_EVSYS;     // Switch on the event system peripheral
 
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                      GCLK_CLKCTRL_GEN_GCLK0 |     // ....on GCLK0
                      GCLK_CLKCTRL_ID_TCC2_TC3;    // Feed the GCLK0 to TCC2 and TC3
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  // Enable the port multiplexer on digital pin D10 count and D12 input
  PORT->Group[g_APinDescription[10].ulPort].PINCFG[g_APinDescription[10].ulPin].reg |= PORT_PINCFG_PULLEN | PORT_PINCFG_PMUXEN;
  PORT->Group[g_APinDescription[12].ulPort].PINCFG[g_APinDescription[12].ulPin].reg |= PORT_PINCFG_PULLEN | PORT_PINCFG_PMUXEN;
 
  // Set-up the pin as an EIC (interrupt) peripheral on D10 and D12
  PORT->Group[g_APinDescription[12].ulPort].PMUX[g_APinDescription[12].ulPin >> 1].reg = PORT_PMUX_PMUXO_A | PORT_PMUX_PMUXE_A ;

  //attachInterrupt(10, NULL, HIGH);                                       // Attach interrupts to digital pin 10 (external interrupt 2)
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO2;                                 // Enable event output on external interrupt 2
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE2_HIGH;                            // Set event on detecting a HIGH level
 
  //attachInterrupt(12, NULL, HIGH);                                       // Attach interrupts to digital pin 12 (external interrupt 3)
  EIC->EVCTRL.reg |= EIC_EVCTRL_EXTINTEO3;                                 // Enable event output on external interrupt 3
  EIC->CONFIG[0].reg |= EIC_CONFIG_SENSE3_HIGH;                            // Set event on detecting a HIGH level
  EIC->CTRL.reg |= EIC_CTRL_ENABLE;                                        // Enable EIC peripheral
  while (EIC->STATUS.bit.SYNCBUSY);                                        // Wait for synchronization

  EVSYS->USER.reg = EVSYS_USER_CHANNEL(1) |                                // Attach the event user (receiver) to channel 0 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_0);              // Set the event user (receiver) as timer TCC2, event 0
 
  EVSYS->USER.reg = EVSYS_USER_CHANNEL(2) |                                // Attach the event user (receiver) to channel 1 (n + 1)
                    EVSYS_USER_USER(EVSYS_ID_USER_TCC2_EV_1);              // Set the event user (receiver) as timer TCC2, event 1

  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_3) |    // Set event generator (sender) as external interrupt 3
                       EVSYS_CHANNEL_CHANNEL(0);                           // Attach the generator (sender) to channel 0
 
  EVSYS->CHANNEL.reg = EVSYS_CHANNEL_EDGSEL_NO_EVT_OUTPUT |                // No event edge detection
                       EVSYS_CHANNEL_PATH_ASYNCHRONOUS |                   // Set event path as asynchronous
                       EVSYS_CHANNEL_EVGEN(EVSYS_ID_GEN_EIC_EXTINT_2) |    // Set event generator (sender) as external interrupt 2
                       EVSYS_CHANNEL_CHANNEL(1);                           // Attach the generator (sender) to channel 1
 
  TCC2->EVCTRL.reg |= TCC_EVCTRL_TCEI1 |                                   // Enable the TCC event 1 input
                      TCC_EVCTRL_TCEI0 |                                   // Enable the TCC event 0 input
                      //TCC_EVCTRL_TCINV1 |                                  // Invert the event 1 input
                      //TCC_EVCTRL_TCINV0 |                                  // Invert the event 0 input
                      TCC_EVCTRL_EVACT1_DIR |                              // Set event 1 to change the counter direction
                      TCC_EVCTRL_EVACT0_COUNTEV;                           // Set event 0 to count the incoming events

  TCC2->WAVE.reg = TCC_WAVE_WAVEGEN_NFRQ;                                  // Set the TCC2 timer counter to normal frequency mode
  while (TCC2->SYNCBUSY.bit.WAVE);                                         // Wait for synchronization
                                                                                     
  TCC2->CTRLA.bit.ENABLE = 1;                                             // Enable TCC2
  while (TCC2->SYNCBUSY.bit.ENABLE);                                      // Wait for synchronization
}

void loop()
{
  TCC2->CTRLBSET.reg = TCC_CTRLBSET_CMD_READSYNC;                         // Trigger a read synchronization on the COUNT register
  while (TCC2->SYNCBUSY.bit.CTRLB);                                       // Wait for the CTRLB register write synchronization
  while (TCC2->SYNCBUSY.bit.COUNT);                                       // Wait for the COUNT register read sychronization
  SerialUSB.println(TCC2->COUNT.reg);                                     // Output the TCC2 COUNT register
}


aromring

Hello Everyone,
I have implemented some of your code (dutifully acknowledged) in my project:

https://www.instructables.com/id/How-to-Measure-High-Frequency-and-Duty-Cycle-Simul/

Thank you for posting!

nappo

I would like to thank the many people contributing to the topic "Zero TCC Capture"! The information I found here regarding the configuration of the timers was very valuable for my project.

In particular the code of MartinL using the DMAC for reading the PWM values is a wonderful piece of software, since it can deliver a fast capture of pulse count rate including timestamps without any load of the main CPU (#107, TCCCapture_DMAC_TCCount.ino). Thank you very much!

By adapting the code for my needs, I found a little flaw. Though the TCC counter compare registers have 24 bit, the measured output for the period (CC0) already  overflows at 16bit, for example, by feeding the counter with a low enough frequency (e.g. 700 Hz). It turned out that this behaviour is due to the chosen beat size (HWORD, 16bit) for the DMA transfer. Changing the beat size to WORD (32bit) fixes this issue.

Simply replace the two instances of

descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_HWORD | DMAC_BTCTRL_VALID;

by

descriptor.btctrl = DMAC_BTCTRL_BEATSIZE_WORD | DMAC_BTCTRL_VALID;

Thanks again for your generous help!


Go Up