/** * \brief Enable PLLB clock. * * \param mulb PLLB multiplier. * \param pllbcount PLLB counter. * \param divb Divider. */ void pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb) { pmc_disable_pllbck(); // Hardware BUG FIX : first disable the PLL to unlock the lock! // It occurs when re-enabling the PLL with the same parameters. PMC->CKGR_PLLBR = CKGR_PLLBR_DIVB(divb) | CKGR_PLLBR_PLLBCOUNT(pllbcount) | CKGR_PLLBR_MULB(mulb); while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0); }
/** * \brief Enable PLLB clock. * * \param mulb PLLB multiplier. * \param pllbcount PLLB counter. * \param divb Divider. */ void pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb) { /* first disable the PLL to unlock the lock!*/ pmc_disable_pllbck(); PMC->CKGR_PLLBR = CKGR_PLLBR_DIVB(divb) | CKGR_PLLBR_PLLBCOUNT(pllbcount) | CKGR_PLLBR_MULB(mulb); while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0); }
/** * \brief Enable PLLB clock. * * \param mulb PLLB multiplier. * \param pllbcount PLLB counter. * \param divb Divider. */ void pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb) { /* first disable the PLL to unlock the lock */ pmc_disable_pllbck(); #if SAMG55 PMC->CKGR_PLLAR = CKGR_PLLAR_PLLAEN(divb) | CKGR_PLLAR_PLLACOUNT(pllbcount) | CKGR_PLLAR_MULA(mulb); #else PMC->CKGR_PLLBR = CKGR_PLLBR_DIVB(divb) | CKGR_PLLBR_PLLBCOUNT(pllbcount) | CKGR_PLLBR_MULB(mulb); #endif while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0); }
/** * Save clock settings and shutdown PLLs */ __always_inline static void pmc_save_clock_settings( uint32_t *p_osc_setting, uint32_t *p_pll0_setting, uint32_t *p_pll1_setting, uint32_t *p_mck_setting) { if (p_osc_setting) { *p_osc_setting = PMC->CKGR_MOR; } if (p_pll0_setting) { *p_pll0_setting = PMC->CKGR_PLLAR; } if (p_pll1_setting) { #if (SAM3S || SAM4S) *p_pll1_setting = PMC->CKGR_PLLBR; #elif (SAM3U || SAM3XA) *p_pll1_setting = PMC->CKGR_UCKR; #else *p_pll1_setting = 0; #endif } if (p_mck_setting) { *p_mck_setting = PMC->PMC_MCKR; } /* Switch MCK to internal 4/8/12M RC for fast wakeup and disable unused clock for power saving. */ pmc_switch_mck_to_sclk(PMC_MCKR_PRES_CLK_1); pmc_switch_mainck_to_fastrc(CKGR_MOR_MOSCRCF_12_MHz); pmc_osc_disable_xtal(0); pmc_disable_pllack(); #if (SAM3S || SAM4S) pmc_disable_pllbck(); #elif (SAM3U || SAM3XA) pmc_disable_upll_clock(); #endif pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_1); }
/** * Save clock settings and shutdown PLLs */ __always_inline static void pmc_save_clock_settings( uint32_t *p_osc_setting, uint32_t *p_pll0_setting, uint32_t *p_pll1_setting, uint32_t *p_mck_setting, uint32_t *p_fmr_setting, #if defined(EFC1) uint32_t *p_fmr_setting1, #endif const bool disable_xtal) { uint32_t mor = PMC->CKGR_MOR; uint32_t mckr = PMC->PMC_MCKR; uint32_t fmr = EFC0->EEFC_FMR; # if defined(EFC1) uint32_t fmr1 = EFC1->EEFC_FMR; # endif if (p_osc_setting) { *p_osc_setting = mor; } if (p_pll0_setting) { *p_pll0_setting = PMC->CKGR_PLLAR; } if (p_pll1_setting) { #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP) *p_pll1_setting = PMC->CKGR_PLLBR; #elif (SAM3U || SAM3XA) *p_pll1_setting = PMC->CKGR_UCKR; #else *p_pll1_setting = 0; #endif } if (p_mck_setting) { *p_mck_setting = mckr; } if (p_fmr_setting) { *p_fmr_setting = fmr; } #if defined(EFC1) if (p_fmr_setting1) { *p_fmr_setting1 = fmr1; } #endif /* Enable FAST RC */ PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor | CKGR_MOR_MOSCRCEN; /* if MCK source is PLL, switch to mainck */ if ((mckr & PMC_MCKR_CSS_Msk) > PMC_MCKR_CSS_MAIN_CLK) { /* MCK -> MAINCK */ mckr = (mckr & (~PMC_MCKR_CSS_Msk)) | PMC_MCKR_CSS_MAIN_CLK; PMC->PMC_MCKR = mckr; while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); } /* MCK prescale -> 1 */ if (mckr & PMC_MCKR_PRES_Msk) { mckr = (mckr & (~PMC_MCKR_PRES_Msk)); PMC->PMC_MCKR = mckr; while(!(PMC->PMC_SR & PMC_SR_MCKRDY)); } /* Disable PLLs */ pmc_disable_pllack(); #if (SAM3S || SAM4S || SAM4C || SAM4CM || SAM4CP) pmc_disable_pllbck(); #elif (SAM3U || SAM3XA) pmc_disable_upll_clock(); #endif /* Prepare for entering WAIT mode */ /* Wait fast RC ready */ while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)); /* Switch mainck to FAST RC */ #if SAMG /** * For the sleepwalking feature, we need an accurate RC clock. Only 24M and * 16M are trimmed in production. Here we select the 24M. * And so wait state need to be 1. */ EFC0->EEFC_FMR = (fmr & (~EEFC_FMR_FWS_Msk)) | EEFC_FMR_FWS(1); PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_MOSCRCF_24_MHz | CKGR_MOR_KEY_PASSWD; #else PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL) | CKGR_MOR_KEY_PASSWD; #endif while (!(PMC->PMC_SR & PMC_SR_MOSCSELS)); #if (!SAMG) /* FWS update */ EFC0->EEFC_FMR = fmr & (~EEFC_FMR_FWS_Msk); #if defined(EFC1) EFC1->EEFC_FMR = fmr1 & (~EEFC_FMR_FWS_Msk); #endif #endif /* Disable XTALs */ if (disable_xtal) { PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN) | CKGR_MOR_KEY_PASSWD; } }
/** * \brief Application entry point for pmc_clock switch example. * * \return Unused (ANSI-C compatibility). */ int main(void) { /* Initialize the SAM system */ sysclk_init(); board_init(); /* Initialize the console uart */ configure_console(); /* Output example information */ puts(STRING_HEADER); /* Configure PCK */ ioport_set_pin_mode(GCLK_PIN, GCLK_PIN_MUX); ioport_disable_pin(GCLK_PIN); /* Configure the push button */ configure_buttons(); puts("-I- Press Button "BUTTON_NAME" to continue.\r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } puts("\n\r-I- Switch 8Mhz fast RC oscillator to be the source of the main clock \n\r" "-I- The master clock is main clock divided by 2\n\r" "-I- From now on, the UART baud rate is 2400bps. So please change the terminal setting before the next clock switch\r\n" "-I- Press Button "BUTTON_NAME" to switch next clock configuration... \r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* First switch to slow clock */ pmc_switch_mck_to_sclk(PMC_MCKR_PRES_CLK_1); #if (SAM3S || SAM4S || SAM4C) /* Then cut the PLL B */ pmc_disable_pllbck(); #endif /* Switch the mainck clock to the Fast RC, parameter '1' stands for 8Mhz */ pmc_switch_mainck_to_fastrc(CKGR_MOR_MOSCRCF_8_MHz); /* And finalize by switching to Fast RC */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_2); /* The clock source for the UART is the PCK, so the uart needs re-configuration */ config_uart_and_pck(PMC_PCK_CSS_MAIN_CLK, PMC_PCK_PRES_CLK_2, (CHIP_FREQ_MAINCK_RC_8MHZ / 2)); for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } puts("\n\r-I- Switch the XTAL 32K crystal oscillator to be the source of the slow clock\n\r" "-I- The master clock is slow clock\n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration after it has been measured.\r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* Enable the External 32K oscillator */ pmc_switch_sclk_to_32kxtal(PMC_OSC_XTAL); /* If a new value for CSS field corresponds to Main Clock or Slow Clock, * program the CSS field first. */ pmc_switch_mck_to_sclk(PMC_MCKR_PRES_CLK_1); /* The clock source for the UART is the PCK, so the uart needs *re-configuration. */ config_uart_and_pck(PMC_PCK_CSS_SLOW_CLK, PMC_PCK_PRES_CLK_1, BOARD_FREQ_SLCK_XTAL); for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } /* Switch the mainck to the Fast RC, parameter '2' stands for 12Mhz */ pmc_switch_mainck_to_fastrc(CKGR_MOR_MOSCRCF_12_MHz); /* If a new value for CSS field corresponds to Main Clock or Slow Clock, * program the CSS field first. */ pmc_switch_mck_to_mainck(PMC_PCK_PRES_CLK_1); /* The clock source for the UART is the PCK, so the uart needs * re-configuration. */ config_uart_and_pck(PMC_PCK_CSS_MAIN_CLK, PMC_PCK_PRES_CLK_1, CHIP_FREQ_MAINCK_RC_12MHZ); puts("\n\r-I- Switch 12Mhz fast RC oscillator to be the source of the main clock\n\r" "-I- The master clock is the main clock\n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration after it has been measured.\r\n"); for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } #if SAM4C puts("-I- Switch to 8.192Mhz PLLA clock as the source of the master clock \n\r" "-I- The master clock is PLLA clock divided by 2 \n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration... \r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* Enable the PLLA clock, the mainck equals 32.768K * 250 = 8.192Mhz */ pmc_enable_pllack((250 - 1), 0x3f, 1); #else puts("-I- Switch to 128Mhz PLLA clock as the source of the master clock \n\r" "-I- The master clock is PLLA clock divided by 2 \n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration... \r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* Enable the PLLA clock, the mainck equals 12Mhz * (32-1+1) / 3 = 128Mhz */ pmc_enable_pllack((32 - 1), 0x3f, 3); #endif /* If a new value for CSS field corresponds to PLL Clock, Program the PRES * field first. */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_2); /* Delay for a while */ /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* Then program the CSS field. */ pmc_switch_mck_to_pllack(PMC_MCKR_PRES_CLK_2); /* The clock source for the UART is the PCK, so the uart needs * re-configuration */ config_uart_and_pck(PMC_PCK_CSS_PLLA_CLK, PMC_PCK_PRES_CLK_2, PMC_CLOCK_SWITCHING_EXAMPLE_FIXED_PLLA/2); for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } puts("\n\r-I- Switch the XTAL 32K crystal oscillator to be the source of the slow clock\n\r" "-I- The master clock is slow clock\n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration...\r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* Switch slow clck to extern 32k xtal */ pmc_switch_sclk_to_32kxtal(PMC_OSC_XTAL); /* Delay for a while to make sure the clock is stable */ /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; /* If a new value for CSS field corresponds to Main Clock or Slow Clock, * program the CSS field first. */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_2); /* Switch the mck to sclk but keep the PRES field same */ pmc_switch_mck_to_sclk(PMC_MCKR_PRES_CLK_2); /* Then program the PRES field. */ pmc_switch_mck_to_sclk(PMC_MCKR_PRES_CLK_1); /* The clock source for the UART is the PCK, so the uart needs * re-configuration */ config_uart_and_pck(PMC_PCK_CSS_SLOW_CLK, PMC_PCK_PRES_CLK_1, BOARD_FREQ_SLCK_XTAL); for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } /* Switch mainck to external xtal */ pmc_switch_mainck_to_xtal(0, BOARD_OSC_STARTUP_US); /* If a new value for CSS field corresponds to Main Clock or Slow Clock, * program the CSS field first. */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_1); /* Then program the PRES field. */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_16); /* The clock source for the UART is the PCK, so the uart needs * re-configuration. */ config_uart_and_pck(PMC_PCK_CSS_MAIN_CLK, PMC_PCK_PRES_CLK_16, (BOARD_FREQ_MAINCK_XTAL / 16)); #if SAM4C puts("\n\r-I- Switch the external 8MHz crystal oscillator to be the source of the main clock\n\r" "-I- The master clock is main clock divided by 16\n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration...\r\n"); #else puts("\n\r-I- Switch the external 12MHz crystal oscillator to be the source of the main clock\n\r" "-I- The master clock is main clock divided by 16\n\r" "-I- Press Button "BUTTON_NAME" to switch next clock configuration...\r\n"); #endif #if (SAM3S || SAM4S || SAM4C) for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } puts("-I- Switch to 96Mhz PLLB clock as the source of the master clock\n\r" "-I- The master clock is PLLB clock divided by 2 \r"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; #if SAM4C /* Enable the PLLB clock, the mainck equals (8Mhz * (11+1) / 1) = 96Mhz * with the initialize counter be 0x3f */ pmc_enable_pllbck(11, 0x3f, 1); #else /* Enable the PLLB clock, the mainck equals (12Mhz * (7+1) / 1) = 96Mhz * with the initialize counter be 0x3f */ pmc_enable_pllbck(7, 0x3f, 1); #endif /* If a new value for CSS field corresponds to PLL Clock, Program the PRES * field first. */ pmc_switch_mck_to_mainck(PMC_MCKR_PRES_CLK_2); /* Then program the CSS field. */ pmc_switch_mck_to_pllbck(PMC_MCKR_PRES_CLK_2); /* The clock source for the UART is the PCK, so the uart needs * re-configuration. */ #if SAM4C config_uart_and_pck(PMC_PCK_CSS_PLLB_CLK, PMC_PCK_PRES_CLK_2, (BOARD_FREQ_MAINCK_XTAL * 12 / 2)); #else config_uart_and_pck(PMC_PCK_CSS_PLLB_CLK, PMC_PCK_PRES_CLK_2, (BOARD_FREQ_MAINCK_XTAL * 8 / 2)); #endif puts("-I- Press Button "BUTTON_NAME" to switch next clock configuration...\r\n"); #endif for (gs_uc_wait_button = 1; gs_uc_wait_button;) { } puts("\r\n\r\n-I- Done.\r\n"); /* Wait for UART transmit done */ while (!uart_is_tx_empty(CONF_UART)) { }; while (1) { } }