一、定时器:STM32 的 “时间管家”
在嵌入式开发中,“时间控制” 是核心需求之一。比如让 LED 以精确的频率闪烁、控制电机转速渐变、测量传感器信号周期等。STM32 的 ** 定时器(Timer)** 就是解决这些问题的关键组件,它就像一个精准的 “电子钟表”,能实现计时、计数、波形生成等功能。本文将以 STM32F103C8T6 为例,带你掌握定时器的核心应用。
二、定时器分类与核心概念
1. 定时器类型(以 STM32F103 为例)
类型 | 型号 | 总线 | 核心功能 | 典型场景 |
---|---|---|---|---|
高级定时器 | TIM1/TIM8 | APB2 | 支持 PWM 互补输出、死区控制(电机驱动)、刹车输入 | 无刷电机控制 |
通用定时器 | TIM2-TIM5 | APB1 | 输入捕获(测频率)、输出比较(PWM)、编码器接口(测速) | 呼吸灯、电机调速 |
基本定时器 | TIM6/TIM7 | APB1 | 简单定时、触发 DAC 转换 | 周期性数据采样 |
2. 核心工作原理
- 时钟源:定时器的 “心跳” 来自时钟,可选内部时钟(如 72MHz)、外部时钟(如传感器脉冲)或其他定时器输出。
- 预分频器(PSC):对时钟分频,降低计数频率。例如 72MHz 时钟经 PSC=71 分频后,计数频率变为 1MHz(公式:
f = 时钟频率 / (PSC+1)
)。 - 计数器(CNT):从 0 开始向上 / 向下计数,达到自动重装值(ARR)时触发中断或事件。
- 自动重装寄存器(ARR):决定计数周期,例如 ARR=999 时,计数到 999 后归零,周期为
(999+1)/f
秒。
三、实战一:基于定时器的精准延时(替代 Delay 函数)
需求分析
- 问题:
HAL_Delay()
是阻塞式延时,期间 CPU 无法处理其他任务。 - 目标:用定时器中断实现非阻塞式延时,让 CPU 在延时期间执行其他操作(如按键扫描)。
硬件与工具
- 开发板:STM32F103C8T6(TIM2 定时器对应 PA0 引脚)。
- 软件:Keil MDK + STM32CubeMX。
实现步骤(以 TIM2 为例)
1. 在 CubeMX 中配置定时器
① 新建工程:沿用之前的 LED 工程,或新建 STM32F103C8 工程。
② 配置 TIM2:
- 路径:
Timers → TIM2
- 模式:向上计数(Up Counter)
- 时钟源:内部时钟(Internal Clock)
- 预分频器(PSC):71(72MHz ÷ (71+1) = 1MHz,计数周期 1μs)
- 自动重装值(ARR):999(实现 1ms 定时,
1μs × 1000 = 1ms
) - 使能中断:勾选 “NVIC Settings” 中的 “Enable”)。
③ 生成代码:确保勾选 “Generate interrupts”,点击生成工程。
2. 编写定时器中断代码
① 在 main.c 中定义全局变量:
uint32_t tick = 0; // 定时器计数器,记录经过的毫秒数
② 编写中断回调函数:
在stm32f10x_it.c
中找到TIM2_IRQHandler
函数,修改为:
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { // 检查更新中断
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除中断标志
tick++; // 每1ms自增1
}
}
③ 在 main 函数中初始化定时器并启动中断:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init(); // 初始化TIM2
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
while (1) {
if (tick >= 500) { // 每500ms翻转LED状态
HAL_GPIO_TogglePin(GPIOC, GPIO_Pin_13);
tick = 0; // 重置计数器
}
// 其他任务(如按键检测)
}
}
关键优势
- 非阻塞性:CPU 在等待延时期间可执行其他代码(如
按键扫描
、串口通信
)。 - 高精度:定时器依赖系统时钟,误差远小于
HAL_Delay()
(受 CPU 负载影响小)。
四、实战二:呼吸灯效果(PWM 控制 LED 亮度)
需求分析
- 原理:通过 PWM(脉冲宽度调制)调节 LED 的亮灭占空比,人眼感知为亮度渐变。
- 目标:用 TIM3 定时器生成 PWM 波形,控制 LED 从暗到亮再变暗,循环往复。
硬件连接
- LED 连接 PC13(GPIO 输出),定时器 TIM3 的 PWM 通道对应引脚需配置为复用功能(如 PA6 对应 TIM3_CH1)。
实现步骤(以 TIM3 为例)
1. 在 CubeMX 中配置 PWM 输出
① 配置 TIM3:
- 模式:向上计数,自动重装载预装载使能(ARR 预装载)。
- 时钟源:内部时钟(72MHz)。
- PSC:71(分频后 1MHz)。
- ARR:999(PWM 周期 1ms,
(999+1)/1MHz = 1ms
)。 - 通道 1(CH1)配置:PWM 模式 1,占空比初始值 0(CCR=0)。
- 引脚映射:PA6→TIM3_CH1(需在 GPIO 配置中设为 “复用推挽输出”)。
② 生成代码:确保勾选 “Generate peripheral initialization”。
编写 PWM 控制代码
① 在 main.c 中定义占空比变量:
uint16_t dutyCycle = 0; // 占空比(0~999)
uint8_t direction = 1; // 1=递增,0=递减
② 在 main 函数中启动 PWM 输出:
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); // 启动TIM3通道1的PWM输出
while (1) {
if (direction) {
dutyCycle++; // 占空比递增
if (dutyCycle >= 999) direction = 0;
} else {
dutyCycle--; // 占空比递减
if (dutyCycle == 0) direction = 1;
}
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, dutyCycle); // 更新占空比
HAL_Delay(5); // 调节渐变速度
}
}
代码解析
- PWM 占空比:
TIM3_CH1
的输出电平由比较寄存器(CCR)值决定。- 当
CCR=0
时,低电平占比 0%(LED 最暗); - 当
CCR=999
时,低电平占比 100%(LED 最亮)。
- 当
- 渐变逻辑:通过
direction
变量控制占空比增减,实现周期性亮度变化。
五、定时器高级应用:输入捕获测频率
需求分析
- 目标:用 TIM2 的输入捕获功能测量外部信号频率(如红外传感器脉冲)。
- 原理:通过捕获信号的上升沿或下降沿,计算脉冲周期。
实现步骤
1. CubeMX 配置(TIM2 输入捕获模式)
- 通道:TIM2_CH1(PA0 引脚)。
- 模式:输入捕获,上升沿触发。
- 预分频器:0(不分频,直接计数)。
uint32_t captureValue1 = 0, captureValue2 = 0; // 两次捕获值
uint32_t frequency = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) {
if (captureValue1 == 0) {
captureValue1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 第一次捕获
} else {
captureValue2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 第二次捕获
uint32_t period = captureValue2 - captureValue1;
frequency = 72000000 / period; // 计算频率(72MHz时钟)
captureValue1 = captureValue2 = 0; // 重置捕获值
}
}
}
六、常见问题与优化技巧
1. PWM 输出无波形
- 原因:
- 引脚未配置为复用功能(需设为 “复用推挽输出”)。
- 定时器时钟未使能(检查 RCC 配置)。
- 解决:在 CubeMX 中重新配置引脚,并生成代码时勾选相关时钟。
2. 定时器中断不触发
- 原因:
- 中断使能未勾选(CubeMX 的 NVIC 设置)。
- 中断优先级设置错误(优先级分组需匹配)。
- 解决:检查 CubeMX 中的中断配置,确保 “Enable” 勾选,并在代码中正确编写中断回调函数。
3. 优化建议
- 使用 HAL 库回调函数:避免直接操作寄存器,用
HAL_TIM_PWM_Start()
等接口简化开发。 - 时钟树优化:根据需求调整 PSC 和 ARR,平衡精度与定时器溢出周期(如高精度延时需增大 ARR)。
七、总结:定时器的无限可能
通过本文实战,你已掌握 STM32 定时器的核心应用:
- 精准延时:替代阻塞式 Delay,释放 CPU 资源。
- PWM 控制:实现呼吸灯、电机调速等模拟量控制。
- 输入捕获:扩展至频率测量、编码器测速等场景。
下一篇预告:《STM32 ADC 实战:从模拟信号到数字世界的桥梁》
(关注我,解锁传感器数据采集技能~)
互动话题:你想用定时器实现什么创意项目?欢迎在评论区分享你的想法! 🚀