跳转至

定时器驱动基础与应用

概述

定时器(Timer)是嵌入式系统中最重要的外设之一,它提供精确的时间基准和时序控制功能。定时器不仅可以用于产生精确的延时和周期性中断,还能实现PWM输出、输入捕获、编码器接口等高级功能。掌握定时器驱动开发是嵌入式工程师的必备技能。

本文将系统介绍定时器的工作原理、配置方法和典型应用场景,帮助你建立对定时器驱动的全面理解。

完成本文学习后,你将能够:

  • 理解定时器的工作原理和分类
  • 掌握定时器的基本配置方法
  • 实现定时器中断和精确延时
  • 配置PWM输出控制LED亮度或电机速度
  • 使用输入捕获测量脉冲宽度和频率
  • 理解定时器的高级应用场景

背景知识

定时器的作用

定时器在嵌入式系统中扮演着时间管理者的角色:

  1. 时间基准:为系统提供精确的时间参考
  2. 周期任务:定期执行特定任务(如数据采集、状态更新)
  3. PWM输出:产生可调占空比的脉冲信号(电机控制、LED调光)
  4. 输入捕获:测量外部信号的频率和脉宽(编码器、超声波测距)
  5. 事件计数:统计外部事件发生次数
  6. 时序控制:实现精确的时序协议(如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

定时器的工作原理

定时器的核心是一个计数器,它按照预设的时钟频率递增或递减。

基本结构

时钟源 → 预分频器 → 计数器 → 比较/捕获单元 → 输出
         (PSC)      (CNT)     (CCR)

计数过程

  1. 时钟源:选择定时器的时钟来源(通常是APB时钟)
  2. 预分频器(PSC):将时钟频率降低到合适的计数频率
  3. 计数器(CNT):按照计数模式递增或递减
  4. 自动重载寄存器(ARR):定义计数器的最大值
  5. 比较/捕获寄存器(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 → ...

定时器时钟计算

理解定时器的时钟计算对于配置定时器至关重要。

时钟频率计算公式

定时器时钟频率 = APB时钟频率 × 倍频系数

计数器频率 = 定时器时钟频率 / (PSC + 1)

更新频率 = 计数器频率 / (ARR + 1)

定时周期 = 1 / 更新频率

示例计算

假设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)用于测量外部信号的频率、脉宽和占空比。

工作原理

  1. 定时器计数器持续运行
  2. 当输入引脚检测到指定边沿(上升沿/下降沿)时
  3. 自动将当前计数器值保存到捕获寄存器(CCR)
  4. 可选触发中断

测量方法

测量频率:
    捕获两次上升沿,计算时间差
    频率 = 1 / 时间差

测量脉宽:
    捕获上升沿和下降沿,计算时间差
    脉宽 = 下降沿时间 - 上升沿时间

测量占空比:
    占空比 = 高电平时间 / 周期时间

核心内容

1. 定时器基本配置

定时器的基本配置包括时钟使能、预分频器和自动重载值设置。

配置步骤

  1. 使能定时器时钟
  2. 配置预分频器(PSC)
  3. 配置自动重载值(ARR)
  4. 选择计数模式
  5. 使能定时器

寄存器说明

寄存器 全称 功能
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):外部触发时触发

配置要点

  1. 使能定时器更新中断
  2. 配置NVIC中断优先级
  3. 编写中断服务函数
  4. 在中断中清除标志位

3. PWM输出配置

PWM输出是定时器最常用的功能之一。

PWM模式

  • PWM模式1:CNT < CCR时输出高电平,CNT ≥ CCR时输出低电平
  • PWM模式2:CNT < CCR时输出低电平,CNT ≥ CCR时输出高电平

占空比计算

占空比 = CCR / (ARR + 1) × 100%

例如:ARR = 999, CCR = 500
占空比 = 500 / 1000 × 100% = 50%

配置步骤

  1. 配置GPIO为复用功能(定时器通道)
  2. 配置定时器基本参数(PSC、ARR)
  3. 配置通道为PWM模式
  4. 设置比较值(CCR)控制占空比
  5. 使能输出和定时器

