/** * \brief Retrieves the clock frequency of a Generic Clock generator. * * Determines the clock frequency (in Hz) of a specified Generic Clock * generator, used as a source to a Generic Clock Channel module. * * \param[in] generator Generic Clock Generator index * * \return The frequency of the generic clock generator, in Hz. */ uint32_t system_gclk_gen_get_hz( const uint8_t generator) { while (system_gclk_is_syncing(generator)) { /* Wait for synchronization */ }; system_interrupt_enter_critical_section(); /* Get the frequency of the source connected to the GCLK generator */ uint32_t gen_input_hz = system_clock_source_get_hz( (enum system_clock_source)GCLK->GENCTRL[generator].bit.SRC); uint8_t divsel = GCLK->GENCTRL[generator].bit.DIVSEL; uint32_t divider = GCLK->GENCTRL[generator].bit.DIV; system_interrupt_leave_critical_section(); /* Check if the generator is using fractional or binary division */ if (!divsel && divider > 1) { gen_input_hz /= divider; } else if (divsel) { gen_input_hz >>= (divider+1); } return gen_input_hz; }
/** * \brief Enables a Generic Clock Generator that was previously configured. * * Starts the clock generation of a Generic Clock Generator that was previously * configured via a call to \ref system_gclk_gen_set_config(). * * \param[in] generator Generic Clock Generator index to enable */ void system_gclk_gen_enable( const uint8_t generator) { while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; system_interrupt_enter_critical_section(); /* Select the requested generator */ *((uint8_t*)&GCLK->GENCTRL.reg) = generator; while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; /* Enable generator */ GCLK->GENCTRL.reg |= GCLK_GENCTRL_GENEN; system_interrupt_leave_critical_section(); }
/** * \brief Disables a Generic Clock Generator that was previously enabled. * * Stops the clock generation of a Generic Clock Generator that was previously * started via a call to \ref system_gclk_gen_enable(). * * \param[in] generator Generic Clock Generator index to disable */ void system_gclk_gen_disable( const uint8_t generator) { while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; system_interrupt_enter_critical_section(); /* Select the requested generator */ *((uint8_t*)&GCLK->GENCTRL.reg) = generator; while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; /* Disable generator */ GCLK->GENCTRL.reg &= ~GCLK_GENCTRL_GENEN; while (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN) { /* Wait for clock to become disabled */ } system_interrupt_leave_critical_section(); }
/** * \brief Enables a Generic Clock Generator that was previously configured. * * Starts the clock generation of a Generic Clock Generator that was previously * configured via a call to \ref system_gclk_gen_set_config(). * * \param[in] generator Generic Clock Generator index to enable */ void system_gclk_gen_enable( const uint8_t generator) { while (system_gclk_is_syncing(generator)) { /* Wait for synchronization */ }; system_interrupt_enter_critical_section(); /* Enable generator */ GCLK->GENCTRL[generator].reg |= GCLK_GENCTRL_GENEN; system_interrupt_leave_critical_section(); }
/** * \brief Writes a Generic Clock Generator configuration to the hardware module. * * Writes out a given configuration of a Generic Clock Generator configuration * to the hardware module. * * \note Changing the clock source on the fly (on a running * generator) can take additional time if the clock source is configured * to only run on-demand (ONDEMAND bit is set) and it is not currently * running (no peripheral is requesting the clock source). In this case * the GCLK will request the new clock while still keeping a request to * the old clock source until the new clock source is ready. * * \note This function will not start a generator that is not already running; * to start the generator, call \ref system_gclk_gen_enable() * after configuring a generator. * * \param[in] generator Generic Clock Generator index to configure * \param[in] config Configuration settings for the generator */ void system_gclk_gen_set_config( const uint8_t generator, struct system_gclk_gen_config *const config) { /* Sanity check arguments */ Assert(config); /* Cache new register configurations to minimize sync requirements. */ uint32_t new_genctrl_config = (generator << GCLK_GENCTRL_ID_Pos); uint32_t new_gendiv_config = (generator << GCLK_GENDIV_ID_Pos); /* Select the requested source clock for the generator */ new_genctrl_config |= config->source_clock << GCLK_GENCTRL_SRC_Pos; /* Configure the clock to be either high or low when disabled */ if (config->high_when_disabled) { new_genctrl_config |= GCLK_GENCTRL_OOV; } /* Configure if the clock output to I/O pin should be enabled. */ if (config->output_enable) { new_genctrl_config |= GCLK_GENCTRL_OE; } /* Set division factor */ if (config->division_factor > 1) { /* Check if division is a power of two */ if (((config->division_factor & (config->division_factor - 1)) == 0)) { /* Determine the index of the highest bit set to get the * division factor that must be loaded into the division * register */ uint32_t div2_count = 0; uint32_t mask; for (mask = (1UL << 1); mask < config->division_factor; mask <<= 1) { div2_count++; } /* Set binary divider power of 2 division factor */ new_gendiv_config |= div2_count << GCLK_GENDIV_DIV_Pos; new_genctrl_config |= GCLK_GENCTRL_DIVSEL; } else { /* Set integer division factor */ new_gendiv_config |= (config->division_factor) << GCLK_GENDIV_DIV_Pos; /* Enable non-binary division with increased duty cycle accuracy */ new_genctrl_config |= GCLK_GENCTRL_IDC; } } /* Enable or disable the clock in standby mode */ if (config->run_in_standby) { new_genctrl_config |= GCLK_GENCTRL_RUNSTDBY; } while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; system_interrupt_enter_critical_section(); GCLK->GENDIV.reg = new_gendiv_config; while (system_gclk_is_syncing()) { /* Wait for synchronization */ }; GCLK->GENCTRL.reg = new_genctrl_config | (GCLK->GENCTRL.reg & GCLK_GENCTRL_GENEN); system_interrupt_leave_critical_section(); }