ATSAMD21G18 32-bit Timer counter.

Hi All. I'm wanting to set up a 100Khz 32-bit timer on my little Industruino.. I figured it couldn't be too hard... but as my eyes poured over the datasheet my brain went a bit melty... they're a lot more complex than AVR T/Cs... so many registers.
Basically just wanna start the T/C.. let it tick away at around ~100kHz and occasionally read it's value..
Can someone help me out a bit?

Hi Cheetor,

I’m assuming that you require the TC timer to be clocked at 100kHz, rather than overflow at 100kHz?

Here’s the code that clocks the TC timer at 100kHz:

// Setup TC4/TC5 in 32-bit mode with 100kHz tick
void setup()
{
  SerialUSB.begin(115200);
  while (!SerialUSB);
  
  GCLK->GENDIV.reg = GCLK_GENDIV_DIV(30) |         // Divide the 48MHz system clock by 3 = 1.6MHz
                     GCLK_GENDIV_ID(5);            // Set division on Generic Clock Generator (GCLK) 5
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->GENCTRL.reg = GCLK_GENCTRL_IDC |           // Set the duty cycle to 50/50 HIGH/LOW
                      GCLK_GENCTRL_GENEN |         // Enable GCLK 5
                      GCLK_GENCTRL_SRC_DFLL48M |   // Set the clock source to 48MHz
                      GCLK_GENCTRL_ID(5);          // Set clock source on GCLK 5
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization

  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |         // Enable the generic clock...
                      GCLK_CLKCTRL_GEN_GCLK5 |     // ....on GCLK5
                      GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK5 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);               // Wait for synchronization
 
  TC4->COUNT32.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV16 |     // Set prescaler to 16, 1.6MHz/16 = 100kHz
                            TC_CTRLA_PRESCSYNC_PRESC |     // Reload timer on next prescaler clock
                            TC_CTRLA_MODE_COUNT32;         // Set the TC4 timer to 32-bit mode in conjuction with timer TC5
                   
  TC4->COUNT32.CTRLA.bit.ENABLE = 1;               // Enable TC4
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  
  TC4->COUNT32.READREQ.reg = TC_READREQ_RCONT |            // Enable a continuous read request
                             TC_READREQ_ADDR(0x10);        // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);                // Wait for (read) synchronization
}

void loop()
{
  SerialUSB.println(TC4->COUNT32.COUNT.reg);       // Output the results
  delay(1000);                                     // Wait for 1 second
}

Bear in mind though that running the TC timer at a 100kHz tick, requires it to be clocked at 1.6MHz (rather than 48MHz). This will dramatically increase the synchronization time when accessing the TC4 timer’s registers, as it affects the whole timer peripheral and not just the timer’s COUNT register.

Hi, Thanks for that. And yes you were right, ticking at 100kHz.... I'd hate to think how fast I'd have to run it to overflow at 100kHz.... Hmm 2^32 * 100k... That'd require me to clock the timer from 429THz clock :o ... Now about this synchronisation, Tell me if I've got it right.

There are 2 asynchronous clock sources involved.. The core running off the 48MHz PLL clock... but we're clocking the timer/counter from this separate 1.6MHz clock. So am I right that if I want to read the T/C COUNT reg, that will block until the next tick on the 1.6MHz Clock?

Hi Cheetor,

So am I right that if I want to read the T/C COUNT reg, that will block until the next tick on the 1.6MHz Clock?

According to the SAMD21 datasheet, if you’re using continous read synchronization as in the example code above then the register is continously synchronized and the latest value is always available. This implies that the code doesn’t block, but simply gives you the last synchronized register value.

If the other hand you’re using basic read synchronization with a while loop, then the code will block until the 1.6MHz on GCLK5 synchronizes with the 48MHz PLL on GCLK0:

void loop()
{
  TC4->COUNT32.READREQ.reg = TC_READREQ_RREQ |       // Enable a basic read request
                             TC_READREQ_ADDR(0x10);  // Offset of the 32-bit COUNT register
  while (TC4->COUNT32.STATUS.bit.SYNCBUSY);          // Wait for (read) synchronization
  SerialUSB.println(TC4->COUNT32.COUNT.reg);         // Output the results
  delay(1000);                                       // Wait for 1 second
}

The synchonization delay given by the formula:

Synchronization Delay
The synchronization will delay write and read accesses by a certain amount. This delay is within the
range of:

5 × Period(GCLK) + 2 × Period(APB) < Delay < 6 × Period(GCLK) + 3 × Period(APB)

Where Period(GCLK) is the period of the generic clock (at 1.6MHz) and Period(APB) is the period of the peripheral bus clock (at 48MHz). A normal peripheral bus register access duration is 2 × Period(APB).

Thanks so much for that.. I've got it doing exactly what I need now.