基于特定的开发板上的时钟策略:
倍频/分频系数需要在使能 PLL 之前进行配置,所以需要在 Open PLL 之前将所有系统的时钟分频器系数以及PLL的倍频系数配置好。整个时钟的配置流程如下所示:
(1) 开启HSE,等待HSE稳定
(2) 设置APB2、APB1、AHB分频系数
(3) 设置PLL的时钟来源和PLL的倍频系数
(4) 开启PLL,等待PLL稳定
(5) 设置SYSCLK源为 PLL 的输出,读取时钟切换状态,确保PLLCLK被选为系统时钟
(1) OSC_IN/OSC_OUT 上外接 8M 晶振。要使用外接晶振,上电后(默认使用 8M 的HSI),首先需要使能 HSE,位于RCC_CR寄存器的 bit16,即:
/* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON);
接着需要等待外部时钟 HSE 稳定(轮询 RCC_CR寄存器的 bit17):
/* Wait till HSE is ready and if Time out is reached exit */ do { HSEStatus = RCC->CR & RCC_CR_HSERDY; StartUpCounter++; } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
(2) 接着配置 AHB 时钟为 SYSCLK ,即 AHB 分频系数为1,不分频,SYSCLK直接供给SDIO, FSMC, AHB BUS, FCLK并直接作用于APB1/APB2的输入端:
/* HCLK = SYSCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
配置高速APB2 分频系数为1,即 SYSCLK ->AHB->APB2:
/* PCLK2 = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
由于APB1的时钟最大频率为 36MHz,当前希望输入的SYSCLK为72MHz(最大频率),故需要配置低速 APB1 分频系数为2,即两分频:
/* PCLK1 = HCLK */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
(3) 继续,配置 PLL 时钟源,以及 PLL 的倍频系数:
首先将需要配置的 bit 先清零:
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMULL));
接着
A. 选择HSE 时钟为 PLL 的输入 ,RCC_CFGR 的 bit16.
B. 对HSE输入晶振频率不分频,直接供给 PLL 作为输入
C. 由于外部晶振时钟为 8M,得到 72MHz 最大时钟频率,故设置 PLL 倍频系数为 9:
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | RCC_CFGR_PLLMULL9);
至此,分频/倍频系数基本设置完毕(未设置的部分采取系统 Reset 后的默认值)
(4) 开启 PLL 并等待稳定
/* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { }
(5) 切换系统时钟源为 PLL 的输出:
/* Select PLL as system clock source */ 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 */ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) { }
至此时钟配置完毕,本次配置是基于板载外接 8MHz 晶振进行的配置,若是板载其他的频率的晶振,只需改变 PLL 的倍频系数即可,此刻,系统运行在最大支持频率 72MHz 下。
完整的代码如下:
static void SetSysClockTo72(void) { __IO uint32_t StartUpCounter = 0, HSEStatus = 0; /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/ /* Enable HSE */ RCC->CR |= ((uint32_t)RCC_CR_HSEON); /* Wait till HSE is ready and if Time out is reached exit */ 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->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 */ RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2; /* PLL configuration: PLLCLK = HSE * 9 = 72 MHz */ 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); /* Enable PLL */ RCC->CR |= RCC_CR_PLLON; /* Wait till PLL is ready */ while((RCC->CR & RCC_CR_PLLRDY) == 0) { } /* Select PLL as system clock source */ 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 */ 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 */ } }