8. 输入捕获
📝本节您将了解输入捕获的工作原理,学习如何利用定时器的捕获功能测量外部信号的频率和占空比。
🏆本章目标
1️⃣ 理解输入捕获的工作原理
2️⃣ 了解频率和占空比的测量方法
3️⃣ 通过 SysConfig 配置定时器输入捕获
4️⃣ 实现外部 PWM 信号的频率和占空比测量
8.1 输入捕获概念
输入捕获是 PWM 输出的"逆操作"——PWM 是产生一个已知频率和占空比的信号,输入捕获则是测量一个未知信号的频率和占空比。
工作原理:当捕获引脚检测到指定边沿(上升沿或下降沿)时,硬件自动将当前定时器计数值锁存到捕获寄存器中。通过读取连续两次捕获的计数值之差,即可计算出信号的周期(进而得到频率)。
外部信号: ┌───┐ ┌───┐ ┌───┐
│ │ │ │ │ │
────┘ └───┘ └───┘ └───
↑ ↑ ↑
C1 C2 C3 ← 上升沿捕获时刻
信号周期 T = C2 - C1(计数值之差)
信号频率 f = 计数频率 / (C2 - C1)2
3
4
5
6
7
8
测量占空比
要测量占空比,需要同时捕获上升沿和下降沿:
┌───────┐ ┌───────┐
│ │ │ │
────┘ └───────┘ └────
↑ ↑ ↑
R1 F1 R2
周期 T = R2 - R1(两次上升沿之差)
高电平时间 = F1 - R1(上升沿到下降沿之差)
占空比 = (F1 - R1) / (R2 - R1) × 100%2
3
4
5
6
7
8
9
8.2 MSPM0G3519 输入捕获资源
MSPM0G3519 的 TIMG 和 TIMA 定时器均支持输入捕获功能。每个捕获/比较(CC)通道可配置为捕获模式,独立触发。
| 定时器 | 位宽 | 捕获通道数 | 适用场景 |
|---|---|---|---|
| TIMG0 | 16-bit | 2 | 基础频率测量 |
| TIMG7 | 16-bit | 2 | 基础频率测量 |
| TIMG8 | 32-bit | 2 | 高精度 / 长周期测量 |
| TIMA0 | 16-bit | 4 | 多通道同时捕获 |
| TIMA1 | 16-bit | 4 | 编码器 / 多通道 |
位宽选择
- 16-bit 定时器:最大计数 65535。如果计数频率为 1MHz,最大可测量周期为 65.535ms(最低频率约 15Hz)。
- 32-bit 定时器(TIMG8):最大计数 4294967295,可测量极低频率信号。
如果被测信号频率较低(< 15Hz),使用 TIMG8 或增大预分频。
天巧星编码器引脚
天巧星板载旋转编码器接在 TIMG8:
- PA29 → TIMG8 CCP0(编码器 A 相)
- PA30 → TIMG8 CCP1(编码器 B 相)
- PA31 → 编码器按键
本章实验使用其他空闲定时器通道进行输入捕获演示。
8.3 SysConfig 配置输入捕获
8.3.1 配置定时器为捕获模式
- 打开
.syscfg文件 - 左侧面板找到 CAPTURE 或 TIMER 选项卡
- 添加一个 Timer 实例,配置为捕获模式
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | CAPTURE_0 | 实例名称 |
| Timer Module | TIMG0 | 选择定时器 |
| Timer Mode | Capture | 捕获模式 |
| Timer Clock Source | BUSCLK | 32MHz |
| Timer Clock Prescaler | 32 | 预分频(计数频率 1MHz) |
| Capture Edge | Rising Edge | 上升沿捕获 |
| Capture Pin | 选择输入引脚 | 连接被测信号的引脚 |
| Interrupt Enable | Enable | 使能捕获中断 |
计数频率选择
计数频率决定了测量精度和量程:
- 计数频率越高 → 分辨率越高(测量越精确)→ 但量程越小(溢出越快)
- 计数频率越低 → 分辨率越低 → 但量程越大(可测低频信号)
1MHz 计数频率是一个平衡选择:分辨率 1μs,16-bit 定时器量程约 65ms(15Hz ~ 500kHz)。
保存并编译。
8.4 频率测量实验
8.4.1 测量原理
使用上升沿捕获。每次上升沿到来时,记录当前计数值。相邻两次捕获值之差即为信号周期(以计数值为单位)。
频率 = 计数频率 / (capture2 - capture1)8.4.2 实验代码
#include "ti_msp_dl_config.h"
#include <stdio.h>
volatile uint32_t capture_val1 = 0;
volatile uint32_t capture_val2 = 0;
volatile uint8_t capture_flag = 0;
volatile uint8_t capture_count = 0;
void uart_send_string(const char *str)
{
while (*str) {
while (DL_UART_Main_isBusy(UART_0_INST));
DL_UART_Main_transmitData(UART_0_INST, *str++);
}
}
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
DL_TimerG_startCounter(CAPTURE_0_INST);
char buf[64];
while (1) {
if (capture_flag) {
capture_flag = 0;
// 计算频率(计数频率 = 1MHz)
uint32_t period_count = capture_val2 - capture_val1;
if (period_count > 0) {
uint32_t freq = 1000000 / period_count; // Hz
sprintf(buf, "Freq: %lu Hz, Period: %lu us\r\n", freq, period_count);
uart_send_string(buf);
}
}
delay_cycles(3200000); // 100ms 输出一次
}
}
// 定时器捕获中断
void TIMG0_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(CAPTURE_0_INST)) {
case DL_TIMER_IIDX_CC0_DN: // CC0 捕获事件
if (capture_count == 0) {
capture_val1 = DL_TimerG_getCaptureCompareValue(
CAPTURE_0_INST, DL_TIMER_CC_0_INDEX);
capture_count = 1;
} else {
capture_val2 = DL_TimerG_getCaptureCompareValue(
CAPTURE_0_INST, DL_TIMER_CC_0_INDEX);
capture_count = 0;
capture_flag = 1;
}
break;
default:
break;
}
}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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
代码说明
- 第一次上升沿到来时保存计数值到
capture_val1 - 第二次上升沿到来时保存到
capture_val2,并设置capture_flag - 主循环检测 flag 后计算频率并通过串口输出
- 计数频率为 1MHz,所以周期计数值直接等于微秒数
8.4.3 测试方法
将天巧星的一个 PWM 输出引脚(如上一章配置的 PWM 输出)用杜邦线连接到捕获输入引脚,即可测量自身产生的 PWM 信号频率。
或使用信号发生器输出已知频率的方波,验证测量精度。
8.5 占空比测量实验
测量占空比需要同时捕获上升沿和下降沿。一种实现方式是在每次捕获中断中动态切换捕获边沿:
#include "ti_msp_dl_config.h"
#include <stdio.h>
volatile uint32_t rising_val = 0;
volatile uint32_t falling_val = 0;
volatile uint32_t period_val = 0;
volatile uint8_t measure_state = 0; // 0:等待上升沿1, 1:等待下降沿, 2:等待上升沿2
volatile uint8_t result_ready = 0;
void uart_send_string(const char *str)
{
while (*str) {
while (DL_UART_Main_isBusy(UART_0_INST));
DL_UART_Main_transmitData(UART_0_INST, *str++);
}
}
int main(void)
{
SYSCFG_DL_init();
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
DL_TimerG_startCounter(CAPTURE_0_INST);
char buf[64];
while (1) {
if (result_ready) {
result_ready = 0;
uint32_t high_time = falling_val - rising_val;
uint32_t total_period = period_val - rising_val;
if (total_period > 0) {
uint32_t freq = 1000000 / total_period;
uint32_t duty = high_time * 100 / total_period;
sprintf(buf, "Freq: %lu Hz, Duty: %lu%%\r\n", freq, duty);
uart_send_string(buf);
}
}
delay_cycles(3200000);
}
}
void TIMG0_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(CAPTURE_0_INST)) {
case DL_TIMER_IIDX_CC0_DN: {
uint32_t cap = DL_TimerG_getCaptureCompareValue(
CAPTURE_0_INST, DL_TIMER_CC_0_INDEX);
switch (measure_state) {
case 0: // 第一个上升沿
rising_val = cap;
// 切换为下降沿捕获
DL_TimerG_setCaptCompEdgeDetectMode(CAPTURE_0_INST,
DL_TIMER_CC_0_INDEX, DL_TIMER_CC_EDGE_DETECTION_FALLING);
measure_state = 1;
break;
case 1: // 下降沿
falling_val = cap;
// 切换回上升沿捕获
DL_TimerG_setCaptCompEdgeDetectMode(CAPTURE_0_INST,
DL_TIMER_CC_0_INDEX, DL_TIMER_CC_EDGE_DETECTION_RISING);
measure_state = 2;
break;
case 2: // 第二个上升沿
period_val = cap;
measure_state = 0;
result_ready = 1;
break;
}
break;
}
default:
break;
}
}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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
注意事项
- 捕获值可能发生溢出(计数器回绕),实际应用中需要处理溢出情况
- 高频信号测量时注意中断处理速度是否跟得上
- 低频信号测量时考虑使用 32-bit 定时器(TIMG8)避免溢出
- 动态切换边沿检测有一定延迟,极高频信号下可能引入误差
8.6 定时器溢出处理
当被测信号频率很低时,定时器可能在两次捕获之间发生溢出(计数器回绕到 0)。需要额外计数溢出次数:
volatile uint32_t overflow_count = 0;
// 在定时器溢出中断中
case DL_TIMER_IIDX_ZERO:
overflow_count++;
break;
// 计算实际周期时
uint32_t actual_period = (overflow_count * 65536) + capture_val2 - capture_val1;2
3
4
5
6
7
8
9
对于大多数实际应用(测量 50Hz 以上的信号),使用 1MHz 计数频率和 16-bit 定时器不会溢出。
8.7 知识总结
| 内容 | 说明 |
|---|---|
| 输入捕获本质 | 边沿触发时锁存定时器计数值 |
| 频率测量 | 两次同类型边沿的计数差 → 周期 → 频率 |
| 占空比测量 | 需捕获上升沿和下降沿,计算高电平占比 |
| 精度 | 取决于计数频率(1MHz → 精度 1μs) |
| 量程 | 取决于计数器位宽和预分频 |
| 溢出处理 | 低频信号需计数溢出次数 |
输入捕获使你能够精确测量外部信号参数。下一章将学习 ADC(模数转换),实现对模拟电压信号的数字化采集。