一、定时器分类

1
2
3
4
5
6
7
F103C8T6 定时器资源:

高级定时器:TIM1, TIM8 ← 互补 PWM + 死区(电机控制)
通用定时器:TIM2, TIM3, TIM4 ← PWM + 输入捕获 + 编码器
基本定时器:TIM6, TIM7 ← 仅定时中断(无引脚输出)

高级 > 通用 > 基本(功能逐级减少)
功能 高级 TIM1/8 通用 TIM2~4 基本 TIM6/7
定时中断
PWM 输出 ✅ 互补 ✅ 4 路
输入捕获
编码器接口
死区插入
刹车输入

二、定时器本质

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
定时器 = 计数器 + 自动重载寄存器

CK_CNT(时钟源)


┌──────────┐ ┌──────────┐
│ Prescaler│────→│ Counter │────→ 溢出事件(中断 / DMA)
│ PSC │ │ CNT │
└──────────┘ └────┬─────┘
│ 比较

┌──────────┐
│ AutoReload│
│ ARR │
└──────────┘

CNT 从 0 开始每个时钟脉冲 +1,计到 ARR 后归零(溢出)。

核心公式

1
2
3
4
5
6
7
8
9
10
定时器时钟 = 总线时钟 / (PSC + 1)
= 72MHz / (7199 + 1) = 10KHz

溢出频率 = 定时器时钟 / (ARR + 1)
= 10KHz / (9999 + 1) = 1Hz

溢出周期 = 1 / 溢出频率 = 1 秒

通用公式:
T = (PSC + 1) × (ARR + 1) / TIM_CLK

定时精度计算

1
2
3
4
5
6
7
8
9
10
11
12
例:要产生 50Hz(T = 20ms)
TIM_CLK = 72MHz

方案1: PSC=71, ARR=19999
T = (71+1) × (19999+1) / 72M = 20ms ✓

方案2: PSC=7199, ARR=199
T = (7199+1) × (199+1) / 72M = 20ms ✓

→ PSC 决定每个 tick 的分辨率
ARR 决定溢出的计数次数
调 PSC 改变精度,调 ARR 改变周期

三、基本定时(TIM6/TIM7)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
TIM_HandleTypeDef htim6;

/* 定时器初始化(仅定时中断,无外部引脚) */
void MX_TIM6_Init(void) {
__HAL_RCC_TIM6_CLK_ENABLE();

htim6.Instance = TIM6;
htim6.Init.Prescaler = 7199; // 72MHz / 7200 = 10KHz
htim6.Init.Period = 9999; // 10KHz / 10000 = 1Hz
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_Base_Init(&htim6);

HAL_NVIC_SetPriority(TIM6_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM6_IRQn);

HAL_TIM_Base_Start_IT(&htim6); // 启动
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM6) {
// 每秒执行一次
}
}

四、PWM 输出(输出比较)

原理

1
2
3
4
5
6
7
8
9
10
           ┌──── ARR=999 ────┐
CNT: 0 ───→ 0..999 ───→ 0 ───→

PWM1: ┌────────┐ ┌── CCR=750
│ 高电平 │ 低电平 │ 占空比 = 750/1000 = 75%
└────────┘ └──

PWM2: ┌───┐ ┌── CCR=200
│高 │ 低电平 │ 占空比 = 200/1000 = 20%
└───┘ └──

HAL 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
TIM_HandleTypeDef htim2;

void MX_TIM2_PWM_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM2_CLK_ENABLE();

htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 72M/72 = 1MHz
htim2.Init.Period = 999; // 1M/1000 = 1KHz PWM
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_PWM_Init(&htim2);

sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500; // 初始占空比 50%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);

HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // PA0 输出
}

/* 动态改占空比 */
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 750); // 75%

PWM 模式选择

模式 说明
PWM1 CNT < CCR → 有效电平,CNT ≥ CCR → 无效
PWM2 与 PWM1 相反
有效电平 OCPolarity 决定高或低
1
2
3
4
5
6
7
PWM1 + OCPolarity_HIGH:
CNT: 0 ──────→ CCR ──────→ ARR
高电平 低电平

PWM1 + OCPolarity_LOW:
CNT: 0 ──────→ CCR ──────→ ARR
低电平 高电平

五、输入捕获(测量脉宽/频率)

原理

1
2
3
4
5
6
7
8
9
10
11
12
输入信号 ──┐     ┌──────┐     ┌────
└─────┘ └─────┘

边沿触发 ─→ 硬件自动把 CNT 快照到 CCR 寄存器

┌──── 第1个上升沿:CCR1 = CNT(t1)
│ 第1个下降沿:CCR2 = CNT(t2)
│ 第2个上升沿:CCR3 = CNT(t3)

├─ 周期 = t3 - t1
├─ 高电平 = t2 - t1
└─ 频率 = TIM_CLK / 周期

测频率(超声波 HC-SR04 回波)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
TIM_HandleTypeDef htim3;
volatile uint32_t pulse_width = 0; // 脉宽,单位 μs
volatile uint8_t capture_done = 0;

void MX_TIM3_IC_Init(void) {
TIM_IC_InitTypeDef sConfigIC = {0};
__HAL_RCC_TIM3_CLK_ENABLE();

htim3.Instance = TIM3;
htim3.Init.Prescaler = 71; // 1MHz → 1μs 分辨率
htim3.Init.Period = 65535; // 最大 65ms 测量
HAL_TIM_IC_Init(&htim3);

sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
sConfigIC.ICFilter = 0;
HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1);

