3. 时钟与延时
📝本节您将了解 MSPM0G3519 的时钟系统架构,学习如何配置系统主频,掌握常用延时方法的原理和使用方式。
🏆本章目标
1️⃣ 理解 MSPM0G3519 时钟树结构和各时钟源特点
2️⃣ 掌握默认 32MHz 和 80MHz 主频的配置方法
3️⃣ 掌握 delay_cycles、自适应宏、SysTick 等延时方法及选择依据
4️⃣ 通过 LED 闪烁实验验证时钟频率与延时的关系
3.1 时钟系统概述
时钟是微控制器运行的心跳——所有外设的工作、CPU 的指令执行、通信接口的波特率计算,都依赖系统时钟提供精确的时间基准。时钟频率越高,CPU 单位时间内执行的指令越多,处理速度越快,但功耗也相应增加。
MSPM0G3519 提供多种时钟源和灵活的时钟分配机制,开发者可根据性能和功耗需求选择合适的配置方案。
3.2 MSPM0G3519 时钟源
MSPM0G3519 内部集成了以下时钟源:
SYSOSC — 系统内部振荡器
- 频率:32MHz(默认)
- 特点:上电即可用,无需外部元件,免配置
- 精度:±2%(常温),适合大多数通用场景
- 用途:默认系统主时钟源,适合 GPIO、定时器、UART(低波特率)等外设
LFOSC — 低频内部振荡器
- 频率:32.768kHz
- 特点:低功耗时钟源,用于低功耗定时唤醒
- 用途:看门狗定时器、低功耗模式下的时钟源
HFXT — 高频外部晶振
- 频率:天巧星板载 40MHz 晶振(PA5 / PA6)
- 特点:精度极高(ppm 级),需配合 PLL 使用可达 80MHz
- 用途:需要精确时钟基准的场景(高速 UART、USB、WS2812 等)
LFXT — 低频外部晶振
- 频率:32.768kHz(如板载)
- 特点:高精度低频时钟
- 用途:RTC 实时时钟、精确低功耗定时
3.3 时钟树结构
MSPM0G3519 的时钟树可简化为以下路径:

关键概念:
| 时钟信号 | 说明 |
|---|---|
| CPUCLK | CPU 执行频率,决定指令执行速度 |
| MCLK | 主要外设时钟(UART、SPI、I2C、Timer 等) |
| ULPCLK | 低功耗外设时钟,MCLK 的分频 |
| LFCLK | 低频时钟,用于看门狗和 RTC |
3.4 默认时钟配置(32MHz)
MSPM0G3519 上电后默认使用 SYSOSC 作为系统时钟源,主频 32MHz。SysConfig 中默认配置即为此状态,无需额外操作。
默认状态下:
- CPUCLK = 32MHz
- MCLK = 32MHz
- 适合大多数入门实验(GPIO、LED、按键、基础串口)
验证方法:编译后查看生成的头文件中 CPUCLK_FREQ 的值:
// ti_msp_dl_config.h 中应看到:
#define CPUCLK_FREQ 320000002

3.5 配置 80MHz 主频(HFXT + PLL)
某些应用场景需要更高的主频(如 WS2812 时序、高速 PWM、大量数据处理)。天巧星板载 40MHz HFXT 晶振,配合 PLL 可将系统主频提升到 80MHz。
SysConfig 配置步骤
- 打开
.syscfg文件 - 在左侧面板找到 SYSCTL(系统控制)模块
- 勾选 User Clock Tree(使能时钟树配置)
点击查看截图 — 使能 User Clock Tree

