TimeOne Library for MKR1000?

Hi, everyone:

I am trying to implement a function that runs periodically. I find that TimeOne and TimeThree libraries have good functions to implement the scheduling. However, neither of them supports MKR1000. I am wondering if there is a similar library for MKR1000.

Thanks!

Hi cloudfarm,

The following code sets up timer TC4 in 8-bit mode. In this example the interrupt service routine is called roughly every millisecond (1ms):

void setup() {
   // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(3) |          // Divide the 48MHz clock source by divisor 3: 48MHz/3=16MHz
                    GCLK_GENDIV_ID(4);            // Select Generic Clock (GCLK) 4
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

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

  // Feed GCLK4 to TC4 and TC5
  REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TC4 and TC5
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TC4_TC5;     // Feed the GCLK4 to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
 
  REG_TC4_CTRLA |= TC_CTRLA_MODE_COUNT8;           // Set the counter to 8-bit mode
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  REG_TC4_COUNT8_CC0 = 0x55;                      // Set the TC4 CC0 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_CC1 = 0xAA;                      // Set the TC4 CC1 register to some arbitary value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
  REG_TC4_COUNT8_PER = 0xFF;                      // Set the PER (period) register to its maximum value
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization

  //NVIC_DisableIRQ(TC4_IRQn);
  //NVIC_ClearPendingIRQ(TC4_IRQn);
  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  REG_TC4_INTFLAG |= TC_INTFLAG_MC1 | TC_INTFLAG_MC0 | TC_INTFLAG_OVF;        // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_MC1 | TC_INTENSET_MC0 | TC_INTENSET_OVF;     // Enable TC4 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_MC1 | TC_INTENCLR_MC0 | TC_INTENCLR_OVF;     // Disable TC4 interrupts
 
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV64 |     // Set prescaler to 64, 16MHz/64 = 256kHz
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT8.STATUS.bit.SYNCBUSY);        // Wait for synchronization
}

void loop() {
  // put your main code here, to run repeatedly:

}

void TC4_Handler()                              // Interrupt Service Routine (ISR) for timer TC4
{     
  // Check for overflow (OVF) interrupt
  if (TC4->COUNT8.INTFLAG.bit.OVF && TC4->COUNT8.INTENSET.bit.OVF)             
  {
    // Put your timer overflow (OVF) code here:     
    // ...
   
    REG_TC4_INTFLAG = TC_INTFLAG_OVF;         // Clear the OVF interrupt flag
  }

  // Check for match counter 0 (MC0) interrupt
  if (TC4->COUNT8.INTFLAG.bit.MC0 && TC4->COUNT8.INTENSET.bit.MC0)             
  {
    // Put your counter compare 0 (CC0) code here:
    // ...
   
    REG_TC4_INTFLAG = TC_INTFLAG_MC0;         // Clear the MC0 interrupt flag
  }

  // Check for match counter 1 (MC1) interrupt
  if (TC4->COUNT8.INTFLAG.bit.MC1 && TC4->COUNT8.INTENSET.bit.MC1)           
  {
    // Put your counter compare 1 (CC1) code here:
    // ...
   
    REG_TC4_INTFLAG = TC_INTFLAG_MC1;        // Clear the MC1 interrupt flag
  }
}

In this example the source for generic clock 4 (GCLK4) is set at 48MHz. This is then divided by 3 to create 16MHz. The 16MHz signal is then passed to timer TC4. The prescaler for timer TC4 is set to divide the 16MHz signal by 64 giving a timer clocking frequency of 250kHz. In 8-bit mode the timer's PER (period) register defines it TOP value. In this case, as timers PER register is set to 0xFF (or 255 in decimal), dividing 250kHz by 255 + 1 gives 976Hz, or in other words a period of just over 1ms.

The timer has three main interrupts: overflow (OVF), match counter 0 (MC0) and match counter 1 (MC1). MC0 and MC1 interrupts are called when the timer's count value matches the counter compare registers: CC0 and CC1.

It's possible to change the generic clock divider, timer prescaler or the timer's PER register to alter the interrupt frequency.

The frequency is calculated from the equation:

frequency = gclk_frequency / (N * (PER + 1))

