8. 输入捕获
📝本节您将了解输入捕获的工作原理,学习如何利用定时器的捕获功能测量外部信号的频率和占空比。
🏆本章目标
1️⃣ 理解输入捕获的工作原理
2️⃣ 了解频率和占空比的测量方法
3️⃣ 通过 SysConfig 配置定时器输入捕获
4️⃣ 实现外部 PWM 信号的频率和占空比测量
8.1 输入捕获概念
输入捕获是 PWM 输出的"逆操作"——PWM 是产生一个已知频率和占空比的信号,输入捕获则是测量一个未知信号的频率和占空比。
工作原理:当捕获引脚检测到指定边沿(上升沿或下降沿)时,硬件自动将当前定时器计数值锁存到捕获寄存器中。通过读取连续两次捕获的计数值之差,即可计算出信号的周期,进而得到频率。

测量占空比
要测量占空比,需要同时捕获上升沿和下降沿:

8.2 MSPM0G3519 输入捕获资源
MSPM0G3519 的 TIMG 和 TIMA 定时器均支持输入捕获功能。每个捕获/比较(CC)通道可配置为捕获模式,独立触发。
| 定时器 | 位宽 | 捕获通道数 | 适用场景 |
|---|---|---|---|
| TIMG0 | 16-bit | 2 | 基础频率测量 |
| TIMG7 | 16-bit | 2 | 基础频率测量 |
| TIMG8 | 16-bit | 2 | 输入捕获(支持 QEI 正交编码器接口) |
| TIMG12 | 32-bit | 2 | 高精度 / 长周期测量 |
| TIMA0 | 16-bit | 4 | 多通道同时捕获 |
| TIMA1 | 16-bit | 2 | 编码器 / 多通道 |
位宽选择
- 16-bit 定时器:最大计数 65535。如果计数频率为 1MHz,最大可测量周期为 65.535ms(最低频率约 15 Hz)。
- 32-bit 定时器(TIMG12):最大计数 4294967295,可测量极低频率信号。
如果被测信号频率较低(< 15 Hz),建议使用 TIMG12(32-bit,量程大但无预分频),或在当前 16-bit 定时器上增大预分频值。
天巧星编码器引脚
天巧星板载旋转编码器接在 TIMG8:
- PA29 → TIMG8 CCP0(编码器 A 相)
- PA30 → TIMG8 CCP1(编码器 B 相)
- PA31 → 编码器按键
本章实验使用其他空闲定时器通道进行输入捕获演示。
8.3 SysConfig 配置输入捕获
8.3.1 配置定时器为捕获模式
- 打开
.syscfg文件 - 左侧面板找到 CAPTURE 或 TIMER 选项卡
- 添加一个 Timer 实例,配置为捕获模式
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | CAPTURE_0 | 实例名称 |
| Timer Peripheral | TIMG0 | 选择定时器 |
| Timer Mode | Capture | 捕获模式 |
| Timer Clock Source | BUSCLK | 32MHz |
| Timer Clock Prescale | 32 | 预分频(计数频率 1MHz) |
| Capture Edge | Rising Edge | 上升沿捕获 |
| Capture Pin | PB0 | 连接被测信号的引脚 |
| Interrupt Enable | Enable | 使能捕获中断 |


