6. 定时器
📝本节您将了解定时器的工作原理,学习 MSPM0G3519 的定时器资源分类,掌握通用定时器(TIMG)的周期定时中断配置方法。
🏆本章目标
1️⃣ 理解定时器的计数原理和基本参数
2️⃣ 了解 MSPM0G3519 的 TIMG 和 TIMA 定时器资源
3️⃣ 通过 SysConfig 配置定时器周期中断
4️⃣ 实现定时器中断驱动 LED 闪烁
6.1 定时器基本概念
定时器是 MCU 内部的硬件计数器模块。它在时钟信号的驱动下自动递增(或递减)计数,当计数值达到预设目标时产生事件(如中断)。
与 delay_cycles 软件延时相比,定时器的优势在于:
定时器核心参数
| 参数 | 说明 |
|---|---|
| 时钟源 | 驱动计数器的时钟信号(通常为 MCLK 或其分频) |
| 预分频器(Prescaler) | 对时钟源分频后再驱动计数器,降低计数频率 |
| 计数位宽 | 计数器的最大值(16位 = 0~65535,32位 = 0~4294967295) |
| 重载值(Period) | 计数器的目标值,到达后自动重载并触发事件 |
| 计数模式 | 向上计数、向下计数、上下计数 |
定时器工作流程
五个环节串联成一个完整的定时流水线:
时钟源 ──→ 预分频器 ──→ 计数器 ──→ 比较匹配 ──→ 事件输出
│ │ │ │ │
32MHz ÷32=1MHz 0→999 CNT==999? 中断/PWM/触发2
3
- 时钟源:来自系统时钟 MCLK(如 32MHz),提供基础计数节拍
- 预分频器:把高频时钟降速。比如 32MHz ÷ 32 = 1MHz,换算为时间就是每 1μs 计数器加 1
- 计数器(CNT):按降速后的节拍从 0 开始递增
- 比较匹配:当计数器计数的值 CNT 达到设置的重载值(Period)时,产生匹配信号
- 事件输出:匹配信号可触发中断、翻转引脚、启动 ADC 等操作

三种计数模式
| 模式 | 计数轨迹 | 适用场景 |
|---|---|---|
| 向上计数 | 0 → 1 → 2 → … → Period → 0(循环) | 周期定时、边沿对齐 PWM |
| 向下计数 | Period → … → 2 → 1 → 0 → Period(循环) | 某些 PWM 场景 |
| 上下计数 | 0 → 1 -> … → Period → … -> 2 → 1 → 0(循环) | 中心对齐 PWM(电机控制) |

定时时间计算
定时时间 = (重载值 + 1) × 预分频值 / 时钟源频率例如:时钟源 32MHz,预分频 32,重载值 999:
定时时间 = (999 + 1) × 32 / 32,000,000 = 1ms
+1是因为计数器从 0 开始数,0~999 共 1000 个节拍。
6.2 MSPM0G3519 定时器资源
MSPM0G3519 拥有多个定时器,分为两大类:
通用定时器(TIMG)
| 定时器 | 位宽 | 捕获/比较通道 | 典型用途 |
|---|---|---|---|
| TIMG0 | 16-bit | 2 通道 | 周期定时、基础 PWM |
| TIMG6 | 16-bit | 2 通道 | 周期定时、PWM(蜂鸣器) |
| TIMG7 | 16-bit | 2 通道 | 周期定时、PWM |
| TIMG8 | 16-bit | 2 通道 | 长周期定时、输入捕获、QEI正交编码器 |
| TIMG9 | 16-bit | 2 通道 | 长周期定时、输入捕获、QEI正交编码器 |
| TIMG12 | 16-bit | 2 通道 | 周期定时 |
| TIMG14 | 16-bit | 4 通道 | 长周期定时、输入捕获 |
高级控制定时器(TIMA)
| 定时器 | 位宽 | 捕获/比较通道 | 典型用途 |
|---|---|---|---|
| TIMA0 | 16-bit | 4 通道 | PWM(带死区)、电机控制 |
| TIMA1 | 16-bit | 2 通道 | PWM(WS2812)、编码器 |
主要区别:
- TIMG:通用定时器,支持基本定时、PWM 输出、输入捕获
- TIMA:高级定时器,在 TIMG 基础上增加死区插入、故障检测、互补输出、正交编码器接口等功能
天巧星板载占用
- TIMA1 CCP0(PB26)— WS2812 RGB LED
- TIMG6 CCP1(PB27)— 蜂鸣器 PWM
- TIMG8 CCP0/CCP1(PA29/PA30)— 旋转编码器 QEI
选择定时器时注意避开已被板载外设占用的通道。
6.3 SysConfig 配置定时器周期中断
本实验使用 TIMG0 配置 500ms 周期中断,在中断中翻转 LED。
6.3.1 添加定时器
- 打开
.syscfg文件 - 左侧面板找到 TIMER 选项卡
- 点击 ADD 添加一个 Timer 实例

