7. PWM 输出
📝本节您将了解 PWM(脉宽调制)的工作原理,学习如何通过定时器的捕获/比较功能生成频率和占空比可调的 PWM 信号,并驱动天巧星板载蜂鸣器。
🏆本章目标
1️⃣ 理解 PWM 信号的频率、占空比等关键参数
2️⃣ 了解 MSPM0G3519 定时器的 PWM 输出机制
3️⃣ 通过 SysConfig 配置 PWM 输出
4️⃣ 实现可变占空比的 PWM 输出,驱动蜂鸣器发声
7.1 PWM 基本概念
PWM(Pulse Width Modulation,脉宽调制)是一种用数字信号(只有 0 和 1)来传递信息的调制方式。它不改变脉冲的幅度(电压始终是 0 或 3.3V),而是通过改变每个周期内高电平的持续时长——也就是占空比——来实现不同的控制效果。
打个比方:手电筒的开关只有"开"和"关",但如果按固定节奏快速拨动开关、并控制每次"开"的时长,从远处看手电筒就会呈现出不同的亮度。

核心参数
| 参数 | 定义 | 举例 |
|---|---|---|
| 周期(Period) | 一个完整的开→关→开循环的时间 | 蜂鸣器 1ms 周期 = 1000Hz 音调 |
| 频率(Frequency) | 每秒钟完成多少个周期 | LED 调光用 1kHz(人眼看不出闪烁) |
| 占空比(Duty Cycle) | 每个周期中"开"的时间占比 | 50% = 半开半关,亮度约一半 |

不同场景下,PWM 控制的"是什么"
PWM 不是只有一个套路——参数怎么调,取决于你控制的是什么东西:
| 场景 | 调什么 | 为什么 | 是否涉及 |
|---|---|---|---|
| LED 呼吸灯 | 只调占空比 | 频率固定(>100Hz 人眼看不出闪烁),占空比越大灯越亮 | 实验一 |
| 蜂鸣器发声 | 调频率,占空比锁 50% | 频率决定音调高低,占空比 50% 达到最大音量 | 实验二 |
| 舵机控制 | 调脉宽(绝对时间) | 0.5ms~2.5ms 的脉宽对应 0°~180°,频率固定 50Hz | 本章不涉及 |
| 电机调速 | 只调占空比 | 频率一般在 10kHz 以上(超出人耳,不啸叫) | 本章不涉及 |
占空比与效果对照
| 占空比 | 波形特征 | 等效电压(3.3V) | 典型效果 | 应用场景 |
|---|---|---|---|---|
| 0% | 始终低电平 | 0V | 完全关闭 | 待机、停止 |
| 25% | 窄脉冲 | 0.83V | LED 微亮 | LED 调光 |
| 50% | 对称方波 | 1.65V | 最大音量 | 蜂鸣器驱动 |
| 75% | 宽脉冲 | 2.48V | 中高速 | 电机调速 |
| 100% | 始终高电平 | 3.3V | 全速运行 | 全功率 |
7.2 MSPM0G3519 的 PWM 实现
PWM 输出是定时器捕获/比较(CC)通道的功能之一。工作原理:
- 定时器按设定周期循环计数
- 捕获/比较寄存器存储一个比较值
- 当计数值 < 比较值时输出高电平,≥ 比较值时输出低电平(或反之)
- 修改比较值即可改变占空比,修改周期值即可改变频率