HAL_NVIC_SetPriority(TIM3_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM3_IRQn);

HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1);
}

/* 中断处理:上升沿清 CNT,下降沿读脉宽 */
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance != TIM3) return;

static uint32_t last_capture = 0;

if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin)) {
// 下降沿 → 计算脉宽
uint32_t now = HAL_TIM_ReadCapturedValue(&htim3, TIM_CHANNEL_1);
pulse_width = (now > last_capture) ? (now - last_capture) : (65535 - last_capture + now);
capture_done = 1;
} else {
// 上升沿 → 重置计数
__HAL_TIM_SET_COUNTER(&htim3, 0);
last_capture = 0;
}
}

/* 距离计算 */
float distance_cm = pulse_width / 58.0f; // 超声波: 58μs/cm

六、编码器模式(电机测速)

原理

1
2
3
4
5
6
7
8
9
10
11
编码器 A/B 相输出 90° 相位差的方波:

A ┌──┐ ┌──┐ ┌──┐
└──┘ └──┘ └──┘
B ┌──┐ ┌──┐ ┌──┐
└──┘ └──┘ └──┘

正向旋转:A 超前 B 90° → CNT 递增
反向旋转:B 超前 A 90° → CNT 递减

硬件自动处理,无需软件判断方向。

HAL 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
TIM_HandleTypeDef htim4;
volatile int32_t encoder_count = 0;

void MX_TIM4_Encoder_Init(void) {
TIM_Encoder_InitTypeDef sEncoder = {0};
__HAL_RCC_TIM4_CLK_ENABLE();

htim4.Instance = TIM4;
htim4.Init.Prescaler = 0; // 不分频
htim4.Init.Period = 65535; // 16 位自动重载
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
HAL_TIM_Base_Init(&htim4);

sEncoder.EncoderMode = TIM_ENCODERMODE_TI12; // A 和 B 都计数(×4 分辨率)
sEncoder.IC1Polarity = TIM_ICPOLARITY_RISING;
sEncoder.IC1Selection = TIM_ICSELECTION_DIRECTTI;
sEncoder.IC1Prescaler = TIM_ICPSC_DIV1;
sEncoder.IC1Filter = 0;
sEncoder.IC2Polarity = TIM_ICPOLARITY_RISING;
sEncoder.IC2Selection = TIM_ICSELECTION_DIRECTTI;
sEncoder.IC2Prescaler = TIM_ICPSC_DIV1;
sEncoder.IC2Filter = 0;
HAL_TIM_Encoder_Init(&htim4, &sEncoder);

HAL_TIM_Encoder_Start(&htim4, TIM_CHANNEL_ALL);
}

/* 读取速度和角度 */
void Read_Encoder(void) {
int32_t cnt = __HAL_TIM_GET_COUNTER(&htim4);

// 速度 = 增量 / 时间
int32_t delta = cnt - encoder_count;
encoder_count = cnt;

// 假设:500 线编码器 ×4 = 2000 脉冲/圈
float rpm = (float)delta / 2000.0f * 600.0f; // 10Hz 采样 → ×60s/0.1s
}

编码器模式选择

1
2
3
TIM_ENCODERMODE_TI1:  仅 A 相计数(×1 分辨率)
TIM_ENCODERMODE_TI2: 仅 B 相计数(×1 分辨率)
TIM_ENCODERMODE_TI12: A 和 B 都计数(×4 分辨率,推荐)

七、定时器联动(门控/触发)

主从模式:TIM2 触发 TIM3 启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* TIM2 溢出产生 TRGO 信号 → TIM3 以此为触发开始计数 */
void TIM2_Master_TIM3_Slave(void) {
TIM_MasterConfigTypeDef sMaster = {0};

// TIM2 主模式:溢出产生 TRGO
sMaster.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMaster.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMaster);

// TIM3 从模式:外部触发启动(ITR1=TIM2_TRGO)
TIM_SlaveConfigTypeDef sSlave = {0};
sSlave.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlave.InputTrigger = TIM_TS_ITR1;
HAL_TIM_SlaveConfigSynchro(&htim3, &sSlave);
}

八、CubeMX 定时器引脚速查

定时器 通道 1 通道 2 通道 3 通道 4
TIM1 PA8 PA9 PA10 PA11
TIM2 PA0 PA1 PA2 PA3
TIM3 PA6 PA7 PB0 PB1
TIM4 PB6 PB7 PB8 PB9

九、常见问题

现象 原因 解决
PWM 无输出 没调 HAL_TIM_PWM_Start 初始化后调用 Start
占空比不对 PSC/ARR 计算错误 TIM_CLK / (PSC+1) / (ARR+1) 反向验证
输入捕获读到 0 ICPrescaler 不对 确保 TIM_ICPSC_DIV1
编码器计数不增加 A/B 接反 交换 A/B,或看 CNT 是否负增长
定时中断频率不对 忘记 APB1 ×2 规则 TIM2~4 时钟 = PCLK1×2 = 72MHz
CCR 写入无效 未使能预装载 OCFastMode 或手动 TIM_GenerateEvent

十、定时器速算表(72MHz 输入)

目标频率 PSC ARR 实际频率
1Hz 7199 9999 1.000Hz
10Hz 7199 999 10.00Hz
50Hz 719 1999 50.00Hz
100Hz 7199 99 100.0Hz
1KHz 71 999 1.000KHz
10KHz 71 99 10.00KHz
50KHz 7 179 50.00KHz
100KHz 71 9 90.00KHz