stm32 RCC时钟

时钟介绍

众所周知,要让单片机的各个设备(如ADC、定时器TIMx……)正常工作,必须要为其提供一定的时钟频率,因此,为了能进行更多、更灵活的时钟配置,stm32单片机提供了了多个时钟源,通过对时钟源进行简单配置(如倍频、分频),然后提供给各个需要使用的设备,使其正常工作。

时钟树

image-20210921143328915

图中的梯形符号可以理解为一个多入单出的开关,即从多个输入中选择一个输出

时钟源

从上图可以看出,stm32有5个时钟源:HSI、HSE、LSI、LSE、PLL(其实只有四个,因为PLL时钟并不能算“源”,因为它也是来源于HSI或者HSE,从时钟树中也能看出来)

说明:时钟源的字母"H"代表高速时钟,"L"代表低速时钟,"I"代表内部时钟,"E"代表外部时钟,外部时钟都有2个引脚引出去,用来接外部晶振。

高速内部时钟(HSI):由内部RC振荡器产生,频率为8MHz,但不稳定。

高速外部时钟(HSE):以外部晶振作时钟源,晶振频率可取范围为4~16MHz,一般采用8MHz的晶振。

低速内部时钟(LSI):由内部RC振荡器产生,也主要提供给实时时钟模块,频率大约为40KHz。

低速外部时钟(LSE):以外部晶振作时钟源,主要提供给实时时钟模块,所以一般采用32.768KHz。

高速、低速时钟

高速时钟(HSE和HSI)提供给芯片主体的主时钟

低速时钟(LSE和LSI)只是提供给芯片中的RTC(实时时钟)及独立看门狗使用

从图中可以看出高速时钟也可以提供给RTC。

image-20210921145256005

外部、内部时钟

内部时钟是在芯片内部RC振荡器产生的,起振较快,所以时钟在芯片刚上电的时候,默认使用内部高速时钟。

外部时钟信号是由外部的晶振输入的,在精度和稳定性上都有很大优势,所以上电之后我们再通过软件配置,转而采用外部时钟信号。

外部高速时钟HSE

  1. 从外部晶振(8MHz)接到OSC_OUT和OSC_IN引脚开始
  2. 经过第一个分频器PLLXTPRE(不分频或者分频——频率除以2)
  3. 遇到开关PLLSRC(PLL entry clock source),我们可以选择其输出,输出为外部高速时钟(HSE)或是内部高速时钟(HSI)。这里选择输出为HSE
  4. 接着遇到锁相环PLL,具有倍频作用,在这里我们可以输入倍频因子PLLMUL(可以是 2,3,4,...,16),建议不要超过72MHz,但要是想超频,就得在这个寄存器上做手脚啦。

image-20210921150643550

PLLCLK与SYSCLK时钟输出到外设

系统时钟SYSCLK经过一系列配置后提供给相应外设。

  1. 经过PLL的时钟称为PLLCLK。分频因子选1,即不分频;倍频因子我们设定为9倍频。也就是说,经过PLL之后,我们的时钟从原来HSE的8MHz变为72MHz的PLLCLK(8M/1 * 9=72M)

  2. 紧接着又遇到了一个开关SW,通过这个开关,可以切换SYSCLK的时钟源,可以选择为HSI、PLLCLK、HSE;经过这个开关之后就是STM32的系统时钟SYSCLK了。

  3. 我们选择PLLCLK时钟,所以得到SYSCLK为72MHz。PLLCLK在输入到SW前,还流向了USB预分频器,这个分频器输出为USB外设的时钟(USBCLK)

  4. SYSCLK经过AHB预分频器,分频后再通过一些其它配置(如APB1,APB2分频等)输出到其它外设,给这些外设提供时钟

    经过AHB预分频的时钟可以成为HCLK时钟,经过APB1预分频的时钟称为PCLK1时钟,经过APB2预分频的时钟称为PCLK2时钟

  5. GPIO外设挂载在APB2总线上,所以把APB2预分频器设置为不分频,就可以得到GPIO外设的时钟也等于HCLK,为72MHz了

image-20210921152627375

总结:

SYSCLK:系统时钟,STM32大部分器件的时钟来源。主要由AHB预分频器分配到各个部件,一般最大配置为72MHz(虽然可以超频,但不建议,因为会不稳定)

