RCC时钟配置实践

 

程序运行首先会在启动文件(即startup_stm32f10x_xx.s)里调用SystemInit()函数(最终调用的是SetSysClockTo72()函数)把系统时钟初始化为72MHz。换言之,在没有修改系统时钟时,其默认为72MHz,如果需要修改,则应重新定义并调用初始化函数。

我们通过观察不同时钟频率下LED灯闪烁的快慢来判断时钟配置情况。

一般情况下,我们会使用HSE经过PLL倍频之后配置系统时钟。而HSE,我们经常使用8MHz的无源晶振。我们就以HSE配置系统时钟为例,按照时钟树构造流程来完成代码。下图4-1是STM32的时钟树。

图4-1 STM32时钟树

图中的数字顺序便是配置流程顺序,代码也是按照这个流程写的。我们简要分析下各个步骤的相关配置。


1、如图4-2,这一步取决于第二步的PLL时钟来源PLLSRC,由时钟配置寄存器RCC_CFGR的位17:PLLXTPRE设置,“清0”为HSE不分频,“置1”为HSE 2分频。我们这里选择HSE不分频,即“清0”。代码如第二步所示,宏RCC_PLLSource_HSE_Div1即为HSE不分频作为PLL输入时钟。 图4-2

我们需要先使能HSE,等待HSE稳定,并且在此之前我们还需要将RCC寄存器复位,代码如下。

#define RCC_HSE_ON                       ((uint32_t)0x00010000)
ErrorStatus HSEStatus;
	
// 把RCC寄存器复位
RCC_DeInit();
	
// 使能HSE
RCC_HSEConfig(RCC_HSE_ON);
	
// 返回HSE状态,等待HSE稳定 
HSEStatus = RCC_WaitForHSEStartUp();

2、这一步即为PLL时钟源配置了。由CFGR寄存器的位16:PLLSRC设置,“清0”为HSI经二分频后作为PLL输入时钟,“置1”为HSE作为PLL输入时钟,如图4-3。

图4-3

我们这里选择HSE作为PLL的时钟来源,即“置1”。

#define RCC_PLLSource_HSE_Div1           ((uint32_t)0x00010000)
#define RCC_PLLMul_9                     ((uint32_t)0x001C0000)
// 配置锁相环(PLLCLK = HSE * 9 = 72MHz),必须在使能PLL前配置
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);

这个函数在stm32f10x_rcc.c文件,用于配置PLL时钟源和倍频因子。

3、设置PLL的倍频因子,对PLL时钟来源进行倍频,具体为多少倍频,由CFGR寄存器的位21-18:PLLMUL[3:0]配置。例如我们可以设置为9倍频,前面我们提到HSE为8MHz的无源晶振,所以倍频之后,PLL时钟PLLCLK = 8MHz * 9 = 72MHz,这是ST官方推荐的稳定时钟频率。我们也可以选择增大倍频因子进行超频,其最高为128MHz。如图4-4为倍频因子描述。代码如上步所示,配置为RCC_PLLMul_9。


图4-4

4、配置系统时钟SYSCLK。SYSCLK来源可以是HSI、PLLCLK、HSE,这里我们配置为SYSCLK = PLLCLK = 72MHz,由CFGR寄存器的位1-0:SW[1:0]配置。如图4-5。


图4-5
#define RCC_SYSCLKSource_PLLCLK          ((uint32_t)0x00000002)
// 选择PLLCLK作为系统时钟
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);

5、AHB总线时钟HCLK。HCLK由SYSCLK经过AHB预分频器分频得到,由CFGR寄存器的位7-4:HPRE[3:0]配置。如图4-1的时钟树所示,大部分的片上外设时钟都由HCLK分频得到,这里我们设置为不分频,即:HCLK = SYSCLK = 72MHz。如下图4-6。

图4-6
#define RCC_SYSCLK_Div1                  ((uint32_t)0x00000000)
// 配置AHB总线时钟HCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);

