1. STM32F103定时器概述与分类
在嵌入式开发领域,定时器是最基础也最重要的外设之一。作为STM32F103系列微控制器的核心功能模块,定时器不仅能实现精确的时间控制,还能完成PWM输出、输入捕获等复杂任务。对于初学者而言,理解定时器的工作原理是掌握STM32编程的关键一步。
STM32F103的定时器资源非常丰富,根据功能复杂度可以分为三大类:
1.1 基本定时器(TIM6/TIM7)
基本定时器是STM32定时器家族中最简单的成员,具有以下特点:
- 仅支持最基本的定时功能
- 16位可编程预分频器(PSC)
- 16位自动重装载计数器(ARR)
- 可产生更新中断和DAC触发信号
- 挂载在APB1总线上,最高时钟频率为72MHz
典型应用场景包括:
- 简单的定时中断
- 为DAC提供触发信号
- 作为其他定时器的基准时钟源
注意:基本定时器没有输入捕获和输出比较功能,无法生成PWM波形。如果需要这些功能,必须选择通用或高级定时器。
1.2 通用定时器(TIM2-TIM5)
通用定时器是STM32开发中最常用的定时器类型,相比基本定时器增加了多项实用功能:
- 完整的定时功能(PSC+ARR)
- 4个独立通道,每个通道可配置为:
- 输入捕获模式
- 输出比较模式
- PWM生成模式
- 单脉冲模式
- 支持编码器接口
- 支持外部时钟源触发
- 同样挂载在APB1总线,72MHz时钟
输入捕获功能可以精确测量脉冲宽度,常用于:
- 红外遥控信号解码
- 超声波测距
- 旋转编码器计数
输出比较和PWM功能则广泛应用于:
- 电机速度控制
- LED调光
- 蜂鸣器驱动
1.3 高级定时器(TIM1/TIM8)
高级定时器在通用定时器基础上增加了面向电机控制的专业功能:
- 死区时间插入(防止H桥上下管直通)
- 互补输出(带可编程死区)
- 刹车输入(紧急停止功能)
- 挂载在APB2总线,时钟可达72MHz
- 更复杂的中断和DMA请求机制
典型应用包括:
- 无刷直流电机(BLDC)控制
- 步进电机驱动
- 开关电源控制
2. 定时器工作原理深度解析
2.1 时钟树与定时器时钟源
STM32的定时器时钟来源于系统时钟,但需要经过复杂的时钟树分配。以72MHz系统时钟为例:
- 首先通过AHB总线分频(通常不分频)
- 然后通过APB1/APB2预分频器
- 如果APB预分频系数≠1,定时器时钟会×2
例如:
- APB1预分频=2,APB1总线时钟=36MHz
- 但定时器时钟=36MHz×2=72MHz
提示:在CubeMX的Clock Configuration界面可以直观看到时钟树结构,建议将主频设置为最大值72MHz以获得最佳性能。
2.2 定时器核心部件详解
2.2.1 预分频器(PSC)
预分频器的作用是将定时器时钟分频,得到适合的计数频率。它是一个16位寄存器,可设置值范围为0-65535。
计算公式:
code复制定时器时钟频率 = 输入时钟频率 / (PSC + 1)
例如:
- 输入时钟=72MHz
- PSC=71
- 定时器时钟=72MHz/(71+1)=1MHz
2.2.2 计数器(CNT)
计数器是定时器的核心部件,根据预分频后的时钟进行递增或递减计数。它也是一个16位寄存器,范围为0-65535。
计数模式包括:
- 向上计数(0→ARR)
- 向下计数(ARR→0)
- 中心对齐模式(先上后下)
2.2.3 自动重装载寄存器(ARR)
ARR决定了定时器的溢出周期。当CNT达到ARR值时:
- 产生更新事件(UEV)
- CNT复位(向上模式复位到0,向下模式复位到ARR)
- 更新中断标志置位(如果使能)
2.3 定时时间计算公式
定时器溢出时间(即定时周期)的计算公式为:
code复制T = (PSC + 1) × (ARR + 1) / Tclk
其中:
- Tclk:定时器输入时钟频率(通常72MHz)
- PSC:预分频器值(0-65535)
- ARR:自动重装载值(0-65535)
例如,配置1秒定时器:
- 选择PSC=7199 → 72MHz/(7199+1)=10kHz
- 选择ARR=9999 → (9999+1)/10kHz=1s
- 总定时时间=(7199+1)×(9999+1)/72MHz=1s
常见误区:直接设置ARR=72,000,000想得到1秒定时。实际上ARR是16位寄存器,最大值65535,必须配合PSC使用。
3. CubeMX定时器配置实战
3.1 时钟源配置
在CubeMX中配置定时器时,首先需要设置时钟源:
-
Internal Clock(内部时钟):
- 使用系统时钟(通常72MHz)
- 大多数定时应用的选择
-
ETR2(外部触发):
- 通过特定引脚输入外部时钟
- 用于特殊场合如精确外部同步
-
TI1/TI2(外部引脚输入):
- 使用通道1或2的输入引脚作为时钟源
- 可用于脉冲计数等应用
3.2 触发源与从模式
高级定时器配置涉及触发源和从模式:
触发源选择:
- ITRx:内部定时器级联
- TI1F_ED/TI1FP1/TI2FP2:外部引脚触发
从模式选择:
- Disabled:独立工作模式
- Reset:触发信号复位计数器
- Gated:触发信号控制计数使能
- Trigger:触发信号启动计数器
3.3 参数设置详解
-
Prescaler (PSC):
- 根据所需定时周期计算得出
- 例如1秒定时:PSC=7199
-
Counter Mode:
- Up:向上计数(最常用)
- Down:向下计数
- Center aligned:中心对齐(PWM应用)
-
Counter Period (ARR):
- 决定定时周期的主要参数
- 1秒定时示例:ARR=9999
-
auto-reload preload:
- Enable:缓冲ARR值,避免运行时修改导致问题
- Disable:直接修改ARR,可能引起时序问题
-
Clock Division:
- 通常保持默认(不分频)
- 在噪声环境下可适当分频
3.4 NVIC中断配置
要使定时器能够触发中断,必须配置NVIC:
- 在CubeMX的NVIC配置中勾选对应定时器的全局中断
- 设置合适的中断优先级(抢占优先级和子优先级)
- 生成代码后,中断服务程序会自动配置
4. HAL库定时器编程实践
4.1 定时器初始化流程
-
CubeMX生成初始化代码后,系统会自动配置:
- 时钟
- GPIO(如果使用PWM输出)
- 定时器基本参数
-
用户需要手动启动定时器:
c复制HAL_TIM_Base_Start_IT(&htim2); // 启动定时器2并开启中断
4.2 中断回调函数实现
HAL库使用回调机制处理定时器中断:
c复制void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2) {
// 处理TIM2中断
HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
}
}
4.3 精确延时替代方案
使用定时器替代HAL_Delay()实现非阻塞延时:
- 定义一个全局变量:
c复制volatile uint32_t timer_count = 0;
- 在中断回调中递增计数器:
c复制void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2) {
timer_count++;
}
}
- 实现精确延时函数:
c复制void delay_ms(uint32_t ms)
{
uint32_t start = timer_count;
while((timer_count - start) < ms);
}
5. 进阶应用与问题排查
5.1 PWM输出配置
-
在CubeMX中:
- 选择定时器通道为PWM Generation
- 设置Pulse值(决定占空比)
- 保持ARR不变(决定PWM频率)
-
代码中启动PWM:
c复制HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
- 动态调整占空比:
c复制__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, new_pulse);
5.2 输入捕获配置
-
CubeMX设置:
- 选择通道为Input Capture
- 配置触发边沿(上升/下降/双边)
- 设置预分频器(如果需要)
-
代码实现:
c复制HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动输入捕获中断
// 捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
uint32_t capture = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// 处理捕获值
}
}
5.3 常见问题与解决方案
问题1:定时不准
- 检查时钟树配置是否正确
- 确认PSC和ARR计算无误
- 检查是否有其他高优先级中断阻塞
问题2:中断不触发
- 确认NVIC中已使能定时器中断
- 检查是否调用了Start_IT函数
- 验证回调函数是否正确定义
问题3:PWM无输出
- 检查GPIO是否配置为Alternate Function
- 确认已调用PWM启动函数
- 验证ARR和Pulse值设置合理
问题4:输入捕获值异常
- 检查触发边沿设置是否正确
- 确认信号质量良好(无抖动)
- 考虑添加硬件滤波(在CubeMX中配置)
6. 性能优化技巧
-
中断优化:
- 保持中断服务程序尽可能简短
- 避免在中断中进行复杂计算或I/O操作
- 使用DMA传输减轻CPU负担
-
资源管理:
- 多个定时任务可考虑使用一个定时器+多个比较通道
- 低频定时可使用RTC替代
- 高精度需求可选择高级定时器
-
低功耗考虑:
- 不需要时关闭定时器时钟
- 使用低功耗定时器(LPTIM)
- 合理配置自动唤醒功能
定时器是STM32最灵活的外设之一,掌握其原理和应用技巧对嵌入式开发至关重要。从简单的LED闪烁到复杂的电机控制,定时器都能发挥关键作用。建议通过实际项目不断积累经验,逐步深入理解定时器的各种高级功能。