HCLK:由AHB预分频器直接输出得到,它是高速总线AHB的时钟信号,提供给存储器,DMA及cortex内核,是cortex内核运行的时钟,cpu主频就是这个信号,它的大小与STM32运算速度,数据存取速度密切相关。

FCLK:同样由AHB预分频器输出得到,是内核的“自由运行时钟”。“自由”表现在它不来自时钟 HCLK,因此在HCLK时钟停止时 FCLK 也继续运行。它的存在,可以保证在处理器休眠时,也能够采样和到中断和跟踪休眠事件 ,它与HCLK互相同步。

PCLK1:外设时钟,由APB1预分频器输出得到,最大频率为36MHz,提供给挂载在APB1总线上的外设

PCLK2:外设时钟,由APB2预分频器输出得到,最大72MHz,提供给挂载在APB2总线上的外设如GPIO

folding blue,查看APBx总线挂载的外设

【在头文件 stm32f10x_rcc.h头文件中指出了各个外设挂载在哪个总线上】

/** @defgroup APB2_peripheral 
  * @{
  */

#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
#define RCC_APB2Periph_GPIOE             ((uint32_t)0x00000040)
#define RCC_APB2Periph_GPIOF             ((uint32_t)0x00000080)
#define RCC_APB2Periph_GPIOG             ((uint32_t)0x00000100)
#define RCC_APB2Periph_ADC1              ((uint32_t)0x00000200)
#define RCC_APB2Periph_ADC2              ((uint32_t)0x00000400)
#define RCC_APB2Periph_TIM1              ((uint32_t)0x00000800)
#define RCC_APB2Periph_SPI1              ((uint32_t)0x00001000)
#define RCC_APB2Periph_TIM8              ((uint32_t)0x00002000)
#define RCC_APB2Periph_USART1            ((uint32_t)0x00004000)
#define RCC_APB2Periph_ADC3              ((uint32_t)0x00008000)
#define RCC_APB2Periph_TIM15             ((uint32_t)0x00010000)
#define RCC_APB2Periph_TIM16             ((uint32_t)0x00020000)
#define RCC_APB2Periph_TIM17             ((uint32_t)0x00040000)
#define RCC_APB2Periph_TIM9              ((uint32_t)0x00080000)
#define RCC_APB2Periph_TIM10             ((uint32_t)0x00100000)
#define RCC_APB2Periph_TIM11             ((uint32_t)0x00200000)

#define IS_RCC_APB2_PERIPH(PERIPH) ((((PERIPH) & 0xFFC00002) == 0x00) && ((PERIPH) != 0x00))
/**
  * @}
  */ 

/** @defgroup APB1_peripheral 
  * @{
  */

#define RCC_APB1Periph_TIM2              ((uint32_t)0x00000001)
#define RCC_APB1Periph_TIM3              ((uint32_t)0x00000002)
#define RCC_APB1Periph_TIM4              ((uint32_t)0x00000004)
#define RCC_APB1Periph_TIM5              ((uint32_t)0x00000008)
#define RCC_APB1Periph_TIM6              ((uint32_t)0x00000010)
#define RCC_APB1Periph_TIM7              ((uint32_t)0x00000020)
#define RCC_APB1Periph_TIM12             ((uint32_t)0x00000040)
#define RCC_APB1Periph_TIM13             ((uint32_t)0x00000080)
#define RCC_APB1Periph_TIM14             ((uint32_t)0x00000100)
#define RCC_APB1Periph_WWDG              ((uint32_t)0x00000800)
#define RCC_APB1Periph_SPI2              ((uint32_t)0x00004000)
#define RCC_APB1Periph_SPI3              ((uint32_t)0x00008000)
#define RCC_APB1Periph_USART2            ((uint32_t)0x00020000)
#define RCC_APB1Periph_USART3            ((uint32_t)0x00040000)
#define RCC_APB1Periph_UART4             ((uint32_t)0x00080000)
#define RCC_APB1Periph_UART5             ((uint32_t)0x00100000)
#define RCC_APB1Periph_I2C1              ((uint32_t)0x00200000)
#define RCC_APB1Periph_I2C2              ((uint32_t)0x00400000)
#define RCC_APB1Periph_USB               ((uint32_t)0x00800000)
#define RCC_APB1Periph_CAN1              ((uint32_t)0x02000000)
#define RCC_APB1Periph_CAN2              ((uint32_t)0x04000000)
#define RCC_APB1Periph_BKP               ((uint32_t)0x08000000)
#define RCC_APB1Periph_PWR               ((uint32_t)0x10000000)
#define RCC_APB1Periph_DAC               ((uint32_t)0x20000000)
#define RCC_APB1Periph_CEC               ((uint32_t)0x40000000)

