定时器基础
Author:余生
一、引入:为什么需要定时器?
- 生活类比:用 “沙漏”、“闹钟”、“秒表” 来比喻定时器,帮助学生建立直观印象。
- 实际需求:
- 精确延时:
delay_ms(1000)
为什么比for
循环更可靠? - 周期性任务:每隔 1 秒读取一次传感器数据,如何实现?
- 信号生成:如何产生让 LED 呼吸灯的 PWM 信号?
- 事件测量:如何测量按键按下的时长?
- 精确延时:
- 结论:定时器是 MCU 的 “内置时钟”,能独立于 CPU 工作,实现高精度、低功耗的时间相关功能。
二、什么是定时器
- TIM(Timer)定时器
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
- 16 位计数器、预分频器、自动重装寄存器的时基单元,在 72MHz 计数时钟下可以实现最大 59.65s 的定时
- 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
- 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
三、定时器基础概念
1. 从 “波” 和 “频率” 开始
想象一下水面上的波浪:一个波峰接着一个波谷,再一个波峰…… 如此往复。这种周期性的运动是理解定时器的基础。
- 周期 (Period, T):完成一个完整循环(例如,从一个波峰到下一个波峰)所需的时间。单位是秒(s)。
- 频率 (Frequency, f):在 1 秒钟内,完成了多少个这样的完整循环。单位是赫兹(Hz)。
- 核心关系:频率 (f) = 1 / 周期 (T) 或 周期 (T) = 1 / 频率 (f)。
- 例如:一个波每 0.5 秒重复一次(T=0.5s),那么它的频率就是
f = 1 / 0.5 = 2 Hz
,表示每秒振荡 2 次。 - 再如:市电是 50Hz,意味着电流方向每秒改变 50 次,周期
T = 1/50 = 0.02秒 = 20毫秒
。
- 例如:一个波每 0.5 秒重复一次(T=0.5s),那么它的频率就是
在数字电路中,最常见的 “波” 是方波。它只有两个状态:高电平(通常代表 1 或 3.3V)和低电平(通常代表 0 或 0V)。方波同样有周期和频率。
2. 如何产生一个方波?—— 需要一个 “计时器”
假设我们想让一个 LED 以 1Hz 的频率闪烁(亮 1 秒,灭 1 秒,周期 2 秒)。最笨的方法是用 CPU 不停地数数:
cpp
while(1) {
LED_ON();
delay_long_time(); // 这个函数内部可能是一个巨大的for循环
LED_OFF();
delay_long_time();
}
1
2
3
4
5
6
2
3
4
5
6
这种方法占用 CPU 资源,CPU 在这段时间内什么都不能做,效率极低。
解决方案:使用一个独立的 “小闹钟”—— 这就是定时器 (Timer)。CPU 只需要设置好这个 “闹钟”,然后就可以去干别的事了。当 “闹钟” 时间到,它会 “叫醒” CPU(通过中断),或者自己直接去翻转 LED 的状态。
3. 定时器的核心:计数器 (Counter)
STM32 的定时器本质上是一个数字计数器。它是一个可以自动加 1(或减 1)的寄存器。
- 时钟源 (Clock Source):这个计数器需要一个 “心跳” 来驱动它。这个心跳就是时钟信号。STM32 的定时器通常使用 APB 总线时钟(如 72MHz)作为源头。
- 预分频器 (Prescaler, PSC):72MHz 的时钟太快了!如果计数器直接接 72MHz,它每秒要加 7200 万次!我们通常需要更慢、更精确的 “滴答” 声。预分频器的作用就是把高速时钟 “切碎”。
- 工作原理:预分频器接收高速时钟,每累计
PSC + 1
个时钟脉冲,才输出一个 “滴答” 给计数器。 - 公式:计数器时钟频率 (f_counter) = 输入时钟频率 / (PSC + 1)
- 例子:输入时钟 = 72MHz,PSC = 7199。那么
f_counter = 72,000,000 / (7199 + 1) = 72,000,000 / 7200 = 10,000 Hz = 10kHz
。
- 计数器每秒加 1 万次,每次 “滴答” 间隔
T_tick = 1 / 10,000 = 0.0001秒 = 100微秒 (μs)
。
- 工作原理:预分频器接收高速时钟,每累计
4. 计数器如何 “闹钟”?—— 自动重装载 (Auto-Reload, ARR)
现在计数器有了自己的 “滴答” 节奏(10kHz)。我们需要设定它多久 “闹” 一次。
- 自动重装载寄存器 (ARR):这是一个设定值。计数器从 0 开始,每收到一个 “滴答” 就加 1。当计数器的值等于 ARR 时,会发生两件事:
- 溢出 (Overflow):计数器的值被清零(或根据模式重新加载),准备开始下一轮计数。
- 事件触发:可以配置定时器在此时产生一个更新事件 (Update Event)。这个事件可以:
- 触发一个中断 (Interrupt),让 CPU 执行中断服务程序(ISR)。
- 触发一个 DMA 请求。
- 让某个输出通道翻转状态(用于 PWM)。
- 计算定时周期:
- 定时器溢出周期 (T_timer):计数器从 0 数到 ARR 再清零所需的时间。
- 公式:T_timer = (ARR + 1) /f_counter
(ARR + 1)
是因为计数器从 0 数到 ARR,总共经历了ARR + 1
个 “滴答”。
- 例子:
f_counter = 10kHz (T_tick = 100μs)
,我们想要 1 秒的周期。- 需要的 “滴答” 数
N = T_timer / T_tick = 1s / 0.0001s = 10,000
。 - 因为
N = ARR + 1
,所以ARR = N - 1 = 10,000 - 1 = 9999
。 - 验证:
T_timer = (9999 + 1) / 10,000 Hz = 10,000 / 10,000 = 1秒
。完美!
- 需要的 “滴答” 数
5. 定时器的 “工作模式”
根据计数器的计数方式,主要有:
- 向上计数模式 (Up Counter):计数器从 0 开始,递增到 ARR,然后溢出清零。这是最常用的模式。
- 向下计数模式 (Down Counter):计数器从 ARR 开始,递减到 0,然后溢出并重载 ARR。
- 中央对齐模式 (Center-Aligned):计数器先向上计数到 ARR-1,再向下计数到 1,最后溢出。主要用于生成对称 PWM。
6. 定时器如何控制 LED?—— 输出比较 (Output Compare, OC)
定时器不仅能 “闹”,还能 “动手”。通过输出比较通道,它可以精确地在某个时间点改变 GPIO 引脚的状态。
- 比较寄存器 (Compare Register, CCRx):每个输出通道(如 CH1, CH2)都有一个对应的 CCR(如 CCR1, CCR2)。
- 工作原理:
- 计数器不断运行。
- 定时器硬件会持续比较计数器的当前值和 CCRx 的值。
- 当 计数器值 == CCRx 值 时,会发生比较匹配 (Compare Match) 事件。
- 根据通道的配置模式,可以:
- 翻转输出引脚的电平(Toggle)。
- 将输出引脚置高(Set)。
- 将输出引脚置低(Reset)。
- 实现 PWM:这是输出比较最强大的应用。
- PWM 频率:由
ARR
和PSC
共同决定(f_pwm = f_counter / (ARR + 1)
)。 - 占空比 (Duty Cycle):高电平时间占整个周期的比例。由
CCR
值决定。占空比 = CCRx / (ARR + 1)
- 例子:
ARR=99
(周期 100 个滴答),f_counter=1MHz
->T_period=100μs
,f_pwm=10kHz
。- 如果
CCR1=25
,则占空比= 25 / 100 = 25%
。输出波形:高电平 25μs,低电平 75μs。 - 如果
CCR1=75
,则占空比= 75 / 100 = 75%
。输出波形:高电平 75μs,低电平 25μs。
- 如果
- 模式:通常使用 PWM 模式 1(计数器 <CCRx 时输出有效电平,>= CCRx 时输出无效电平)或 PWM 模式 2。
- PWM 频率:由
7. 总结:定时器的完整工作流
- 配置时钟:选择定时器时钟源(通常是 APB 时钟)。
- 设置预分频器 (PSC):将高速时钟分频,得到计数器的 “滴答” 频率
f_counter
。 - 设置自动重装载值 (ARR):决定计数器的计数范围,从而确定定时器的基本周期
T_timer
。 - 选择计数模式:通常为向上计数。
- (可选)配置输出通道 (OC):
- 将 GPIO 引脚复用为定时器通道。
- 设置比较寄存器
CCR
的值。 - 配置通道模式(如 PWM 模式)。
- (可选)使能中断:在 NVIC 中使能定时器中断,以便在溢出或比较匹配时执行 ISR。
- 启动定时器:调用
HAL_TIM_xxx_Start()
或HAL_TIM_xxx_Start_IT()
。 - 运行:
- 计数器在
f_counter
的驱动下开始计数。 - 当计数器值达到
ARR
时,发生更新事件(溢出),计数器清零,可触发中断。 - 当计数器值等于某个
CCR
时,发生比较匹配事件,可改变对应 GPIO 引脚的状态(如 PWM)。 - 循环往复。
- 计数器在
通过这种方式,STM32 定时器就能独立于 CPU,精确地产生时间延迟、周期性中断和 PWM 信号,是嵌入式开发中不可或缺的工具。