时钟树
时钟和时钟树的概念
时钟就是高低变化的方波信号------单片机的心跳。因此,在使用任何一个片上外设之前,都必须先使能对应的时钟,把它"激活"。
单片机的时钟系统非常像人体的循环系统:时钟信号就像血液一样,通过总线输送到各个片上外设。我们把这种层次分明、分支繁多的时钟网络结构,称作时钟树。

分频器、锁相环和复用器
在时钟信号从源头输送到外设的过程中,需要经过一系列加工和处理。最核心的三个器件分别是:
-
分频器:将输入频率按比例降低(÷N)。
-
锁相环(PLL):将输入频率倍频放大(×N),用于产生高速时钟。
-
复用器:在多路时钟源中选择一路作为输出。

树根------时钟源
树根部分是整个时钟树的起点,也就是时钟源。我们这款单片机一共有四个时钟源,相当于四颗不同的心脏,对应四套循环系统。
根据产生的频率高低,可分为高频时钟源 和低频时钟源 ;根据所处位置,又可分为内置时钟源 (集成在芯片内部)和外置时钟源(需外接晶振)。
为什么还要费事外接时钟源呢?
因为内置时钟源(RC振荡器)虽然使用方便,但精度相对较低;而外置时钟源采用石英晶振,能够提供更加精准和稳定的时钟信号。

树干------系统时钟的产生
树干从树根获取原始的时钟信号,然后通过锁相环(PLL)等电路进行倍频加工,最终产生供整个系统使用的系统时钟(SYSCLK)。STM32F103 的系统时钟最高可达 72MHz。


树枝------总线与片上外设
树枝部分负责将系统时钟进一步输送到各个片上外设。这些传输路径就是总线。STM32 的总线分为 AHB、APB1 和 APB2,不同外设挂载在不同的总线上,其运行频率也可能有所差异。



时钟树编程
时钟树的初始状态
所谓"初始状态",指的是单片机上电或按下复位键后,时钟树的默认运行状态。

验证初始状态
按照数据手册,时钟树初始状态下的系统频率为 8MHz(来自内部 HSI 振荡器)。此时 CPU 执行代码的速度也是 8MHz。我们可以写一个简单的延时程序来验证这一点。

然而,实际下载运行后你会发现,LED 的闪烁频率远远快于我们基于 8MHz 预估的速度。这是为什么呢?请接着往下看。
标准库的启动代码
打开项目中的 startup 文件夹,你会看到一个以 .s 结尾的汇编文件------这就是标准库的启动代码。向下翻阅,找到 Reset_Handler 这个标签:

原来,在进入 main 函数之前,启动代码已经悄悄调用了 SystemInit 函数,将系统时钟从默认的 8MHz 初始化到了 72MHz。这就解释了为什么 LED 闪得比预想中快那么多!

为了进一步验证,我们可以把调用 SystemInit 的这条指令暂时注释掉,然后再下载一次程序。

这次你会发现,LED 闪烁速度确实慢下来了,但还是比按 8MHz 理论计算的速度偏快一点。这又是为什么呢?
原因在于,时钟频率不同时,for 循环单次执行所消耗的机器周期数并不相同。在 8MHz 的初始配置下,一个循环大约消耗 6 个周期,而不是我们预估的 10 个周期。所以即便去掉了倍频,实际延时时长仍然会有细微偏差。
时钟树的编程接口
标准库为我们提供了一系列用于配置时钟树的函数。乍一看确实有点多,但请不要被吓倒。这些接口和右侧时钟树图中的各个环节是严格对应的。在实际项目中,我们很少需要从零手写时钟配置代码,所以这里大家重在理解每个函数的作用即可。
注:PLL 的全称是 Phase Lock Loop(锁相环)。

img-ycfi7Snk-1776834551513)
配置时钟树




配置Flash指令预取
Flash 存储器相当于单片机的"硬盘",用来存放我们烧录的程序。CPU 在执行代码时,需要不断地从 Flash 中读取指令。然而,CPU 的处理速度远远高于 Flash 的读取速度,这就形成了一个速度瓶颈。
为了提高运行效率,我们可以在 CPU 和 Flash 之间加入一个预取缓冲区 ,并设置合适的等待周期(Latency),让两者能够协调工作。

片上外设开关和复位
在使用任何一个片上外设(如 GPIO、USART、TIM)之前,不仅要在代码里配置好参数,还必须先打开对应外设的时钟开关。此外,标准库还提供了外设复位功能,可以在必要时将该外设的所有寄存器恢复为默认值。
