Setting up a generic clock on the SAMD21

I've written a sketch to set up generic clock 2 to output the 32 kHz crystal oscillator on pin PA16 (Pin eight on the MKRGSM1400). As written below, I get a 32 KHz signal on PA16 as expected. But there are some issues. When I change the clock source to (say) GCLK_GENCTRL_SRC_OSC8M (8 MHz oscillator) or GCLK_GENCTRL_SRC_DFLL48M (48 MHz DLL) I still get a 32 kHz signal. If I try a different generic clock and pin combination, such as generic clock 4 on PA10 (as per the I/O multiplexing section of the SAMD21 datasheet), I just get a high or low signal corresponding to the bit GCLK_GENCTRL_OOV (output off value) in register GENCTRL.

I can't imagine what causes this behaviour and I'm out of ideas for debugging this :confused:

void setupGenericClock(unsigned int clockGeneratorID, unsigned int divisionFactor, bool divFactorIsLogarithmic, 
unsigned int oscillatorToUse, bool outputPinDefaultsHigh, bool outputPinIsEnabled,
bool outputPinClocksInStandby, unsigned int peripheralToClock, bool clockIsWriteProtected)
{
  //1. Write to the GENDIV register to set the division factor
  GCLK->GENDIV.reg =  GCLK_GENDIV_ID(clockGeneratorID) |
                      GCLK_GENDIV_DIV(divisionFactor);

  //2. Write to the GENCTRL register to select the oscillator source
  GCLK->GENCTRL.reg = GCLK_GENCTRL_ID(clockGeneratorID) |
                      GCLK_GENCTRL_SRC(oscillatorToUse) |
                      GCLK_GENCTRL_GENEN |
                      GCLK_GENCTRL_IDC |
                      (GCLK_GENCTRL_OOV * outputPinDefaultsHigh) |
                      (GCLK_GENCTRL_OE * outputPinIsEnabled) |
                      (GCLK_GENCTRL_DIVSEL * divFactorIsLogarithmic) |
                      (GCLK_GENCTRL_RUNSTDBY * outputPinClocksInStandby);

  //3. Write to the CLKCTRL register to enable the clock generator and point it at the peripheral
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(peripheralToClock) | //datasheet is inconsistent; this is the id of the peripheral.
                      GCLK_CLKCTRL_GEN(clockGeneratorID) |
                      GCLK_CLKCTRL_CLKEN;
}

const int PA{0};
const int PB{1};

// To use pin Pxy as one of the available peripheral functions, the corresponding PMUXEN bit of the PINCFGy register must be '1'.
void configurePinForClockOutput(int port, int pinNumberOfPort) {
  const int PMUX_register_number = pinNumberOfPort/2; //intentionally narrowing to an int b/c each register serves two pins (see p.433)
  const int PINCFG_register_number{pinNumberOfPort};
  const bool pinNumberIsOdd = pinNumberOfPort % 2;
  if(pinNumberIsOdd)
  {
    PORT->Group[port].PMUX[PMUX_register_number].bit.PMUXO = PORT_PMUX_PMUXO_H_Val;
  } else
  {
    PORT->Group[port].PMUX[PMUX_register_number].bit.PMUXE = PORT_PMUX_PMUXE_H_Val;
  }
  PORT->Group[port].PINCFG[PINCFG_register_number].bit.PMUXEN = 1;
}


int main() {
  setupGenericClock(2, 1, false, GCLK_GENCTRL_SRC_XOSC32K, true, true, true, GCM_WDT, false);
  configurePinForClockOutput(PA, 16);
  while(1);
}

/* Clock choices:
GCLK_GENCTRL_SRC_XOSC     
GCLK_GENCTRL_SRC_GCLKIN   
GCLK_GENCTRL_SRC_GCLKGEN1 
GCLK_GENCTRL_SRC_OSCULP32K
GCLK_GENCTRL_SRC_OSC32K   
GCLK_GENCTRL_SRC_XOSC32K  
GCLK_GENCTRL_SRC_OSC8M    
GCLK_GENCTRL_SRC_DFLL48M  
 */

Hi turboultra,

The Arduino core assigns various clock sources to generic clock 0 through to generic clock 3 in its "startup.c" file:

GCLK0 -> 48MHz Digital Frequency Locked Loop
GCLK1 -> 32.768kHz external crystal, otherwise 32.768kHz internal oscillator (for CRYSTALLESS operation)
GCLK2 -> 32.768kHz ultra low power internal oscillator
GCLK3 -> 8MHz internal oscillator

