2. HAL库PWM配置:时基单元配置、输出比较通道配置、PWM模式选择

好,咱们进入正题。PWM 配置,说白了就是告诉定时器三件事:多快跑一个周期、什么时候翻转电平、翻转成什么模式。这三件事分别对应时基单元、输出比较通道和 PWM 模式。我刚开始学的时候,总觉得寄存器多到记不住,后来用 HAL 库才发现,其实就几个结构体的事。

2.1 时基单元配置——定周期

时基单元是 PWM 的“心跳”。它决定了 PWM 信号的频率。你想想看,一个周期多长,全靠它说了算。

在 HAL 库中,时基配置通过 TIM_Base_InitTypeDef 结构体完成。核心参数就四个:

参数 作用 典型值
Prescaler 预分频系数,对时钟源分频 71(72MHz → 1MHz)
CounterMode 计数方向 TIM_COUNTERMODE_UP
Period 自动重装载值,决定周期 999(1kHz)
ClockDivision 时钟分频(用于滤波) TIM_CLOCKDIVISION_DIV1

这里有个公式,我建议你记在笔记本上:

PWM频率 = 定时器时钟 / (Prescaler + 1) / (Period + 1)

举个例子。STM32F4 的定时器时钟通常是 84MHz(APB1)或 168MHz(APB2)。我想要一个 1kHz 的 PWM:

  • Prescaler = 83 → 84MHz / 84 = 1MHz
  • Period = 999 → 1MHz / 1000 = 1kHz
我的习惯:先定 Prescaler 让计数时钟变成整数(比如 1MHz),再调 Period 得到目标频率。这样调试时心里有数。

2.2 输出比较通道配置——定占空比

时基定好了周期,接下来要告诉定时器:什么时候把输出脚拉高或拉低。这就是输出比较通道干的事。

每个定时器有 4 个通道(CH1~CH4),每个通道独立配置。核心结构体是 TIM_OC_InitTypeDef:

参数 含义 常用值
OCMode 输出比较模式 TIM_OCMODE_PWM1
Pulse 比较值,决定占空比 500(50% 占空比)
OCPolarity 输出极性 TIM_OCPOLARITY_HIGH
OCFastMode 快速模式 TIM_OCFASTMODE_DISABLE

占空比的计算很简单:

占空比 = Pulse / (Period + 1) × 100%

比如 Period = 999,Pulse = 500,占空比就是 50%。

我曾经踩过的坑:Pulse 的值不能大于 Period。否则输出会一直保持高电平,占空比变成 100%。调试时如果发现波形不对,先检查这两个值的关系。

2.3 PWM 模式选择——PWM1 vs PWM2

HAL 库支持两种 PWM 模式,区别在于电平翻转的时机:

模式 计数 < Pulse 计数 ≥ Pulse 典型应用
PWM1 有效电平 无效电平 绝大多数场景
PWM2 无效电平 有效电平 反相需求

说白了,PWM1 是“先高后低”,PWM2 是“先低后高”。我个人 99% 的情况都用 PWM1,除非硬件上需要反相。

2.4 完整配置流程

嗯,这里我把整个配置流程画成一张图,你一看就明白:

PWM 配置流程 1. 时钟使能 2. 时基配置 3. 通道配置 4. 选择 PWM 模式 5. 启动定时器 关键说明 1. 时钟使能: __HAL_RCC_TIMx_CLK_ENABLE() 2. 时基配置: Prescaler + Period 3. 通道配置: Pulse + 极性 4. PWM 模式: PWM1 或 PWM2 5. 启动: HAL_TIM_PWM_Start() 顺序不能乱! 先配置,再启动

2.5 代码示例——生成 1kHz、50% 占空比的 PWM

光说不练假把式。下面是一个完整的配置代码,我加了详细注释:

// 1. 定义句柄
TIM_HandleTypeDef htim2;

// 2. 时基配置
htim2.Instance = TIM2;
htim2.Init.Prescaler = 83;          // 84MHz / 84 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 999;            // 1MHz / 1000 = 1kHz
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim2);

// 3. 通道配置
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500;              // 50% 占空比
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFASTMODE_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);

// 4. 启动 PWM
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
调试小技巧:启动后如果没波形,先检查 GPIO 是否配置为复用功能(AF),并且复用号是否正确。我遇到过好几次 GPIO 没配对,折腾了半天。

2.6 避坑指南

  • Prescaler 和 Period 都是 16 位寄存器,最大值 65535。如果需要更低频率,可以考虑使用 32 位定时器(TIM2/TIM5)。
  • 修改占空比时,直接改 Pulse 值,然后调用 __HAL_TIM_SET_COMPARE(&htim, TIM_CHANNEL_x, newPulse)。不需要重新配置整个通道。
  • 多通道同步:如果多个通道需要同步输出,建议先配置所有通道,最后统一调用 HAL_TIM_PWM_Start。