PWM 频率和占空比计算
PWM频率 = 时钟源频率 / (预分频 × (周期值 + 1))
占空比 = 比较值 / (周期值 + 1) × 100%2
例如:时钟 32MHz,预分频 32,周期值 1000:
- PWM 频率 = 32000000 / (32 × (1000 + 1)) ≈ 999Hz ≈ 1kHz
- 比较值设为 500 → 占空比 ≈ 500/(1000+1) ≈ 50%
天巧星 PWM 引脚速查
MSPM0G3519 拥有丰富的定时器资源(TIMA0、TIMA1、TIMG0~TIMG14 等),大部分 GPIO 可通过 IOMUX 配置为 PWM 输出。下表仅列出本章涉及及板上已占用的 PWM 引脚:
| 定时器通道 | 引脚 | 用途 |
|---|---|---|
| TIMA1 CCP0 | PB26 | WS2812 RGB LED(板上已占用) |
| TIMG8 CCP0 | PA29 | 编码器 A(板上已占用) |
| TIMG8 CCP1 | PA30 / PB22 | 编码器 B(板上已占用)/ 板载 LED(本章实验一) |
| TIMG6 CCP1 | PB27 | 蜂鸣器(本章实验二) |
更多 PWM 引脚
如需使用其他定时器或通道,请在 SysConfig 中配置 PWM 实例时查看可选的引脚列表,或查阅 MSPM0G3519 数据手册的引脚属性表(IOMUX 章节)。
本实验使用 TIMG8 输出 PWM,通道 CCP1 映射到 PB22(天巧星板载 LED,无需外接)。蜂鸣器实验使用 TIMG6 CCP1(PB27)。
关于 PB22 板载 LED
PB22 板载 LED 是 active-low(低电平点亮)。因此占空比 = 0% 时 LED 最亮,占空比 = 100% 时 LED 熄灭——呼吸灯视觉效果不变,只是亮暗方向相反。
7.3 SysConfig 配置 PWM 输出
7.3.1 基础 PWM 配置
- 打开
.syscfg文件 - 左侧面板找到 PWM 选项卡(或在 TIMER 中选择 PWM 模式)
- 点击 ADD 添加 PWM 实例
配置参数:
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | PWM_0 | 实例名称 |
| Timer Clock Source | BUSCLK | 32MHz 总线时钟 |
| Timer Clock Divider | Divided by 1 | 不分频 |
| Timer Clock Prescale | 32 | 预分频(计数频率 = 32MHz / 32 = 1MHz) |
| Timer Peripheral | TIMG8 | 选择定时器 |
| PWM Mode | Edge-aligned Up Counting | 边沿对齐、向上计数 |
| PWM Period Count | 1000 | 周期计数值(PWM 频率 ≈ 1kHz) |
| PWM Channel(s) | PWM Channel 1 | 启用通道 1(CCP1) |
| Channel 1 Pin | PB22 | TIMG8 CCP1,板载 LED |
| Channel 1 Duty(%) | 50 | 占空比 50%(SysConfig 按百分比输入) |
| Initial Value | Low | 初始输出低电平 |
保存并编译。


