Changing Arduino Zero PWM Frequency

Hi rjastap,

The following code sets-up 50kHz, at 50% duty cycle on D9:

// Output 50kHz PWM on timer TCC1 (9-bit resolution)
void setup()
{
  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

  // Enable the port multiplexer for the PWM channel on pin D9  
  PORT->Group[g_APinDescription[9].ulPort].PINCFG[g_APinDescription[9].ulPin].bit.PMUXEN = 1;
  
  // Connect the TCC1 timer to the port outputs - port pins are paired odd PMUO and even PMUXE
  // F & E peripherals specify the timers: TCC0, TCC1 and TCC2
  PORT->Group[g_APinDescription[9].ulPort].PMUX[g_APinDescription[9].ulPin >> 1].reg |= PORT_PMUX_PMUXO_E; 

  // 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
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization

  // Normal (single slope) PWM operation: timers countinuously count up to PER register value and then is reset to 0
  REG_TCC1_WAVE |= TCC_WAVE_WAVEGEN_NPWM;        // Setup single slope PWM on TCC1
  while (TCC1->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: 959 = 50kHz
  REG_TCC1_PER = 959;      // Set the frequency of the PWM on TCC1 to 50kHz
  while(TCC1->SYNCBUSY.bit.PER);

  // The CCBx register value corresponds to the pulsewidth in microseconds (us) 
  REG_TCC1_CC1 = 480;      // Set the duty cycle of the PWM on TCC0 to 50%
  while(TCC1->SYNCBUSY.bit.CC1);
 
  // Enable TCC1 timer
  REG_TCC1_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 |    // Divide GCLK4 by 1
                    TCC_CTRLA_ENABLE;             // Enable the TCC0 output
  while (TCC1->SYNCBUSY.bit.ENABLE);              // Wait for synchronization
}

void loop() { }

Digital pin 9, or PA07 uses timer TCC1 on perhiperal E. The timer is clocked a 48MHz, as the generic clock divisor and the timer prescaler are set to 1. I've also set-up the timer work in normal PWM mode, rather than dual slope, as at 50kHz this will increase your resolution by 1-bit.

The formula for working out the frequency (in normal PWM mode) is:

timer frequency = generic clock frequency / (N * (PER + 1))

where:
N = timer prescaler (in this case 1)
PER = value in the period (PER) register

We can therefore rearrange this formula to find the PER value for a given timer frequency:

PER = 48MHz / 50kHz - 1 = 959

This will give you a 50kHz PWM output.

Loading the counter compare 1 register (REG_TCC1_CC1) with a value half that of the PER gives a 50% duty cycle.

To change the duty cycle during operation however, it's better to load the buffered counter compare register, in this case REG_TCC1_CCB1. Note the 'B'. For example, to set the duty cycle to 25%:

REG_TCC1_CCB1 = 240;                      // Load the CCB1 register with a new value
while(TCC1->SYNCBUSY.bit.CCB1);                  // Wait for synchronization

The buffered register loads the new value into the unbuffered CC1 register at the beginning of each timer cycle. Loading the CC1 register directly (not using the buffered register) may cause glitches on you output PWM waveform when changing the duty cycle.

The value of the CCB1 register can range from 0 = 0% duty cycle (0V), through to the value in the PER register, in this case 959 = 100% duty cycle (3.3V).

This configuration will give out an output resolution of 9-bits.