配置时钟树各节点:
- HFXT:Enable = true,Input Frequency = 40MHz
点击查看截图 — 时钟树 HFXT 配置
SysConfig 时钟树 — HFXT 配置
- EXHFMUX:选择 XTAL 作为 输入,用来输出 HFCLK
- SYSPLLMUX:选择 HFCLK 作为 PLL 输入
点击查看截图 — 时钟树 SYSPLLMUX 配置
SysConfig 时钟树 — SYSPLLMUX 配置
- PLL 配置:PLL PDIV:/1 -> PLL QDIV:x4 -> CLK0_DIV:/2 -> 得到SYSPLL0 为 80MHz。
点击查看截图 — 时钟树 PLL 参数配置
SysConfig 时钟树 — PLL 参数配置
- HSCLKMUX:选择 SYSPLL0 作为高速时钟源
- ULPCLK最高40MHZ,必须限制最高40MHz。
点击查看截图 — 时钟树 ULPCLK 频率限制
SysConfig 时钟树 — ULPCLK 频率限制
保存配置
⚠️时钟配置关键注意事项
必须勾选 "User Clock Tree"(即 SYSCTL.clockTreeEn = true)!
这是一个常见陷阱:配置了时钟树各节点参数后,如果忘记启用时钟树配置,SysConfig 不会报错、编译也能通过、烧录也正常——但系统会静默回退到 32MHz。依赖精确频率的功能(WS2812 全白异常、PWM 频率偏差、高波特率乱码)会莫名其妙地出错。
3.6 延时方法
嵌入式开发中经常需要延时操作。MSPM0 提供了几种不同的延时方式,适用于不同精度和场景需求。
3.6.1 delay_cycles — CPU 忙等延时
delay_cycles(n) 是 MSPM0 SDK 提供的内联延时函数,让 CPU 空转 n 个时钟周期。
计算公式:
延时时间 = n / CPUCLK_FREQ
即:n = 延时秒数 × CPUCLK_FREQ2
3
| 延时时间 | delay_cycles | 计算 |
|---|---|---|
| 1ms | 32000 | 0.001 × 32000000 |
| 10ms | 320000 | 0.01 × 32000000 |
| 100ms | 3200000 | 0.1 × 32000000 |
| 500ms | 16000000 | 0.5 × 32000000 |
| 1s | 32000000 | 1 × 32000000 |
| 延时时间 | delay_cycles | 计算 |
|---|---|---|
| 1ms | 80000 | 0.001 × 80000000 |
| 10ms | 800000 | 0.01 × 80000000 |
| 100ms | 8000000 | 0.1 × 80000000 |
| 500ms | 40000000 | 0.5 × 80000000 |
| 1s | 80000000 | 1 × 80000000 |
注意
delay_cycles为阻塞延时,CPU 在延时期间无法响应中断或执行其他任务- 适合简单的短延时(消抖、协议时序),不适合需要精确长定时的场景
- 精确定时建议使用硬件定时器(见第 6 章)
3.6.2 自适应延时宏
为了让延时代码在不同主频下都能正确工作,推荐使用 CPUCLK_FREQ 宏进行计算:
#include "ti_msp_dl_config.h"
// 自适应延时宏,单位毫秒
#define DELAY_MS(ms) delay_cycles((uint32_t)(CPUCLK_FREQ / 1000) * (ms))
int main(void)
{
SYSCFG_DL_init();
while (1) {
DL_GPIO_togglePins(GPIO_LED_PORT, GPIO_LED_PIN_PIN);
DELAY_MS(500); // 无论 32MHz 还是 80MHz 都是 500ms
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
这种写法的好处是:当你修改主频配置时,延时时间自动适配,无需逐个修改 delay_cycles 的参数。
3.6.3 自定义延时函数
对于非精确延时场景,也可以用循环实现简单的延时函数:
/**
* @brief 非精确毫秒延时函数
* @param ms 延时毫秒数
* @note 实际延时受编译优化级别影响,仅适合对精度要求不高的场景
*/
void delay_ms(uint32_t ms)
{
while (ms--) {
delay_cycles(CPUCLK_FREQ / 1000);
}
}2
3
4
5
6
7
8
9
10
11
3.6.4 SysTick 滴答定时器
SysTick 是 Cortex-M0+ 内核内置的 24 位向下计数器,所有 MSPM0 芯片都带有这个定时器,方便代码在不同器件间移植。它使用 MCLK 作为时钟源(默认 32MHz),支持自动重装载和中断。
与 delay_cycles 依赖 CPU 指令计数不同,SysTick 使用独立的硬件计数器,定时精度不受编译优化和指令排布的影响,适合需要精确毫秒级延时的场景。
SysConfig 配置
- 打开
.syscfg文件,在左侧面板找到 SYSTICK 选项卡,点击 ADD 添加滴答定时器 - 配置计数值:MCLK 默认 32MHz,每计一个数耗时 1/32000000 秒。要实现 1ms 定时,计数值设为 32000(32000 × 1/32000000 = 1ms)
- 勾选 Enable SysTick Interrupt(开启中断)
- 勾选 Enable SysTick And Start Counting,开启滴答定时器计数。

核心代码模式
基于 SysTick 的毫秒延时遵循以下固定模式:
volatile unsigned int delay_times = 0;
// 阻塞等待:设置计数值后轮询,直到中断将其减到 0
void delay_ms(unsigned int ms) {
delay_times = ms;
while (delay_times != 0);
}
// SysTick 中断服务函数(函数名固定为 SysTick_Handler)
void SysTick_Handler(void) {
if (delay_times != 0) {
delay_times--;
}
}2
3
4
5
6
7
8
9
10
11
12
13
14
工作原理:
delay_ms(500)将delay_times设为 500- SysTick 每 1ms 触发一次中断,
SysTick_Handler将delay_times减 1 - 当
delay_times减到 0,delay_ms退出等待,延时结束
volatile关键字:告诉编译器不要优化该变量,因为它的值会在中断中被异步修改。如果去掉volatile,编译器可能将while (delay_times != 0)优化成死循环。
完整可运行代码见 3.7 实验 4,该实验在 80MHz 主频下完整展示了 SysTick 延时方案。
3.7 实验:LED 闪烁 — 时钟与延时综合验证
用一颗 LED 贯穿全部延时方法,通过 4 个实验逐层理解时钟频率与延时的关系。
实验 1:32MHz + delay_cycles — 基准 500ms
使用默认主频和固定参数,得到 500ms 的 LED 闪烁。
#include "ti_msp_dl_config.h"
int main(void)
{
SYSCFG_DL_init();
while (1) {
DL_GPIO_togglePins(LED_PORT, LED_B22_PIN);
delay_cycles(16000000); // 32MHz 下约 500ms
}
}2
3
4
5
6
7
8
9
10
11
实验 2:80MHz + delay_cycles — 频率变化,延时变短
SysConfig 配置 80MHz 后,同样参数,LED 闪烁明显变快(约 200ms)。
#include "ti_msp_dl_config.h"
int main(void)
{
SYSCFG_DL_init();
while (1) {
DL_GPIO_togglePins(LED_PORT, LED_B22_PIN);
delay_cycles(16000000); // 80MHz 下约 200ms,闪烁频率翻倍
}
}2
3
4
5
6
7
8
9
10
11
结论:delay_cycles 依赖 CPU 频率——同样的参数在不同主频下产生不同的延时。
实验 3:80MHz + DELAY_MS 自适应宏 — 频率无关
使用 CPUCLK_FREQ 的自适应写法,恢复正确的 500ms 闪烁。
#include "ti_msp_dl_config.h"
#define DELAY_MS(ms) delay_cycles((uint32_t)(CPUCLK_FREQ / 1000) * (ms))
int main(void)
{
SYSCFG_DL_init();
while (1) {
DL_GPIO_togglePins(LED_PORT, LED_B22_PIN);
DELAY_MS(500); // 无需手动调整,自适应主频
}
}2
3
4
5
6
7
8
9
10
11
12
13
结论:自适应宏解决了 delay_cycles 的频率依赖——切换主频后延时自动正确。
实验 4:80MHz + SysTick delay_ms — 硬件定时器精确控制
SysConfig 中启用 SYSTICK(计数值 40000,使能中断),使用硬件 delay_ms()。
#include "ti_msp_dl_config.h"
volatile unsigned int delay_times = 0;
void delay_ms(unsigned int ms)
{
delay_times = ms;
while (delay_times != 0);
}
int main(void)
{
SYSCFG_DL_init();
while (1) {
DL_GPIO_togglePins(LED_PORT, LED_B22_PIN);
delay_ms(500);
}
}
void SysTick_Handler(void)
{
if (delay_times != 0) delay_times--;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
结论:SysTick 使用独立硬件计数器,不受编译优化影响,定时精度最稳定。
四个实验对比
| 实验 | 主频 | 延时方法 | LED 闪烁 | 关键发现 |
|---|---|---|---|---|
| 实验 1 | 32MHz | delay_cycles(16M) | 约 500ms | 基准对照 |
| 实验 2 | 80MHz | delay_cycles(16M) | 约 200ms | 参数不变,延时随频率缩放 |
| 实验 3 | 80MHz | DELAY_MS(500) | 500ms | 自适应宏恢复正确时间 |
| 实验 4 | 80MHz | delay_ms(500) | 500ms | 硬件定时器,最高精度 |
3.8 延时方法选择建议
如何选择延时方法?
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 按键消抖(10~20ms) | delay_cycles | 简单直接,阻塞时间短 |
| LED 闪烁(100ms~1s) | DELAY_MS 自适应宏 | 切换主频自动适配,推荐首选 |
| 非精确长延时 | SysTick delay_ms | 基于硬件,精度比空循环高 |
| 精确周期任务 | 硬件定时器中断 | 不占用 CPU,精度高(第 6 章) |
| 通信协议时序(μs 级) | delay_cycles | 需要精确的短延时 |
| 低功耗定时唤醒 | RTC 或低功耗定时器 | CPU 可进入睡眠 |
至此,你已经掌握了 MSPM0G3519 的时钟系统配置和常用延时方法。下一章将学习外部中断,实现按键触发的事件驱动编程。