计数频率选择
计数频率决定了测量精度和量程:
- 计数频率越高 → 分辨率越高(测量越精确)→ 但量程越小(溢出越快)
- 计数频率越低 → 分辨率越低 → 但量程越大(可测低频信号)
1MHz 计数频率是一个平衡选择:分辨率 1μs,16-bit 定时器量程约 65ms(可测量范围约 15Hz ~ 500kHz)。
保存并编译。
8.4 频率测量实验
8.4.1 测量原理
MSPM0 的输入捕获在 EDGE_TIME 模式下,定时器向下计数(从周期值递减到 0)。每次捕获边沿到来时,硬件自动将当前计数值锁存到捕获寄存器。捕获值越小,说明从定时器复位后经过的时间越长。
通过每次捕获后立即复位定时器到周期值,下一次的"周期值 - 捕获值"即为两次边沿之间的计数值,即信号周期:
频率 = 计数频率 / (周期值 - 捕获值)以本章配置为例:计数频率 1MHz,周期值 65534,若捕获值为 64533,则信号周期 = 65534 - 64533 = 1001 个计数 = 1001μs,频率约为 999Hz。
EDGE_TIME 模式说明
EDGE_TIME 是 MSPM0 输入捕获的核心工作模式。在此模式下,定时器以递减方式从预设的周期值向下计数到 0 后自动重载。每次检测到指定边沿时,硬件自动将当前递减计数值锁存到捕获寄存器。由于定时器不断递减,周期值 - 捕获值 就是自上次复位以来经过的计数值。配合每次捕获后立即复位,即可实现"每次捕获值直接对应时间间隔"的效果。该模式因此得名 EDGE_TIME(边沿时间测量)。
8.4.2 实验代码
#include "ti_msp_dl_config.h"
#include <stdio.h>
volatile uint32_t capture_period = 0;
volatile uint8_t capture_flag = 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();
uart_send_string("Capture Freq Test\r\n");
DL_TimerG_startCounter(PWM_0_INST);
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
DL_TimerG_startCounter(CAPTURE_0_INST);
char buf[64];
while (1) {
if (capture_flag) {
NVIC_DisableIRQ(CAPTURE_0_INST_INT_IRQN);
capture_flag = 0;
uint32_t period = capture_period;
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
if (period > 0) {
uint32_t freq = 1000000 / period;
sprintf(buf, "Freq: %u Hz, Period: %u us\r\n",
freq, period);
uart_send_string(buf);
}
}
delay_cycles(CPUCLK_FREQ / 10);
}
}
void TIMG0_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(CAPTURE_0_INST)) {
case DL_TIMER_IIDX_CC0_DN:
// 周期值 - 捕获值 = 经过的计数值(即信号周期)
capture_period = 65534 - DL_TimerG_getCaptureCompareValue(
CAPTURE_0_INST, DL_TIMER_CC_0_INDEX);
// 复位计数器,准备下一次测量
DL_TimerG_setTimerCount(CAPTURE_0_INST, 65534);
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
代码说明
- EDGE_TIME 模式下,定时器从 65534 递减计数。捕获事件锁存的是当前递减计数值。
- 每次捕获后,ISR(Interrupt Service Routine,中断服务函数)立即将定时器复位到 65534,这样下一次捕获得到的"周期值 - 捕获值"就是两次边沿之间的精确时间间隔。
DL_TIMER_IIDX_CC0_DN表示 CC0 通道的捕获事件中断。名称中的DN代表定时器向下计数模式下的捕获锁存事件。- 主循环通过
NVIC_DisableIRQ/NVIC_EnableIRQ(CMSIS 中断开关函数)保护共享变量,避免 ISR 更新变量时主循环读到不完整的数据(临界区保护)。 delay_cycles(CPUCLK_FREQ / 10)约 100ms,控制串口输出速度。delay_cycles()是 MSPM0 SDK 提供的 CPU 周期级延时函数(参数 = CPU 周期数,32MHz 下CPUCLK_FREQ / 10= 3,200,000 周期)。
8.4.3 测量精度说明
捕获的精度受以下因素影响:
| 因素 | 影响 | 典型误差 |
|---|---|---|
| 时钟源精度 | BUSCLK 由内部振荡器产生,存在固有偏差 | ±2% 以内 |
| ISR 延迟 | 中断响应 + 寄存器读写会影响复位时机 | 1~2 个计数(约 1~2μs) |
| 计数量化 | 匹配度取决于信号周期与计数周期的整除关系 | ±1 个计数 |
实际测试数据(用板载 PWM 自测):
- 输入 1kHz → 测量值约 997~1003 Hz(误差 < 1%)
- 输入 10kHz → 测量值约 10309 Hz(误差约 3%)
10kHz 时误差增大是因为 ISR 延迟在 100μs 周期中的占比(约 3μs)高于 1kHz 时在 1000μs 周期中的占比。每次捕获后 ISR 需要先读取捕获寄存器、计算差值、再复位定时器,这一过程约需数微秒,导致实际复位时机略晚于边沿。如需更高精度,可在捕获 ISR 中只做最少操作(置标志位),将计算放到主循环,或使用更高计数频率降低量化误差。但在入门实验中,该精度已满足学习需求。
8.4.4 测试方法
本实验在代码中同时配置了 PWM 输出(PWM_0)和输入捕获(CAPTURE_0)。将天巧星的 PWM 输出引脚用杜邦线连接到捕获输入引脚,即可测量自身产生的 PWM 信号频率。
SysConfig 关键配置
PWM_0(输出信号源):
- 定时器:TIMG7,时钟分频 32(1MHz),计数周期 1000(输出约 1kHz),占空比 50%
- 引脚:PA17
CAPTURE_0(输入捕获):
- 定时器:TIMG0,时钟分频 32(1MHz),周期 65.535ms
- 捕获边沿:上升沿,引脚:PB0
- 中断:使能 CC0_DN
接线:用杜邦线连接 PA17(PWM 输出)→ PB0(捕获输入)。
也可使用信号发生器输出已知频率的方波,验证测量精度。
8.5 占空比测量实验
测量占空比需要在同一个捕获通道上依次捕获上升沿和下降沿。通过在 ISR 中动态切换捕获边沿,利用状态机依次获取高电平时间和低电平时间。
每次边沿捕获后复位定时器,三次捕获即可测得一个完整周期的频率和占空比。
8.5.1 实验代码
#include "ti_msp_dl_config.h"
#include <stdio.h>
volatile uint32_t high_time = 0; // 高电平时间(微秒)
volatile uint32_t low_time = 0; // 低电平时间(微秒)
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();
uart_send_string("Capture Duty Test\r\n");
uart_send_string("Connect PA17 -> PB0 with wire\r\n");
DL_TimerG_startCounter(PWM_0_INST);
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
DL_TimerG_startCounter(CAPTURE_0_INST);
char buf[64];
while (1) {
if (result_ready) {
NVIC_DisableIRQ(CAPTURE_0_INST_INT_IRQN);
result_ready = 0;
uint32_t ht = high_time;
uint32_t lt = low_time;
NVIC_EnableIRQ(CAPTURE_0_INST_INT_IRQN);
uint32_t period = ht + lt; // 周期 = 高 + 低
if (period > 0) {
uint32_t freq = 1000000 / period;
uint32_t duty = ht * 100 / period;
sprintf(buf, "Freq: %u Hz, Duty: %u%%, "
"High: %u us, Period: %u us\r\n",
freq, duty, ht, period);
uart_send_string(buf);
}
}
delay_cycles(CPUCLK_FREQ / 10);
}
}
void TIMG0_IRQHandler(void)
{
// 状态: 0=初始(等第一个上升沿), 1=等下降沿, 2=等上升沿
// 周期: 0 → 1 → 2 → 1 → 2 → …(首轮后跳过状态0)
static uint8_t state = 0;
switch (DL_TimerG_getPendingInterrupt(CAPTURE_0_INST)) {
case DL_TIMER_IIDX_CC0_DN: {
// 1. 读取捕获值(递减计数值 → 转为经过的时间)
uint32_t cap = 65534 - DL_TimerG_getCaptureCompareValue(
CAPTURE_0_INST, DL_TIMER_CC_0_INDEX);
// 2. 根据当前状态,决定本次捕获值对应什么
if (state == 1) high_time = cap; // 下降沿 → 高电平时间
if (state == 2) { low_time = cap; // 上升沿 → 低电平时间
result_ready = 1; } // → 通知主循环
// 3. 切换边沿 + 复位定时器(所有分支的共同操作)
DL_Timer_setCaptureCompareCtl(CAPTURE_0_INST,
DL_TIMER_CC_MODE_CAPTURE,
(state == 1) ? DL_TIMER_CC_CCOND_TRIG_RISE // 刚测完高电平 → 等上升沿
: DL_TIMER_CC_CCOND_TRIG_FALL, // 否则 → 等下降沿
DL_TIMER_CC_0_INDEX);
DL_TimerG_setTimerCount(CAPTURE_0_INST, 65534);
// 4. 推进状态:0→1, 1→2, 2→1(跳过0,循环往复)
state = (state == 2) ? 1 : state + 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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
代码说明
ISR 逻辑分为四步,每步一行注释:
| 步骤 | 说明 |
|---|---|
| 1. 读取 | 获取捕获值,65534 - cap 转为经过的微秒数 |
| 2. 分类 | 状态 1(下降沿到来)→ 存入高电平时间;状态 2(上升沿到来)→ 存入低电平时间并通知主循环 |
| 3. 切换 | 先切换边沿、再复位定时器(避免错过新边沿)。状态 1 后等上升沿,否则等下降沿 |
| 4. 推进 | 0 → 1 → 2 → 1 → 2 → …,首轮之后跳过初始化状态直接循环 |
首次捕获(状态 0)不计算、不输出——仅完成"切换为下降沿 + 复位"的准备工作,之后进入正常的 1→2 循环。
接线和 SysConfig 配置
与 8.4 节相同:
- 杜邦线连接 PA17(PWM 输出)→ PB0(捕获输入)
- PWM 配置:TIMG7,1MHz 计数,周期 1000,占空比 50%
- 捕获配置:TIMG0,1MHz 计数,周期 65.535ms,上升沿
注意事项
- 动态切换边沿检测需要调用底层 API
DL_Timer_setCaptureCompareCtl,该函数直接操作捕获控制寄存器 - 高频信号测量时 ISR 执行时间可能影响精度,详见 8.4.3 精度说明
- 低频信号测量时考虑使用 32-bit 定时器(TIMG12)避免溢出
8.6 知识总结
| 内容 | 说明 |
|---|---|
| 输入捕获本质 | 边沿触发时锁存定时器计数值 |
| 计数方向 | MSPM0 的 EDGE_TIME 模式为递减计数(周期值 → 0) |
| 频率测量 | 每次捕获后复位定时器,捕获值对应的差值即为周期 |
| 占空比测量 | 动态切换捕获边沿(上升/下降),用状态机依次测得高/低电平时间 |
| 精度 | 取决于计数频率(1MHz → 分辨率 1μs),受时钟精度和 ISR 延迟影响 |
| 量程 | 取决于计数器位宽和预分频,16-bit 定时器可测约 15Hz ~ 500kHz |
输入捕获使你能够精确测量外部信号参数。下一章将学习 ADC(模数转换),实现对模拟电压信号的数字化采集。