1. 什么是时钟
时钟是嵌入式系统中驱动所有外设和内核工作的基础节拍信号。它像人的心跳一样,为 CPU、存储器、总线以及各类外设提供统一的时序基准。没有时钟,芯片内部的逻辑电路就无法同步运行,程序也无法按预期顺序执行。
在 STM32 中,时钟系统不仅决定了 CPU 的运行速度,还决定了外设(如定时器、串口、ADC、DMA 等)的工作频率。理解时钟的来源与作用,是掌握 STM32 定时器、通信接口等模块的前提。
2. 相关概念
在深入 STM32 时钟系统之前,先梳理几个关键概念:
- 石英晶振(Crystal):利用石英晶体的压电效应产生稳定振荡频率的元件,是时钟信号的源头。STM32 常用的外部晶振有 8MHz(HSE)和 32.768kHz(LSE,用于 RTC)。
- PLL(锁相环):一种频率合成电路,可以对输入时钟进行倍频或分频,从而得到更高或更精确的频率。STM32 内部常用 PLL 将 HSE 或 HSI 倍频到系统所需的主频。
- DIVIDER(分频器):将输入时钟频率按比例降低的电路。例如 AHB 分频器、APB 分频器,用于给不同总线提供合适的时钟频率。
- PRESC(预分频器):Prescaler 的缩写,通常位于定时器或通信外设内部,用于在计数或采样前对时钟进行分频,从而调整计数精度或波特率。
- SW(系统时钟切换):System Clock Switch,用于选择系统时钟 SYSCLK 的来源,可在 HSI、HSE、PLLCLK 之间切换。
3. 为什么需要时钟
时钟的存在有以下几个核心原因:
- 同步运行:数字电路需要统一的节拍来同步各模块的状态变化,避免竞争与亚稳态问题。
- 功耗控制:通过降低或关闭某些外设的时钟,可以显著降低芯片功耗,这在电池供电场景中尤为重要。
- 性能调节:不同任务对运算速度要求不同,合理配置时钟频率可以在性能与功耗之间取得平衡。
- 外设时序:定时器、PWM、串口波特率、ADC 采样率等都依赖精确的时钟源,时钟配置直接影响这些外设的工作精度。
4. STM32F4xx 时钟系统
注:时钟系统(时钟树)请参考《STM32F4xx 中文参考手册》107 页
4.1 时钟来源
STM32F4xx 的时钟系统主要由以下时钟源构成:
- HSI(高速内部时钟):内部 RC 振荡器,频率为 16MHz,无需外部晶振即可工作,但精度较低,受温度影响较大。
- HSE(高速外部时钟):外部晶振或外部时钟源,典型值为 8MHz,精度高,是系统主时钟的首选来源。
- LSI(低速内部时钟):约 32kHz,用于独立看门狗和 RTC 的备用时钟。
- LSE(低速外部时钟):32.768kHz 外部晶振,主要用于 RTC,保证低功耗下的精确计时。
- PLLCLK(PLL 输出时钟):由 PLL 对 HSI 或 HSE 进行倍频后得到,是系统时钟 SYSCLK 的常用来源。
4.2 系统时钟来源
系统时钟 SYSCLK 是 STM32 内核和存储器系统的工作时钟,它有三个可选来源:
- HSI:内部 16MHz RC 振荡器,启动阶段默认使用,精度较低。
- HSE:外部高速晶振,精度高,适合对时序要求严格的场景。
- PLLCLK:PLL 倍频后的时钟,一般默认为 PLLCLK,因为它可以将外部晶振倍频到更高的主频,充分发挥 STM32F4xx 的性能。
系统上电后,默认使用 HSI 作为 SYSCLK 源,随后由用户代码(通常通过 SystemInit 函数)切换到 HSE 并经 PLL 倍频到目标主频。
4.3 PLLCLK 频率计算
PLLCLK 的频率由输入时钟、分频系数和倍频系数共同决定。STM32F4xx 的 PLL 配置通常涉及以下参数:
- PLLM:输入时钟分频系数,用于将 HSE 或 HSI 分频到 1MHz 到 2MHz 的参考频率。
- PLLN:倍频系数,将参考频率倍频到 VCO 输出频率。
- PLLP:系统时钟分频系数,将 VCO 输出分频得到 PLLCLK。
- PLLQ:USB/SDIO 等外设时钟分频系数。
PLLCLK 的计算公式如下:
c
PLLCLK = (输入时钟频率 / PLLM) × PLLN / PLLP
在计算 PLLCLK 之前,需要先明确 VCO 的输入与输出频率,其关系如下:
c
VCO_IN = HSE_VALUE / PLLM
VCO_OUT = VCO_IN × PLLN
其中 HSE_VALUE 为外部晶振频率(如 8MHz),PLLM 为输入分频系数,PLLN 为倍频系数。得到 VCO_OUT 后,再经 PLLP 分频即可得到最终的 PLLCLK:
c
PLLCLK = VCO_OUT / PLLP
以常见的 8MHz HSE 为例,若配置 PLLM=8、PLLN=336、PLLP=2,则:
c
PLLCLK = (8MHz / 8) × 336 / 2 = 1MHz × 168 = 168MHz
这就是 STM32F407 常见的 168MHz 主频配置。需要注意的是,PLLN 的取值范围、VCO 输出频率范围以及 PLLP 的可选值都受芯片数据手册约束,配置时必须确保各参数在合法范围内。
4.4 时钟计算
在 STM32F4xx 中,SYSCLK 经过 AHB 预分频器、APB1 预分频器和 APB2 预分频器逐级分频,为不同总线及挂载其上的外设提供时钟。各总线时钟的计算关系如下:
c
AHB_CLK = SYSCLK / AHB_Prescaler
APB1_CLK = AHB_CLK / APB1_Prescaler
APB2_CLK = AHB_CLK / APB2_Prescaler
以常见的 168MHz 主频配置为例,若 AHB 预分频为 1、APB1 预分频为 4、APB2 预分频为 2,则各总线时钟为:
c
AHB_CLK = 168MHz / 1 = 168MHz
APB1_CLK = 168MHz / 4 = 42MHz
APB2_CLK = 168MHz / 2 = 84MHz
其中 AHB_CLK 直接作为 SYSTICK(系统滴答定时器)的时钟源。SYSTICK 的时钟频率由 AHB 预分频器输出后再经固定 8 分频得到,即 SYSTICK 的默认时钟为 AHB_CLK / 8。以 168MHz 主频配置为例,SYSTICK 的默认时钟为 168MHz / 8 = 21MHz。SYSTICK 常用于操作系统的时基或延时函数,其计数频率由 AHB_CLK / 8 决定。
APB1 和 APB2 总线上的定时器时钟需要特别注意:当 APB 预分频系数为 1 时,定时器时钟等于对应的 APB 时钟;当 APB 预分频系数大于 1 时,定时器时钟会被自动倍频 2 倍。计算公式如下:
c
TIMx_CLK = APB1_CLK × 2(当 APB1 预分频 > 1)
TIMx_CLK = APB2_CLK × 2(当 APB2 预分频 > 1)
因此,在上述配置下,APB1 定时器(如 TIM2、TIM3、TIM4、TIM5)的默认时钟为:
c
TIM_APB1_CLK = 42MHz × 2 = 84MHz
APB2 定时器(如 TIM1、TIM8、TIM9、TIM10、TIM11)的默认时钟为:
c
TIM_APB2_CLK = 84MHz × 2 = 168MHz
需要注意的是,APB1 总线上的定时器最高时钟不能超过 84MHz,APB2 总线上的定时器最高时钟不能超过 168MHz,这是由芯片设计约束决定的。理解这些总线时钟与定时器时钟的换算关系,是正确配置定时器预分频和计数周期的前提。
5. STM32F4xx 定时器
定时器是 STM32 中最常用的外设之一,用于产生精确的时间基准、测量脉冲宽度、生成 PWM 波形以及捕获外部信号等。理解定时器的工作原理,是掌握延时、PWM 调速、编码器测速等应用的基础。
5.1 什么是定时器
定时器(Timer)本质上是一个可编程的计数器,它按照固定的时钟节拍进行计数,当计数值达到预设值时触发中断或更新事件。通过配置预分频器和自动重载寄存器,可以灵活地控制计数频率和计数周期,从而实现精确的定时功能。
定时器在嵌入式系统中的应用非常广泛,主要包括:
- 延时功能:通过定时器产生精确的时间间隔,替代软件循环延时,提高 CPU 利用率。
- PWM 输出:生成占空比可调的方波信号,用于电机调速、LED 调光等场景。
- 输入捕获:测量外部脉冲的宽度、频率或相位差,常用于编码器测速和信号分析。
- 输出比较:在指定时刻翻转引脚电平,用于产生精确的时序控制信号。
5.2 定时器的基本原理
从功能结构上看,STM32 定时器主要由三个部分组成:时基单元 、输入捕获单元 和输出比较单元。这三个部分协同工作,共同实现了定时、测量和波形生成等核心功能。
- 时基单元(Time Base Unit):负责产生计数时钟并驱动计数器工作,是整个定时器的核心。它由预分频器(PSC)、计数器(CNT)和自动重载寄存器(ARR)组成,决定了定时器的计数频率和计数周期。无论是基本定时器、通用定时器还是高级定时器,都包含时基单元。
- 输入捕获单元(Input Capture Unit):用于测量外部信号的频率、周期和脉宽。当外部信号发生跳变时,输入捕获单元会记录当前计数器的值,从而计算出信号的时间特征。该单元主要存在于通用定时器和高级定时器中。
- 输出比较单元(Output Compare Unit):用于产生精确的时序控制信号和 PWM 波形。当计数器的值与捕获/比较寄存器(CCR)的值相等时,输出比较单元会触发相应的输出动作,如翻转引脚电平或产生 PWM 信号。该单元同样主要存在于通用定时器和高级定时器中。
基本定时器(TIM6、TIM7)仅包含时基单元,因此只支持定时功能;而通用定时器和高级定时器则同时具备这三个部分,功能更加丰富。
- 时钟源(Clock Source):定时器的计数时钟来源,可以是内部时钟(通常为 APB 总线时钟的倍频)或外部时钟。内部时钟经过预分频器分频后,得到计数器的计数频率。
- 预分频器(PSC,Prescaler):将定时器时钟进行分频,分频系数为 PSC + 1。例如,若定时器时钟为 84MHz,PSC 设置为 83,则计数频率为 84MHz / 84 = 1MHz,即每 1 微秒计数一次。
- 计数器(CNT,Counter):在计数时钟的驱动下递增或递减。计数模式包括向上计数、向下计数和中心对齐模式。当计数值达到自动重载寄存器(ARR)的值时,计数器清零并产生更新事件。
- 自动重载寄存器(ARR,Auto-Reload Register):决定计数周期。当 CNT 计数到 ARR 时,产生更新事件(溢出中断),计数器重新从 0 开始计数。定时周期计算公式如下:
c
定时周期 = (PSC + 1) × (ARR + 1) / 定时器时钟频率
以 84MHz 的定时器时钟为例,若 PSC = 83、ARR = 999,则计数频率为 1MHz,计数周期为 1000 个计数,定时周期为 1000 微秒,即 1 毫秒。
更新事件与中断(Update Event & Interrupt):当计数器溢出(达到 ARR 值)时,会产生更新事件(UEV)。如果使能了更新中断,CPU 会进入中断服务函数,从而执行定时任务。这是实现延时函数和周期性任务调度的基础。
控制寄存器(CR1、CR2):用于配置定时器的基本工作模式,包括计数器使能、计数方向、自动重载预装载使能、更新事件源选择等。例如 CR1 中的 CEN 位控制计数器是否启动,DIR 位控制计数方向。
状态寄存器(SR):记录定时器的各种事件标志,如更新中断标志(UIF)、捕获比较标志(CC1IF)等。中断服务函数中需要读取 SR 判断事件来源,并在处理后清除相应标志位。
中断使能寄存器(DIER):控制定时器各事件中断的使能,如更新中断使能(UIE)、捕获比较中断使能(CC1IE)等。只有在此寄存器中使能的中断,才会在事件发生时触发 CPU 中断。
捕获/比较寄存器(CCR1~CCR4):用于输入捕获或输出比较功能。在输出比较模式下,CCR 的值与 CNT 比较,相等时触发输出动作;在输入捕获模式下,CCR 用于保存捕获时刻的计数值。
捕获/比较模式寄存器(CCMR1、CCMR2):配置每个通道的工作模式,如 PWM 模式、强制输出、输入捕获的滤波与分频等。通用定时器和高级定时器通过该寄存器实现 PWM 生成和输入捕获功能。
捕获/比较使能寄存器(CCER):控制各通道的输入捕获或输出比较功能是否使能,以及输出极性、互补输出使能等。例如 CCER 中的 CC1E 位使能通道 1 的输出。
重复计数寄存器(RCR,仅高级定时器):控制更新事件产生的频率。当 RCR 不为 0 时,计数器需要溢出 RCR + 1 次才会产生一次更新事件,常用于精确控制 PWM 的更新周期。
刹车与死区寄存器(BDTR,仅高级定时器):配置死区时间、刹车输入极性以及主输出使能(MOE)。高级定时器在输出互补 PWM 前必须设置 MOE 位,否则信号不会输出到引脚。
更新事件与中断:当计数器溢出(达到 ARR 值)时,会产生更新事件(UEV)。如果使能了更新中断,CPU 会进入中断服务函数,从而执行定时任务。这是实现延时函数和周期性任务调度的基础。
5.3 STM32F4xx 定时器
STM32F4xx 系列内置了多种类型的定时器,按功能和复杂度可分为以下几类:
- SysTick(系统滴答定时器):集成在 Cortex-M4 内核中,用于操作系统时基和简单延时。
- 基本定时器(TIM6、TIM7):功能最简单,仅支持定时功能,常用于产生周期性中断。
- 通用定时器(TIM2~TIM5、TIM9~TIM14):功能丰富,支持输入捕获、输出比较、PWM 生成等。
- 高级定时器(TIM1、TIM8):功能最强大,在通用定时器基础上增加了互补输出、死区控制、刹车功能等,适用于电机控制。
不同类型的定时器挂载在不同的总线上,其时钟频率也不同。根据第 4 章的时钟计算,APB1 定时器(TIM2~TIM5、TIM6、TIM7)的默认时钟为 84MHz,APB2 定时器(TIM1、TIM8、TIM9~TIM11)的默认时钟为 168MHz。
5.4 SysTick 定时器
SysTick 是 Cortex-M4 内核自带的 24 位向下计数器,它不属于 STM32 外设,而是内核的一部分。SysTick 的时钟源为 AHB_CLK / 8,即系统时钟经固定 8 分频得到。以 168MHz 主频为例,SysTick 的时钟为 21MHz。
SysTick 定时器寄存器详情信息请参考《Cortex M3 权威指南》133 页
SysTick 的核心寄存器包括:
- CTRL(控制与状态寄存器):控制 SysTick 的使能、中断使能和时钟源选择。
- LOAD(重载值寄存器):设置计数初值,计数器递减到 0 后自动重载该值。
- VAL(当前值寄存器):保存当前计数值,读取该寄存器可获取剩余计数。
- CALIB(校准寄存器):提供 SysTick 的校准信息,通常用于操作系统时基校准。
SysTick 的计数频率为 21MHz,即每计数一次耗时 1/21MHz 秒。要实现 1 毫秒延时,需要计数 21000 次。因此,LOAD 寄存器的值应设置为 21000 - 1 = 20999。
下面给出一个基于 SysTick 的毫秒级延时函数示例:
c
volatile uint32_t sysTickCount = 0;
void SysTick_Init(void)
{
// 时钟源为 AHB_CLK / 8,即 21MHz
SysTick->CTRL = 0;
SysTick->LOAD = 21000 - 1; // 1ms 中断一次
SysTick->VAL = 0;
SysTick->CTRL = 0x07; // 使能 SysTick、使能中断、使用内核时钟
}
void SysTick_Handler(void)
{
sysTickCount++;
}
void delay_ms(uint32_t ms)
{
uint32_t start = sysTickCount;
while (sysTickCount - start < ms);
}
上述代码中,SysTick 每 1 毫秒触发一次中断,在中断服务函数中累加 sysTickCount。delay_ms 函数通过比较计数差值实现毫秒级延时,精度高且不占用 CPU 忙等待。
5.5 基本定时器
基本定时器 TIM6 和 TIM7 是 STM32F4xx 中功能最简单的定时器,挂载在 APB1 总线上,默认时钟为 84MHz。它们仅支持基本的定时功能,不具备输入捕获、输出比较或 PWM 生成能力。
基本定时器的核心功能包括:
- 定时中断:计数器溢出时产生更新中断,用于周期性任务调度。
- 触发 DAC:TIM6 和 TIM7 可以触发 DAC 转换,用于产生模拟波形。
- 主模式触发:可作为主定时器触发其他外设。
基本定时器的配置流程如下:
c
void TIM6_Init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM6EN; // 使能 TIM6 时钟
TIM6->PSC = 83; // 84MHz / 84 = 1MHz,计数频率为 1MHz
TIM6->ARR = 999; // 计数 1000 次,定时周期为 1ms
TIM6->DIER |= TIM_DIER_UIE; // 使能更新中断
TIM6->CR1 |= TIM_CR1_CEN; // 使能计数器
NVIC_EnableIRQ(TIM6_DAC_IRQn); // 使能 NVIC 中断
}
void TIM6_DAC_IRQHandler(void)
{
if (TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF; // 清除更新中断标志
// 定时任务处理
}
}
基本定时器虽然功能简单,但在需要周期性中断、不涉及 PWM 或捕获的场景下非常实用,代码简洁且易于理解。
5.6 通用定时器
通用定时器是 STM32F4xx 中功能最丰富、应用最广泛的定时器类型,包括 TIM2~TIM5 和 TIM9~TIM14。其中 TIM2 和 TIM5 是 32 位定时器,计数范围更大;其余为 16 位定时器。
通用定时器的主要功能包括:
- 定时中断:与基本定时器相同的定时功能。
- 输入捕获:测量外部信号的频率、周期和脉宽。
- 输出比较:在指定时刻翻转引脚电平。
- PWM 生成:输出占空比可调的方波信号。
- 编码器接口:直接连接正交编码器,用于电机测速。
通用定时器的时钟来源:TIM2~TIM5 挂载在 APB1 总线,默认时钟为 84MHz;TIM9~TIM11 挂载在 APB2 总线,默认时钟为 168MHz;TIM12~TIM14 挂载在 APB1 总线,默认时钟为 84MHz。
以 PWM 输出为例,通用定时器的配置流程如下:
c
void TIM3_PWM_Init(void)
{
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN; // 使能 TIM3 时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // 使能 GPIOA 时钟
// 配置 PA6 为复用功能(TIM3_CH1)
GPIOA->MODER |= GPIO_MODER_MODER6_1;
GPIOA->AFR[0] |= 0x02 << (6 * 4); // AF2 = TIM3
TIM3->PSC = 83; // 84MHz / 84 = 1MHz
TIM3->ARR = 999; // PWM 周期为 1ms,频率为 1kHz
TIM3->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2; // PWM 模式 1
TIM3->CCER |= TIM_CCER_CC1E; // 使能输出通道 1
TIM3->CCR1 = 500; // 占空比为 50%
TIM3->CR1 |= TIM_CR1_CEN; // 使能计数器
}
上述代码配置 TIM3 的通道 1 输出频率为 1kHz、占空比为 50% 的 PWM 波形。通过修改 CCR1 的值,可以实时调整占空比,实现电机调速或 LED 亮度控制。
5.7 高级定时器
高级定时器 TIM1 和 TIM8 采用 16 位计数器,计数范围为 0 到 65535。与通用定时器类似,其计数模式可以通过 CR1 寄存器中的 DIR 位进行配置,支持以下三种模式:
- 递增计数模式(Upcounting):计数器从 0 开始向上计数,当计数值达到自动重载寄存器(ARR)的值时产生更新事件,随后计数器清零并重新开始计数。这是最常用的计数模式,适用于常规定时和 PWM 生成。
- 递减计数模式(Downcounting):计数器从 ARR 的值开始向下计数,当计数值递减到 0 时产生更新事件,随后重新装载 ARR 的值并继续递减。该模式适用于需要倒计时或对称波形输出的场景。
- 先递增后递减模式(Center-Aligned,中心对齐模式):计数器先向上计数到 ARR,再向下计数到 0,如此往复。更新事件在计数器达到 0 或 ARR 时产生。该模式常用于生成中心对齐的 PWM 波形,可有效降低谐波,适用于电机控制和电源变换等对波形对称性要求较高的场景。
除了灵活的计数模式,高级定时器还支持 重复计数器(TIMx_RCR,Repetition Counter Register)。重复计数器用于控制更新事件产生的频率,其工作原理如下:
- 当 RCR 的值为 0 时,计数器每溢出一次就产生一次更新事件,这是默认行为。
- 当 RCR 的值不为 0 时,计数器需要溢出 RCR + 1 次才会产生一次更新事件。例如,若 RCR = 2,则计数器需要溢出 3 次才会产生一次更新事件。
- 重复计数器在每次更新事件产生时自动重新装载 RCR 的值,无需软件干预。
重复计数器在 PWM 应用中非常实用。例如,在电机控制中,通过设置 RCR 的值,可以让 PWM 波形每 N 个周期才更新一次占空比,从而减少更新中断的频率,降低 CPU 负担,同时保证波形的稳定性。
高级定时器 TIM1 和 TIM8 是 STM32F4xx 中功能最强大的定时器,挂载在 APB2 总线上,默认时钟为 168MHz。它们在通用定时器的基础上增加了以下高级功能:
- 互补输出:每个通道可输出一对互补的 PWM 信号,用于驱动 H 桥或半桥电路。
- 死区控制:在互补信号之间插入可编程的死区时间,防止上下桥臂同时导通。
- 刹车功能:检测到外部故障信号时,立即强制关闭 PWM 输出,保护电机和功率器件。
- 重复计数:支持重复计数寄存器,可控制更新事件产生的频率。
高级定时器的这些特性使其特别适合电机控制、开关电源和逆变器等应用场景。
以 TIM1 输出带死区的互补 PWM 为例,核心配置如下:
c
void TIM1_PWM_Init(void)
{
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // 使能 TIM1 时钟
TIM1->PSC = 167; // 168MHz / 168 = 1MHz
TIM1->ARR = 999; // PWM 频率为 1kHz
// 配置通道 1 为 PWM 模式,使能互补输出
TIM1->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2;
TIM1->CCER |= TIM_CCER_CC1E | TIM_CCER_CC1NE; // 使能 CH1 和 CH1N
TIM1->CCR1 = 500; // 占空比 50%
TIM1->BDTR |= TIM_BDTR_MOE; // 使能主输出
TIM1->CR1 |= TIM_CR1_CEN; // 使能计数器
}
需要注意的是,高级定时器在使能输出前必须设置 BDTR 寄存器中的 MOE(主输出使能)位,否则 PWM 信号不会输出到引脚。此外,死区时间通过 BDTR 寄存器的 DTG 位配置,具体数值需根据功率器件的开关特性确定。
5.8 实战示例
本节以 STM32F407 为例,演示如何使用 TIM13 的通道 1(CH1) 在 PF8 引脚输出 PWM 方波,驱动一个高电平响的有源蜂鸣器。TIM13 属于通用定时器,挂载在 APB1 总线上,默认时钟为 84MHz。其通道 1 的复用功能映射到 PF8,复用功能编号为 AF9。
整体配置流程分为以下五个步骤:
- 配置定时器通道引脚:使能 GPIOF 时钟,将 PF8 配置为复用功能,并选择 AF9 映射到 TIM13_CH1。
- 配置定时器时基单元(Time Base Unit):设置预分频器 PSC 和自动重载寄存器 ARR,确定 PWM 的频率。
- 配置定时器输出通道:将通道 1 配置为 PWM 模式,设置占空比,并使能输出。
- 定时器使能:使能 TIM13 计数器,开始产生 PWM 波形。
- 中断配置:使能 TIM13 更新中断并配置 NVIC(可选,用于周期性任务)。
下面给出基于寄存器操作的系统函数库配置流程。首先说明用到的关键结构体:
GPIO_InitTypeDef:GPIO 初始化结构体,用于配置引脚模式、输出类型、速度及上下拉。本示例中 PF8 配置为复用功能(GPIO_Mode_AF)、推挽输出(GPIO_OType_PP)、高速(GPIO_Speed_100MHz)和不上拉不下拉(GPIO_PuPd_NOPULL)。其定义如下:
c
typedef struct
{
uint32_t GPIO_Pin; // 指定要配置的引脚,可多个引脚按位或组合
// 如 GPIO_Pin_8 表示 PF8
GPIOMode_TypeDef GPIO_Mode; // 引脚工作模式
// GPIO_Mode_IN 输入模式
// GPIO_Mode_OUT 输出模式
// GPIO_Mode_AF 复用功能模式
// GPIO_Mode_AN 模拟模式
GPIOSpeed_TypeDef GPIO_Speed; // 输出速度等级
// GPIO_Speed_2MHz 低速
// GPIO_Speed_25MHz 中速
// GPIO_Speed_50MHz 快速
// GPIO_Speed_100MHz 高速
GPIOOType_TypeDef GPIO_OType; // 输出类型
// GPIO_OType_PP 推挽输出
// GPIO_OType_OD 开漏输出
GPIOPuPd_TypeDef GPIO_PuPd; // 上下拉配置
// GPIO_PuPd_NOPULL 无上下拉
// GPIO_PuPd_UP 上拉
// GPIO_PuPd_DOWN 下拉
} GPIO_InitTypeDef;
TIM_TimeBaseInitTypeDef:定时器时基初始化结构体,用于配置定时器的时基单元。其定义如下:
c
typedef struct
{
uint16_t TIM_ClockDivision; // 数字滤波器的采样频率,分频值
// TIM_CKD_DIV1 不分频
// TIM_CKD_DIV2 2分频
// TIM_CKD_DIV4 4分频
uint16_t TIM_CounterMode; // 指定计数器模式
// TIM_CounterMode_Up 递增模式
// TIM_CounterMode_Down 递减模式
// TIM_Counter_CenterAligned1 中心对齐模式1(向上向下交替计数,中断标志位在向下计数时设置)
// TIM_Counter_CenterAligned2 中心对齐模式2(向上向下交替计数,中断标志位在向上计数时设置)
// TIM_Counter_CenterAligned3 中心对齐模式3(向上向下交替计数,中断标志位向上向下都设置)
uint32_t TIM_Period; // 指定定时器中断周期,其实为自动重装载值
uint16_t TIM_Prescaler; // 定时器时钟预分频值
// 定时器时钟频率 = 定时器输入时钟 / (TIM_Prescaler + 1)
// = 84M / (TIM_Prescaler + 1)
uint8_t TIM_RepetitionCounter; // 计数器溢出重复次数,只有 TIM1 和 TIM8 才有
} TIM_TimeBaseInitTypeDef;
本示例中 Prescaler = 83,Period = 999,计数模式为向上计数,得到 1kHz 的 PWM 频率。
TIM_OCInitTypeDef:定时器输出比较初始化结构体,用于配置输出比较模式(OCMode)、脉冲宽度(Pulse)、输出极性(OCPolarity)以及输出状态(OutputState)。本示例中 OCMode = TIM_OCMode_PWM1,Pulse = 500,占空比为 50%。其定义如下:
c
typedef struct
{
uint16_t TIM_OCMode; // 输出比较模式
// TIM_OCMode_Timing 冻结模式,仅用于定时
// TIM_OCMode_Active 匹配时输出高电平
// TIM_OCMode_Inactive 匹配时输出低电平
// TIM_OCMode_Toggle 匹配时翻转输出电平
// TIM_OCMode_PWM1 PWM 模式 1(CNT < CCR 时输出有效电平)
// TIM_OCMode_PWM2 PWM 模式 2(CNT > CCR 时输出有效电平)
uint16_t TIM_OutputState; // 输出状态使能
// TIM_OutputState_Disable 禁止输出
// TIM_OutputState_Enable 使能输出
uint16_t TIM_Pulse; // 捕获/比较寄存器值(CCR),决定占空比
// 占空比 = TIM_Pulse / (TIM_Period + 1)
uint16_t TIM_OCPolarity; // 输出极性
// TIM_OCPolarity_High 高电平有效
// TIM_OCPolarity_Low 低电平有效
} TIM_OCInitTypeDef;
NVIC_InitTypeDef:NVIC 中断初始化结构体,用于配置中断通道、抢占优先级和使能状态。本示例中使能 TIM8_UP_TIM13_IRQn 中断。其定义如下:
c
typedef struct
{
uint8_t NVIC_IRQChannel; // 中断通道号
// 如 TIM8_UP_TIM13_IRQn
uint8_t NVIC_IRQChannelPreemptionPriority; // 抢占优先级(数值越小优先级越高)
uint8_t NVIC_IRQChannelSubPriority; // 子优先级(数值越小优先级越高)
FunctionalState NVIC_IRQChannelCmd; // 中断使能状态
// DISABLE 禁止
// ENABLE 使能
} NVIC_InitTypeDef;
完整的配置代码如下:
c
// 1. 配置定时器通道引脚(PF8 -> TIM13_CH1,AF9)
void TIM13_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
// 使能 GPIOF 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; // PF8
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOF, &GPIO_InitStructure);
// PF8 复用功能映射到 TIM13_CH1(AF9)
GPIO_PinAFConfig(GPIOF, GPIO_PinSource8, GPIO_AF_TIM13);
}
// 2. 配置定时器时基单元(Time Base Unit)
void TIM13_TimeBase_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 使能 TIM13 时钟(APB1,84MHz)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM13, ENABLE);
TIM_TimeBaseStructure.TIM_Prescaler = 83; // 84MHz / 84 = 1MHz
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_TimeBaseStructure.TIM_Period = 999; // PWM 周期 1ms,频率 1kHz
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM13, &TIM_TimeBaseStructure);
}
// 3. 配置定时器输出通道(PWM 模式)
void TIM13_OC_Init(void)
{
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; // PWM 模式 1
TIM_OCInitStructure.TIM_Pulse = 500; // 占空比 50%
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; // 高电平有效
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // 使能输出
TIM_OC1Init(TIM13, &TIM_OCInitStructure);
}
// 4. 定时器使能
void TIM13_Enable(void)
{
TIM_Cmd(TIM13, ENABLE); // 使能 TIM13 计数器,开始输出 PWM
}
// 5. 中断配置(可选,用于周期性任务)
void TIM13_NVIC_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM8_UP_TIM13_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM13, TIM_IT_Update, ENABLE); // 使能更新中断
}
// 中断服务函数
void TIM8_UP_TIM13_IRQHandler(void)
{
if (TIM_GetITStatus(TIM13, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM13, TIM_IT_Update);
// 周期性任务处理
}
}
// 汇总初始化函数
void Buzzer_PWM_Init(void)
{
TIM13_GPIO_Init(); // 1. 配置引脚
TIM13_TimeBase_Init(); // 2. 配置时基单元
TIM13_OC_Init(); // 3. 配置输出通道
TIM13_NVIC_Init(); // 5. 中断配置(可选)
TIM13_Enable(); // 4. 使能定时器
}
上述代码中,TIM13 的通道 1 在 PF8 引脚输出 1kHz、占空比 50% 的 PWM 方波。由于蜂鸣器为高电平响 ,当 CCR1 的值大于 CNT 时输出高电平,驱动蜂鸣器发声。通过修改 TIM_OCInitStructure.TIM_Pulse 的值,可以调整占空比,从而控制蜂鸣器的响度;通过修改 TIM_Period 和 TIM_Prescaler,可以调整 PWM 频率,改变蜂鸣器的音调。
需要注意的是,TIM13 的更新中断与 TIM8 共用 TIM8_UP_TIM13_IRQn 中断通道,在中断服务函数中需要判断中断来源,避免误处理。若不需要周期性任务,可以省略中断配置步骤。
6. 总结
本文围绕 STM32F4xx 的时钟与定时器两大核心主题,系统梳理了从基础概念到实战应用的完整知识链路。
在时钟部分,我们首先明确了时钟作为嵌入式系统"心跳"的基础地位,并介绍了石英晶振、PLL、分频器、预分频器以及系统时钟切换等关键概念。随后深入讲解了 STM32F4xx 的时钟树结构,包括 HSI、HSE、LSI、LSE 等时钟来源,以及 SYSCLK 的三种可选来源。通过 PLLM、PLLN、PLLP 等参数,我们推导出 PLLCLK 的计算公式,并以 8MHz HSE 配置出 168MHz 主频为例进行了验证。在此基础上,进一步分析了 AHB、APB1、APB2 各级预分频对总线时钟的影响,并特别强调了 APB 预分频大于 1 时定时器时钟自动倍频 2 倍的规律,这是正确配置定时器时基的关键前提。
在定时器部分,我们从定时器的本质------可编程计数器出发,介绍了时基单元、输入捕获单元和输出比较单元三大组成,以及预分频器、计数器、自动重载寄存器等核心部件的工作原理。随后分类讲解了 SysTick、基本定时器、通用定时器和高级定时器的功能特点与适用场景:SysTick 作为内核自带的 24 位滴答定时器,适合操作系统时基和简单延时;基本定时器功能单一,适合周期性中断;通用定时器功能丰富,支持 PWM、输入捕获和编码器接口;高级定时器则在通用定时器基础上增加了互补输出、死区控制和刹车功能,适用于电机控制等复杂场景。最后,通过 TIM13 驱动蜂鸣器的实战示例,完整演示了从引脚配置、时基配置、输出通道配置到中断配置的 PWM 输出全流程。
掌握时钟系统的配置方法,是理解 STM32 外设工作的基础;而灵活运用各类定时器,则是实现精确延时、波形生成和信号测量的核心手段。希望本文能帮助读者建立起从时钟到定时器的完整知识框架,为后续深入学习 STM32 其他外设打下坚实基础。