PWM 频率选择指南
| 应用 | 推荐频率 | 原因 |
|---|---|---|
| LED 调光 | 1kHz ~ 10kHz | 高于人眼闪烁感知阈值(>100Hz) |
| 蜂鸣器 | 500Hz ~ 5kHz | 人耳可听范围内的音调 |
| 电机驱动 | 10kHz ~ 20kHz | 超出人耳听觉,减少电磁噪音 |
| 舵机控制 | 50Hz(20ms 周期) | 舵机协议要求 |
自动生成的宏
保存 SysConfig 后,工具会自动生成 ti_msp_dl_config.h。你在 SysConfig 中填入的 Name(如 PWM_0)会自动生成对应的宏(如 PWM_0_INST 指向 TIMG8 寄存器地址、DL_TIMER_CC_1_INDEX 指向通道 1),在代码中直接使用即可,无需手动定义。
7.4 PWM 相关函数
常用 PWM DriverLib 函数
设置占空比(比较值):
void DL_TimerG_setCaptureCompareValue(TIMER_Regs *timer, uint32_t value, DL_TIMER_CC_INDEX index);修改比较值即改变占空比。value 范围 0 ~ 周期值。
启动定时器:
void DL_TimerG_startCounter(TIMER_Regs *timer);停止定时器:
void DL_TimerG_stopCounter(TIMER_Regs *timer);设置周期值(修改频率):
void DL_TimerG_setLoadValue(TIMER_Regs *timer, uint32_t value);修改周期值可改变 PWM 频率。建议先停止定时器再修改,避免异常状态。
获取当前周期值:
uint32_t DL_TimerG_getLoadValue(TIMER_Regs *timer);返回当前配置的周期值,用于计算占空比比例。
7.5 实验一:LED 呼吸灯
关于 CPUCLK_FREQ
CPUCLK_FREQ 是 SysConfig 在 ti_msp_dl_config.h 中自动生成的宏,默认值为 32000000(32MHz)。delay_cycles(CPUCLK_FREQ / 1000 * N) 即延时 N 毫秒。delay_cycles 是 DriverLib 提供的阻塞延时函数。
利用 PWM 占空比渐变实现 LED 亮度的平滑过渡(呼吸效果)。
#include "ti_msp_dl_config.h"
int main(void)
{
SYSCFG_DL_init();
DL_TimerG_startCounter(PWM_0_INST);
uint32_t duty = 0;
int8_t direction = 1; // 1:递增, -1:递减
while (1) {
// 更新占空比
DL_TimerG_setCaptureCompareValue(PWM_0_INST, duty, DL_TIMER_CC_1_INDEX);
// 占空比递增/递减
duty += direction * 10;
// 到达边界则反向
if (duty >= 1000) {
duty = 1000;
direction = -1;
} else if (duty == 0) {
direction = 1;
}
delay_cycles(CPUCLK_FREQ / 1000 * 10); // 延时 10ms,控制呼吸速度
}
}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
运行效果:LED 亮度从亮到暗再从暗到亮循环变化,呈现呼吸灯效果。

