一、定时器是什么?STM32 的 “时间管家”
在嵌入式系统中,“时序控制” 是核心需求 —— 小到 LED 闪烁的精准延时,大到电机调速的 PWM 信号、传感器数据的定时采集,都离不开定时器(Timer) 这一外设。简单来说,STM32 的定时器是基于硬件的 “时间计数器”,通过对内部时钟信号的计数实现时间测量、信号生成等功能,无需 CPU 持续参与,极大提升了系统效率。
以经典的 STM32F103 系列为例,其包含 3 类定时器,功能覆盖从基础到高级的所有时序场景:
|
定时器类型 |
代表型号 |
核心功能 |
适用场景 |
|
基本定时器 |
TIM6、TIM7 |
16 位计数、更新中断、触发 ADC |
精准延时、ADC 定时采样 |
|
通用定时器 |
TIM2-TIM5 |
16 位计数、输入捕获、输出比较、PWM |
电机 PWM 调速、脉冲宽度测量 |
|
高级定时器 |
TIM1、TIM8 |
16 位计数、死区控制、互补 PWM |
三相电机驱动、高精度功率控制 |
定时器的核心优势在于 “硬件独立运行”:一旦初始化完成,定时器可自主计数并触发中断或输出信号,CPU 只需在中断发生时处理任务,避免了软件延时对 CPU 资源的占用。
二、定时器的核心原理:计数与时钟的 “协作游戏”
所有定时器的工作原理都围绕 “时钟源→预分频器→计数器→自动重装载寄存器” 的核心链路展开,理解这一链路就能掌握定时器的本质。
1. 核心结构拆解
- 时钟源(Clock Source):定时器的 “动力来源”,可选择内部时钟(APB 总线时钟)、外部引脚时钟(如 TIM2_CH1 的外部脉冲)或其他定时器的时钟。STM32F1 中,通用定时器的内部时钟默认来自 APB1 总线(72MHz,经倍频后为 72MHz)。
- 预分频器(Prescaler):对时钟源进行分频,降低计数频率。计算公式为:分频后频率 = 时钟源频率 / (预分频器值 + 1)。例如,72MHz 时钟经 7199 分频后,频率为 72000000/(7199+1)=10kHz。
- 计数器(Counter):16 位寄存器,每接收一个分频后的时钟脉冲就加 1(或减 1),计数范围为 0~65535。
- 自动重装载寄存器(ARR):存储计数的上限值,当计数器的值等于 ARR 时,计数器清零并触发 “更新事件”(可产生中断或触发其他外设)。
2. 关键时序参数计算
定时器的核心时序参数(如定时时间、PWM 频率)可通过以下公式精确计算:
- 定时时间(基础定时器 / 通用定时器定时功能)
定时时间 = (预分频器值 + 1) × (自动重装载值 + 1) / 时钟源频率
例:时钟源 72MHz,预分频器 7199,ARR=9999,定时时间 = 7200×10000/72000000 = 1 秒。
- PWM 频率(通用 / 高级定时器 PWM 功能)
PWM 频率 = 时钟源频率 / [(预分频器值 + 1) × (自动重装载值 + 1)]
例:同上参数,PWM 频率 = 72000000/(7200×10000) = 1Hz。
- PWM 占空比(通用 / 高级定时器 PWM 功能)
占空比 = (比较寄存器值 + 1) / (自动重装载值 + 1) × 100%
例:ARR=999,比较寄存器值 = 499,占空比 = 500/1000×100% = 50%。
三、定时器的核心功能:从基础到高级的应用拆解
不同类型的定时器功能差异较大,新手需重点掌握通用定时器的三大核心功能,再逐步拓展到高级定时器。
1. 基础功能:定时中断(以 TIM2 为例)
定时中断是定时器最基础的应用,通过设定固定时间触发中断,实现周期性任务(如每秒刷新传感器数据)。
- 工作流程:初始化定时器→设置预分频器和 ARR→使能更新中断→启动定时器→中断发生时执行回调函数。
- 适用场景:精准延时(替代 HAL_Delay,避免阻塞 CPU)、周期性数据采集、定时报警。
2. 核心功能:输出比较与 PWM
输出比较(OC)是通用定时器的核心能力,通过比较计数器与比较寄存器(CCR)的值,控制 GPIO 输出电平,最典型的应用就是生成 PWM 信号。
PWM(脉冲宽度调制)通过高低电平的周期性交替,以 “平均电压” 控制外设 —— 占空比越高,平均电压越高(如 LED 越亮、电机转速越快)。STM32 的 PWM 支持 4 种输出模式,最常用的是 “PWM 模式 1”(计数器小于 CCR 时输出高电平,大于等于时输出低电平)。
- 适用场景:LED 亮度调节、直流电机 PWM 调速、舵机角度控制、音频信号输出。
3. 高级功能:输入捕获
输入捕获通过监测 GPIO 引脚的电平变化(上升沿 / 下降沿),记录计数器的值,从而实现对外部信号的频率、周期、脉冲宽度的测量。
- 工作流程:配置 GPIO 为输入捕获模式→设置触发边沿(上升沿 / 下降沿)→启动捕获→捕获到边沿时读取计数器值→计算信号参数。
- 适用场景:红外遥控信号解码、脉冲信号频率测量、按键长按 / 短按识别。
4. 高级定时器专属功能
高级定时器(TIM1、TIM8)在通用定时器基础上增加了死区控制、互补 PWM 输出等功能,专为电机控制设计:
- 死区控制:避免 H 桥电路中上下桥臂功率管同时导通导致短路,是直流无刷电机驱动的必备功能。
- 互补 PWM 输出:同时输出一路 PWM 信号及其反相信号,简化电机驱动电路设计。
四、定时器配置实操:CubeMX+HAL 库手把手教学
以 STM32F103C8T6 的 TIM2 为例,分别演示 “定时中断” 和 “PWM 输出” 的配置流程,新手建议优先掌握 CubeMX 图形化配置。
1. 实战 1:定时中断(1 秒触发一次中断,翻转 LED)
(1)CubeMX 配置步骤
- 时钟配置:配置 APB1 总线时钟为 36MHz(STM32F1 中 APB1 最大 36MHz,定时器时钟为 APB1 时钟 ×2=72MHz)。
- 定时器配置:
- 选择 TIM2,在 “Mode” 中勾选 “Internal Clock”。
- 配置 “Parameter Settings”:
-
- Prescaler(预分频器):7199(72MHz/7200=10kHz)。
-
- Counter Period(ARR):9999(10kHz/10000=1Hz,即 1 秒)。
-
- Auto-reload Preload:Enable(允许自动重装载)。
- 中断配置:在 “NVIC Settings” 中勾选 “TIM2 global interrupt”,设置优先级。
- GPIO 配置:PA0 设为推挽输出(控制 LED)。
- 生成代码:选择 MDK-ARM,生成工程。
(2)核心代码
// 定时器初始化(CubeMX自动生成,在tim.c中)
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7199;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 9999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
}
// 中断回调函数(在main.c中重写)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) { // 判断是TIM2的中断
HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); // 翻转PA0电平,LED闪烁
}
}
// main函数中启动定时器
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2); // 启动TIM2定时中断
while (1) {
// 主循环无需处理,中断自动执行
}
}
2. 实战 2:PWM 输出(控制 LED 亮度渐变)
(1)CubeMX 配置步骤
- 时钟配置:同实战 1,APB1 时钟 36MHz,TIM2 时钟 72MHz。
- 定时器配置:
- 选择 TIM2,在 “Mode” 中勾选 “PWM Generation CH1”(PA0 为 TIM2_CH1 复用引脚)。
- 配置 “Parameter Settings”:
-
- Prescaler:71(72MHz/72=1MHz)。
-
- Counter Period:999(1MHz/1000=1kHz,PWM 频率 1kHz)。
-
- PWM Mode:PWM Mode 1。
-
- Pulse(CCR 值):0(初始占空比 0%,LED 熄灭)。
- GPIO 配置:PA0 自动设为 “Alternate Function Push-Pull”(复用推挽输出)。
- 生成代码。
(2)核心代码
// 定时器PWM初始化(CubeMX自动生成,在tim.c中)
void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) {
Error_Handler();
}
}
// main函数中实现亮度渐变
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动TIM2_CH1的PWM输出
uint16_t pwm_val = 0;
uint8_t dir = 1; // 1表示亮度增加,0表示亮度降低
while (1) {
HAL_Delay(10); // 控制渐变速度
if (dir) {
pwm_val++;
if (pwm_val >= 1000) {
dir = 0;
}
} else {
pwm_val--;
if (pwm_val <= 0) {
dir = 1;
}
}
TIM2->CCR1 = pwm_val; // 更新比较寄存器值,改变占空比
}
}
五、定时器使用避坑指南:新手常犯的 6 个错误
- 时钟源频率计算错误
STM32F1 中,当 APB1 总线预分频系数为 1 时,定时器时钟 = APB1 时钟;当预分频系数大于 1 时,定时器时钟 = APB1 时钟 ×2。若忽略倍频,会导致定时时间或 PWM 频率偏差一倍。
- 忘记使能定时器时钟
与 GPIO 类似,定时器的时钟默认关闭,需在HAL_TIM_Base_Init中通过__HAL_RCC_TIM2_CLK_ENABLE()使能(CubeMX 会自动生成,手动配置时易遗漏)。
- PWM 引脚模式配置错误
输出 PWM 时,GPIO 需配置为 “复用推挽输出”,而非普通推挽输出。若配置为普通输出,引脚由 GPIO 寄存器控制,无法输出 PWM 信号。
- 中断优先级设置不当
多个定时器中断同时使用时,未合理设置优先级会导致高优先级中断被低优先级中断阻塞。建议按任务重要性分配优先级(如电机控制中断优先级高于 LED 闪烁中断)。
- 计数器溢出未处理
16 位计数器最大计数 65535,当定时时间过长(如超过 65535× 分频后周期),会导致计数器溢出,需通过多周期计数或使用 32 位定时器(如 STM32F4 的 TIM2 为 32 位)解决。
- PWM 频率与占空比冲突
当需要同时满足高频率和高占空比精度时,需平衡预分频器和 ARR 的值。例如,要实现 10kHz PWM 且占空比精度 0.1%,需 ARR=999(1000 级精度),预分频器 = 7199(72MHz/7200/1000=10kHz)。
六、总结:定时器是 STM32 的 “时序灵魂”
如果说 GPIO 是 STM32 的 “手脚”,那么定时器就是 “生物钟”—— 它赋予了嵌入式系统精准的时间感知和控制能力。从基础的定时中断到复杂的 PWM 电机控制,定时器的应用贯穿了嵌入式开发的各个层级。
掌握定时器后,你可以实现更复杂的项目:比如用 PWM 控制四足机器人的舵机角度,用输入捕获解码红外遥控器信号,用定时中断实现多任务调度(简易 RTOS 的核心思想)。
下一篇我们将聚焦 “STM32 ADC 外设”,讲解如何通过模拟输入采集温度、光照等物理量,结合定时器实现 “定时采集 + 数据处理 + PWM 反馈控制” 的完整闭环系统。如果想深入某个定时器知识点(如输入捕获实战、高级定时器死区
733

被折叠的 条评论
为什么被折叠?