6、APB2总线时钟PCLK2。PCLK2由高速APB2预分频器得到,由CFGR寄存器的位13-11:PPRE2[2:0]配置。APB2总线为高速总线,片上的高速外设,如GPIO、USART1、SPI1等,都挂载到APB2总线上。所以我们这里设置不分频,即:PCLK2 = HCLK = 72MHz。如下图4-7。

图4-7
#define RCC_HCLK_Div1                    ((uint32_t)0x00000000)
// 配置APB2总线时钟PCLK2
RCC_PCLK2Config(RCC_HCLK_Div1);

7、APB1总线时钟PCLK1。PCLK1由低速APB1预分频器得到,由CFGR寄存器的位10-8:PPRE1[2:0]配置。PCLK1为低速总线时钟,最高为36MHz,片上的低速外设,如USART2/3/4/5、SPI2/3、I2C1/2等,都挂载到APB1总线上。我们设置其为2分频,即:PCLK1 = HCLK/2 = 36MHz。如下图4-8。

图4-8
#define RCC_HCLK_Div2                    ((uint32_t)0x00000400)
// 配置APB1总线时钟PCLK1
RCC_PCLK1Config(RCC_HCLK_Div2);

经过以上的简要分析后,我们大概清楚时钟的配置流程了,现在整理整理以上的代码,将其封装为函数。

/**
  * @brief  配置HSE作为系统时钟来源
  * @param  RCC_PLLMul_x:PLL倍频因子,
  *         x可以是:[2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]
  * @retval 无
  */
void HSE_SetSysClk(uint32_t RCC_PLLMul_x){
	
	ErrorStatus HSEStatus;
	
	// 把RCC寄存器复位
	RCC_DeInit();
	
	// 使能HSE
	RCC_HSEConfig(RCC_HSE_ON);
	
	// 返回HSE状态,等待HSE稳定 
        HSEStatus = RCC_WaitForHSEStartUp();
	
	if(HSEStatus == SUCCESS){
		// 使能预取指
		FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
		// 2个等待周期
		FLASH_SetLatency(FLASH_Latency_2);
		
		// 配置AHB总线时钟HCLK
		RCC_HCLKConfig(RCC_SYSCLK_Div1);
                // 配置APB2总线时钟PCLK2,PCLK2 = HCLK
		RCC_PCLK2Config(RCC_HCLK_Div1);
                // 配置APB1总线时钟PCLK1,PCLK1 = HCLK/2
		RCC_PCLK1Config(RCC_HCLK_Div2);
		
		// 配置锁相环时钟(PLLCLK = HSE * 9 = 72MHz),必须在使能PLL前配置
		RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_x);
		
                // 使能PLL
		RCC_PLLCmd(ENABLE);
		// 等待PLL稳定
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
		
		// 选择PLLCLK作为系统时钟
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
		while(RCC_GetSYSCLKSource() != 0x08);
	}else{
		/* 如果HSE启动失败,则可以在这里添加处理错误的代码 */
	}

}

以上就算封装好函数了,可以在main函数中调用,以重新配置系统时钟。

int main(void){

	// 倍频因子设置为9,则系统时钟配置为8MHZ * 9 = 72MHz
	HSE_SetSysClk(RCC_PLLMul_9);
	
        // 初始化LED灯
	LED_GPIO_Config();
	
	while(1){
		GPIO_SetBits(GPIOB, GPIO_Pin_0);    // 高电平关闭LED
		Delay(0xFFFFF);
		GPIO_ResetBits(GPIOB, GPIO_Pin_0);  // 低电平开启LED
		Delay(0xFFFFF);
	}
	
}

我们可以通过改变倍频因子大小,来输出不同的时钟频率,如修改为RCC_PLLMul_16,则时钟频率输出为8MHZ * 16 = 128MHz。不同的时钟频率输出,灯闪烁的速度也不同,倍频因子越大,频率越高,灯闪烁越快;倍频因子越小,则实验效果反之。

另外,图4-1时钟树中的左下角有一个MCO输出,可以对外输出时钟频率,我们可以利用示波器监控MCO引脚的输出频率来检测系统时钟是否配置正确。

原文地址:https://www.cnblogs.com/fire909090/p/8874827.html