where
gclk_frequency = generic clock frequency fed to the timer
N = timer's prescaler
PER = timer's period register value

So for the above example:

frequency = 16MHz / (64 * (255 + 1)) = 976Hz

Thank you so much, Martin. This thread contains great information and solves my problem. For curiosity, is this implementation different from RTC library (GitHub - arduino-libraries/RTCZero: RTC Library for SAMD21 based boards)?

For curiosity, is this implementation different from RTC library (GitHub - arduino-libraries/RTCZero: RTC Library for SAMD21 based boards)?

I'm not familiar with the Zero's RTC library.

Thank you Martin. Could you let me know where to find the documentation of interrupt? I want to change divider but not sure where they are defined.

Thanks!

MartinL:
I'm not familiar with the Zero's RTC library.

Hi

Could you let me know where to find the documentation of interrupt?

The documentation for the interrupts isn't described in the SAMD21 datasheet. Instead the interrupt functions are listed in the ARM document: Cortex M0+ Devices Generic User Guide, page 4-7. There's also the Cortex M0+ Technical Reference Manual as well.

Here's a link to the ARM Information Center the Cortex M0+ Generic User Guide: Documentation – Arm Developer

The #defines for the SAMD21 registers are stored in a number of Atmel files, on my Windows machine they're loccated at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\samd21\include...

In this directory there are the "component" and "instance" sub-directories. The "component" sub-directory contains a number of files that define the structure and bitfields for each peripheral. For example file "tc.h" is for the TC counter timers. The "instance" sub-diretory on the other hand contains the register definitions for each instance of the peripheral. For example "tc3.h" defines the registers for counter timer TC3, "tc4.h" is for TC4 and so on.

Documentation for the TC timer counter and generic clock GCLK registers themselves can be found in the SAMD21 datasheet.

Thank you so much!

MartinL:
Hi

The documentation for the interrupts isn't described in the SAMD21 datasheet. Instead the interrupt functions are listed in the ARM document: Cortex M0+ Devices Generic User Guide, page 4-7. There's also the Cortex M0+ Technical Reference Manual as well.

Here's a link to the ARM Information Center the Cortex M0+ Generic User Guide: Documentation – Arm Developer

The #defines for the SAMD21 registers are stored in a number of Atmel files, on my Windows machine they're loccated at:

C:\Users\Computer\AppData\Local\Arduino15\packages\arduino\tools\CMSIS\4.0.0-atmel\Device\ATMEL\samd21\include...

In this directory there are the "component" and "instance" sub-directories. The "component" sub-directory contains a number of files that define the structure and bitfields for each peripheral. For example file "tc.h" is for the TC counter timers. The "instance" sub-diretory on the other hand contains the register definitions for each instance of the peripheral. For example "tc3.h" defines the registers for counter timer TC3, "tc4.h" is for TC4 and so on.

Documentation for the TC timer counter and generic clock GCLK registers themselves can be found in the SAMD21 datasheet.

based on info posted here and based on the "AudioZero" lib for the Arduino zero I tried to make a simple-to-use "Timer5" library which uses the timer5 of the SAMD21 - see:

I'm using it for low frequencies (1 .. 400Hz) by using the 32kHz clock "GCLK1", but if you change line 64 in timer5.cpp and use the 48Mhz clock GCLK0 instead of GCLK1 you can increase the frequency.

rgs,
Michael.

MartinL:
Hi cloudfarm,

The following code sets up timer TC4 in 8-bit mode. In this example the interrupt service routine is called roughly every millisecond (1ms):

Thank you very much for this helpful example.

I'd like to use the match counter interrupt in this example. How do I access the CC0/CC1 register?
Where can I find the file where the timer structure is defined?

thanks!

Hi endrew,

How do I access the CC0/CC1 register?

To access the TC4 timer CC0 register directly in 8-bit mode:

TC4->COUNT8.CC[0].reg = 0;

For the CC1 just substitute 1 for 0 in the CC array.

Where can I find the file where the timer structure is defined?

The location is described in post #5 above, although the latest CMSIS files are located in the "CMSIS-Atmel" rather than "CMSIS" folder, under in version "1.2.0" sub-directory.