前置介绍
什么是定时器?

定时器分类

- 高级定时器
- 通用定时器
- 基本定时器
高级定时器 > 通用定时器 > 基本定时器
高级的定时器包含低级的定时器的所有功能.
需要注意定时器挂载的总线是指时钟总线, 即在 72MHz 的总频上进行分频的总线, 分别是 APB1, APB2
其中只有高级定时器在 APB2 上面. 其他都在 APB1 上面.
TIM 定时器分类

图中的表格是通用参考, C8T6 仅有 TIM1 ~ 4
|-------------|--------------------------------------------|
| 定时器 | 定时器名称(如 TIM1, TIM2...) |
| 计数器分辨率 | 计数器位数,均为 16 位 → 最大计数值 65535 |
| 计数器类型 | 支持向上计数、向下计数或两者兼有 |
| 预分频系数 | 可对输入时钟进行分频,范围 1~65535,用于调整计时精度 |
| 产生DMA | 是否支持通过 DMA 传输数据(如更新事件触发 DMA)→ "可以"表示支持 |
| 捕获/比较通道 | 可用于输入捕获或输出比较的通道数量(0~4),决定 PWM 输出或信号测量能力 |
| 互补输出 | 是否支持互补 PWM 输出(常用于电机控制等需要死区控制的场景)→ "有"或"没有" |
TIM 定时器框图

来自 RCC(复位与时钟控制单元)的 TIMxCLK → 经过内部处理后成为 CK_INT(内部时钟)。
对输入的时钟进行分频: 公式为 CK_CNT = CK_PSC / (PSC + 1)
CK_CNT 即为实际驱动计数器的时钟. 向右进入 CNT 计数器.
CNT 计数器 与 自动重装载寄存器 ARR 合作, 当计数器达到设定的值(ARR)时, 会产生更新事件, 自动将 ARR 的值重新加载到 CNT (例如: 向上计数, 则就是将其归零)
ARR 还有一个 "影子寄存器" 的机制, 简单来说就是确保你设定的值永远是在上一个设定的定时任务完成之后才生效. (预装载机制)
不然当你设定的值比上一次小时, 且计数已经超过了你现在设定的值, 那么这个定时就得等到计数器溢出后再加到你现在设定的值.
(例如: 初始设定值为 1000, 已经计数到了 900 的时候. 变更他的目标值为 500, 但是已经计到了 900, 向上递增到不了 500 (除非溢出), 所以这个周期就会变得特别长. )
TIM 通用定时器基础中断应用
1、HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim)
开启定时器中断模式运行
2、__HAL_TIM_SET_AUTORELOAD(HANDLE, AUTORELOAD)
修改定时器自动重装载值(ARR)
3、__HAL_TIM_SET_COUNTER(HANDLE, COUNTER)
设置当前定时器计数器值
4、HAL_TIM_Base_Stop_IT(TIM_HandleTypeDef *htim)
关闭定时器中断模式运行
5、HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
TIM 定时器更新中断回调函数
项目配置
TIM2 配置
本节中使用的定时器为基本定时器, 所以在 TIM2 ~ 4 中随便用一个即可.

TIM2 - Parameter Settings 相关参数配置解释
1. Prescaler (PSC - 16 bits value) → 7200-1
-
含义:预分频系数,对输入时钟进行分频。
-
公式:
CK_CNT = CK_TIM / (PSC + 1)
假设系统时钟为 72MHz(常见于 STM32F103):
CK_CNT = 72,000,000 / (7200 - 1 + 1) = 72M / 7200 = 10,000 Hz = 10kHz
→ 即每 0.1ms 计数器加1。
作用:调整计数速度,从纳秒级到秒级均可覆盖。
2. Counter Mode → Up
- 含义:计数方向。
- 可选值:
-
Up→ 向上计数(0 → ARR)Down→ 向下计数(ARR → 0)Center-Aligned 1/2/3→ 中央对齐模式(对称PWM用)
应用场景:
- 普通定时/PWM → 选
Up - 减少谐波干扰 → 选
Center-Aligned
3. Counter Period (AutoReload Register - 16 bits value) → 1000-1
-
含义:自动重装载值(ARR),决定计数周期长度。
-
实际周期计算:
周期时间 = (ARR + 1) × (PSC + 1) / CK_TIM
= (1000 - 1 + 1) × 7200 / 72,000,000
= 1000 × 7200 / 72M = 7,200,000 / 72,000,000 = 0.1 秒 = 100ms
所以:这个定时器每 100ms 产生一次更新事件(UI)
注意:CubeMX 中显示的是 "1000-1",是因为寄存器值是 0-based,你写 999 就是 1000 个计数周期。
4. Internal Clock Division (CKD) → No Division
- 含义:内部时钟分频因子,主要用于数字滤波器采样频率。
- 可选值:
-
No Division→ CK_INT 不分频Division by 2→ CK_INT / 2Division by 4→ CK_INT / 4
应用场景:
- 输入捕获时,若外部信号噪声大,可降低 CKD 来提高滤波精度
- 一般 PWM 或基本定时 → 保持
No Division
5. auto-reload preload → Enable
- 含义:启用 ARR 的"预装载寄存器"机制(即影子寄存器)。
- 作用:确保你在运行时修改 ARR 值不会立即生效,而是在下一次更新事件时才同步到硬件寄存器,避免计数错误。
建议始终开启!否则动态调整周期可能导致波形畸变或计数异常。
开启 TIM2 的中断使能

这样, 每 100ms 产生一次中断.
配置 GPIO

配置一个按键输入(Pull-down 下拉模式), 再配置三个 LED 灯, 默认输出高电平.
代码部分
/* USER CODE BEGIN PV */
uint32_t tim2_arr[2] = { 1000 - 1, 10000 - 1}; // index 0: 每100ms产生一次中断, index 1: 每(1000ms)秒产生一次中断
uint8_t mode = 0; // 模式
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
// 启动定时器中断
HAL_TIM_Base_Start_IT(&htim2);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
if (HAL_GPIO_ReadPin(GPIOA, KEY1_Pin) == GPIO_PIN_SET)
{
// KEY1 被按下
mode = !mode; // 切换模式
HAL_TIM_Base_Stop_IT(&htim2); // 先关定时器
__HAL_TIM_SET_AUTORELOAD(&htim2, tim2_arr[mode]); // 修改 ARR (自动重装值)
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清零 CNT
// 规范, 虽然已经停止了定时器, 但是为了防止之前的计数没有完成.
HAL_TIM_Base_Start_IT(&htim2); // 重启定时器
// 等待按键松开, 防止多次修改.
while(HAL_GPIO_ReadPin(GPIOA, KEY1_Pin) == GPIO_PIN_SET);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE BEGIN 4 */
/**
* @brief TIM 定时器更新中断回调函数
* @param htim 定时器句柄指针
* @retval 无
* @note 本函数由 HAL 库在定时器溢出时自动调用
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
// 判断是否是 TIM2 的中断
if (htim -> Instance == TIM2)
{
// 翻转 LED 电平
HAL_GPIO_TogglePin(GPIOA, LED_R_Pin|LED_G_Pin|LED_B_Pin);
}
}
/* USER CODE END 4 */
实验现象


按下按键 (KEY1), LED 灯在两种模式(100ms 闪烁 / 1000ms 闪烁)中切换.