时钟系统
Author:余生
在嵌入式系统中,时钟系统是整个硬件架构的核心之一。它不仅决定了 CPU 的运行速度,还影响着外设的工作频率、定时器的精度以及系统的功耗。本文将从 最底层的角度 讲解 STM32 微控制器中的时钟结构,并结合定时器的应用场景,展示如何通过配置时钟来优化系统性能。
一、引言
STM32 系列微控制器基于 ARM Cortex-M 内核,其内部集成了复杂的时钟树结构。理解这些时钟源及其分配机制对于开发高效能、低功耗的嵌入式系统至关重要。本文将详细讲解 STM32 的时钟系统,包括时钟源的选择、分频与倍频、PLL(锁相环)的工作原理,以及如何利用这些知识配置定时器等外设。
二、时钟源概述
STM32 提供了多种时钟源,开发者可以根据具体需求选择合适的时钟源。常见的时钟源包括:
- HSI(High-Speed Internal Clock):
- 内部 RC 振荡器,通常为 8 MHz。
- 优点:启动速度快,无需外部晶振。
- 缺点:精度较低,受温度和电压变化影响大。
- HSE(High-Speed External Clock):
- 外部晶振或陶瓷谐振器,典型值为 8 MHz 或 25 MHz。
- 优点:高精度,稳定性好。
- 缺点:需要额外的外部元件,启动时间较长。
- LSE(Low-Speed External Clock):
- 用于 RTC(实时时钟),典型值为 32.768 kHz。
- 优点:高精度,适合长时间计时。
- 缺点:需要额外的外部元件。
- LSI(Low-Speed Internal Clock):
- 内部 RC 振荡器,典型值为 40 kHz。
- 优点:无需外部元件,启动快。
- 缺点:精度较低。
- PLL(Phase-Locked Loop):
- 锁相环电路,用于倍频输入时钟信号。
- 可以将 HSI 或 HSE 的频率倍频至更高的频率,如 72 MHz。
三、时钟树结构
STM32 的时钟树是一个复杂的网络,负责将不同的时钟源分配给各个模块。以下是 STM32F103 系列的一个简化版时钟树:
+-------------------+
| HSI |
| (Internal, 8 MHz) |
+-------------------+
|
v
+-------------------+
| PLL |
| (Phase-Locked Loop)|
+-------------------+
|
v
+-------------------+
| SYSCLK |
| (System Clock) |
+-------------------+
|
+---------------------+----------------------+
| | |
v v v
+---------------+ +--------------+ +-------------+
| AHB | | APB1 | | APB2 |
| (Advanced High-| | (Advanced | | (Advanced |
| Performance Bus)| | Peripheral Bus 1) | | Peripheral Bus 2) |
+---------------+ +--------------+ +-------------+
| | |
v v v
+---------------+ +--------------+ +-------------+
| CPU | | TIMx | | TIMx |
| | | (Timers) | | (Timers) |
+---------------+ +--------------+ +-------------+
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
3.1 系统时钟(SYSCLK)
SYSCLK 是整个系统的核心时钟,驱动 CPU 和高速外设。可以通过以下方式设置 SYSCLK:
- 直接使用 HSI 或 HSE 作为 SYSCLK。
- 使用 PLL 对 HSI 或 HSE 进行倍频后作为 SYSCLK。
// 设置系统时钟为 PLL 输出的 72 MHz
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
2
3.2 AHB 和 APB 总线时钟
AHB 和 APB 总线时钟分别驱动不同的外设模块。通常情况下,AHB 总线时钟(HCLK)直接等于 SYSCLK,而 APB1 和 APB2 总线时钟则通过对 HCLK 进行分频得到。
// 设置 AHB 时钟为 SYSCLK / 1
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// 设置 APB1 时钟为 HCLK / 2
RCC_PCLK1Config(RCC_HCLK_Div2);
// 设置 APB2 时钟为 HCLK / 1
RCC_PCLK2Config(RCC_HCLK_Div1);
2
3
4
5
6
7
8
3.3 定时器时钟
定时器(TIMx)挂载在 APB1 或 APB2 总线上,其时钟源取决于所挂载的总线。需要注意的是,某些定时器的时钟可能会被自动倍频。例如,在 STM32F103 中,APB1 上的定时器时钟会被自动倍频两倍。
// 配置定时器时钟
void TIM_Config(void) {
// 启用定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
在这个例子中,假设 PCLK1 为 36 MHz,预分频系数为 7199,则定时器的实际计数频率为:
Timer Frequency=36 MHz7200=5 kHzTimer Frequency=720036 MHz=5 kHz
因此,定时器每 200 微秒产生一次中断。
四、PLL 工作原理
PLL 是一种用于生成高频时钟的电路,广泛应用于现代微控制器中。其基本工作原理如下:
- 输入时钟:PLL 接收一个稳定的参考时钟(可以是 HSI 或 HSE)。
- 分频器:对输入时钟进行分频,生成一个较低频率的信号。
- 压控振荡器(VCO):将分频后的信号放大并调整频率。
- 反馈回路:将 VCO 输出的一部分信号反馈到 PLL 的比较器中,与原始输入时钟进行比较。
- 输出时钟:经过多次循环调整后,VCO 输出稳定且高精度的时钟信号。
PLL 配置示例
假设我们希望将 HSE(8 MHz)倍频至 72 MHz 作为系统时钟:
// 启用 HSE 时钟
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); // 等待 HSE 准备就绪
// 配置 PLL
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); // 8 MHz * 9 = 72 MHz
// 启用 PLL
RCC_PLLCmd(ENABLE);
while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); // 等待 PLL 准备就绪
// 设置系统时钟为 PLL 输出
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// 更新 Flash 等待状态
FLASH_SetLatency(FLASH_Latency_2);
2
3
4
5
6
7
8
9
10
11
12
13
在这个过程中,我们首先启用了 HSE 时钟,并等待其准备就绪。然后配置 PLL 将 HSE 时钟倍频至 72 MHz,并启用 PLL。最后,我们将系统时钟切换到 PLL 输出,并根据新的时钟频率调整 Flash 等待状态,确保指令执行不会出错。
五、定时器的应用与优化
5.1 基本定时功能
定时器是嵌入式系统中最常用的外设之一,可用于实现延时、PWM 输出、捕获等功能。下面是一个简单的定时器配置示例:
void TIM_Delay_Init(uint16_t ms) {
// 启用定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// 配置定时器参数
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 7199; // 预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 配置中断优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 使能定时器中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
// 清除中断标志位
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 执行定时任务
// ...
}
}
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
在这个例子中,我们配置了一个定时器,使其每隔 200 微秒产生一次中断。通过调整 TIM_Period
和 TIM_Prescaler
参数,可以灵活地控制定时器的周期。
5.2 PWM 输出
定时器还可以用于生成 PWM 信号。下面是一个简单的 PWM 输出配置示例:
void TIM_PWM_Init(void) {
// 启用定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
// 配置定时器参数
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
// 配置 PWM 输出
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 499; // 占空比为 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStructure);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
// 使能定时器
TIM_Cmd(TIM3, ENABLE);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在这个例子中,我们配置了定时器 TIM3 生成一个占空比为 50% 的 PWM 信号。通过调整 TIM_Pulse
参数,可以改变 PWM 信号的占空比。
六、总结
通过对 STM32 时钟系统的深入探讨,我们可以看到,合理的时钟配置对于构建高效能、低功耗的嵌入式系统至关重要。时钟源的选择、PLL 的配置、AHB 和 APB 总线的分配,都直接影响着系统的整体性能。结合定时器的应用场景,展示了如何通过配置时钟来实现精确的时间控制和高效的外设操作。
理解并掌握这些基础知识,不仅可以帮助开发者更好地设计嵌入式系统,还能在面对复杂应用场景时提供更多的优化手段。无论是提高系统的实时响应能力,还是降低功耗,合理配置时钟都是关键步骤之一。希望本文能够为读者提供有价值的参考,并激发更多关于嵌入式系统设计的思考。