6.3.2 配置参数
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | TIMER_0 | 实例名称 |
| Timer Module | TIMG0 | 选择 TIMG0 |
| Timer Mode | Periodic | 周期模式(自动重载) |
| Timer Clock Source | BUSCLK | 使用总线时钟(32MHz) |
| Timer Clock Prescaler | 32 | 预分频 32(计数频率 = 1MHz) |
| Timer Period | 500000 | 计数 500000 次 = 500ms |
| Interrupt Enable | Enable | 使能溢出中断 |
定时时间计算验证
- 时钟源:BUSCLK = 32MHz
- 预分频后计数频率:32MHz ÷ 32 = 1MHz(每计一次 = 1μs)
- 重载值 500000:500000 × 1μs = 500ms ✓
如果需要 1 秒定时:将 Timer Period 改为 1000000。 如果需要 100ms 定时:将 Timer Period 改为 100000。
6.3.3 同时配置 LED GPIO
确保 .syscfg 中已配置 PB22 GPIO 输出(参照第 2 章 LED 配置)。
保存并编译。
6.4 定时器中断实验
6.4.1 LED 定时翻转
#include "ti_msp_dl_config.h"
int main(void)
{
SYSCFG_DL_init();
// 使能 TIMG0 中断
NVIC_EnableIRQ(TIMER_0_INST_INT_IRQN);
// 启动定时器
DL_TimerG_startCounter(TIMER_0_INST);
while (1) {
// 主循环空闲,LED 翻转由定时器中断完成
__WFI();
}
}
// TIMG0 中断服务函数
void TIMG0_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
case DL_TIMER_IIDX_ZERO:
// 定时器计数到零(周期到达),翻转 LED
DL_GPIO_togglePins(GPIO_LED_PORT, GPIO_LED_PIN_PIN);
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
代码说明
DL_TimerG_startCounter(TIMER_0_INST)
启动定时器计数。SysConfig 的 SYSCFG_DL_init() 只完成定时器的参数配置,并不会自动启动计数,需要手动调用 start 函数。
DL_TIMER_IIDX_ZERO
定时器向下计数到零时的中断标志。在周期模式下,计数器到零后自动重载继续计数,每个周期触发一次此中断。
TIMG0_IRQHandler
TIMG0 的中断服务函数名(固定,不可修改)。SysConfig 生成的宏 TIMER_0_INST_INT_IRQN 对应此中断号。
6.4.2 编译下载验证
编译下载后,LED 以精确的 500ms 间隔闪烁——不受主循环中其他代码的影响。即使在 while(1) 中加入其他耗时操作,LED 闪烁频率不变。
6.5 定时器驱动多任务
定时器中断的真正价值在于:CPU 不被延时阻塞,可以同时处理多个任务。
以下示例展示:定时器中断翻转 LED,主循环同时通过串口发送数据。
#include "ti_msp_dl_config.h"
#include <stdio.h>
volatile uint32_t tick_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(TIMER_0_INST_INT_IRQN);
NVIC_EnableIRQ(UART_0_INT_IRQN);
DL_TimerG_startCounter(TIMER_0_INST);
char buf[32];
while (1) {
// 主循环每 2 秒通过串口报告定时器中断次数
sprintf(buf, "Tick: %lu\r\n", tick_count);
uart_send_string(buf);
delay_cycles(64000000); // 约 2 秒延时
}
}
void TIMG0_IRQHandler(void)
{
switch (DL_TimerG_getPendingInterrupt(TIMER_0_INST)) {
case DL_TIMER_IIDX_ZERO:
DL_GPIO_togglePins(GPIO_LED_PORT, GPIO_LED_PIN_PIN);
tick_count++;
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
运行结果:LED 精确 500ms 闪烁不受影响,同时串口每 2 秒输出一次当前 tick 计数值。
6.6 定时器常用函数
| 函数 | 功能 |
|---|---|
DL_TimerG_startCounter(inst) | 启动定时器计数 |
DL_TimerG_stopCounter(inst) | 停止定时器计数 |
DL_TimerG_getTimerCount(inst) | 读取当前计数值 |
DL_TimerG_getPendingInterrupt(inst) | 获取挂起中断类型并清除标志 |
DL_TimerG_setTimerCount(inst, val) | 设置计数器初始值 |
注意事项
- 定时器启动前确保
SYSCFG_DL_init()已执行完成 - 修改定时器参数(周期、预分频)应在停止状态下进行
- 16-bit 定时器最大计数值为 65535,如果预分频后仍不够长,使用 32-bit 的 TIMG8
volatile修饰 ISR 中修改的全局变量
6.7 定时周期快速参考
以下为 32MHz 时钟源、不同预分频下可实现的定时范围(16-bit 定时器,最大 65535):
| 预分频 | 计数频率 | 最小定时 | 最大定时 |
|---|---|---|---|
| 1 | 32MHz | 31.25ns | 2.048ms |
| 8 | 4MHz | 250ns | 16.384ms |
| 32 | 1MHz | 1μs | 65.535ms |
| 128 | 250kHz | 4μs | 262.14ms |
| 256 | 125kHz | 8μs | 524.28ms |
如果需要更长的定时(如 1 秒),有两种方式:
- 使用 32-bit 定时器 TIMG8(最大计数 4294967295)
- 在中断中用软件计数器累加(如 500ms 中断 × 2 次 = 1 秒)
掌握了定时器后,你已经拥有了精确的时间控制能力。下一章将学习 PWM 输出,利用定时器的捕获/比较功能生成可控的方波信号。