定时器驱动基础与应用¶
概述¶
定时器(Timer)是嵌入式系统中最重要的外设之一,它提供精确的时间基准和时序控制功能。定时器不仅可以用于产生精确的延时和周期性中断,还能实现PWM输出、输入捕获、编码器接口等高级功能。掌握定时器驱动开发是嵌入式工程师的必备技能。
本文将系统介绍定时器的工作原理、配置方法和典型应用场景,帮助你建立对定时器驱动的全面理解。
完成本文学习后,你将能够:
- 理解定时器的工作原理和分类
- 掌握定时器的基本配置方法
- 实现定时器中断和精确延时
- 配置PWM输出控制LED亮度或电机速度
- 使用输入捕获测量脉冲宽度和频率
- 理解定时器的高级应用场景
背景知识¶
定时器的作用¶
定时器在嵌入式系统中扮演着时间管理者的角色:
- 时间基准:为系统提供精确的时间参考
- 周期任务:定期执行特定任务(如数据采集、状态更新)
- PWM输出:产生可调占空比的脉冲信号(电机控制、LED调光)
- 输入捕获:测量外部信号的频率和脉宽(编码器、超声波测距)
- 事件计数:统计外部事件发生次数
- 时序控制:实现精确的时序协议(如WS2812 LED驱动)
定时器的分类¶
STM32微控制器通常包含多种类型的定时器:
1. 高级定时器(Advanced Timer)
- 型号:TIM1、TIM8
- 特点:功能最强大,支持所有定时器功能
- 通道数:4个独立通道
- 特殊功能:互补输出、死区时间、刹车输入
- 应用:电机控制、高级PWM
2. 通用定时器(General-purpose Timer)
- 型号:TIM2、TIM3、TIM4、TIM5
- 特点:功能全面,最常用
- 通道数:4个独立通道
- 功能:PWM、输入捕获、输出比较、编码器接口
- 应用:通用时序控制、PWM输出
3. 基本定时器(Basic Timer)
- 型号:TIM6、TIM7
- 特点:功能简单,主要用于时间基准
- 通道数:无独立通道
- 功能:定时中断、DAC触发
- 应用:系统时钟、周期任务
定时器对比表:
| 类型 | 型号 | 位数 | 通道数 | PWM | 输入捕获 | 编码器 | 互补输出 | 死区 |
|---|---|---|---|---|---|---|---|---|
| 高级 | TIM⅛ | 16 | 4 | ✓ | ✓ | ✓ | ✓ | ✓ |
| 通用 | TIM2-5 | 16/32 | 4 | ✓ | ✓ | ✓ | ✗ | ✗ |
| 基本 | TIM6/7 | 16 | 0 | ✗ | ✗ | ✗ | ✗ | ✗ |
定时器的工作原理¶
定时器的核心是一个计数器,它按照预设的时钟频率递增或递减。
基本结构:
计数过程:
- 时钟源:选择定时器的时钟来源(通常是APB时钟)
- 预分频器(PSC):将时钟频率降低到合适的计数频率
- 计数器(CNT):按照计数模式递增或递减
- 自动重载寄存器(ARR):定义计数器的最大值
- 比较/捕获寄存器(CCR):用于PWM输出或输入捕获
计数模式:
1. 向上计数模式(Up-counting)
CNT: 0 → 1 → 2 → ... → ARR → 0 → 1 → ...
2. 向下计数模式(Down-counting)
CNT: ARR → ARR-1 → ... → 1 → 0 → ARR → ...
3. 中心对齐模式(Center-aligned)
CNT: 0 → 1 → ... → ARR → ARR-1 → ... → 0 → ...
定时器时钟计算¶
理解定时器的时钟计算对于配置定时器至关重要。
时钟频率计算公式:
示例计算:
假设APB1时钟为84MHz,要产生1ms的定时中断:
目标:1ms = 0.001s
更新频率 = 1 / 0.001s = 1000Hz
设置PSC = 8399,则:
计数器频率 = 84MHz / (8399 + 1) = 10kHz
设置ARR = 9,则:
更新频率 = 10kHz / (9 + 1) = 1000Hz
定时周期 = 1 / 1000Hz = 1ms ✓
PWM原理¶
PWM(Pulse Width Modulation,脉宽调制)通过改变脉冲的占空比来控制平均功率。
PWM参数:
- 周期(Period):一个完整脉冲的时间长度
- 占空比(Duty Cycle):高电平时间占整个周期的百分比
- 频率(Frequency):1秒内脉冲的个数
PWM波形:
占空比 = 25%
___ ___ ___
___| |___| |___| |___
|<->|
25% |<---100%---->|
占空比 = 50%
_____ _____ _____
___| |_| |_| |_
|<--->|
50% |<---100%---->|
占空比 = 75%
_______ _______
___| |_| |_____
|<----->|
75% |<---100%---->|
PWM频率选择:
| 应用 | 推荐频率 | 说明 |
|---|---|---|
| LED调光 | 1-10kHz | 避免闪烁 |
| 电机控制 | 10-20kHz | 超出人耳听觉范围 |
| 舵机控制 | 50Hz | 标准舵机频率 |
| 开关电源 | 50-500kHz | 提高效率 |
输入捕获原理¶
输入捕获(Input Capture)用于测量外部信号的频率、脉宽和占空比。
工作原理:
- 定时器计数器持续运行
- 当输入引脚检测到指定边沿(上升沿/下降沿)时
- 自动将当前计数器值保存到捕获寄存器(CCR)
- 可选触发中断
测量方法:
核心内容¶
1. 定时器基本配置¶
定时器的基本配置包括时钟使能、预分频器和自动重载值设置。
配置步骤:
- 使能定时器时钟
- 配置预分频器(PSC)
- 配置自动重载值(ARR)
- 选择计数模式
- 使能定时器
寄存器说明:
| 寄存器 | 全称 | 功能 |
|---|---|---|
| CR1 | Control Register 1 | 控制寄存器:使能、计数模式 |
| CR2 | Control Register 2 | 控制寄存器2:主模式选择 |
| DIER | DMA/Interrupt Enable Register | 中断和DMA使能 |
| SR | Status Register | 状态寄存器 |
| CNT | Counter | 计数器当前值 |
| PSC | Prescaler | 预分频器 |
| ARR | Auto-Reload Register | 自动重载值 |
| CCR1-4 | Capture/Compare Register | 捕获/比较寄存器 |
2. 定时器中断配置¶
定时器中断是最基本也是最常用的功能。
中断类型:
- 更新中断(Update Interrupt):计数器溢出时触发
- 捕获中断(Capture Interrupt):输入捕获时触发
- 比较中断(Compare Interrupt):计数器值等于比较值时触发
- 触发中断(Trigger Interrupt):外部触发时触发
配置要点:
- 使能定时器更新中断
- 配置NVIC中断优先级
- 编写中断服务函数
- 在中断中清除标志位
3. PWM输出配置¶
PWM输出是定时器最常用的功能之一。
PWM模式:
- PWM模式1:CNT < CCR时输出高电平,CNT ≥ CCR时输出低电平
- PWM模式2:CNT < CCR时输出低电平,CNT ≥ CCR时输出高电平
占空比计算:
配置步骤:
- 配置GPIO为复用功能(定时器通道)
- 配置定时器基本参数(PSC、ARR)
- 配置通道为PWM模式
- 设置比较值(CCR)控制占空比
- 使能输出和定时器
4. 输入捕获配置¶
输入捕获用于测量外部信号的时序参数。
捕获模式:
- 直接模式:输入信号直接连接到捕获单元
- 间接模式:输入信号交叉连接(用于测量PWM)
- TRC模式:由内部触发信号触发
边沿选择:
- 上升沿触发
- 下降沿触发
- 双边沿触发
配置步骤:
- 配置GPIO为复用功能(定时器通道)
- 配置定时器基本参数
- 配置通道为输入捕获模式
- 选择捕获边沿
- 使能捕获中断
- 在中断中读取捕获值
5. 定时器级联¶
多个定时器可以级联使用,实现更长的定时周期或更复杂的功能。
级联方式:
- 主从模式:一个定时器的溢出触发另一个定时器
- 外部时钟模式:一个定时器的输出作为另一个的时钟源
应用场景:
- 超长定时(秒级、分钟级)
- 精确的频率分频
- 复杂的时序控制
典型应用场景¶
场景1:定时中断 - 周期任务调度¶
使用定时器中断实现周期性任务,如每1ms读取传感器数据。
应用示例: - 系统时钟(SysTick的替代方案) - 周期性数据采集 - LED闪烁 - 按键扫描 - 状态机更新
实现要点: - 选择合适的定时周期(通常1ms-100ms) - 中断服务函数要简短高效 - 避免在中断中执行耗时操作 - 使用标志位与主循环配合
场景2:PWM输出 - LED调光¶
通过改变PWM占空比实现LED亮度调节。
应用示例: - LED呼吸灯效果 - RGB LED颜色控制 - 背光亮度调节 - 指示灯状态显示
实现要点: - PWM频率选择1-10kHz(避免闪烁) - 占空比范围0-100% - 可使用查表法实现非线性调光 - 多通道可实现RGB混色
场景3:PWM输出 - 电机控制¶
使用PWM控制直流电机的转速。
应用示例: - 直流电机调速 - 舵机角度控制 - 风扇转速控制 - 小车运动控制
实现要点: - 电机PWM频率通常10-20kHz - 舵机PWM频率固定50Hz - 需要配合H桥驱动电路 - 注意电机启动电流
场景4:输入捕获 - 频率测量¶
测量外部信号的频率,如编码器脉冲、超声波回波。
应用示例: - 编码器测速 - 超声波测距 - 频率计 - 转速测量
实现要点: - 捕获两次上升沿计算周期 - 使用定时器溢出计数处理低频信号 - 滤波处理提高精度 - 注意计数器溢出问题
场景5:输入捕获 - 脉宽测量¶
测量脉冲信号的宽度,如红外遥控、超声波回波。
应用示例: - 红外遥控解码 - 超声波测距 - PPM信号解码 - 脉宽调制信号分析
实现要点: - 捕获上升沿和下降沿 - 计算两次捕获值的差值 - 处理计数器溢出情况 - 使用双通道可同时测量周期和脉宽
场景6:编码器接口¶
使用定时器的编码器模式读取旋转编码器。
应用示例: - 电机位置反馈 - 旋钮输入 - 机器人定位 - 精密测量
实现要点: - 使用定时器的编码器模式 - 两个通道分别连接A相和B相 - 计数器值直接反映位置 - 可检测旋转方向和速度
设计考虑¶
1. 定时器选择¶
根据应用需求选择合适的定时器:
选择依据:
| 需求 | 推荐定时器 | 原因 |
|---|---|---|
| 简单定时中断 | 基本定时器(TIM6/7) | 资源占用少 |
| PWM输出 | 通用定时器(TIM2-5) | 4个独立通道 |
| 电机控制 | 高级定时器(TIM⅛) | 支持互补输出和死区 |
| 编码器接口 | 通用定时器(TIM2-5) | 支持编码器模式 |
| 32位计数 | TIM2/TIM5 | 32位计数器 |
2. 时钟配置¶
合理配置定时器时钟以满足精度和范围要求:
配置原则:
- 精度优先:需要高精度时,使用较小的预分频值
- 范围优先:需要长时间定时时,使用较大的预分频值
- 平衡选择:在精度和范围之间找到平衡点
示例:
需求:1ms定时,APB1时钟84MHz
方案1(高精度):
PSC = 83, ARR = 999
计数器频率 = 84MHz / 84 = 1MHz
分辨率 = 1μs
方案2(平衡):
PSC = 8399, ARR = 9
计数器频率 = 84MHz / 8400 = 10kHz
分辨率 = 100μs
方案3(大范围):
PSC = 41999, ARR = 1
计数器频率 = 84MHz / 42000 = 2kHz
分辨率 = 500μs
3. 中断优先级¶
定时器中断的优先级设置影响系统的实时性:
优先级原则:
- 高频定时器:优先级应该较高(如1ms系统时钟)
- 低频定时器:优先级可以较低(如1s周期任务)
- PWM定时器:通常不需要中断,优先级最低
- 输入捕获:根据实时性要求设置
示例配置:
4. 资源管理¶
合理分配定时器资源,避免冲突:
资源规划:
- 统计需求:列出所有需要定时器的功能
- 分配定时器:根据功能特点分配合适的定时器
- 通道复用:一个定时器的多个通道可以独立使用
- 预留资源:为未来扩展预留定时器
示例规划:
TIM1: 电机PWM控制(4通道)
TIM2: 编码器接口(2通道)
TIM3: LED PWM调光(3通道)
TIM4: 输入捕获(2通道)
TIM6: 系统时钟(1ms中断)
TIM7: 周期任务(100ms中断)
5. 功耗优化¶
在低功耗应用中,合理使用定时器可以降低功耗:
优化策略:
- 按需使能:不使用时关闭定时器时钟
- 降低频率:在满足需求的前提下使用较低的时钟频率
- 减少中断:合并多个定时任务,减少中断次数
- 睡眠模式:使用低功耗定时器(LPTIM)在睡眠模式下工作
常见问题¶
Q1: 定时器中断不触发?¶
可能原因:
- 定时器时钟未使能
- 中断未使能(DIER寄存器)
- NVIC中断未配置
- 定时器未启动(CR1的CEN位)
- 中断标志未清除
排查步骤:
- 检查时钟使能:
RCC->APB1ENR或RCC->APB2ENR - 检查中断使能:
TIMx->DIER |= TIM_DIER_UIE - 检查NVIC配置:
NVIC_EnableIRQ(TIMx_IRQn) - 检查定时器启动:
TIMx->CR1 |= TIM_CR1_CEN - 在中断中清除标志:
TIMx->SR &= ~TIM_SR_UIF
Q2: PWM输出没有波形?¶
可能原因:
- GPIO未配置为复用功能
- 通道未使能(CCER寄存器)
- 主输出未使能(高级定时器需要)
- CCR值设置不当(等于0或大于ARR)
- PWM模式配置错误
排查步骤:
- 检查GPIO配置:复用功能、推挽输出
- 检查通道使能:
TIMx->CCER |= TIM_CCER_CC1E - 检查主输出(TIM⅛):
TIMx->BDTR |= TIM_BDTR_MOE - 检查CCR值:
0 < CCR < ARR - 检查PWM模式:
TIMx->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2
Q3: 定时不准确?¶
可能原因:
- 时钟频率计算错误
- PSC或ARR设置不当
- 系统时钟配置错误
- 中断响应延迟
- 中断服务函数耗时过长
解决方案:
- 重新计算并验证时钟参数
- 使用示波器或逻辑分析仪测量实际频率
- 检查系统时钟配置(HSE、PLL)
- 优化中断服务函数,减少执行时间
- 提高定时器中断优先级
Q4: 输入捕获测量不准确?¶
可能原因:
- 输入信号频率过高或过低
- 计数器溢出未处理
- 边沿检测不稳定(抖动)
- 滤波器配置不当
解决方案:
- 调整预分频器以适应信号频率
- 使用溢出计数器处理低频信号
- 配置输入滤波器:
TIMx->CCMR1 |= TIM_CCMR1_IC1F - 硬件上添加施密特触发器或RC滤波
- 软件滤波:多次测量取平均值
Q5: 多个定时器同时使用时出现冲突?¶
可能原因:
- 中断优先级设置不当
- 中断服务函数耗时过长
- 共享资源访问冲突
- 定时器级联配置错误
解决方案:
- 合理设置中断优先级
- 优化中断服务函数,使用标志位与主循环配合
- 使用互斥机制保护共享资源
- 检查定时器级联的主从配置
最佳实践¶
1. 代码组织¶
模块化设计:
// timer_driver.h - 定时器驱动接口
void TIM_Init(TIM_TypeDef *TIMx, uint16_t psc, uint16_t arr);
void TIM_Start(TIM_TypeDef *TIMx);
void TIM_Stop(TIM_TypeDef *TIMx);
void TIM_SetPWM(TIM_TypeDef *TIMx, uint8_t channel, uint16_t duty);
uint32_t TIM_GetCapture(TIM_TypeDef *TIMx, uint8_t channel);
// timer_driver.c - 定时器驱动实现
// 实现具体功能
// app_timer.c - 应用层
// 使用定时器驱动接口
2. 参数检查¶
在驱动函数中添加参数检查:
void TIM_SetPWM(TIM_TypeDef *TIMx, uint8_t channel, uint16_t duty) {
// 参数检查
if (TIMx == NULL) return;
if (channel < 1 || channel > 4) return;
if (duty > 100) duty = 100;
// 计算CCR值
uint16_t ccr = (TIMx->ARR + 1) * duty / 100;
// 设置CCR
switch (channel) {
case 1: TIMx->CCR1 = ccr; break;
case 2: TIMx->CCR2 = ccr; break;
case 3: TIMx->CCR3 = ccr; break;
case 4: TIMx->CCR4 = ccr; break;
}
}
3. 中断处理¶
中断服务函数要简短:
// 不好的做法:在中断中执行复杂操作
void TIM6_IRQHandler(void) {
if (TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF;
// ❌ 不要在中断中执行耗时操作
ReadSensor();
ProcessData();
SendToUART();
}
}
// 好的做法:使用标志位
volatile uint8_t g_timer_flag = 0;
void TIM6_IRQHandler(void) {
if (TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF;
g_timer_flag = 1; // ✓ 只设置标志位
}
}
// 在主循环中处理
int main(void) {
while (1) {
if (g_timer_flag) {
g_timer_flag = 0;
ReadSensor();
ProcessData();
SendToUART();
}
}
}
4. 资源释放¶
不使用时关闭定时器以节省功耗:
void TIM_DeInit(TIM_TypeDef *TIMx) {
// 停止定时器
TIMx->CR1 &= ~TIM_CR1_CEN;
// 禁用中断
TIMx->DIER = 0;
// 清除标志
TIMx->SR = 0;
// 关闭时钟(根据定时器选择)
if (TIMx == TIM2) {
RCC->APB1ENR &= ~RCC_APB1ENR_TIM2EN;
}
// ... 其他定时器
}
5. 调试技巧¶
使用GPIO输出调试:
// 在中断中翻转GPIO,用示波器观察
void TIM6_IRQHandler(void) {
if (TIM6->SR & TIM_SR_UIF) {
TIM6->SR &= ~TIM_SR_UIF;
// 翻转调试引脚
GPIOA->ODR ^= GPIO_ODR_OD0;
}
}
打印调试信息:
void TIM_PrintConfig(TIM_TypeDef *TIMx) {
printf("Timer Configuration:\r\n");
printf(" PSC: %d\r\n", TIMx->PSC);
printf(" ARR: %d\r\n", TIMx->ARR);
printf(" CNT: %d\r\n", TIMx->CNT);
printf(" CR1: 0x%04X\r\n", TIMx->CR1);
printf(" DIER: 0x%04X\r\n", TIMx->DIER);
printf(" SR: 0x%04X\r\n", TIMx->SR);
}
进阶学习¶
1. DMA与定时器结合¶
使用DMA可以实现定时器的高效数据传输,无需CPU干预。
应用场景: - PWM波形表输出(音频播放) - 高速ADC采样(定时器触发) - WS2812 LED驱动(PWM+DMA)
2. 定时器同步¶
多个定时器可以同步工作,实现复杂的时序控制。
同步方式: - 主从模式(Master-Slave) - 外部触发模式 - 门控模式
3. 高级PWM技术¶
互补PWM输出: - 用于H桥电机驱动 - 自动插入死区时间 - 刹车保护功能
相位移PWM: - 多相电机控制 - 开关电源控制 - 降低EMI干扰
4. 编码器接口¶
定时器的编码器模式可以直接解码正交编码器信号。
优势: - 硬件解码,无需软件干预 - 自动判断方向 - 高速响应 - 抗干扰能力强
5. 定时器级联应用¶
通过级联可以实现超长定时和复杂分频。
示例: - 32位定时器(两个16位定时器级联) - 精确的秒级定时 - 复杂的频率合成
学习资源¶
官方文档¶
- STM32参考手册
- 定时器章节详细说明
- 寄存器定义和配置
-
时序图和波形图
-
STM32数据手册
- 定时器电气特性
- 引脚复用功能
-
时钟树结构
-
应用笔记
- AN4013: STM32定时器概述
- AN4776: 通用定时器手册
- AN4776: PWM生成技术
推荐工具¶
- STM32CubeMX
- 图形化配置定时器
- 自动生成初始化代码
-
时钟树可视化
-
示波器/逻辑分析仪
- 观察PWM波形
- 测量频率和占空比
-
调试输入捕获
-
Keil MDK调试器
- 实时查看寄存器
- 设置断点调试
- 观察变量变化
实践项目¶
- LED呼吸灯
- 使用PWM实现渐变效果
- 学习占空比控制
-
难度:⭐
-
超声波测距
- 使用输入捕获测量回波
- 计算距离
-
难度:⭐⭐
-
直流电机调速
- PWM控制电机转速
- H桥驱动电路
-
难度:⭐⭐
-
编码器测速
- 使用编码器模式
- 计算转速和位置
-
难度:⭐⭐⭐
-
四轴飞行器电机控制
- 4路PWM输出
- 精确的速度控制
- 难度:⭐⭐⭐⭐
总结¶
定时器是嵌入式系统中最重要的外设之一,掌握定时器驱动开发是每个嵌入式工程师的必备技能。
核心要点回顾:
- 定时器分类:高级定时器、通用定时器、基本定时器各有特点
- 时钟计算:理解PSC、ARR与定时周期的关系
- 中断配置:正确配置中断使能和优先级
- PWM输出:掌握占空比计算和通道配置
- 输入捕获:理解边沿触发和捕获值读取
- 资源管理:合理分配定时器资源,避免冲突
学习建议:
- 从简单开始:先掌握基本的定时中断
- 动手实践:通过实际项目加深理解
- 阅读手册:仔细阅读芯片参考手册
- 使用工具:善用示波器和调试器
- 循序渐进:逐步学习高级功能
下一步学习:
- 深入学习高级定时器的互补输出和死区控制
- 掌握定时器与DMA的结合使用
- 学习定时器在电机控制中的应用
- 研究定时器的低功耗模式
定时器驱动是连接软件和硬件的桥梁,熟练掌握定时器将为你的嵌入式开发之路打开新的大门!
相关内容¶
- GPIO驱动开发:LED控制实战 - 学习GPIO基础
- UART串口驱动开发与调试 - 串口通信基础
- 通用定时器高级应用 - 定时器进阶
- PWM驱动开发:电机调速控制 - PWM深入应用
- 中断系统基础概念 - 中断原理
版权声明:本文档由嵌入式知识平台创建,采用 CC BY-NC-SA 4.0 许可协议。
更新日志: - 2024-01-15: 初始版本发布