/* * Copyright (C) 2013,2014 by Uwe Bonnes * (bon@elektron.ikp.physik.tu-darmstadt.de) * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holders nor the names of * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * For additional information see http://www.ethernut.de/ */ #include #include #include #include #include #if defined(MCU_STM32F0) || defined(MCU_STM32F3) #include #else #warning "Unknown STM32 family" #endif /* Prepare some defaults if configuration is incomplete */ #if !defined(SYSCLK_SOURCE) #define SYSCLK_SOURCE SYSCLK_HSI #endif static uint32_t SystemCoreClock = 0; static const uint8_t AHBPrescTable[16] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; static const uint8_t APBPrescTable[8] = {1, 1, 1, 1, 2, 4, 8, 16}; /* Equalize missing STM32F0 register bits */ #if !defined(RCC_CFGR_PPRE2) #define RCC_CFGR_PPRE2 0 #endif #if !defined(RCC_CFGR_PPRE2_0) #define RCC_CFGR_PPRE2_0 0 #endif #if defined( RCC_CFGR_PPRE) && !defined( RCC_CFGR_PPRE1) #define RCC_CFGR_PPRE1 RCC_CFGR_PPRE #endif #if defined (RCC_CFGR_PPRE_DIV1) && !defined(RCC_CFGR_PPRE1_DIV1) #define RCC_CFGR_PPRE1_DIV1 RCC_CFGR_PPRE_DIV1 #endif #if defined (RCC_CFGR_PPRE_DIV2) && !defined(RCC_CFGR_PPRE1_DIV2) #define RCC_CFGR_PPRE1_DIV2 RCC_CFGR_PPRE_DIV2 #endif #if !defined(RCC_CFGR_PPRE2_DIV1) #define RCC_CFGR_PPRE2_DIV1 0 #endif #if !defined(RCC_CFGR_PPRE2_DIV2) #define RCC_CFGR_PPRE2_DIV2 0 #endif #if defined(FLASH_ACR_LATENCY) && !defined(FLASH_ACR_LATENCY_0) #define FLASH_ACR_LATENCY_0 FLASH_ACR_LATENCY #endif #if defined(RCC_CFGR_PPRE_0) && !defined(RCC_CFGR_PPRE1_0) #define RCC_CFGR_PPRE1_0 RCC_CFGR_PPRE_0 #endif /*---------------- Clock Setup Procedure ------------------------------ * * Clock system ist arranged like this: * * /Q------------------------------ USB * | ,--------------- CPU * | +--------------- SDIO * (1)4-32MHz HSE-+--/M*N+---+-AHBPRES---+-- APB1PRESC--- APB1 * | | +-- ABP2PRESC--- ABP2 * 8MHz HSI ------+----------' '-- ADCPRESC---- ADC * +-- Flash * '-- Option byte Loader * * M = 1..16 * N = 2..16 * Q = 1/1.5 * * ***** Setup of system clock configuration ***** * * 1) Select system clock sources * * To setup system to use HSI call: SetSysClockSource( SYSCLK_HSI); * To setup system to use HSE call: SetSysClockSource( SYSCLK_HSE); * * To setup system to use the PLL output, first setup the PLL source: * SetPllClockSource( PLLCLK_HSI); * or * SetPllClockSource( PLLCLK_HSE); * Then call SetSysClockSource( SYSCLK_PLL); * * 2) Configure prescalers * After selecting the right clock sources, the prescalers need to * be configured: * Call SetSysClock(); to do this automatically. * */ /*! * \brief Update SystemCoreClock according to Clock Register Values * * This function reads out the CPUs clock and PLL registers and assembles * the actual clock speed values into the SystemCoreClock local variable. */ void SystemCoreClockUpdate(void) { RCC_TypeDef *rcc = (RCC_TypeDef*) RCC_BASE; uint32_t cfgr; uint32_t tmp = 0; uint32_t hpre; /* Get SYSCLK source ---------------------------------------------------*/ cfgr = RCC->CFGR; switch(cfgr & RCC_CFGR_SWS) { case RCC_CFGR_SWS_HSE: tmp = HSE_VALUE; break; case RCC_CFGR_SWS_PLL: { uint32_t cfgr2 = rcc->CFGR2; uint32_t prediv; uint32_t pllmull; prediv = (cfgr2 & RCC_CFGR2_PREDIV1) >> _BI32(RCC_CFGR2_PREDIV1_0); prediv += 1; pllmull = (cfgr & RCC_CFGR_PLLMULL) >> _BI32(RCC_CFGR_PLLMULL_0); pllmull += 2; if (pllmull > 16) pllmull = 16; if ((cfgr & RCC_CFGR_PLLSRC ) == RCC_CFGR_PLLSRC ) tmp = HSE_VALUE; else tmp = HSI_VALUE / 2; tmp = (tmp / prediv) * pllmull; break; } default: tmp = HSI_VALUE; } hpre = (cfgr & RCC_CFGR_HPRE) >> _BI32(RCC_CFGR_HPRE_0); SystemCoreClock = tmp >> AHBPrescTable[hpre]; } /* Functional same as F1 */ /*! * \brief Control HSE clock. * * \param ena 0 disable clock, any other value enable it. * \return 0 on success, -1 on HSE start failed. */ int CtlHseClock( uint8_t ena) { int rc = 0; uint32_t tout = HSE_STARTUP_TIMEOUT; volatile uint32_t HSEStatus = 0; if( ena) { /* Enable HSE */ RCC->CR |= RCC_CR_HSEON; #if defined(HSE_BYPASS) /* Assume HSE if off */ RCC->CR |= RCC_CR_HSEBYP; #else RCC->CR &= ~RCC_CR_HSEBYP; #endif /* Wait till HSE is ready or time out is reached */ do { tout--; HSEStatus = RCC->CR & RCC_CR_HSERDY; } while((HSEStatus == 0) && (tout > 0)); if ((RCC->CR & RCC_CR_HSERDY) == RESET) { /* HSE failed to start */ rc = -1; } } else { /* Disable HSE clock */ RCC->CR &= ~RCC_CR_HSEON; } return rc; } /* Functional same as F1 */ /*! * \brief Control HSI clock. * * \param ena 0 disable clock, any other value enable it. * \return 0 on success, -1 on HSI start failed. */ int CtlHsiClock( uint8_t ena) { int rc = 0; uint32_t tout = HSE_STARTUP_TIMEOUT; volatile uint32_t HSIStatus = 0; if( ena) { /* Enable HSI */ RCC->CR |= RCC_CR_HSION; /* Wait till HSI is ready or time out is reached */ do { tout--; HSIStatus = RCC->CR & RCC_CR_HSIRDY; } while((HSIStatus == 0) && (tout > 0)); if ((RCC->CR & RCC_CR_HSIRDY) == RESET) { /* HSI failed to start */ rc = -1; } } else { /* Disable HSE clock */ RCC->CR &= ~RCC_CR_HSION; } return rc; } /* Functional same as F1 */ /*! * \brief Control PLL clock. * * \param ena 0 disable clock, any other value enable it. * \return 0 on success, -1 on PLL start failed. */ int CtlPllClock( uint8_t ena) { int rc = 0; uint32_t tout = HSE_STARTUP_TIMEOUT; volatile uint32_t PLLStatus = 0; if( ena) { /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready or time out is reached */ do { tout--; PLLStatus = RCC->CR & RCC_CR_PLLRDY; } while((PLLStatus == 0) && (tout > 0)); if ((RCC->CR & RCC_CR_PLLRDY) == RESET) { /* PLL failed to start */ rc = -1; } } else { /* Disable HSE clock */ RCC->CR &= ~RCC_CR_PLLON; } return rc; } /*! * \brief Configures the System clock source: HSE or HSI. * \note This function should be used with PLL disables * \param src is one of PLLCLK_HSE, PLLCLK_HSI. * \return 0 if clock is running ale -1. */ int SetPllClockSource( int src) { int rc = -1; if (src == PLLCLK_HSE) { rc = CtlHseClock(ENABLE); if (rc==0) { CM3BBSET(RCC_BASE, RCC_TypeDef, CFGR2, _BI32(RCC_CFGR_PLLSRC_PREDIV1)); } } else if (src == PLLCLK_HSI) { rc = CtlHsiClock(ENABLE); /* Select HSI/2 as PLL clock source */ if (rc==0) { CM3BBCLR(RCC_BASE, RCC_TypeDef, CFGR2, _BI32(RCC_CFGR_PLLSRC_PREDIV1)); } } return rc; } /*! * \brief Configures the System clock source: HSI, HS or PLL. * \note This function should be used only after reset. * \param src is one of SYSCLK_HSE, SYSCLK_HSI or SYSCLK_PLL. * \return 0 if selected clock is running else -1. */ int SetSysClockSource( int src) { int rc = -1; if (src == SYSCLK_HSE) { rc = CtlHseClock(ENABLE); if (rc == 0) { /* Select HSE as system clock source */ RCC->CFGR &= ~RCC_CFGR_SW; RCC->CFGR |= RCC_CFGR_SW_HSE; /* Wait till HSE is used as system clock source */ while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSE); } } else if (src == SYSCLK_HSI) { rc = CtlHsiClock(ENABLE); if (rc == 0) { /* Select HSI as system clock source */ RCC->CFGR &= ~RCC_CFGR_SW; RCC->CFGR |= RCC_CFGR_SW_HSI; /* Wait till HSI is used as system clock source */ while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI); } } else if (src == SYSCLK_PLL) { rc = CtlPllClock(ENABLE); if (rc == 0) { /* Select HSI as system clock source */ RCC->CFGR &= ~RCC_CFGR_SW; RCC->CFGR |= RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source */ while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL); } } /* Update core clock information */ SystemCoreClockUpdate(); return rc; } #if (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE) /*! * \brief Configures the System clock coming from HSE or HSI oscillator. * * Enable HSI/HSE clock and setup HCLK, PCLK2 and PCLK1 prescalers. * * \param None. * \return 0 on success, -1 on fault of HSE. */ int SetSysClock(void) { int rc = 0; register uint32_t cfgr; /* Todo: Check Voltage range! Here 2.7-3.6 Volt is assumed */ /* For 2.7-3.6 Volt up to 30 MHz no Wait state required */ cfgr = RCC->CFGR; cfgr &= ~(RCC_CFGR_HPRE|RCC_CFGR_PPRE1|RCC_CFGR_PPRE2); /* HCLK = SYSCLK */ cfgr |= (uint32_t)RCC_CFGR_HPRE_DIV1; /* PCLK2 = HCLK */ cfgr |= (uint32_t)RCC_CFGR_PPRE2_DIV1; /* PCLK1 = HCLK */ cfgr |= (uint32_t)RCC_CFGR_PPRE1_DIV1; RCC->CFGR = cfgr; rc = SetSysClockSource(SYSCLK_SOURCE); return rc; } #elif (SYSCLK_SOURCE == SYSCLK_PLL) /** * @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 * and PCLK1 prescalers. * @note This function should be used only after reset. * @param None * @retval None */ #if (PLLCLK_SOURCE==PLLCLK_HSE) #define PLLCLK_IN HSE_VALUE #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV == 0) #undef PLLCLK_DIV #if (((PLLCLK_IN % 24000000) == 0) && ((SYSCLK_FREQ % 24000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 24000000) #elif (((PLLCLK_IN % 16000000) == 0) && ((SYSCLK_FREQ % 16000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 16000000) #elif (((PLLCLK_IN % 14400000) == 0) && ((SYSCLK_FREQ % 14400000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 14400000) #elif (((PLLCLK_IN % 12000000) == 0) && ((SYSCLK_FREQ % 12000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 12000000) #elif (((PLLCLK_IN % 9600000) == 0) && ((SYSCLK_FREQ % 9600000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 9600000) #elif (((PLLCLK_IN % 8000000) == 0) && ((SYSCLK_FREQ % 8000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 8000000) #elif (((PLLCLK_IN % 6000000) == 0) && ((SYSCLK_FREQ % 6000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 6000000) #elif (((PLLCLK_IN % 4800000) == 0) && ((SYSCLK_FREQ % 4800000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 4800000) #elif (((PLLCLK_IN % 4000000) == 0) && ((SYSCLK_FREQ % 4000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 4000000) #elif (((PLLCLK_IN % 3000000) == 0) && ((SYSCLK_FREQ % 3000000) == 0)) #define PLLCLK_DIV (PLLCLK_IN / 3000000) #endif #endif #else /* HSI Used*/ /* HSI_VALUE value from vendor provided file as illegal type */ #define PLLCLK_IN 4000000 #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV == 0) #undef PLLCLK_DIV #define PLLCLK_DIV 1 #elif (PLLCLK_DIV != 0) #warning "HSI/2 Input has no clock divider" #endif #endif #if (PLLCLK_MULT == 0 ) && (PLLCLK_DIV > 0) #undef PLLCLK_MULT #define PLLCLK_MULT (SYSCLK_FREQ/(PLLCLK_IN / PLLCLK_DIV)) #endif #if (PLLCLK_DIV < 1) || (PLLCLK_DIV > 16) # warning "Illegal PLLCLK_DIV value" #endif #if (PLLCLK_MULT < 2) || (PLLCLK_MULT > 16) # warning "Illegal PLLCLK_MULT value" #endif #if (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT > 72000000) #warning "Clock frequency too high" #elif (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT >48000000) #define NUT_FLASH_LATENCY FLASH_ACR_LATENCY_1 #if defined(MCU_STM32F0) #warning "Clock frequency too high" #endif #elif (PLLCLK_IN /PLLCLK_DIV * PLLCLK_MULT >24000000) #define NUT_FLASH_LATENCY FLASH_ACR_LATENCY_0 #else #define NUT_FLASH_LATENCY 0 #endif /* Easy Approach: Try to reach fpll = 72 Mhz with M/N. */ int SetSysClock(void) { int rc = 0; uint32_t rcc_reg; RCC->APB1ENR |= RCC_APB1ENR_PWREN; rcc_reg = RCC->CFGR; rcc_reg &= ~(RCC_CFGR_PLLMULL |RCC_CFGR_PLLSRC |RCC_CFGR_PPRE2 | RCC_CFGR_PPRE1 |RCC_CFGR_HPRE); #if defined(MCU_STM32F0) /* APB Bus can run with full SYSCLK speed (48 MHz) */ rcc_reg |= ((PLLCLK_MULT -2) * RCC_CFGR_PLLMULL_0); #else /* APB1 Bus (Slow APB) bus can only run with half SYSCLK speed 36 MHz) */ rcc_reg |= ((PLLCLK_MULT -2) * RCC_CFGR_PLLMULL_0) | RCC_CFGR_PPRE1_DIV2; #endif #if (PLLCLK_SOURCE == PLLCLK_HSE) if (CtlHseClock(ENABLE) != 0) return -1; rcc_reg |= RCC_CFGR_PLLSRC; #else if (CtlHsiClock(ENABLE) != 0) return -1; #endif RCC->CFGR = rcc_reg; rcc_reg = FLASH->ACR; rcc_reg &= ~FLASH_ACR_LATENCY; rcc_reg |= NUT_FLASH_LATENCY | FLASH_ACR_PRFTBE ; FLASH->ACR = rcc_reg; rcc_reg = RCC->CFGR2; rcc_reg &= ~(RCC_CFGR2_PREDIV1); /* HCLK = SYSCLK, PCLK2 = HCLK , PCLK1 = HCLK/2 */ rcc_reg |= (PLLCLK_DIV - 1); RCC->CFGR2 = rcc_reg; /* Start PLL, wait ready and switch to it as clock source */ rc = SetSysClockSource(SYSCLK_SOURCE); if (rc) { /* Something went wrong with the PLL startup! */ SetSysClockSource(SYSCLK_HSI); return rc; } return rc; } #endif /* (SYSCLK_SOURCE == SYSCLK_HSI) || (SYSCLK_SOURCE == SYSCLK_HSE) */ /** * @brief requests System clock frequency * * @note This function should be used only after reset. * @param None * @retval None */ uint32_t SysCtlClockGet(void) { SystemCoreClockUpdate(); return SystemCoreClock; } /** * @brief requests frequency of the given clock * * @param idx NUT_HWCLK Index * @retval clock or 0 if idx points to an invalid clock */ uint32_t STM_ClockGet(int idx) { SystemCoreClockUpdate(); switch(idx) { case NUT_HWCLK_CPU: return SystemCoreClock; break; case NUT_HWCLK_PCLK1: { uint32_t tmp = (RCC->CFGR & RCC_CFGR_PPRE1) >> _BI32( RCC_CFGR_PPRE1_0); return SystemCoreClock/APBPrescTable[tmp]; break; } case NUT_HWCLK_PCLK2: { uint32_t tmp = (RCC->CFGR & RCC_CFGR_PPRE2) >> _BI32( RCC_CFGR_PPRE2_0); return SystemCoreClock/APBPrescTable[tmp]; break; } default: return 0; break; } }