STM32f429在启动时会在startup_stm32f429_439xx.s中调用static void SetSysClock(void)函数。默认使用的是25M晶振,把系统时钟设置为180M.
在system_stm32f4xx.c中给出了相关的默认时钟参数设置。static void SetSysClock(void)函数执行的就是这个参数设置的过程。
*============================================================================= * Supported STM32F42xxx/43xxx devices *----------------------------------------------------------------------------- * System Clock source | PLL (HSE) *----------------------------------------------------------------------------- * SYSCLK(Hz) | 180000000 *----------------------------------------------------------------------------- * HCLK(Hz) | 180000000 *----------------------------------------------------------------------------- * AHB Prescaler | 1 *----------------------------------------------------------------------------- * APB1 Prescaler | 4 *----------------------------------------------------------------------------- * APB2 Prescaler | 2 *----------------------------------------------------------------------------- * HSE Frequency(Hz) | 25000000 *----------------------------------------------------------------------------- * PLL_M | 25 *----------------------------------------------------------------------------- * PLL_N | 360 *----------------------------------------------------------------------------- * PLL_P | 2 *----------------------------------------------------------------------------- * PLL_Q | 7 *----------------------------------------------------------------------------- * PLLI2S_N | NA *----------------------------------------------------------------------------- * PLLI2S_R | NA *----------------------------------------------------------------------------- * I2S input clock | NA *----------------------------------------------------------------------------- * VDD(V) | 3.3 *----------------------------------------------------------------------------- * Main regulator output voltage | Scale1 mode *----------------------------------------------------------------------------- * Flash Latency(WS) | 5 *----------------------------------------------------------------------------- * Prefetch Buffer | ON *----------------------------------------------------------------------------- * Instruction cache | ON *----------------------------------------------------------------------------- * Data cache | ON *----------------------------------------------------------------------------- * Require 48MHz for USB OTG FS, | Disabled * SDIO and RNG clock | *----------------------------------------------------------------------------- *=============================================================================
假设外部晶振改成了其他的数值比如说8M ,要修改 "stm32f4xx.h " 文件中的 "HSE_VALUE" 宏定义。
如果程序在运行过程中动态修改了PLL倍频系数,或者切换了时钟源,请务必执行一次 SystemCoreClockUpdate()函数,这个函数会自动根据PLL倍频参数计算出实际的主频。
void SystemCoreClockUpdate(void)函数会检测 RCC clock configurat ion register (RCC_CFGR)寄存器的 Bits 3:2 这两位是只读的,由硬件清除和设置,不能软件写入。
Bits 3:2 SWS: System clock switch status
Set and cleared by hardware to indicate which clock source is used as the system clock.
00: HSI oscillator used as the system clock
01: HSE oscillator used as the system clock
10: PLL used as the system clock
11: not applicable
根据这两位通过置swtich语句分析设置源的选择,进一步进行设置,如果是用PLL还要读取RCC_PLLCFGR寄存器的值
获取P N M PLLSRC等参数,来更新系统时钟。系统时钟SYSCLK 有下列公式决定。
PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N
SYSCLK = PLL_VCO / PLL_P
最后获得HCLK frequency :
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK frequency */
SystemCoreClock >>= tmp;
void SystemCoreClockUpdate(void) { uint32_t tmp = 0, pllvco = 0, pllp = 2, pllsource = 0, pllm = 2; #if defined(STM32F446xx) uint32_t pllr = 2; #endif /* STM32F446xx */ /* Get SYSCLK source -------------------------------------------------------*/ tmp = RCC->CFGR & RCC_CFGR_SWS; switch (tmp) { case 0x00: /* HSI used as system clock source */ SystemCoreClock = HSI_VALUE; break; case 0x04: /* HSE used as system clock source */ SystemCoreClock = HSE_VALUE; break; case 0x08: /* PLL P used as system clock source */ /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N SYSCLK = PLL_VCO / PLL_P */ pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; #if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F446xx) || defined(STM32F469_479xx) if (pllsource != 0) { /* HSE used as PLL clock source */ pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } else { /* HSI used as PLL clock source */ pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } #elif defined(STM32F410xx) || defined(STM32F411xE) #if defined(USE_HSE_BYPASS) if (pllsource != 0) { /* HSE used as PLL clock source */ pllvco = (HSE_BYPASS_INPUT_FREQUENCY / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } #else if (pllsource == 0) { /* HSI used as PLL clock source */ pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } #endif /* USE_HSE_BYPASS */ #endif /* STM32F40_41xxx || STM32F427_437xx || STM32F429_439xx || STM32F401xx || STM32F446xx || STM32F469_479xx */ pllp = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLP) >>16) + 1 ) *2; SystemCoreClock = pllvco/pllp; break; #if defined(STM32F446xx) case 0x0C: /* PLL R used as system clock source */ /* PLL_VCO = (HSE_VALUE or HSI_VALUE / PLL_M) * PLL_N SYSCLK = PLL_VCO / PLL_R */ pllsource = (RCC->PLLCFGR & RCC_PLLCFGR_PLLSRC) >> 22; pllm = RCC->PLLCFGR & RCC_PLLCFGR_PLLM; if (pllsource != 0) { /* HSE used as PLL clock source */ pllvco = (HSE_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } else { /* HSI used as PLL clock source */ pllvco = (HSI_VALUE / pllm) * ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> 6); } pllr = (((RCC->PLLCFGR & RCC_PLLCFGR_PLLR) >>28) + 1 ) *2; SystemCoreClock = pllvco/pllr; break; #endif /* STM32F446xx */ default: SystemCoreClock = HSI_VALUE; break; } /* Compute HCLK frequency --------------------------------------------------*/ /* Get HCLK prescaler */ tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; /* HCLK frequency */ SystemCoreClock >>= tmp; }
另外也可以自己实现两个设置时钟的函数:
/* * 使用HSE时,设置系统时钟的步骤 * 1、开启HSE ,并等待 HSE 稳定 * 2、设置 AHB、APB2、APB1的预分频因子 * 3、设置PLL的时钟来源 * 设置VCO输入时钟 分频因子 m * 设置VCO输出时钟 倍频因子 n * 设置PLLCLK时钟分频因子 p * 设置OTG FS,SDIO,RNG时钟分频因子 q * 4、开启PLL,并等待PLL稳定 * 5、把PLLCK切换为系统时钟SYSCLK * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟 */ /* * m: VCO输入时钟 分频因子,取值2~63 * n: VCO输出时钟 倍频因子,取值192~432 * p: PLLCLK时钟分频因子 ,取值2,4,6,8 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15 * 函数调用举例,使用HSE设置时钟 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M * HSE_SetSysClock(25, 360, 2, 7); * HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法 * 系统时钟超频到216M爽一下 * HSE_SetSysClock(25, 432, 2, 9); */ void HSE_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q) { __IO uint32_t HSEStartUpStatus = 0; // 使能HSE,开启外部晶振,秉火F429使用 HSE=25M RCC_HSEConfig(RCC_HSE_ON); // 等待HSE启动稳定 HSEStartUpStatus = RCC_WaitForHSEStartUp(); if (HSEStartUpStatus == SUCCESS) { // 调压器电压输出级别配置为1,以便在器件为最大频率 // 工作时使性能和功耗实现平衡 RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR |= PWR_CR_VOS; // HCLK = SYSCLK / 1 RCC_HCLKConfig(RCC_SYSCLK_Div1); // PCLK2 = HCLK / 2 RCC_PCLK2Config(RCC_HCLK_Div2); // PCLK1 = HCLK / 4 RCC_PCLK1Config(RCC_HCLK_Div4); // 如果要超频就得在这里下手啦 // 设置PLL来源时钟,设置VCO分频因子m,设置VCO倍频因子n, // 设置系统时钟分频因子p,设置OTG FS,SDIO,RNG分频因子q RCC_PLLConfig(RCC_PLLSource_HSE, m, n, p, q); // 使能PLL RCC_PLLCmd(ENABLE); // 等待 PLL稳定 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /*-----------------------------------------------------*/ //开启 OVER-RIDE模式,以能达到更高频率 PWR->CR |= PWR_CR_ODEN; while((PWR->CSR & PWR_CSR_ODRDY) == 0) { } PWR->CR |= PWR_CR_ODSWEN; while((PWR->CSR & PWR_CSR_ODSWRDY) == 0) { } // 配置FLASH预取指,指令缓存,数据缓存和等待状态 FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS; /*-----------------------------------------------------*/ // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 读取时钟切换状态位,确保PLLCLK被选为系统时钟 while (RCC_GetSYSCLKSource() != 0x08) { } } else { // HSE启动出错处理 while (1) { } } } /* * 使用HSI时,设置系统时钟的步骤 * 1、开启HSI ,并等待 HSI 稳定 * 2、设置 AHB、APB2、APB1的预分频因子 * 3、设置PLL的时钟来源 * 设置VCO输入时钟 分频因子 m * 设置VCO输出时钟 倍频因子 n * 设置SYSCLK时钟分频因子 p * 设置OTG FS,SDIO,RNG时钟分频因子 q * 4、开启PLL,并等待PLL稳定 * 5、把PLLCK切换为系统时钟SYSCLK * 6、读取时钟切换状态位,确保PLLCLK被选为系统时钟 */ /* * m: VCO输入时钟 分频因子,取值2~63 * n: VCO输出时钟 倍频因子,取值192~432 * p: PLLCLK时钟分频因子 ,取值2,4,6,8 * q: OTG FS,SDIO,RNG时钟分频因子,取值4~15 * 函数调用举例,使用HSI设置时钟 * SYSCLK=HCLK=180M,PCLK2=HCLK/2=90M,PCLK1=HCLK/4=45M * HSI_SetSysClock(16, 360, 2, 7); * HSE作为时钟来源,经过PLL倍频作为系统时钟,这是通常的做法 * 系统时钟超频到216M爽一下 * HSI_SetSysClock(16, 432, 2, 9); */ void HSI_SetSysClock(uint32_t m, uint32_t n, uint32_t p, uint32_t q) { __IO uint32_t HSIStartUpStatus = 0; // 把RCC外设初始化成复位状态 RCC_DeInit(); //使能HSI, HSI=16M RCC_HSICmd(ENABLE); // 等待 HSI 就绪 HSIStartUpStatus = RCC->CR & RCC_CR_HSIRDY; // 只有 HSI就绪之后则继续往下执行 if (HSIStartUpStatus == RCC_CR_HSIRDY) { // 调压器电压输出级别配置为1,以便在器件为最大频率 // 工作时使性能和功耗实现平衡 RCC->APB1ENR |= RCC_APB1ENR_PWREN; PWR->CR |= PWR_CR_VOS; // HCLK = SYSCLK / 1 RCC_HCLKConfig(RCC_SYSCLK_Div1); // PCLK2 = HCLK / 2 RCC_PCLK2Config(RCC_HCLK_Div2); // PCLK1 = HCLK / 4 RCC_PCLK1Config(RCC_HCLK_Div4); // 如果要超频就得在这里下手啦 // 设置PLL来源时钟,设置VCO分频因子m,设置VCO倍频因子n, // 设置系统时钟分频因子p,设置OTG FS,SDIO,RNG分频因子q RCC_PLLConfig(RCC_PLLSource_HSI, m, n, p, q); // 使能PLL RCC_PLLCmd(ENABLE); // 等待 PLL稳定 while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET) { } /*-----------------------------------------------------*/ //开启 OVER-RIDE模式,以能达到更高频率 PWR->CR |= PWR_CR_ODEN; while((PWR->CSR & PWR_CSR_ODRDY) == 0) { } PWR->CR |= PWR_CR_ODSWEN; while((PWR->CSR & PWR_CSR_ODSWRDY) == 0) { } // 配置FLASH预取指,指令缓存,数据缓存和等待状态 FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN |FLASH_ACR_DCEN |FLASH_ACR_LATENCY_5WS; /*-----------------------------------------------------*/ // 当PLL稳定之后,把PLL时钟切换为系统时钟SYSCLK RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); // 读取时钟切换状态位,确保PLLCLK被选为系统时钟 while (RCC_GetSYSCLKSource() != 0x08) { } } else { // HSI启动出错处理 while (1) { } } }