Porting AVR hardware time code to ARM platforms

I'm looking for some sort of tutorial or documentation that would help me port some hardware specific code regarding timers from AVR platforms to the Arduino Zero. I either need someone to point me in the right direction or better yet a volunteer who would donate some time to an open source project.

I maintain a library of code for transmitting and receiving infrared signals called IRLib2. It makes use of hardware timers on AVR based microcontrollers but I would like to expand support to Arduino Zero and other ARM M0+ base platforms.

I have two particular needs. First of all I need to be able to generate a hardware timer driven interrupt every 50 µs that will call and interrupt service routine defined in the traditional AVR ISR (type) {…} macro format from avr/interrupt.h or some equivalent method. Is there an ARM equivalent to avr/interrupt.h?

Additionally I need to generate a PWM signal with a duty cycle of 33% however I also need to specify the frequency of that signal. I need to be able to set any value in the range approximately 35 kHz up to 59 kHz.

I barely understand how to use AVR hardware timers to do this. Most of my code has been adapted from other examples. Naturally the ARM hardware details are significantly different and I've looked at the SAM-D21 datasheet and I'm thoroughly lost.

Even if I was able to figure this out, I'm concerned that I don't want to do something that is mine to mess up other normal operation of the device. For example I know that AVR based arduino systems use hardware timer TIMER0 for the delay() and delaymicroseconds () functions and the mills() and micro() functions. Is there documentation that explains what they should not mess with to avoid conflicts with standard Arduino Zero infrastructure?

Hi cyborg5,

The 50kHz PWM code can be found on this forum page: Changing Arduino Zero PWM Frequency - Arduino Zero - Arduino Forum.

If you wish to change the frequency during operation then it's best to use the TCC1's buffered period register (REG_TCC1_PERB). The frequency can be calculated from the equation shown.

The code for calling the timer (in this case TC4 using match frequency mode) at 50us intervals:

// Set timer TC4 to call the TC4_Handler every 50us
void setup() {
  // Set up the generic clock (GCLK4) used to clock timers
  REG_GCLK_GENDIV = GCLK_GENDIV_DIV(1) |          // Divide the 48MHz clock source by divisor 1: 48MHz/1=48MHz
                    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_COUNT16_CC0 = 2399;                     // Set the TC4 CC0 register as the TOP value in match frequency mode
  while (TC4->COUNT16.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_OVF;              // Clear the interrupt flags
  REG_TC4_INTENSET = TC_INTENSET_OVF;             // Enable TC4 interrupts
  // REG_TC4_INTENCLR = TC_INTENCLR_OVF;          // Disable TC4 interrupts
 
  REG_TC4_CTRLA |= TC_CTRLA_PRESCALER_DIV1 |      // Set prescaler to 11, 48MHz/1 = 48MHz
                   TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode 
                   TC_CTRLA_ENABLE;               // Enable TC4
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);       // Wait for synchronization
}

void loop() {}

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

The Arduino Zero uses its system ticker to generate delay(), delaymicroseconds(), millis() and micros(). It doesn't use timer0 like the AVR Arduinos, so you won't mess up these functions by using any of the timers. This leaves timer counters peripherals: TCC0, TCC1, TCC2, TC3, TC4 and TC5 free to be used for your own purposes.

nota bene: while the system may not use them, certain libraries might.
Servo.h uses TC4
Tone.h uses TC5

MartinL:
The code for calling the timer (in this case TC4 using match frequency mode) at 50us intervals:

Many thanks for this excellent response. Your sample code uses TC4 and I also think I see references to TC5 in the code itself. Note that another respondent has pointed out that other popular libraries such as Servo.h and Tone.h make use of TC4 and TC5. While I will stick with your sample code initially and simply put warnings about the conflicts with the other libraries, was there a particular reason you chose those timers and how hard would it be to use one of the other sets of timers for the same purpose? Do all six of the timers that you mentioned operate the same or do some of them have special properties?

...how hard would it be to use one of the other sets of timers for the same purpose?

Yes, to avoid conflicts it's possible to use timer TC3. Just change TC4 to TC3 throughout the code and the definition: GCLK_CLKCTRL_ID_TC4_TC5 to GCLK_CLKCTRL_ID_TCC2_TC3.

