抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

时钟介绍

众所周知,要让单片机的各个设备(如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

查看APBx总线挂载的外设

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/** @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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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

示例程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#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);
}
}

评论