掌控时间的核心:STM32定时器深度解析——从PWM呼吸灯到输入捕获测脉宽
前言
在单片机开发中,我们经常需要做两件事:“控制时间” 和 “测量时间”。
- 比如:每隔 1秒 闪烁一次 LED(定时中断)。
- 比如:控制电机转速或 LED 亮度(PWM 输出)。
- 比如:测量超声波模块的高电平时间算出距离(输入捕获)。
这些功能的背后,都是同一个外设在工作——定时器 (TIM)。本文将带你从最底层的计数原理出发,通过 CubeMX 配置和 HAL 库代码,彻底搞懂 PWM 输出 和 输入捕获。
第一部分:定时器的核心原理
1.1 什么是定时器?
想象一个拿着秒表的计数员。
- 他的工作就是看着秒表,每过一个单位时间,就在纸上写一个数字:0, 1, 2, 3...
- 这个“计数员”就是 STM32 内部的 计数器 (Counter, CNT)。
- 这个“秒表”的快慢,由 时钟源 决定。
1.2 三个关键参数 (三驾马车)
理解定时器,必须理解这三个寄存器:
-
PSC (Prescaler, 预分频器):“变速箱”
- 系统时钟(比如 72MHz)太快了,我们需要把它降下来。
- 如果你设置 PSC = 71,那么计数频率 = 72MHz / (71+1) = 1MHz。也就是说,计数器每 1微秒 (us) 加 1。
-
CNT (Counter, 计数器):“当前的计数值”
- 它是一个一直在增加的数字(0 -> 1 -> 2 ...)。
-
ARR (Auto-Reload Register, 自动重装载寄存器):“终点站”
- 这是计数的目标值。
- 当 CNT 数到 ARR 的值时,它会清零(归位),并产生一个更新事件 (Update Event)。
- 比如设置 ARR = 999(PSC已设为1MHz),那么定时周期 = 1000us = 1ms。
第二部分:输出比较 (Output Compare) 与 PWM
2.1 什么是 PWM?
PWM (脉冲宽度调制) 是利用数字信号控制模拟电路的魔法。
通过控制高电平在一个周期内占用的时间比例(占空比),我们可以让 LED 看起来“半亮”,或者让电机“半速”转动。
2.2 PWM 生成原理
在计数器数数的过程中,引入一个新的寄存器:CCR (Capture/Compare Register, 捕获/比较寄存器)。通过计数器的值与CCR中进行比较,高低变化。
逻辑规则:
- 当 CNT < CCR 时:输出 高电平 (有效)。
- 当 CNT >= CCR 时:输出 低电平 (无效)。
效果:
- ARR (自动重装寄存器也就是临界值)决定了 PWM 的 频率(周期)。
- CCR 决定了 PWM 的 占空比(高电平宽度)。
- 调节 CCR 的值,就能调节 LED 的亮度!
第三部分:输入捕获 (Input Capture)
3.1 什么是输入捕获?
如果说 PWM 是“输出时间”,那输入捕获就是“测量时间”。
它用于测量外部信号的频率或脉冲宽度(比如按键按下的时长、红外遥控的信号、超声波回波时间)。
3.2 捕获原理
当引脚上检测到电平跳变(比如从低变高)时,硬件会瞬间做一个动作:
“拍照” —— 将当前 CNT(计数器) 的值瞬间复制到 CCR(捕获寄存器) 中保存下来。
测脉宽的思路:
- 设置上升沿触发:当信号变高,记录 CNT 的值,记为
Value1。 - 设置下降沿触发:当信号变低,记录 CNT 的值,记为
Value2。 - 脉宽时间 = (Value2 - Value1) × 计数周期。
第四部分:STM32CubeMX 配置实战
我们要实现两个功能:
- TIM2 (PA0):产生 PWM 呼吸灯。
- TIM3 (PA6):捕获按键或信号发生器的高电平时间。
4.1 基础配置
- RCC -> HSE (Crystal/Ceramic)。//外部时钟源,配置72MHz
- Clock Configuration -> 72MHz。
4.2 配置 TIM2 (PWM 输出)
- Timers -> TIM2。
- Channel 1:选择 PWM Generation CH1。//配置PWM通道一
- Parameter Settings:
- Prescaler (PSC):
71(分频后频率 1MHz,1us跳一次)。 - Counter Period (ARR):
999(周期 1000us = 1ms,即 1kHz)。 - Pulse (CCR):
0(初始占空比 0%)。
- Prescaler (PSC):
4.3 配置 TIM3 (输入捕获)
- Timers -> TIM3。
- Channel 1:选择 Input Capture direct mode。//直接捕获
- Parameter Settings:
- Prescaler (PSC):
71(1MHz,保证 1us 精度)。 - Counter Period (ARR):
65535(设为最大,防止还没测完就溢出了)。 - Polarity Selection:
Rising Edge(先捕获上升沿)。
- Prescaler (PSC):
- NVIC Settings:勾选 TIM3 global interrupt (一定要开中断!)。
4.4 生成代码
点击 Generate Code。
第五部分:代码编写 (HAL库)
5.1 PWM 呼吸灯逻辑 (main.c)
在 main() 函数中,我们需要启动 PWM,并在 while(1) 里不断修改 CCR 的值。
/* USER CODE BEGIN 2 */
// 启动 TIM2 通道1 的 PWM
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
/* USER CODE END 2 */
/* USER CODE BEGIN 3 */
int16_t pwm_val = 0;
int8_t step = 10;
while (1)
{
// 修改 CCR 寄存器的值(0~999)
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, pwm_val);
pwm_val += step;
// 到达顶峰反向,到达底部反向
if (pwm_val >= 1000 || pwm_val <= 0) step = -step;
HAL_Delay(10); // 呼吸速度
}
/* USER CODE END 3 */
5.2 输入捕获逻辑 (中断处理)
测量脉宽通常在中断回调函数中完成。我们需要定义状态机来记录是“刚遇到上升沿”还是“遇到了下降沿”。
在 main.c 底部添加:
/* USER CODE BEGIN 4 */
// 定义变量
uint32_t rise_val = 0; // 上升沿时刻的 CNT
uint32_t fall_val = 0; // 下降沿时刻的 CNT
uint32_t high_time = 0; // 高电平持续时间 (us)
uint8_t capture_state = 0; // 0:等待上升沿, 1:等待下降沿
// 重写定时器输入捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是 TIM3 的通道 1
if (htim->Instance == TIM3 && htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
{
if (capture_state == 0) // 状态0:抓到了上升沿
{
// 1. 获取当前计数器的值(开始时间)
rise_val = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// 2. 切换为下降沿捕获
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_FALLING);
// 3. 切换状态
capture_state = 1;
}
else if (capture_state == 1) // 状态1:抓到了下降沿
{
// 1. 获取当前计数器的值(结束时间)
fall_val = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
// 2. 计算高电平时间 (考虑溢出情况)
if (fall_val >= rise_val)
{
high_time = fall_val - rise_val;
}
else
{
// 如果计数器溢出重装了 (0xFFFF -> 0)
high_time = (0xFFFF - rise_val) + fall_val + 1;
}
// 3. 打印结果 (可选,或者存到全局变量)
// printf("High Level Time: %d us\r\n", high_time);
// 4. 恢复设置为上升沿捕获,准备下一次测量
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
capture_state = 0;
}
}
}
/* USER CODE END 4 */
别忘了在 main() 初始化部分开启输入捕获中断:
/* USER CODE BEGIN 2 */
HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 开启输入捕获中断
/* USER CODE END 2 */
总结
通过这篇文章,我们打通了定时器的任督二脉:
- 基本原理:基于 PSC(分频)和 ARR(重装载)的计数器。
- PWM输出:利用 CCR(比较值)控制输出电平翻转,实现电压模拟。
- 输入捕获:利用硬件锁存 CNT 的值,精确测量外部信号时长。
掌握了这两个功能,你可以做的项目就非常多了:
- PWM:电机调速、伺服舵机控制(机械臂)、LED 调光。
- 捕获:超声波测距、红外解码、测量风扇转速。
更多推荐




所有评论(0)