Metro M4 Samd51 timer not matching datasheet

Hi All,

I’ve run into a really weird issue with my adafruit Metro M4 samd51 Airlift board that I eventually figured out a solution for but I’m still very confused about the underlying cause / explanation.

On the adafruit schematic, and in the variant.cpp file for the board, pin D9 maps to PA20, and according to the multiplexing table (6-1) that should map (via output F (5)) to TCC1 WO[4].

But after forever trying to figure out why I couldn’t get that to work, I tried all the other TCC output channels, and found that using WO[0] worked! The thing is, I can’t for the life of me figure out why.

Any insight would be appreciated! (example code with working and not working TCC reg calls below.

Also many thanks to MartinL for setting me on the right path with all this stuff.

// Adafruit Metro M4 Only: Set-up digital pin D7 to output 50Hz, single slope PWM with a 50% duty cycle
void setup()
{

  // Set up the generic clock (GCLK7) to clock timer TCC1
  GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) |       // Divide the clock source by divisor 1
                         GCLK_GENCTRL_IDC |          // Set the duty cycle to 50/50 HIGH/LOW
                         GCLK_GENCTRL_GENEN |        // Enable GCLK7
                        // GCLK_GENCTRL_SRC_DFLL;      // Select 48MHz DFLL clock source
                         //GCLK_GENCTRL_SRC_DPLL1;     // Select 100MHz DPLL clock source
                         GCLK_GENCTRL_SRC_DPLL0;     // Select 120MHz DPLL clock source
  while (GCLK->SYNCBUSY.bit.GENCTRL7);               // Wait for synchronization

  GCLK->PCHCTRL[25].reg = GCLK_PCHCTRL_CHEN |        // Enable the TCC0 peripheral channel
                          GCLK_PCHCTRL_GEN_GCLK7;    // Connect generic clock 7 to TCC0

// enable MUX
PORT->Group[g_APinDescription[9].ulPort].PINCFG[g_APinDescription[9].ulPin].bit.PMUXEN = 1;
// Set MUX to ouput 5 (F)
PORT->Group[g_APinDescription[9].ulPort].PMUX[g_APinDescription[9].ulPin >> 1].reg |= PORT_PMUX_PMUXE(5);


TCC1->CTRLA.reg = TC_CTRLA_PRESCALER_DIV1 |        // Set prescaler to 1
                  TC_CTRLA_PRESCSYNC_PRESC;        // Set the reset/reload to trigger on prescaler clock

TCC1->WAVE.reg = TC_WAVE_WAVEGEN_NPWM;             // Set-up TCC1 timer for Normal (single slope) PWM mode (NPWM)
while (TCC1->SYNCBUSY.bit.WAVE)                    // Wait for synchronization

TCC1->PER.reg = 4994;                            // Set-up the PER (period) register 100 kHz PWM
while (TCC1->SYNCBUSY.bit.PER);                    // Wait for synchronization

TCC1->CTRLA.bit.ENABLE = 1;                        // Enable timer TCC1
while (TCC1->SYNCBUSY.bit.ENABLE);                 // Wait for synchronization

TCC1->CCBUF[0].reg = 1000; //  This one WORKS!
// TCC1->CCBUF[4].reg = 1000; // DOES NOT WORK!

}

void loop() {

}

Why is the channel wrong.ino (1.97 KB)

Hi jazzlw,

The timer outputs are one of the most poorly described aspects in the SAMD21 and SAMD51 datasheets.

The SAMD51 TCC timers have a set number of channels:

TCC0 - 6 channels (0 to 5)
TCC1 - 4 channels (0 to 3)
TCC2 - 3 channels (0 to 2)
TCC3 - 2 channels (0 to 1)
TCC4 - 2 channels (0 to 1)

The channel numbers relate to the timer outputs: WO[0], WO[1], WO[2], etc..., however, in the SAMD51 datasheet's I/O Multiplexing table, you'll notice that the timer outputs sometimes exceed the number of channels. For example timer TCC1 has only 4 channels, but 8 outputs: WO[0] through to WO[7].

In this case, the additional outputs can act either as alternative repeated outputs, so channels 0 to 3 are not only output on WO[0] to WO[3], but are also repeated on WO[4] to WO[7], or if dead-time insertion is activated, they can act as complementary low and high side signal pairs: WO[0]/WO[4], WO[1]/WO[5], WO[2]/WO[6] and finally WO[3]/WO[7].

Hi MartinL,

Thanks a bunch!

I was starting to think that the explanation was probably something along those lines because I remembered you saying something about the output channels being mirrored on some outputs in another thread, but I couldn't for the life of me find the place you'd mentioned it or find anything about it in the datasheet.

On a separate but related note, is there anything to recommend one clock source over another if I'm trying to have an accurate frequency pwm output? I'm thinking that I want to set things up so that the PER register is relatively high so that I can have fine control, but is there any difference between clock sources and / or prescalers for getting an accurate frequency reference?

Hi jazzlw,

On a separate but related note, is there anything to recommend one clock source over another if I'm trying to have an accurate frequency pwm output?

I guess it all depends how accurately you need to measure from an absolute reference point in time. Usually absolute accuracy isn't an issue with PWM, because you're just measuring time relative to the last pulse. In this case, it's the timer's resolution and clock rate that come into play.

In terms of clock sources, Adafruit have set up their SAMD51 boards with the 48MHz Digital Frquency Locked Loop (DFLL48M) in open loop mode. In open loop mode the 48MHz DFLL is self contained and uses its own built-in reference, but is less accurate than closed loop mode.

According to the SAMD51 datasheet, in open loop mode the DFLL output frequency can vary between 47.2MHz and 48.8MHz over 0ºC to 60ºC.

Activating 48MHz DFLL in closed loop mode requires a 32kHz reference clock source, (such as an 32.768kHz external crystal), to be routed to it via a generic clock. This source is then multiplied up to 48MHz, allowing the DFLL to lock on to this more accurate reference.

The 120MHz and 100MHz Digital Phase Locked Loops (DPLL0 and DPLL1 respectively) are both derived from the 48MHz DFLL. It's possible to configure either to operate at up to 200MHz. The TCC timers can operate at this higher frequency.

Running the timers at a higher frequency however is double edged sword. At 200MHz you can step up and down the pulse widths in tiny 5ns steps, but with a 24-bit timer you quickly run out of road, as the timer will overflow in a mere 84ms.

Thanks MartinL,

That all makes a lot of sense. I'm just trying to have an output waveform that is as close as possible to the desired frequency, eg adjustable from 1 to 500 Hz and able to hit any of these integers at least to a tenth of a Hz (eg 50.0 Hz).

I've got it set up with the 48 MHz clock and a prescaler of 8, which works quite well overall but there is some variation in the output where it comes out 50.1 or 50.2 or some such, and since sometimes it comes out dead on I figure this is likely because of the variation in the internal clock that you mentioned.

Sounds like I may need to add an external crystal and connect that in if I want more accurate / consistent output.

Thanks again!