时钟配置

系统初始化函数

系统在复位后,会首先调用系统初始化函数SystemInit()进行初始化配置,包括一些时钟初始化配置等,之后才会调用 main 函数进行执行。

在执行 SystemInit()函数后,配置的时钟如下所述:
SYSCLK(系统时钟) = 72MHZ
AHB总线时钟HCLK = 72MHZ
APB1总线时钟PCLK1 = 36MHZ
APB2总线时钟PCLK2 = 72MHZ
PLL主时钟PLLCLK = 72MHZ
(PLL2时钟 = 40MHZ)

系统时钟配置

配置顺序 HSE->PLL->SYSCLK

void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
{
	RCC_DeInit(); //将外设RCC寄存器重设为缺省值
	RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)
	if(RCC_WaitForHSEStartUp()==SUCCESS) //等待HSE起振
	{
		RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟预分频为1(HCLK)
		RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟(PCLK1)
		RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟(PCLK2)
		
		RCC_PLLConfig(div,pllm);//设置PLL时钟源及倍频系数
		RCC_PLLCmd(ENABLE); //使能PLL时钟源
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)的时钟源为为PLL时钟
		while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08表示时钟源为PLL时钟
	}
}

RCC_PLLConfig(div,pllm)就是配置从 HSE->PLL的过程中分频、倍频因子的值,如下图:

image-20210921160835070

示例程序:

#include "stm32f10x.h"
#include "led.h"

void delay(u32 i){
	while(i--);
}

/*******************************************************************************
* 函 数 名         : RCC_HSE_Config
* 函数功能		   : 自定义系统时钟,可以通过修改PLL时钟源和倍频系数实现时钟调整
* 输    入         : div:RCC_PLLSource_HSE_Div1/RCC_PLLSource_HSE_Div2
					pllm:RCC_PLLMul_2-RCC_PLLMul_16
* 输    出         : 无
*******************************************************************************/
void RCC_HSE_Config(u32 div,u32 pllm) //自定义系统时间(可以修改时钟)
{
	RCC_DeInit(); //将外设RCC寄存器重设为缺省值
	RCC_HSEConfig(RCC_HSE_ON);//设置外部高速晶振(HSE)
	if(RCC_WaitForHSEStartUp()==SUCCESS) //等待HSE起振
	{
		RCC_HCLKConfig(RCC_SYSCLK_Div1);//设置AHB时钟(HCLK)
		RCC_PCLK1Config(RCC_HCLK_Div2);//设置低速AHB时钟(PCLK1)
		RCC_PCLK2Config(RCC_HCLK_Div1);//设置高速AHB时钟(PCLK2)
		
		RCC_PLLConfig(div,pllm);//设置PLL时钟源及倍频系数
		RCC_PLLCmd(ENABLE); //使能PLL时钟源
		while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY)==RESET);//检查指定的RCC标志位设置与否,PLL就绪
		RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);//设置系统时钟(SYSCLK)的时钟源为为PLL时钟
		while(RCC_GetSYSCLKSource()!=0x08);//返回用作系统时钟的时钟源,0x08表示时钟源为PLL时钟
	}
}


int main()
{
	RCC_HSE_Config(RCC_PLLSource_HSE_Div2,RCC_PLLMul_9);   // 8M / 2 * 9 = 36M
	LED_Init();
	while(1)
	{
		GPIO_ResetBits(LED1_PORT,LED1_PIN);//点亮LED1
		delay(6000000);
		GPIO_SetBits(LED1_PORT,LED1_PIN);
		delay(6000000);
	}
}

posted @ 2022-03-31 00:00  aJream  阅读(174)  评论(0编辑  收藏  举报