This leaves GCLK4 through to GCLK7 free to use as you wish.

The following code configures the port pin PA10 to output GCLK4. GCLK4 is set to switch between the 32.768kHz external crystal and the 8MHz internal osillator every 2 seconds:

// Output the 32.768kHz external crystal and 8MHz internal oscillator on port pin PA10 via GCLK4
void setup() {
  PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 1;        // Switch on port pin PA10's multiplexer
  PORT->Group[PORTA].PMUX[10 >> 1].reg |= PORT_PMUX_PMUXE_H;  // Switch the PA10's port multiplexer to GCLK IO  
}

void loop() {
  GCLK->GENCTRL.reg = GCLK_GENCTRL_OE |                // Enable GCLK4 output
                      GCLK_GENCTRL_IDC |               // Improve duty-cycle to 50%
                      GCLK_GENCTRL_GENEN |             // Enable generic clock
                      GCLK_GENCTRL_SRC_XOSC32K |       // Set the clock source to the external 32.768kHz crystal 
                      GCLK_GENCTRL_ID(4);              // Set the GCLK ID to GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                   // Wait for synchronization
  delay(2000);                                         // Wait for 2 seconds
  GCLK->GENCTRL.reg = GCLK_GENCTRL_OE |                // Enable GCLK4 output
                      GCLK_GENCTRL_IDC |               // Improve duty-cycle to 50%
                      GCLK_GENCTRL_GENEN |             // Enable generic clock
                      GCLK_GENCTRL_SRC_OSC8M |         // Set the clock source to the internal 8MHz oscillator 
                      GCLK_GENCTRL_ID(4);              // Set the GCLK ID to GCLK4
  while (GCLK->STATUS.bit.SYNCBUSY);                   // Wait for synchronization
  delay(2000);                                         // Wait for 2 seconds
}

Thank you MartinL, this was really helpful.

Just out of interest, I decided to call on the SAMD21's Fractional Digital Phase Locked Loop (FDPLL96M) that can operate up to 96MHz. It can also be configured to operate at lower frequencies as well.

Here's an example that generates a 32MHz output on PA10 via GCLK4 using the FDPLL96M:

// Output the FDPLL96M (Fractional Digital Phase Locked Loop 96MHz) at 32MHz on PA10 via GCLK4
void setup() {
  SYSCTRL->DPLLCTRLB.reg |= SYSCTRL_DPLLCTRLB_REFCLK_REF0;    // Select the external 32.768kHz crystal as the clock source
  //SYSCTRL->DPLLCTRLB.reg |= SYSCTRL_DPLLCTRLB_REFCLK_REF1;    // Select the internal 32.768kHz oscillator as the clock source
  
  SYSCTRL->DPLLRATIO.reg = SYSCTRL_DPLLRATIO_LDRFRAC(9) |    // Generate a 32MHz DPLL clock source from the external 32.768kHz crystal
                           SYSCTRL_DPLLRATIO_LDR(975);       // Frequency = 32.768kHz * (975 + 1 + 9/16) = 32MHz
  
  SYSCTRL->DPLLCTRLA.reg = SYSCTRL_DPLLCTRLA_ENABLE;          // Enable the Digital Phase Locked Loop (DPLL)
  while (!SYSCTRL->DPLLSTATUS.bit.LOCK);                      // Wait for the DPLL to achieve lock

  GCLK->GENCTRL.reg = GCLK_GENCTRL_OE |           // Enable GCLK output 
                      GCLK_GENCTRL_IDC |          // Enable 50% duty cycle
                      GCLK_GENCTRL_GENEN |        // Enable GCLK
                      GCLK_GENCTRL_SRC_FDPLL |    // Set the clock source to FDPLL96M
                      GCLK_GENCTRL_ID(4);         // Select GCLK4 ID
  while (GCLK->STATUS.bit.SYNCBUSY);              // Wait for synchronization
  
  PORT->Group[PORTA].PINCFG[10].bit.PMUXEN = 1;   // Enable the port pin multiplexer on PA10
  PORT->Group[PORTA].PMUX[10 >> 1].reg |= PORT_PMUX_PMUXE_H;  // Enable GCLK IO on PA10
}

void loop() {}

Furthermore, the DPLL96M can drive the SAMD21's TCC and some of its TC timers at 96MHz, two times the speed of the CPU core (at 48MHz).