目标:掌握时基单元、PWM、输入捕获、编码器模式
STM32 定时器是一个复杂的模块,从输入到输出包含:时钟源 → 时基单元 → 输出/输入通道 → 从模式控制。高级定时器(TIM1、TIM8)还包含死区生成、刹车输入等电机控制功能。
定时器实际时钟 = 2 × PCLKx(当 APBx 预分频 > 1 时):
72MHz 配置下(APB1=/2, APB2=/1):
TIM2/3/4/5 时钟 = 2 × PCLK1 = 2 × 36MHz = 72MHz
TIM1/TIM8 时钟 = PCLK2 = 72MHz(APB2=/1,不分频,时钟不变)
配置时基时用 定时器时钟 计算分频,而非 PCLK 总线频率。
本笔记依次讲解:时基单元 → 输出比较/PWM → 输入捕获 → 从模式 → 编码器。
时基单元是定时器的"心脏",由 4 个核心寄存器组成:
PSC CNT ARR
Clock ──→ /(PSC+1) ──→ 计数器(递增/减) ──→ 比较 = ARR ──→ 更新事件(UEV)
↑ │
└────────── 清零重来 ────────┘
频率计算:
CK_CNT = TIM_CLK / (PSC + 1)
定时周期(向上计数模式):
T = (PSC + 1) × (ARR + 1) / TIM_CLK
| 参数 | 功能 | 示例值 (1ms, 72MHz) |
|---|---|---|
| PSC (预分频器) | 对 TIM_CLK 分频 | 71 → CK_CNT = 1MHz |
| CNT (计数器) | 每个时钟递增/减 | 从 0 到 ARR |
| ARR (自动重装) | 计数目标值 | 999 → (999+1)×1µs = 1ms |
| RCR (重复计数器) | 高级定时器专用,控制 N 次 UEV 触发一次中断 | — |
上图中阴影部分即为影子寄存器。
影子寄存器:PSC 和 ARR 写入后不会立即生效,而是先写入"预加载寄存器",等当前定时周期结束时(发生更新事件 UEV),影子寄存器才从预加载寄存器加载新值。
为什么要预加载?
预加载可关闭:在 CubeMX 中取消勾选 auto-reload preload 后,写入 ARR 立即生效(不推荐,除非你清楚后果)。
| 函数 | 说明 |
|---|---|
HAL_TIM_Base_Start(&htim) |
启动定时器(轮询) |
HAL_TIM_Base_Start_IT(&htim) |
启动定时器(中断) |
HAL_TIM_PeriodElapsedCallback(&htim) |
更新中断回调 |
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2);
while (1) { /* 主循环休眠,中断驱动 */ }
}
PWM Mode 1(向上计数):
CNT < CCR → 输出有效电平(高)
CNT ≥ CCR → 输出无效电平(低)
CNT = ARR → 清零重来
占空比 = CCR / (ARR + 1)
频率 = TIM_CLK / (PSC+1) / (ARR+1)
CCR=3 —— 比较值
ARR=9 —— 自动重装值
CNT: 0 1 2 3 4 5 6 7 8 9 | 0 1 2 3 4 5 6 ...
↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ | ↑ ↑ ↑ ↑ ↑ ↑ ↑
OUT: █ █ █ ░ ░ ░ ░ ░ ░ ░ | █ █ █ ░ ░ ░ ░
└──高──┘ └───────低────────┘ | └──高──┘ └──低───
占空比 = 3/10 = 30%
呼吸灯示例:PWM 频率 1kHz(不可见闪烁),占空比从 0→100%→0 循环。
| 函数 | 说明 |
|---|---|
HAL_TIM_PWM_Start(&htim, Channel) |
启动 PWM |
__HAL_TIM_SET_COMPARE(&htim, Channel, CCR) |
修改占空比 |
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
uint16_t duty = 0;
int8_t dir = 1;
while (1)
{
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty);
HAL_Delay(5);
if (dir == 1) duty += 10;
else duty -= 10;
if (duty >= 999) dir = -1;
if (duty == 0) dir = 1;
}
}
外部信号 → TIx 引脚 → 滤波 → 边沿检测 → 捕获 CNT → 存入 CCR
频率测量:
第 1 次上升沿 → 捕获 CCR1
第 2 次上升沿 → 捕获 CCR2
周期 = CCR2 - CCR1(注意溢出处理)
| 函数 | 说明 |
|---|---|
HAL_TIM_IC_Start_IT(&htim, Channel) |
启动输入捕获 |
HAL_TIM_IC_CaptureCallback(&htim) |
捕获完成回调 |
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
uint32_t val = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);
// 计算频率/脉宽
}
}
主定时器 → TRGO (触发输出) → 从定时器的触发输入
常见组合:
TIM1 主 (PWM 输出) → TRGO = 更新事件
TIM2 从 (从模式复位) → 每次 TIM1 更新时 CNT 复位
用途:精确测量 PWM 参数
TIM1 CH1 输入捕获 → 捕获周期
TIM1 从模式复位 → 每次上升沿自动复位 CNT
正交编码器 → TI1 (A相) + TI2 (B相)
定时器自动判断正反转:
A 相领先 B 相 → 正转 (CNT++)
B 相领先 A 相 → 反转 (CNT--)
计数模式:
×1:仅 TI1 边沿计数
×2:TI1 + TI2 边沿计数
×4:所有边沿计数(最高分辨率)
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_TIM3_Init();
HAL_TIM_Encoder_Start(&htim3, TIM_CHANNEL_ALL);
int16_t position;
while (1)
{
position = (int16_t)__HAL_TIM_GET_COUNTER(&htim3);
// 通过串口打印 position 值
HAL_Delay(50);
}
}
int16_t 才能正确表示正负