Hi jazzlw,
Yes, for analogWrite() the Adafruit M4 boards configure the timers for 8-bits, clock them at 120MHz and use a 256 prescaler:
120MHz / 256 / 2^8 = 1.83kHz
...where as Arduino Zero/MKR boards configure the timers for 16-bits, clock them at 48MHz and use no prescaler:
48MHz / 2^16 = 732Hz
The SAMD51J19A used on the Metro M4 has a huge amout of timers, there's five fully featured TCCx timers (TCC0 to TCC4), plus six standard TC timers (TC0 to TC5) and that's excluding the system tick timer and the RTC timer that are available as well. Two of the TCCx timers are 24-bit, the rest are 16-bit. All the TC timers are 16-bit, but can be chained together in pairs to create a 32-bit timer.
The timers need to be driven by a clock source via the generic clock system. On the Metro M4 boards Adafruit have set up the SAMD51's Digital Phase and Frequency Locked Loops to generate 48MHz, 100MHz and 120MHz clock sources. It's possible to configure the generic clock system to route any of these clock sources to the timer.
The timers themselves operate in a very similar manner to the SAMD21's, but unfortunately the generic clock set-up is slightly different.
In addition, it's necessary to also configure the microcontroller's IO pins, to select the timer output. This is achieved by selecting the pin's peripheral multiplexer and switching the multiplexer to the correct timer output. Which timer channel is connected to what pin is defined in the I/O Multiplexing and Considerations chapter in the SAMD51 datasheet.
Here's an example that outputs a 50Hz, 50% duty-cycle PWM signal with 16-bit resolution on D7:
// 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 TCC0
GCLK->GENCTRL[7].reg = GCLK_GENCTRL_DIV(1) | // Divide the 48MHz clock source by divisor 1: 48MHz/1 = 48MHz
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 the peripheral multiplexer on pin D7
PORT->Group[g_APinDescription[7].ulPort].PINCFG[g_APinDescription[7].ulPin].bit.PMUXEN = 1;
// Set the D7 (PORT_PB12) peripheral multiplexer to peripheral (even port number) E(6): TCC0, Channel 0
PORT->Group[g_APinDescription[7].ulPort].PMUX[g_APinDescription[7].ulPin >> 1].reg |= PORT_PMUX_PMUXE(6);
TCC0->CTRLA.reg = TC_CTRLA_PRESCALER_DIV8 | // Set prescaler to 8, 48MHz/8 = 6MHz
TC_CTRLA_PRESCSYNC_PRESC; // Set the reset/reload to trigger on prescaler clock
TCC0->WAVE.reg = TC_WAVE_WAVEGEN_NPWM; // Set-up TCC0 timer for Normal (single slope) PWM mode (NPWM)
while (TCC0->SYNCBUSY.bit.WAVE) // Wait for synchronization
TCC0->PER.reg = 119999; // Set-up the PER (period) register 50Hz PWM
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
TCC0->CC[0].reg = 59999; // Set-up the CC (counter compare), channel 0 register for 50% duty-cycle
while (TCC0->SYNCBUSY.bit.CC0); // Wait for synchronization
TCC0->CTRLA.bit.ENABLE = 1; // Enable timer TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization
}
void loop() {}