STM32F系列ARM Cortex_M3核微控制器之系统时钟三
问题一:若未对系统进行时钟配置,那么系统上电后时钟系统将是怎样配置的?
在STM32启动文件startup_stm32f10x_hd.s中有下面一段汇编:
Reset_Handler PROCEXPORT Reset_Handler [WEAK]IMPORT __mainIMPORT SystemInitLDR R0, =SystemInitBLX R0 LDR R0, =__mainBX R0ENDP
其中IMPORT __main和IMPORT SystemInit为声明要使用C语言中的main函数和IMPORT SystemInit函数。LDR R0,=SystemInit 把SystemInit函数的地址加载到寄存器R0中。BLX R0 程序跳转到R0中的地址处执行。
我们来看看system_stm32f10x.c文件中的SystemInit()函数对系统时钟的配置情况
/*** @brief Setup the microcontroller system* Initialize the Embedded Flash Interface, the PLL and update the * SystemCoreClock variable. 初始化内部Flash,PLL,更新系统时钟变量* @note This function should be used only after reset. 仅能在复位后使用* @param None* @retval None*/
该函数将RCC时钟先置为复位状态,然后调用SystemInit_ExtMemCtl()(如果定义了DATA_IN_ExtSRAM),调用SetSysClock()以及设置向量表的位置。这里不对SystemInit_ExtMemCtl()进行解释,置解释SetSysClock()函数
void SystemInit (void)
{/* Reset the RCC clock configuration to the default reset state(for debug purpose) *///复位RCC时钟配置为默认复位状态(为了调试目的)/* Set HSION bit 置位内部高速时钟使能位*///内部8MHz振荡器开启RCC->CR |= (uint32_t)0x00000001;/* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits *///复位系统时钟切换,AHB预分频,低速APB预分频,高速APB预分频,ADC预分频,微控制器时钟输出位//HSI作为系统时钟,SYSCLK不分频作为HCLK,HCLK不分频做为PCLK1,PCLK2,PCLK2 2分频后作为ADC时钟,MCO引脚没有微控制器时钟输出
#ifndef STM32F10X_CLRCC->CFGR &= (uint32_t)0xF8FF0000;
#elseRCC->CFGR &= (uint32_t)0xF0FF0000;
#endif /* STM32F10X_CL */ /* Reset HSEON, CSSON and PLLON bits *///复位外部高速时钟使能,时钟安全系统使能,PLL使能//外部高速时钟关闭,时钟监测器关闭,PLL关闭RCC->CR &= (uint32_t)0xFEF6FFFF;/* Reset HSEBYP bit *///复位外部高速时钟旁路//外部3-25MHz时钟没有旁路RCC->CR &= (uint32_t)0xFFFBFFFF;/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits *///复位PLL输入时钟源,HSE分频作为PLL输入,PLL倍频系数,USB预分频位//HSI振荡器时钟经2分频后作为PLL输入时钟,HSE不分频作为PLL输入时钟,PLL2倍频输出,PLL时钟1.5倍分频作为USB时钟RCC->CFGR &= (uint32_t)0xFF80FFFF;#ifdef STM32F10X_CL/* Reset PLL2ON and PLL3ON bits */RCC->CR &= (uint32_t)0xEBFFFFFF;/* Disable all interrupts and clear pending bits */RCC->CIR = 0x00FF0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;
#elif defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)/* Disable all interrupts and clear pending bits */RCC->CIR = 0x009F0000;/* Reset CFGR2 register */RCC->CFGR2 = 0x00000000;
#else/* Disable all interrupts and clear pending bits *///失能所有时钟中断,清除挂起位RCC->CIR = 0x009F0000;
#endif /* STM32F10X_CL */#if defined (STM32F10X_HD) || (defined STM32F10X_XL) || (defined STM32F10X_HD_VL)#ifdef DATA_IN_ExtSRAMSystemInit_ExtMemCtl(); #endif /* DATA_IN_ExtSRAM */
#endif /* Configure the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers *///配置系统时钟频率,HCLK,PCLK2,PCLK1预分频/* Configure the Flash Latency cycles and enable prefetch buffer *///配置Flash延时周期,使能预取缓存SetSysClock();#ifdef VECT_TAB_SRAMSCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM. */
#else//向量表放置于内部FLASHSCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH. */
#endif
}
/*** @brief Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.配置系统时钟频率,HCLK,PCLK2,PCLK1预分频* @param None* @retval None*/
这里定义了SYSCLK_FREQ_72MHz系统时钟频率
static void SetSysClock(void)
{
#ifdef SYSCLK_FREQ_HSESetSysClockToHSE();
#elif defined SYSCLK_FREQ_24MHzSetSysClockTo24();
#elif defined SYSCLK_FREQ_36MHzSetSysClockTo36();
#elif defined SYSCLK_FREQ_48MHzSetSysClockTo48();
#elif defined SYSCLK_FREQ_56MHzSetSysClockTo56();
#elif defined SYSCLK_FREQ_72MHzSetSysClockTo72();
#endif/* If none of the define above is enabled, the HSI is used as System clocksource (default after reset) */ //如果上面的宏定义都没定义,HSI用作系统时钟 }
#if defined (STM32F10X_LD_VL) || (defined STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)
这里使能的函数是SetSysClockTo72();
/* #define SYSCLK_FREQ_HSE HSE_VALUE */#define SYSCLK_FREQ_24MHz 24000000
#else
/* #define SYSCLK_FREQ_HSE HSE_VALUE */
/* #define SYSCLK_FREQ_24MHz 24000000 */
/* #define SYSCLK_FREQ_36MHz 36000000 */
/* #define SYSCLK_FREQ_48MHz 48000000 */
/* #define SYSCLK_FREQ_56MHz 56000000 */
#define SYSCLK_FREQ_72MHz 72000000
#endif
/*** @brief Sets System clock frequency to 72MHz and configure HCLK, PCLK2 * and PCLK1 prescalers.设置系统时钟为72MHz,配置HCLK,PCLK2,PCLK1预分频* @note This function should be used only after reset.* @param None* @retval None*/
这里的配置是:HSE的外部晶振使用8MHz,HSE时钟作为PLL输入时钟,PLL 9倍频,SYSCLK = 72MHz,HCLK = AHB时钟为72MHz,PCLK1 = AHB/2 = 36MHz,PCLK2 = AHB = 72MHz。
static void SetSysClockTo72(void)
{__IO uint32_t StartUpCounter = 0, HSEStatus = 0;/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*///配置SYSCLK, HCLK, PCLK2 and PCLK1/* Enable HSE */ //使能高速外部振荡器 RCC->CR |= ((uint32_t)RCC_CR_HSEON);/* Wait till HSE is ready and if Time out is reached exit *///等到HSE稳定,时间到则退出循环do{HSEStatus = RCC->CR & RCC_CR_HSERDY;StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));if ((RCC->CR & RCC_CR_HSERDY) != RESET){HSEStatus = (uint32_t)0x01;}else{HSEStatus = (uint32_t)0x00;} if (HSEStatus == (uint32_t)0x01){/* Enable Prefetch Buffer *///使能预取缓冲FLASH->ACR |= FLASH_ACR_PRFTBE;/* Flash 2 wait state *///FLASH时序延迟2周期FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2; /* HCLK = SYSCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;/* PCLK2 = HCLK */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;/* PCLK1 = HCLK/2 */RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;#ifdef STM32F10X_CL/* Configure PLLs ------------------------------------------------------*//* PLL2 configuration: PLL2CLK = (HSE / 5) * 8 = 40 MHz *//* PREDIV1 configuration: PREDIV1CLK = PLL2 / 5 = 8 MHz */RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL |RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL8 |RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5);/* Enable PLL2 */RCC->CR |= RCC_CR_PLL2ON;/* Wait till PLL2 is ready */while((RCC->CR & RCC_CR_PLL2RDY) == 0){}/* PLL configuration: PLLCLK = PREDIV1 * 9 = 72 MHz */ RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLMULL9);
#else /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz *///PLL配置 HSE时钟作为PLL输入时钟 PLL9倍频RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |RCC_CFGR_PLLMULL));RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
#endif /* STM32F10X_CL *//* Enable PLL *///使能PLLRCC->CR |= RCC_CR_PLLON;/* Wait till PLL is ready */while((RCC->CR & RCC_CR_PLLRDY) == 0){}/* Select PLL as system clock source *///选择PLL作为系统时钟源RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL; /* Wait till PLL is used as system clock source *///等到PLL作为系统时钟源while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08){}}else{ /* If HSE fails to start-up, the application will have wrong clock configuration. User can add here some code to deal with this error *///如果HSE启动失败,应用会产生错误时钟配置,在这里对错误情况进行处理}
}
初始化之后可以通过变量SystemCoreClock获取系统核心时钟频率(HCLK)。SystemCoreClock变量:包含核心时钟(HCLK),可以用于建立SysTick或配置其他参数
/*******************************************************************************
* Clock Definitions
*******************************************************************************/
#ifdef SYSCLK_FREQ_HSEuint32_t SystemCoreClock = SYSCLK_FREQ_HSE; /*!
z再看看该文件下的另一个函数SystemCoreClockUpdate(),
由于SystemCoreClock变量中存储的是核心频率HCLK,每次系统时钟改变时,都需要使用该函数对该变量进行更新。
/*** @brief Update SystemCoreClock variable according to Clock Register Values.* The SystemCoreClock variable contains the core clock (HCLK), it can* be used by the user application to setup the SysTick timer or configure* other parameters.* 根据时钟寄存器值来更新SystemCoreClock变量,SystemCoreClock保存的是系统核心时钟(HCLK) * @note Each time the core clock (HCLK) changes, this function must be called* to update SystemCoreClock variable value. Otherwise, any configuration* based on this variable will be incorrect. * 当核心时钟变化时,必须调用该函数来更新SystemCoreClock变量。* @note - The system frequency computed by this function is not the real * frequency in the chip. It is calculated based on the predefined * constant and the selected clock source:* 该函数计算的系统频率是基于预定义的频率和选择的时钟源,不一定是芯片真正的频率 * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)* * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)* * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) * or HSI_VALUE(*) multiplied by the PLL factors.* * (*) HSI_VALUE is a constant defined in stm32f1xx.h file (default value* 8 MHz) but the real value may vary depending on the variations* in voltage and temperature. * * (**) HSE_VALUE is a constant defined in stm32f1xx.h file (default value* 8 MHz or 25 MHz, depedning on the product used), user has to ensure* that HSE_VALUE is same as the real frequency of the crystal used.* Otherwise, this function may have wrong result.* * - The result of this function could be not correct when using fractional* value for HSE crystal.* @param None* @retval None*/
void SystemCoreClockUpdate (void)
{uint32_t tmp = 0, pllmull = 0, pllsource = 0;#ifdef STM32F10X_CLuint32_t prediv1source = 0, prediv1factor = 0, prediv2factor = 0, pll2mull = 0;
#endif /* STM32F10X_CL */#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)uint32_t prediv1factor = 0;
#endif /* STM32F10X_LD_VL or STM32F10X_MD_VL or STM32F10X_HD_VL *//* Get SYSCLK source -------------------------------------------------------*///得到SYSCLK所选时钟源tmp = RCC->CFGR & RCC_CFGR_SWS;switch (tmp){case 0x00: /* HSI used as system clock */SystemCoreClock = HSI_VALUE;break;case 0x04: /* HSE used as system clock */SystemCoreClock = HSE_VALUE;break;case 0x08: /* PLL used as system clock *//* Get PLL clock source and multiplication factor ----------------------*///得到PLL时钟源和倍频系数pllmull = RCC->CFGR & RCC_CFGR_PLLMULL;pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;#ifndef STM32F10X_CL pllmull = ( pllmull >> 18) + 2;if (pllsource == 0x00){/* HSI oscillator clock divided by 2 selected as PLL clock entry *///HSI 2分频后作为PLL时钟源SystemCoreClock = (HSI_VALUE >> 1) * pllmull;}else{#if defined (STM32F10X_LD_VL) || defined (STM32F10X_MD_VL) || (defined STM32F10X_HD_VL)prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;/* HSE oscillator clock selected as PREDIV1 clock entry */SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; #else/* HSE selected as PLL clock entry */if ((RCC->CFGR & RCC_CFGR_PLLXTPRE) != (uint32_t)RESET){/* HSE oscillator clock divided by 2 *///HSE时钟2分频SystemCoreClock = (HSE_VALUE >> 1) * pllmull;}else{//HSE不分频SystemCoreClock = HSE_VALUE * pllmull;}#endif}
#elsepllmull = pllmull >> 18;if (pllmull != 0x0D){pllmull += 2;}else{ /* PLL multiplication factor = PLL input clock * 6.5 */pllmull = 13 / 2; }if (pllsource == 0x00){/* HSI oscillator clock divided by 2 selected as PLL clock entry */SystemCoreClock = (HSI_VALUE >> 1) * pllmull;}else{/* PREDIV1 selected as PLL clock entry *//* Get PREDIV1 clock source and division factor */prediv1source = RCC->CFGR2 & RCC_CFGR2_PREDIV1SRC;prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;if (prediv1source == 0){ /* HSE oscillator clock selected as PREDIV1 clock entry */SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull; }else{/* PLL2 clock selected as PREDIV1 clock entry *//* Get PREDIV2 division factor and PLL2 multiplication factor */prediv2factor = ((RCC->CFGR2 & RCC_CFGR2_PREDIV2) >> 4) + 1;pll2mull = ((RCC->CFGR2 & RCC_CFGR2_PLL2MUL) >> 8 ) + 2; SystemCoreClock = (((HSE_VALUE / prediv2factor) * pll2mull) / prediv1factor) * pllmull; }}
#endif /* STM32F10X_CL */ break;default:SystemCoreClock = HSI_VALUE;break;}/* Compute HCLK clock frequency ----------------*///计算/* Get HCLK prescaler */tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];/* HCLK clock frequency */SystemCoreClock >>= tmp;
}