3. PWM基础代码生成与验证:HAL库PWM启动函数、占空比调节、逻辑分析仪抓波形
好,咱们进入实战环节。上一章我们把CubeMX配置讲透了,这一章直接上手写代码、调波形。我个人习惯是:配置完先别急着写业务逻辑,先把PWM输出搞出来,用逻辑分析仪看一眼波形再说。你想想看,如果波形都不对,后面调什么都是白搭。
3.1 HAL库PWM启动函数:到底该调哪个?
很多新手拿到HAL库,看到一堆PWM相关的函数就懵了。其实核心就三个:初始化、启动、调占空比。咱们一个一个说。
3.1.1 初始化函数
CubeMX生成代码后,MX_TIMx_Init() 已经帮你配好了时基和输出通道。这个函数在 main() 里会被自动调用,你基本不用动它。但我建议你打开看一眼,确认一下参数对不对。
// HAL库自动生成的初始化代码片段
static void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 72MHz / (71+1) = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999; // 1MHz / (999+1) = 1kHz
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);
}
3.1.2 启动函数:HAL_TIM_PWM_Start
初始化完了,怎么让PWM真正输出?答案是 HAL_TIM_PWM_Start()。这个函数有两个参数:定时器句柄和通道号。
// 启动PWM输出
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); // 启动通道1
这里有个坑——很多人只调了 HAL_TIM_PWM_Init(),忘了调 Start,结果示波器上啥也没有。嗯,我刚开始也犯过这个错。
HAL_TIM_PWM_Start() 必须在 HAL_TIM_PWM_Init() 之后调用。如果你在初始化之前就Start,定时器还没配置好,输出会异常甚至卡死。
3.2 占空比调节:CCR寄存器与__HAL_TIM_SET_COMPARE
PWM的核心参数就两个:频率和占空比。频率由 Prescaler 和 Period 决定,占空比则由 CCR(捕获/比较寄存器)控制。
3.2.1 CCR寄存器原理
说白了,CCR就是一个比较值。定时器从0往上数,数到Period就归零。在这个过程中,当计数值小于CCR时,输出高电平;大于等于CCR时,输出低电平。所以CCR的值直接决定了占空比。
| CCR值 | 占空比 | 说明 |
|---|---|---|
| 0 | 0% | 一直低电平 |
| Period/2 | 50% | 方波,最常用 |
| Period | 100% | 一直高电平 |
3.2.2 用HAL库调节占空比
HAL库提供了两个方法改CCR:
- 直接写寄存器:
TIM2->CCR1 = 500;—— 简单粗暴,但可读性差。 - 用HAL宏:
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, 500);—— 推荐,代码更清晰。
// 动态调节占空比的示例
void set_pwm_duty(uint16_t duty)
{
if(duty > 1000) duty = 1000; // 限幅,防止溢出
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, duty);
}
// 在主循环中渐变占空比
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
uint16_t duty = 0;
uint8_t dir = 1;
while (1)
{
set_pwm_duty(duty);
HAL_Delay(10);
if(dir) duty += 10;
else duty -= 10;
if(duty >= 1000) dir = 0;
if(duty == 0) dir = 1;
}
}
3.3 逻辑分析仪抓波形:验证你的PWM
代码写完了,怎么知道对不对?别靠猜,上逻辑分析仪。我建议每个做嵌入式的人手边都备一个便宜的8通道逻辑分析仪,几十块钱,但能帮你省下大把调试时间。
3.3.1 接线方法
逻辑分析仪的接线很简单:
- 通道0:接STM32的PWM输出引脚(比如PA0/TIM2_CH1)
- GND:接STM32的GND(必须共地!)
不共地的话,波形会乱飘,甚至完全抓不到。我见过有人折腾了一下午,最后发现是地线没接。
3.3.2 软件设置
我用的是Saleae Logic Analyzer(免费版就够用)。设置步骤:
- 采样率设到 24MHz 以上(你的PWM是1kHz,24MHz足够)
- 采样深度设 1M samples(抓个几秒没问题)
- 触发方式选 上升沿(这样每次抓到的波形起始点一致)
- 添加一个 PWM分析器,软件会自动算出频率和占空比
3.3.3 波形验证清单
抓到波形后,对照这个清单检查:
| 检查项 | 期望值 | 如果不对怎么办 |
|---|---|---|
| 频率 | 1kHz ± 5% | 检查Prescaler和Period |
| 占空比 | 与CCR设置一致 | 检查CCR值是否写对 |
| 波形是否稳定 | 无抖动、无毛刺 | 检查电源滤波、引脚是否虚焊 |
| 占空比渐变 | 平滑变化 | 检查循环逻辑、延时是否合理 |
3.4 本章知识体系
下面这张图把PWM从配置到验证的完整流程串起来了,你可以对照着理清思路。
3.5 常见问题与避坑
最后,我把这些年遇到的PWM相关坑总结一下,你遇到了可以直接对照排查。
现象:频率对,但占空比始终是50%或100%。
原因:CCR寄存器没写进去,或者写的位置不对。
解决:检查
__HAL_TIM_SET_COMPARE 是否在 Start 之后调用,且通道号是否正确。
现象:软件上啥也没有,或者全是噪声。
原因:地线没接,或者采样率太低。
解决:先确认GND连接,再把采样率调到24MHz以上。
现象:LED亮度跳变,或者电机转速抖动。
原因:CCR变化太快,或者
HAL_Delay 精度不够。解决:减小每次CCR的变化量,或者用定时器中断做渐变。