4. 输入捕获配置

输入捕获用于测量外部信号的时序参数。

捕获模式

  • 直接模式:输入信号直接连接到捕获单元
  • 间接模式:输入信号交叉连接(用于测量PWM)
  • TRC模式:由内部触发信号触发

边沿选择

  • 上升沿触发
  • 下降沿触发
  • 双边沿触发

配置步骤

  1. 配置GPIO为复用功能(定时器通道)
  2. 配置定时器基本参数
  3. 配置通道为输入捕获模式
  4. 选择捕获边沿
  5. 使能捕获中断
  6. 在中断中读取捕获值

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. 时钟配置

合理配置定时器时钟以满足精度和范围要求:

配置原则

  1. 精度优先:需要高精度时,使用较小的预分频值
  2. 范围优先:需要长时间定时时,使用较大的预分频值
  3. 平衡选择:在精度和范围之间找到平衡点

示例

需求: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. 中断优先级

定时器中断的优先级设置影响系统的实时性:

优先级原则

  1. 高频定时器:优先级应该较高(如1ms系统时钟)
  2. 低频定时器:优先级可以较低(如1s周期任务)
  3. PWM定时器:通常不需要中断,优先级最低
  4. 输入捕获:根据实时性要求设置

示例配置

优先级0(最高):系统时钟(1ms SysTick)
优先级1:高速输入捕获(编码器)
优先级2:通用定时中断(10ms任务)
优先级3:低速定时中断(100ms任务)

4. 资源管理

合理分配定时器资源,避免冲突:

资源规划

  1. 统计需求:列出所有需要定时器的功能
  2. 分配定时器:根据功能特点分配合适的定时器
  3. 通道复用:一个定时器的多个通道可以独立使用
  4. 预留资源:为未来扩展预留定时器

示例规划

TIM1: 电机PWM控制(4通道)
TIM2: 编码器接口(2通道)
TIM3: LED PWM调光(3通道)
TIM4: 输入捕获(2通道)
TIM6: 系统时钟(1ms中断)
TIM7: 周期任务(100ms中断)

5. 功耗优化

在低功耗应用中,合理使用定时器可以降低功耗:

优化策略

  1. 按需使能:不使用时关闭定时器时钟
  2. 降低频率:在满足需求的前提下使用较低的时钟频率
  3. 减少中断:合并多个定时任务,减少中断次数
  4. 睡眠模式:使用低功耗定时器(LPTIM)在睡眠模式下工作

常见问题

Q1: 定时器中断不触发?

可能原因

  1. 定时器时钟未使能
  2. 中断未使能(DIER寄存器)
  3. NVIC中断未配置
  4. 定时器未启动(CR1的CEN位)
  5. 中断标志未清除

排查步骤

  1. 检查时钟使能:RCC->APB1ENRRCC->APB2ENR
  2. 检查中断使能:TIMx->DIER |= TIM_DIER_UIE
  3. 检查NVIC配置:NVIC_EnableIRQ(TIMx_IRQn)
  4. 检查定时器启动:TIMx->CR1 |= TIM_CR1_CEN
  5. 在中断中清除标志:TIMx->SR &= ~TIM_SR_UIF

Q2: PWM输出没有波形?

可能原因

  1. GPIO未配置为复用功能
  2. 通道未使能(CCER寄存器)
  3. 主输出未使能(高级定时器需要)
  4. CCR值设置不当(等于0或大于ARR)
  5. PWM模式配置错误

排查步骤

  1. 检查GPIO配置:复用功能、推挽输出
  2. 检查通道使能:TIMx->CCER |= TIM_CCER_CC1E
  3. 检查主输出(TIM⅛):TIMx->BDTR |= TIM_BDTR_MOE
  4. 检查CCR值:0 < CCR < ARR
  5. 检查PWM模式:TIMx->CCMR1 |= TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2

Q3: 定时不准确?

可能原因

  1. 时钟频率计算错误
  2. PSC或ARR设置不当
  3. 系统时钟配置错误
  4. 中断响应延迟
  5. 中断服务函数耗时过长

