007 - STM32学习笔记 - 系统时钟配置函数SetSysClock
1、代码分析
上节在学习RCC时钟树时,知道了开发板在上电后,会先执行启动代码中的复位程序Reset_Handler
,在Reset_Handler
中会执行系统初始化函数SystemInit
,而在SystemInit
中执行了SetSysClock
函数,SetSysClock
函数中则设置了系统时钟及锁相环等相关配置。
SetSysClock
函数原型在system_stm32f4xx.c中,在SetSysClock
中我们看到如下条件编译语句:
#if defined(STM32F40_41xxx) || defined(STM32F427_437xx) || defined(STM32F429_439xx) || defined(STM32F401xx) || defined(STM32F412xG) || defined(STM32F413_423xx) || defined(STM32F446xx)|| defined(STM32F469_479xx)
说明此文件支持在代码中体现的F4系列的多个芯片,我这里使用的是野火的F429开发板,代码中STM32F429_439xx
就能体现出来。在对SetSysClock
进行分析前,先将此函数进行裁剪,删除与429不相关的语句,裁剪后的函数原型如下(为了保证库函数的完整性,将SetSysClock
函数原型复制出来,新建bsp_rccclkconfig.c及bsp_rccclkconfig.h文件,再将这两个文件加入工程中,此处方法不再描述,不懂得看LED及KEY那两节)。
bsp_rccclkconfig.c
static void SetSysClock(void)
{
/*-------------1、使能HSE,并等待HSE稳定---------------*/
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* 使能 HSE */
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待HSE振荡器启动完成,HSE_STARTUP_TIMEOUT当振荡器长时间没有稳定下来时(超时),则会跳出这个循环 */
/* HSE振荡器稳定下来后, RCC_CR_HSERDY会置1,HSEStatus即为当前HSE的状态值,为0时则表示HSE振荡器未稳定 */
do
{
HSEStatus = RCC->CR & RCC_CR_HSERDY;
StartUpCounter++;
}while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
/* 再次确认HSE状态 */
if ((RCC->CR & RCC_CR_HSERDY) != RESET)
{
HSEStatus = (uint32_t)0x01;
}
else
{
HSEStatus = (uint32_t)0x00;
}
/* 如果HSE启动成功,则进入执行后续操作 */
if(HSEStatus == (uint32_t)0x01)
{
/* 选择电压调节器输出为模式1 */
/* 使能电源接口时钟 */
RCC->APB1ENR |= RCC_APB1ENR_PWREN;
PWR->CR |= PWR_CR_VOS;
/*-------------2、配置AHB\APN2\APB1总线的预分频因子---------------*/
/* 设置AHB总线分频因子 1分频 */
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
/* 设置APB2总线分频因子 2分频 */
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
/* 设置APB1总线分频因子 4分频 */
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
/*-------------3、配置PLL的各种分频因子,并使能PLL---------------*/
/* 配置主锁相环倍频因子 M = 25,N = 360,P = 2,Q = 7*/
RCC->PLLCFGR = PLL_M | (PLL_N << 6) | (((PLL_P >> 1) -1) << 16) |
(RCC_PLLCFGR_PLLSRC_HSE) | (PLL_Q << 24);
/* 使能主锁相环 */
RCC->CR |= RCC_CR_PLLON;
/* 等待锁相环稳定,稳定后硬件会将RCC_CR_PLLRDY置1 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* Enable the Over-drive to extend the clock frequency to 180 Mhz */
PWR->CR |= PWR_CR_ODEN; //这个寄存器的说明在中文参考手册中没有,英文手册中描述大致意思为打开Over-drive模式以便达到180MHz
//等待Over-drive模式启动成功
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;
/*-------------4、选择PLL作为系统时钟来源---------------*/
/* 选择主锁相环为系统时钟 */
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= RCC_CFGR_SW_PLL;
/* 等待PLLCLK切换为主锁相环 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS ) != RCC_CFGR_SWS_PLL);
{
}
}
else
{
//*如果启动不成功,则进入错误处理步骤,这里后续可以添加一些错误处理代码 */
}
}
结合以上代码的分析,可以总结出,系统时钟的配置,需要以下几步操作:
- 使能HSE,并等待HSE稳定;
- 配置AHB\APN2\APB1总线的预分频因子;
- 配置PLL的各种分频因子,并使能PLL;
- 选择PLL作为系统时钟来源
注:
1、在裁剪的时候,注意条件编译的#if defined...#endif
是成对出现的,删除冗余的代码的时候一定要看清楚。
2、在裁剪SetSysClock
时,里面有个条件编译#if defined(USE_HSE_BYPASS)
,这里的USE_HSE_BYPASS
表示如果使用的时有源晶振的话,则执行下面的代码,但是在野火F429中,使用的是无源晶振,因此此段程序就删除掉了。