原理说明
- 占空比从 0% 到 100% 渐变 → PWM 低电平时间减少 → LED 从最亮到灭(PB22 是 active-low)
- 占空比从 100% 到 0% 渐变 → PWM 低电平时间增加 → LED 从灭到最亮
- 每 10ms 改变一次占空比,100 步完成一次渐变 → 单向渐变时间约 1 秒
7.6 实验二:蜂鸣器驱动
实验一和实验二的关系
两个实验可以共存在同一个项目中——只需在 SysConfig 中再 ADD 一个 PWM 实例即可(TIMG8 和 TIMG6 互不冲突)。当然,你也可以为每个实验新建独立项目。
天巧星板载无源蜂鸣器接在 PB27(TIMG6 CCP1)。无源蜂鸣器需要外部提供方波驱动,改变频率即改变音调,占空比固定 50% 即可获得最大音量。
SysConfig 配置蜂鸣器 PWM
| 参数 | 设置值 | 说明 |
|---|---|---|
| Name | PWM_BUZZER | 实例名称 |
| Timer Clock Source | BUSCLK | 32MHz 总线时钟 |
| Timer Clock Divider | Divided by 1 | 不分频 |
| Timer Clock Prescale | 32 | 预分频(计数频率 = 32MHz / 32 = 1MHz) |
| Timer Peripheral | TIMG6 | 蜂鸣器对应定时器 |
| PWM Mode | Edge-aligned Up Counting | 边沿对齐、向上计数 |
| PWM Period Count | 2273 | 频率 ≈ 440Hz(标准音 A4) |
| PWM Channel(s) | PWM Channel 1 | 启用通道 1(CCP1) |
| Channel 1 Pin | PB27 | 蜂鸣器引脚 |
| Channel 1 Duty(%) | 50 | 占空比 50%(SysConfig 按百分比输入) |
| Initial Value | Low | 初始输出低电平 |
音调频率参考
计数频率 1MHz 下,Period(周期值)的计算公式:Period = round(1,000,000 / 目标频率)。例如 A4 = 440Hz → Period = round(1,000,000 / 440) = 2273。
| 音名 | 频率(Hz) | PWM Period(1MHz 计数频率) |
|---|---|---|
| C4(Do) | 262 | 3817 |
| D4(Re) | 294 | 3401 |
| E4(Mi) | 330 | 3030 |
| F4(Fa) | 349 | 2865 |
| G4(Sol) | 392 | 2551 |
| A4(La) | 440 | 2273 |
| B4(Si) | 494 | 2024 |
| C5(高Do) | 523 | 1912 |
播放简单音调序列
#include "ti_msp_dl_config.h"
// 音调频率对应的周期值(1MHz 计数频率)
#define NOTE_C4 3817
#define NOTE_D4 3401
#define NOTE_E4 3030
#define NOTE_F4 2865
#define NOTE_G4 2551
#define NOTE_A4 2273
#define NOTE_B4 2024
#define NOTE_C5 1912
#define NOTE_REST 0 // 休止符
// 设置蜂鸣器频率
void buzzer_set_freq(uint32_t period)
{
if (period == 0) {
// 休止:停止 PWM
DL_TimerG_stopCounter(PWM_BUZZER_INST);
} else {
DL_TimerG_setLoadValue(PWM_BUZZER_INST, period);
DL_TimerG_setCaptureCompareValue(PWM_BUZZER_INST, period / 2, DL_TIMER_CC_1_INDEX);
DL_TimerG_startCounter(PWM_BUZZER_INST);
}
}
// 播放音符(duration 单位 ms)
void buzzer_play_note(uint32_t period, uint32_t duration_ms)
{
buzzer_set_freq(period);
delay_cycles((uint32_t)(CPUCLK_FREQ / 1000) * duration_ms);
buzzer_set_freq(0); // 音符间短暂静音
delay_cycles((uint32_t)(CPUCLK_FREQ / 1000) * 50);
}
int main(void)
{
SYSCFG_DL_init();
// 播放 C 大调音阶
uint32_t melody[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4,
NOTE_G4, NOTE_A4, NOTE_B4, NOTE_C5};
uint8_t len = sizeof(melody) / sizeof(melody[0]);
while (1) {
for (uint8_t i = 0; i < len; i++) {
buzzer_play_note(melody[i], 400); // 每个音符 400ms
}
delay_cycles(CPUCLK_FREQ); // 播放完暂停 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
46
47
48
49
50
51
运行效果:蜂鸣器循环播放 C 大调音阶(Do Re Mi Fa Sol La Si Do)。
7.7 动态调节 PWM
PWM 的频率和占空比可在运行时动态修改:
// 修改占空比(不改变频率)
DL_TimerG_setCaptureCompareValue(PWM_0_INST, new_duty, DL_TIMER_CC_1_INDEX);
// 修改频率(需同时更新周期和占空比)
DL_TimerG_stopCounter(PWM_0_INST);
DL_TimerG_setLoadValue(PWM_0_INST, new_period);
DL_TimerG_setCaptureCompareValue(PWM_0_INST, new_period / 2, DL_TIMER_CC_1_INDEX);
DL_TimerG_startCounter(PWM_0_INST);2
3
4
5
6
7
8
注意
修改周期值(频率)时建议先停止定时器再修改,避免在计数过程中出现比较值 > 周期值的异常状态。修改占空比(比较值)可以在运行中直接修改,下个周期生效。
7.8 知识总结
| 内容 | 说明 |
|---|---|
| PWM 本质 | 定时器计数 + 比较输出的高低电平切换 |
| 频率控制 | 修改定时器周期值(Period) |
| 占空比控制 | 修改捕获比较值(Compare Value) |
| LED 调光 | 占空比越大 → LED 越亮 |
| 蜂鸣器 | 频率决定音调,占空比固定 50% |
| 天巧星蜂鸣器 | PB27,TIMG6 CCP1 |
掌握 PWM 输出后,你可以驱动蜂鸣器、实现 LED 调光、控制电机转速等。下一章将学习输入捕获——PWM 的"逆操作",测量外部信号的频率和占空比。