解决方案

  1. 重新计算并验证时钟参数
  2. 使用示波器或逻辑分析仪测量实际频率
  3. 检查系统时钟配置(HSE、PLL)
  4. 优化中断服务函数,减少执行时间
  5. 提高定时器中断优先级

Q4: 输入捕获测量不准确?

可能原因

  1. 输入信号频率过高或过低
  2. 计数器溢出未处理
  3. 边沿检测不稳定(抖动)
  4. 滤波器配置不当

解决方案

  1. 调整预分频器以适应信号频率
  2. 使用溢出计数器处理低频信号
  3. 配置输入滤波器:TIMx->CCMR1 |= TIM_CCMR1_IC1F
  4. 硬件上添加施密特触发器或RC滤波
  5. 软件滤波:多次测量取平均值

Q5: 多个定时器同时使用时出现冲突?

可能原因

  1. 中断优先级设置不当
  2. 中断服务函数耗时过长
  3. 共享资源访问冲突
  4. 定时器级联配置错误

解决方案

  1. 合理设置中断优先级
  2. 优化中断服务函数,使用标志位与主循环配合
  3. 使用互斥机制保护共享资源
  4. 检查定时器级联的主从配置

最佳实践

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位定时器级联) - 精确的秒级定时 - 复杂的频率合成

学习资源

官方文档

  1. STM32参考手册
  2. 定时器章节详细说明
  3. 寄存器定义和配置
  4. 时序图和波形图

  5. STM32数据手册

  6. 定时器电气特性
  7. 引脚复用功能
  8. 时钟树结构

  9. 应用笔记

  10. AN4013: STM32定时器概述
  11. AN4776: 通用定时器手册
  12. AN4776: PWM生成技术

推荐工具

  1. STM32CubeMX
  2. 图形化配置定时器
  3. 自动生成初始化代码
  4. 时钟树可视化

  5. 示波器/逻辑分析仪

  6. 观察PWM波形
  7. 测量频率和占空比
  8. 调试输入捕获

  9. Keil MDK调试器

  10. 实时查看寄存器
  11. 设置断点调试
  12. 观察变量变化

实践项目

  1. LED呼吸灯
  2. 使用PWM实现渐变效果
  3. 学习占空比控制
  4. 难度:⭐

  5. 超声波测距

  6. 使用输入捕获测量回波
  7. 计算距离
  8. 难度:⭐⭐

  9. 直流电机调速

  10. PWM控制电机转速
  11. H桥驱动电路
  12. 难度:⭐⭐

  13. 编码器测速

  14. 使用编码器模式
  15. 计算转速和位置
  16. 难度:⭐⭐⭐

  17. 四轴飞行器电机控制

  18. 4路PWM输出
  19. 精确的速度控制
  20. 难度:⭐⭐⭐⭐

总结

定时器是嵌入式系统中最重要的外设之一,掌握定时器驱动开发是每个嵌入式工程师的必备技能。

核心要点回顾

  1. 定时器分类:高级定时器、通用定时器、基本定时器各有特点
  2. 时钟计算:理解PSC、ARR与定时周期的关系
  3. 中断配置:正确配置中断使能和优先级
  4. PWM输出:掌握占空比计算和通道配置
  5. 输入捕获:理解边沿触发和捕获值读取
  6. 资源管理:合理分配定时器资源,避免冲突

学习建议

  1. 从简单开始:先掌握基本的定时中断
  2. 动手实践:通过实际项目加深理解
  3. 阅读手册:仔细阅读芯片参考手册
  4. 使用工具:善用示波器和调试器
  5. 循序渐进:逐步学习高级功能

下一步学习

  • 深入学习高级定时器的互补输出和死区控制
  • 掌握定时器与DMA的结合使用
  • 学习定时器在电机控制中的应用
  • 研究定时器的低功耗模式

定时器驱动是连接软件和硬件的桥梁,熟练掌握定时器将为你的嵌入式开发之路打开新的大门!

相关内容


版权声明:本文档由嵌入式知识平台创建,采用 CC BY-NC-SA 4.0 许可协议。

更新日志: - 2024-01-15: 初始版本发布