Changing Arduino Zero PWM Frequency

The following code will output 370Hz on pin D7 with an 11-bit resolution:

// Output 370Hz PWM on timer TCC0 (11-bit resolution) on D7
void setup()
{
  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

  // Enable the port multiplexer for the 1 PWM channel: timer TCC0 output
  const uint8_t CHANNELS = 1;
  const uint8_t pwmPins[] = { 7 };
  for (uint8_t i = 0; i < CHANNELS; i++)
  {
     PORT->Group[g_APinDescription[pwmPins[i]].ulPort].PINCFG[g_APinDescription[pwmPins[i]].ulPin].bit.PMUXEN = 1;
  }
  // Connect the TCC0 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E specify the timers: TCC0, TCC1 and TCC2
  
  PORT->Group[g_APinDescription[6].ulPort].PMUX[g_APinDescription[6].ulPin >> 1].reg = PORT_PMUX_PMUXO_F;

  // Feed GCLK4 to TCC0 and TCC1
  /*REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN |         // Enable GCLK4 to TCC0 and TCC1
                     GCLK_CLKCTRL_GEN_GCLK4 |     // Select GCLK4
                     GCLK_CLKCTRL_ID_TCC0_TCC1;   // Feed GCLK4 to TCC0 and TCC1*/
  GCLK->CLKCTRL.reg = 0x441A;                     // Added for compilation problem with Arduino M0
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Dual slope PWM operation: timers countinuously count up to PER register value then down 0
  REG_TCC0_WAVE |= TCC_WAVE_POL(0xF) |         // Reverse the output polarity on all TCC0 outputs
                    TCC_WAVE_WAVEGEN_DSBOTTOM;    // Setup dual slope PWM on TCC0
  while (TCC0->SYNCBUSY.bit.WAVE);               // Wait for synchronization

  // Each timer counts up to a maximum or TOP value set by the PER register,
  // this determines the frequency of the PWM operation:
  // 20000 = 50Hz, 10000 = 100Hz, 2500  = 400Hz, 2702 = 370Hz
  REG_TCC0_PER = 2702;      // Set the frequency of the PWM on TCC0 to 370Hz
  while(TCC0->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us)
  REG_TCC0_CCB3 = 1500;       // TCC0 CCB3 - center the servo on D7
  while(TCC0->SYNCBUSY.bit.CCB3);

  // Divide the 16MHz signal by 8 giving 2MHz (0.5us) TCC0 timer tick and enable the outputs
  REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV8 |    // Divide GCLK4 by 8
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC0->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

Here the timer is being clocked with a 2MHz signal (48MHz/3 = 16MHz, 16MHz/8 = 2MHz), or in other words each tick has a period of 0.5uS (1/2MHz). We're using dual slope PWM, in this scenario the timer counts up to the PER register value then back down to 0 and so on. So the PER register determines how long the timer takes to cycle counting up and then back down or in other words it determines the PWM's frequency.

This is given by the formula for dual slope PWM: PWM frequency = timer frequency / (2 * PER).

If we know the PWM frequency this formula can be rewritten PER = timer frequency / (2 * PWM frequency).

Therefore PER = 2MHz / (2 * 370) = 2702. That's the value of our PER register for 370Hz.

The CCBx registers determine the duty cycle. They can be any value between 0 and PER. In this case a value of 1351 (2702/2) will give a 50% duty cycle.

The resolution of this signal is given by the formula: resolution = log(PER + 1)/log(2). For a PER register value of 2702 this gives a resolution of 11-bits.