Do all six of the timers that you mentioned operate the same or do some of them have special properties?

Register-wise the TCCx and TCx timers are very different.

The TCCx timers are timer counters for control applications and as such are designed for PWM output. Timers TCC0 and TCC1 include extra functionality such as dithering, fault protection and pattern generation. TCC0 additionally has dead time insertion. Timers TCC0 and TCC1 are 24-bits wide, while TCC2 is only 16-bits. In terms of outputs TCC0 has 4 channels, while TCC1 and TCC2 each have 2, allowing for up to 8 PWM channels in total.

TC3, TC4 and TC5 are standard 16-bit timer counters. They are capable of producing PWM outputs, but have less functionality, so are generally better suited to internal timing duties.

Martin,

You summarized the characteristics of the timers derivable from the SAMD21 lower-numbered GCLK - Thanks.

I have a timer conflict question that I think belongs in this topic.

I have an MKRZero program that has been using TC4 & TC5. I want to integrate the Arduino IDE's built-in Servo library into that program.

The Servo library uses a TC4_Handler, and a TC5_Handler, so to avoid conflicts, I'm considering switching my code to using GCLK6 & GCLK7, and to using TC6 &TC7.

So here's my question. Is there a simple answer to "Do I need to worry about differences between TC4/TC5 and TC6/TC7, or can I simply blindly switch all my names from 4/5 to 6/7?"

If I need to spend some time in the datasheet(s), I will; but if I don't need to do that I'd rather not discover there are no differences by spending hours reading them.

Hi Metron_Ross,

Unfortunately the SAMD21G18A used on the MKRZero doesn't incorporate the TC6 and TC7 timers. These additional timers are only available on the larger, 64-pin SAMD21J variant. This leaves timer TC3, plus TCC timers: TCC0, TCC1 and TCC2.

Perhaps the easiest solution, if your TCCx timers and corresponding output pins are available, is to use the TCCx timers to generate the servo output instead of using the servo library. Or alternatively change your program using the TC4 and TC5 timers to use the TCCx timers.

Thanks Martin.

  1. For indulging my laziness :wink:
  2. For the consistently useful info.

Obviously, I was hoping to avoid "rolling my own" replacement for the Servo library. In the code I'm modifying, TC4 is my 1 ms hard-realtime metronome/heartbeart, and TC5 gets uses whenever I spin my stepper motor.

I've used the Servo library many times, so I don't want to complain about it too much; but if the Arduino gods are listening ...

According to one old-ish post I read, the Servo library uses TC0, TC2, TC3, TC4, and TC5 "_Handler" ISRs. However, inserting fake handlers (with those TC0-TC4 names) into myprogram that uses the SAMD21 version of the library, only triggers a duplicate TC4 complaint from the compiler. Maybe I can just make a "blind conversion from TC4/5 to TC2/3.

A) Wouldn't it be nice if timer use info was part of every library description in the Arduino Reference pages? ? ? ? ? ?

B) Wouldn't it be nice if the Servo and other libraries allowed users to rearrange the Timer use choices (to the extent possible) by invoking a configuration subroutine, or by inserting #defines?

Thanks again Martin.

Hi Metron_Ross,

According to one old-ish post I read, the Servo library uses TC0, TC2, TC3, TC4, and TC5 "_Handler" ISRs. However, inserting fake handlers (with those TC0-TC4 names) into myprogram that uses the SAMD21 version of the library, only triggers a duplicate TC4 complaint from the compiler. Maybe I can just make a "blind conversion from TC4/5 to TC2/3.

Your sketch is most likely not compiling correctly becuse timers 0 to 2 are actually TCC timers, which stands for Timer/Count for Control applications. These are more fully featured than the TC3 to TC5 timer peripherals. Timers TC0 to TC2 don't exist on the SAMD21, (although they do on its sister microcontroller the SAMD51).

One option is to go into the servo library's "ServoTimers.h" file and change references for timer1 from TC4 to TC3. This file is located on my Windows machine at:

C:\Program Files (x86)\Arduino\libraries\Servo\src\samd\ServoTimers.h

Another, if you wish, is to let me know which pins you would like to use for your servos on and I'll get back to you with some code that will make them work like the servo library. I've got some template code for operating servos at 50Hz, so it's not any trouble.