RCC——配置系统时钟
前言
这次讲解RCC这个外设,即复位与时钟控制,主要讲解时钟。时钟像是人的心脏,所有的外设如果想工作,都必须要开启相应的时钟,虽然系统时钟早已被汇编语言设定为72M,但我们也可以通过写程序改变系统时钟甚至超频
时钟树及讲解
以上图片即为时钟树,它上面包含了stm32f10x的所有时钟配置,下面,我将会对它进行讲解。
首先是HSE时钟,即高速的外部时钟,来源为外部晶振(4-16M),通常为8M。
还有HSI时钟,高速的内部时钟,大小为8M,当HSE故障时,系统时钟自动切换到HSI,直到HS启动成功,但精度比HSE低。
PLLCLK:锁相环时钟,它有四种情况
1.锁相环时钟为HSI
2. HSI频率除2后经过倍频后得到PLLCLK
3. HSE倍频得到PLLCLK
4. HSE频率除2后倍频得到PLLCLK
(注意:PLLCLK源头使用HSI/2时,PLLMUL最大只能是16,这个时候PLLCLK最大只能是64M)
得到PLLCLK时钟后,经过AHB预分频器为单片机外设提供时钟,值得注意的是APB1与APB2上也有预分频器,为挂载在APB1和APB2的外设提供时钟,APB1和APB2的时钟成为PCLK1时钟和PCLK2时钟
PCLK1时钟:APB1低速总线时钟,最高为36M
PCLK2时钟:APB2高速总线时钟,最高为72M
RTC时钟:为芯片内部的PTC外设提供时钟
来源有HSE_RTC(HSE分频得到),LSE(低速外部时钟),LSI(低速内部时钟)
MCO时钟输出:微控制器时钟输出引脚,由PA8复用所得
来源为PLLCLK/2,HSE,HSI,SYSCLK
我们在改变单片机的时钟后用MCO实时监测
系统时钟配置函数SetsysClockTo72()讲解
虽然对时钟树有一定了解,但改变系统时钟仍有很多细节需要优化,因此我们可以通过分析固件库函数SetsysClockTo72()来了解细节和需要操作的寄存器
static void SetSysClockTo72(void)
{
__IO uint32_t StartUpCounter = 0, HSEStatus = 0;
/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
/* 使能HSE*/
RCC->CR |= ((uint32_t)RCC_CR_HSEON);
/* 等待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;
}
//如果HSE启动成功,程序则继续往下执行
if (HSEStatus == (uint32_t)0x01)
{
/* 使能预取指 */
FLASH->ACR |= FLASH_ACR_PRFTBE;
/* 等待两个周期 */
FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;
/* HCLK = SYSCLK =72M*/
RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;
/* PCLK2 = HCLK=72M */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;
/* PCLK1 = HCLK=36M */
RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV2;
#else
/* 锁相环配置: 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);
#endif /* STM32F10X_CL */
/*使能PLL */
RCC->CR |= RCC_CR_PLLON;
/* 等待PLL稳定 */
while((RCC->CR & RCC_CR_PLLRDY) == 0)
{
}
/* 选择PLLCLK作为系统时钟*/
RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
/*等待PLLCLK切换为系统时钟 */
while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
{
}
}
else
{ /*如果HSE启动失败,用户可以在这里添加处理错误的代码*/
}
}
操作寄存器
时钟控制寄存器(RCC_CR)
时钟配置寄存器(RCC_CFGR)
代码编写:(HSE配置系统时钟)
改变频率
步骤与上个代码类似只要步骤相同即可,不同的是上个代码直接操作寄存器,而我们要使用固件库编写代码
//使用HSE配置系统时钟
void HSE_SetSysClk(uint32_t RCC_PLLMul_x)
{
ErrorStatus HSEStatus;
RCC_DeInit();//把RCC寄存器复位
RCC_HSEConfig(RCC_HSE_ON);//使能HSE(外部时钟)
HSEStatus=RCC_WaitForHSEStartUp();
if(HSEStatus==SUCCESS)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//使能预取指
FLASH_SetLatency(FLASH_Latency_2);//设置两个等待
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);//配置PLLCLK=HSE*RCC_PLLMul_x
RCC_PLLCmd(ENABLE);//使能PLL
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET );//等待PLL稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//选择系统时钟
while(RCC_GetSYSCLKSource()!=0x08);
}
else{
/*如果HSE启动失败,用户可以在这里添加处理错误的代码*/
}
}
当RCC_PLLMul_x为RCC_PLLMul_10时
PCCLK的频率就变为80M
那我们如何检测呢?
这便需要MCO引脚
检测频率
先设置MCO对应的PA_8为推挽输出模式
void MCO_GPIO_Config()//MCO对应的GPIO口为PA8
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//打开GPIOA时钟
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode= GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);//配置GPIOA
}
然后将MCO的时钟设为SYSCLK,系统时钟已选为PLLCLK,所以检测的就是PLLCLK。
同理也可以用HSI配置系统时钟
代码编写(同理也可以用HSI配置系统时钟)
//使用HSI配置系统时钟
void HSI_SetSysClk(uint32_t RCC_PLLMul_x)
{
__IO uint32_t HSIStatus = 0;
RCC_DeInit();//把RCC寄存器复位
RCC_HSICmd(ENABLE);//使能HSI(内部时钟)
HSIStatus = RCC->CR & RCC_CR_HSIRDY;
if(HSIStatus==RCC_CR_HSIRDY)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//使能预取指
FLASH_SetLatency(FLASH_Latency_2);//设置两个等待
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
RCC_PLLConfig(RCC_PLLSource_HSI_Div2, RCC_PLLMul_x);//配置PLLCLK=HSE*RCC_PLLMul_x
RCC_PLLCmd(ENABLE);//使能PLL
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET );//等待PLL稳定
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//选择系统时钟
while(RCC_GetSYSCLKSource()!=0x08);
}
else{
/* 如果HSE启动失败,用户可以在这里添加处理错误的代码*/
}
}
至此,关于时钟的全部代码已全部写完,有条件的同学可以尝试用示波器接PA8引脚观察MCO输出的频率是否发生变化