STM32定时器基础:定时器类型、时钟源、时基单元、计数模式与溢出中断
定时器是STM32微控制器中最具工程价值的外设之一,它不仅是产生精确延时和PWM波形的核心,更是输入捕获、编码器接口、电机控制等复杂功能的基础。在电机控制场景中,定时器直接决定了PWM载波频率的稳定性、死区时间的精度以及电流采样与PWM更新的同步性。本章将系统梳理STM32定时器的类型划分、时钟树关系、时基单元工作原理、四种计数模式以及溢出中断的配置方法,为后续PWM输入捕获与电机控制实战打下坚实的底层认知。
STM32定时器家族:从基本到高级的层次化设计
STM32系列定时器并非单一外设,而是一个按功能复杂度分层设计的家族。理解这个层次结构是选择合适定时器完成特定任务的前提。以STM32F103系列为例,其定时器可分为三个梯队:高级定时器(TIM1/TIM8)、通用定时器(TIM2-TIM5、TIM9-TIM14)和基本定时器(TIM6/TIM7)。高级定时器在通用定时器的基础上增加了互补PWM输出、死区时间插入、刹车输入(Break Input)以及重复计数功能,这些特性使其成为驱动三相无刷直流电机或永磁同步电机逆变桥的首选。通用定时器则提供了输入捕获、输出比较、PWM生成、编码器接口等标准功能,其中TIM2-TIM5为32位计数器,能够处理更宽的计数范围,适合低频信号测量或长时间延时。基本定时器最为精简,仅保留时基功能,通常用于产生周期性中断或作为DAC的触发源。
从寄存器层面看,所有定时器共享一套核心的时基结构,但高级定时器额外拥有重复计数寄存器(RCR),允许每N次计数器溢出才产生一次更新事件,这在需要精确控制PWM脉冲个数的电机控制场景中极为有用。例如,在步进电机细分驱动中,通过设置RCR可以精确控制一个运动周期内输出的PWM脉冲总数,而无需CPU频繁干预。
| 定时器类型 | 计数器位宽 | 主要特性 | 典型应用场景 |
|---|---|---|---|
| 高级定时器 (TIM1/TIM8) | 16位 | 互补PWM、死区、刹车、重复计数 | 三相电机逆变桥、UPS、开关电源 |
| 通用定时器 (TIM2-TIM5) | 32位 (TIM2-TIM5) / 16位 (TIM9-TIM14) | 输入捕获、输出比较、PWM、编码器 | PWM输入捕获、电机测速、信号频率测量 |
| 基本定时器 (TIM6/TIM7) | 16位 | 仅时基、DAC触发 | 系统节拍、DAC波形生成 |
时钟源:定时器的心跳从哪里来
定时器的计数节奏完全由时钟源决定。STM32定时器的时钟源并非只有内部时钟一种,而是有四种可选模式,理解这些模式是配置定时器的基础。第一种是内部时钟(CK_INT),这是最常用的模式,时钟来自RCC(复位与时钟控制)模块分配给定时器总线的时钟。在STM32F103中,高级定时器TIM1/TIM8挂载在APB2总线上,而通用和基本定时器挂载在APB1总线上。这里有一个关键陷阱:当APB1预分频系数不为1时,定时器时钟频率是APB1总线频率的2倍。例如,若系统主频为72MHz,APB1分频系数为2,则APB1总线频率为36MHz,但TIM2-TIM5的时钟频率自动变为72MHz。初学者若忽略此倍增关系,会导致定时时间计算错误。
第二种是外部时钟模式1(TIx),即使用定时器的输入引脚(如TIM2_CH1对应的PA0)上的外部信号作为时钟。每个通道的输入滤波器和极性选择器可以对信号进行预处理,适合对数字信号进行计数。第三种是外部时钟模式2(ETR),使用专门的ETR引脚,可以配合外部触发控制器实现更复杂的同步。第四种是内部触发(ITRx),允许一个定时器作为另一个定时器的时钟源,实现定时器级联,例如用TIM1的更新事件驱动TIM2计数,从而扩展定时范围或实现多定时器同步。
RCC_GetClocksFreq()函数读取系统时钟树状态,避免因时钟配置错误导致PWM频率偏差。时基单元:PSC、ARR与CNT的协同工作
时基单元是定时器计数的核心硬件,由三个主要寄存器构成:预分频器(PSC)、自动重载寄存器(ARR)和计数器(CNT)。PSC对输入时钟进行分频,其有效分频系数为PSC+1,这意味着PSC可以从0到65535任意取值,从而将高频时钟降至适合应用的低频。ARR定义了计数器的最终值,当CNT计数到ARR时,根据计数模式的不同,会产生更新事件或溢出。CNT是实际计数的寄存器,在向上计数模式下从0递增至ARR,然后归零重新开始;在向下计数模式下则从ARR递减至0。
一个重要的工程细节是PSC的缓冲特性。在STM32中,PSC和ARR都带有影子寄存器,即写入的值不会立即生效,而是在更新事件发生时才被加载到实际工作的寄存器中。这一设计避免了在计数过程中修改参数导致PWM波形异常。例如,在电机运行中需要动态调整PWM频率时,如果直接修改ARR而不等待更新事件,可能会产生一个异常长的脉冲。因此,标准做法是:先写入新的ARR值,然后通过设置UG位(更新生成位)强制产生更新事件,或者等待下一次自然溢出,确保新值在安全边界生效。
// 以TIM2为例,配置1ms定时中断(假设定时器时钟为72MHz)
TIM_TimeBaseInitTypeDef TIM_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InitStruct.TIM_Prescaler = 72 - 1; // 72MHz/72 = 1MHz,即1us计数一次
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数
TIM_InitStruct.TIM_Period = 1000 - 1; // 计数1000次,即1ms
TIM_InitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_InitStruct.TIM_RepetitionCounter = 0; // 高级定时器才使用
TIM_TimeBaseInit(TIM2, &TIM_InitStruct);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM2, ENABLE);
计数模式:向上、向下与中心对齐的工程选择
STM32定时器支持三种主要的计数模式,每种模式对应不同的应用场景。向上计数模式是最基础的模式,计数器从0递增至ARR,然后归零并产生更新事件。这种模式适合生成标准PWM波形,因为PWM的周期和占空比计算直观。在电机控制中,向上计数模式常用于生成边沿对齐的PWM信号,其特点是所有通道的PWM上升沿对齐在计数周期起点,下降沿位置由各通道的比较值决定。
向下计数模式与向上计数对称,计数器从ARR递减至0。这种模式在对称PWM中较少单独使用,但可以与向上计数组合实现特定波形。中心对齐模式则是一种更高级的模式,计数器先向上计数至ARR,再向下计数至0,如此往复。在这种模式下,更新事件在计数器到达0或ARR时产生,PWM输出在计数上升和下降阶段各翻转一次,从而生成对称的PWM波形。中心对齐模式在电机控制中具有显著优势:对称PWM可以降低电流纹波,减少电机噪声和转矩脉动。此外,中心对齐模式下的PWM频率是定时器时钟频率除以(2×ARR),这一点在计算载波频率时容易出错。
溢出中断:从计数器归零到CPU响应
溢出中断(更新中断)是定时器时基功能与CPU交互的主要途径。当计数器发生溢出(向上计数达到ARR后归零,或向下计数达到0后重载ARR)时,更新事件标志位(UIF)被硬件置1。如果使能了更新中断,且NVIC中对应的中断通道已配置,CPU将跳转至中断服务函数执行用户代码。这一机制是实现周期性任务(如LED闪烁、ADC采样触发、PID控制周期计算)的基础。
在配置溢出中断时,有几个关键点需要特别注意。首先是中断标志的清除时机:在中断服务函数中,必须手动清除UIF标志,否则中断会反复触发,导致CPU陷入死循环。标准做法是在中断处理末尾调用TIM_ClearITPendingBit(TIMx, TIM_IT_Update)。其次是中断优先级配置:在电机控制系统中,定时器更新中断通常需要设置为较高优先级,以确保PWM周期内的控制计算能够及时完成。但也要避免优先级过高导致其他关键中断(如通信中断)被饿死。建议将定时器更新中断设置为中等优先级,而将故障保护(如过流检测)相关的外部中断设置为最高优先级。
另一个容易被忽视的细节是更新中断与DMA的协同。在高级应用中,可以配置定时器更新事件触发DMA传输,将新的比较值自动加载到CCR寄存器,而无需CPU介入。这种方式可以显著降低CPU负载,使主循环有更多时间处理其他任务。例如,在生成SPWM(正弦脉宽调制)波形时,可以将正弦波查表数据通过DMA在每次更新事件时写入CCR,实现高频PWM输出而CPU几乎零负担。
案例拆解:用TIM2实现精确的1ms系统节拍
为了将上述理论串联起来,我们以一个实际案例来演示定时器配置的完整流程。假设系统主频为72MHz,APB1分频系数为2,因此TIM2的时钟频率为72MHz。目标是产生1ms周期的更新中断,用于系统时基和LED闪烁控制。
第一步:计算分频参数。定时器时钟为72MHz,若PSC设为71,则计数器时钟为72MHz/(71+1)=1MHz,即每1微秒计数一次。若ARR设为999,则计数周期为1000微秒=1ms。这里的关键是理解PSC和ARR的粒度:PSC决定了计数器的分辨率(1us),ARR决定了定时周期(1ms)。
第二步:初始化时基单元。使用TIM_TimeBaseInit()函数配置PSC、ARR和计数模式。注意在初始化完成后,需要调用TIM_Cmd()使能定时器,否则计数器不会开始工作。
第三步:配置中断通道。使能TIM2的更新中断,并在NVIC中配置中断优先级。这里选择优先级组2,抢占优先级为2,子优先级为0,确保系统节拍中断不会被普通外设中断打断,但允许更高优先级的故障中断抢占。
第四步:编写中断服务函数。在TIM2_IRQHandler中检查更新中断标志,执行LED翻转操作,然后清除标志。为了验证定时精度,可以在中断中翻转一个GPIO,用示波器测量其频率应为500Hz(周期2ms,因为翻转一次产生半个周期)。
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) {
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
GPIO_ToggleBits(GPIOC, GPIO_Pin_13); // 翻转LED
}
}
第五步:验证与误差分析。使用示波器测量LED引脚波形,若频率为500Hz±0.1%,则配置正确。若频率偏差较大,应检查系统主频是否确实为72MHz,以及APB1分频系数是否与预期一致。一个常见错误是忘记使能TIM2的时钟,导致寄存器写入无效。
此案例虽然简单,但涵盖了定时器配置的全部关键环节:时钟使能、分频计算、中断配置、服务函数编写。在后续的PWM输入捕获和电机控制实战中,这些基础步骤将反复出现,只是外围电路和寄存器配置更加复杂。
常见误区与总结复盘
在定时器学习过程中,有几个高频误区值得特别强调。第一个误区是混淆APB1总线频率与定时器时钟频率。如前所述,当APB1分频系数大于1时,定时器时钟是APB1的2倍。很多初学者在计算定时时间时直接使用APB1频率,导致定时时间翻倍。解决方法是查阅参考手册的时钟树图,或使用RCC_GetClocksFreq()函数获取实际时钟值。
第二个误区是忽略影子寄存器的缓冲特性。在PWM输出过程中动态修改ARR或CCR时,如果没有等待更新事件,新值可能立即生效或延迟生效,导致波形异常。正确的做法是:在修改ARR前,先设置TIM_UpdateRequestConfig(TIMx, TIM_UpdateSource_Regular)确保更新事件仅由溢出产生,然后修改ARR,等待更新标志置位后再进行下一步操作。
第三个误区是中断服务函数中未清除标志。这会导致中断反复进入,程序看似“卡死”在中断中。务必在中断处理末尾清除对应的中断标志位。此外,对于高级定时器,还需要注意重复计数寄存器RCR的配置,若RCR不为0,则更新事件不会每次溢出都产生,而是每RCR+1次溢出才产生一次,这会影响中断频率的计算。
第四个误区是中心对齐模式下PWM频率计算错误。在中心对齐模式下,计数器从0到ARR再到0,一个完整PWM周期包含两次计数过程,因此PWM频率 = 定时器时钟 / (2 × (ARR+1))。若误用向上计数模式的公式,计算出的频率会偏高一倍。
总结而言,掌握定时器基础需要从三个维度入手:一是理解定时器家族的分层结构,明确不同定时器的能力边界;二是吃透时钟源和时基单元的数学关系,能够根据目标频率反推PSC和ARR;三是熟练配置计数模式和中断系统,确保定时器与CPU的协作高效可靠。本章内容是后续PWM输入捕获和电机控制的基础,建议读者在开发板上实际运行上述案例,用示波器观察波形,加深对定时器工作原理的理解。