1. 什么是时钟
1.1 时钟和时钟树的概念
1.1.1 时钟:
• 时钟是指 为芯片内部的各个模块(片上外设)提供 时间基准 的信号源,是芯片正常工作的基础。我们在写程序之前,首先都需要给要使用的那个模块(外设)打开时钟。
• 时钟是一种方波信号,时钟的频率越快,外设工作效率就越高。
• 时钟其实也可以理解人的心跳。
1.1.2 时钟树,如图:

• 如上图,用一棵树的形式,生动形象表示单片机的时钟系统。
• 可以把时钟树理解为血液系统,为单片机的每个外设输送时钟信号(血液)。
• 时钟树上的树根表示晶振的来源(时钟的来源),树干表示产生SYSCLK那一部分,树枝表示的AHB,APB1和APB2这三条总线,树叶表示的片上外设。
• 其中时钟树可以为高速时钟树(几十MHz)和低速时钟树(几十kHz)。
1.2 分频器,锁相环和复用器,如图:

• 分频器:对频率做除法。
• 锁相环:对频率做乘法。
• 复用器:对频率做选择。
1.3 树根,如图:

• 树根就是时钟的来源,内部时钟有两个LSI,HSI但是内部的时钟精度不高。
• 时钟的来源有四种,LSI,LSE,HSI,HSE。
1.4 树干,如图:

• 树干:对时钟信号 进行加工 从而得到SYSCLK。
• 如图,能得到的SYSCLK的三种情况:

• HSI得到的SYSCLK:方便,都是精度不高。
• 锁相环得到的SYSCLK:灵活,可以得到不同的 SYSCLK 频率。
• HSE得到的SYSCLK:精度高。
1.5 树枝,如图:

• 树枝其实就是上图的三条总线。
• 树叶指的就是片上外设,如图:
• 通过SYSCLK分频,可以进一步 得到 这三条总线相对应的时钟,也就是说对SYSCLK进行分频,得到的HCLK,PCLK1和PCLK2。
2. 时钟树编程
2.1 时钟树初始状态
• 初始状态:给单片机刚上电或者按了复位按钮之后,还没来得及执行相应代码,此时单片机的状态。如图:

• 此时在初始状态下,单片机上面的时钟为8MHz ,由HSI 提供,所以SYSCLK=HCLK=PCLK1=PCLK2=8MHz,此时的CPU(Cortex-M3的频率)也是8HMz。CPU 频率决定单片机代码的执行效率。
2.2 标准库的启动代码,如图:

• 在启动文件中,可以找到这一段的代码,其中单片机启动的时候,先是执行了SystemInit这一段代码,也就是先配置 系统时钟 为72MHz,然后在执行main里面的函数。
• 如图,经过SystemInit之后,时钟由 8HMz 变成 72MHz

2.3 时钟树的编程接口

• 这些编程接口一一对应时钟树的器件,总线等等。
• 配置的步骤,如图:

• 为什么那个3那里第三,而不是4那里第三。
• 4那里本质就是选择的SYSCLK信号源,3那里就是设置分配器系数,之后的分频系数不变,变仅仅就是信号源。而4就是改变的是信号源。
2.4 配置
2.4.1 步骤一

2.4.2 步骤二

2.4.3 步骤三

2.4.4 步骤四

2.5 代码
#include "stm32f10x.h"
void SystemClock_Init();
int main(void)
{
SystemClock_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC,ENABLE);
GPIO_InitTypeDef gpio_initStruct = {0};
gpio_initStruct.GPIO_Mode = GPIO_Mode_Out_OD;
gpio_initStruct.GPIO_Pin = GPIO_Pin_13;
gpio_initStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC,&gpio_initStruct);
while(1)
{
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_RESET);
for(uint32_t i = 0;i < 666666;i++);
GPIO_WriteBit(GPIOC,GPIO_Pin_13,Bit_SET);
for(uint32_t i = 0;i < 666666;i++);
}
}
void SystemClock_Init(){
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);//开启指令预取
FLASH_SetLatency(FLASH_Latency_2);//设置访问延迟
//1.选择晶振
RCC_HSEConfig(RCC_HSE_ON);
while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);//等待HSE就绪
//2.启动锁相环,并选择乘的系数
RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9);
RCC_PLLCmd(ENABLE);
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);//等待PLL就绪
//3.配置HCLK,PCLK1,PCLK2
RCC_HCLKConfig(RCC_SYSCLK_Div1);
RCC_PCLK1Config(RCC_HCLK_Div2);
RCC_PCLK2Config(RCC_HCLK_Div1);
//4.配置SYSCLK的来源
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
while(RCC_GetSYSCLKSource() != 0x08);//判断当前来源是不是来自锁相环
}
2.6 配置FLASH指令预取
2.6.1 为什么会有指令预取
• 这是因为FLASH是存储代码(程序),而程序的执行是在CPU那里,但是又由于CPU的执行速度比FLASH快太多了,这就需要FLASH把要执行的指令放到一个缓冲区里面,等待CPU的执行,如图:

• 至于要不要访问延迟,就看时钟频率。但是本质还是Flash的速度比CPU慢太多了。
• 还有一点,这两端段代码需要在SYSCLK <= 8MHZ的时候执行。
2.6.2 片上外设开关和复位,如图:

• 注意如果调用复位电路,需要调用两次,一次是ENABLE,一次是DISABLE,相当于按下和松开的步骤。