STM32 PWM 输出全解析:从舵机控制到电机调速与LED调光
👁 2 阅读 · 2026-08-14 · STM32 PWM
PWM(脉冲宽度调制)是嵌入式系统中最常用的输出手段之一,通过调节占空比和频率,可以精确控制舵机角度、电机转速和LED亮度。本文以STM32定时器为核心,深入讲解PWM的工作原理、定时器配置方法,并给出基于HAL库和标准库的完整代码示例,帮助开发者快速掌握PWM应用的核心技巧。
## 1. PWM 原理:为什么能控制这么多设备?
PWM 信号本质上是固定频率的方波,通过改变高电平在一个周期内的占比(即**占空比**)来传递信息。对于不同负载,PWM 的作用机制略有不同:
- **舵机**:舵机内部电路将 PWM 的脉宽(高电平时间)映射为转角。典型周期为 20ms,脉宽 0.5ms~2.5ms 对应 0°~180°。
- **直流电机**:PWM 的平均电压决定了电机两端有效电压,从而控制转速。配合 H 桥电路,还可实现正反转。
- **LED**:人眼感知亮度与平均功率近似线性,高频 PWM 调光无闪烁,且比线性调压效率更高。
STM32 定时器可以自动产生 PWM 波形,无需 CPU 干预,非常适合实时控制。
## 2. STM32 定时器 PWM 工作机制
STM32 的通用定时器(TIM2~TIM5)和高级定时器(TIM1、TIM8)均具备 PWM 生成功能。其核心原理如下:
- **计数寄存器 CNT**:从 0 计数到自动重载值 ARR,然后清零重新计数。
- **比较寄存器 CCRx**:当 CNT < CCRx 时,输出高电平;当 CNT >= CCRx 时,输出低电平(PWM 模式 1)。
- **占空比 = CCRx / (ARR+1)**,频率 = 定时器时钟 / ((ARR+1) * (PSC+1))。
通过配置 ARR 和 PSC 可设定频率,通过修改 CCRx 即可调节占空比。
## 3. 硬件连接与配置步骤
### 3.1 硬件准备
- STM32F103 开发板(或其他系列)
- 舵机(SG90)连接 PA8(TIM1_CH1)
- 直流电机驱动模块(L298N)连接 PA6(TIM3_CH1)
- LED 串联电阻连接 PA7(TIM3_CH2)
### 3.2 配置步骤(以 HAL 库为例)
1. 启用定时器时钟,并配置 GPIO 为复用推挽输出。
2. 设置定时器时基:PSC 和 ARR 决定频率。
3. 配置 PWM 通道:模式、极性、初始占空比。
4. 使能 PWM 输出,并启动定时器。
5. 修改 CCRx 寄存器或使用 `__HAL_TIM_SET_COMPARE` 来更新占空比。
## 4. 完整代码示例(HAL 库)
以下代码同时演示舵机、电机和 LED 的控制。使用 TIM1_CH1(PA8)驱动舵机,TIM3_CH1(PA6)驱动电机,TIM3_CH2(PA7)驱动 LED。
```c
/* 定时器句柄 */
TIM_HandleTypeDef htim1, htim3;
void MX_TIM1_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM1_CLK_ENABLE();
htim1.Instance = TIM1;
htim1.Init.Prescaler = 7199; // 72MHz/7200 = 10kHz
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 199; // 10kHz/200 = 50Hz(20ms周期)
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim1);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 15; // 初始占空比 15/200=7.5%(对应90°)
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
}
void MX_TIM3_Init(void) {
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM3_CLK_ENABLE();
htim3.Instance = TIM3;
htim3.Init.Prescaler = 71; // 72MHz/72 = 1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 999; // 1MHz/1000 = 1kHz
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
/* 通道1:电机 */
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始停止
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
/* 通道2:LED */
sConfigOC.Pulse = 500; // 初始50%亮度
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
}
/* GPIO 复用配置 */
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* PA8: TIM1_CH1, PA6: TIM3_CH1, PA7: TIM3_CH2 */
GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_6 | GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
/* 应用:设置舵机角度(0~180) */
void set_servo_angle(uint8_t angle) {
uint32_t pulse = 10 + (angle * 10) / 180; // 脉宽:0.5ms~2.5ms,按10kHz计数,即5~25
__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, pulse);
}
/* 应用:设置电机转速(-100~100,负值需配合H桥) */
void set_motor_speed(int16_t speed) {
if (speed >= 0) {
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, speed * 10); // 0~1000
}
// 如需反转,通过GPIO控制H桥方向引脚
}
/* 应用:设置LED亮度(0~255) */
void set_led_brightness(uint8_t brightness) {
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, brightness * 4); // 0~1020
}
int main(void) {
HAL_Init();
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM3_Init();
set_servo_angle(90);
set_motor_speed(50);
set_led_brightness(128);
while (1) {
// 动态调节示例:呼吸灯
for (int i = 0; i <= 255; i++) {
set_led_brightness(i);
HAL_Delay(5);
}
for (int i = 255; i >= 0; i--) {
set_led_brightness(i);
HAL_Delay(5);
}
}
}
```
## 5. 关键细节与注意事项
- **频率选择**:舵机必须 50Hz(周期20ms),否则会抖动或烧毁。电机一般 1kHz~20kHz(高频率可降低噪音),LED 调光建议 >1kHz 以避免闪烁。
- **占空比计算**:先确定 ARR,再根据需要的脉宽或百分比计算 CCR。例如舵机:ARR=199,PSC=7199,计数时钟10kHz,则 0.5ms = 5,2.5ms = 25。
- **高级定时器注意**:TIM1/TIM8 默认输出极性可能相反,且需要使能主输出 `__HAL_TIM_MOE_ENABLE(&htim1)`。如果使用 HAL_PWM_Start 则已包含。
- **死区与互补输出**:若控制 H 桥或电机驱动,需注意上下桥臂的互补 PWM 和死区插入,防止直通短路。
- **动态调参**:修改 ARR 会改变频率,建议只修改 CCR 调整占空比。若需可变频率,使用 DMA 或更新事件中断。
- **标准库移植**:将 HAL 的 `__HAL_TIM_SET_COMPARE` 替换为 `TIM_SetCompare1(TIMx, value)` 即可,初始化部分需手动配置寄存器或参考标准库例程。
## 6. 总结
STM32 的 PWM 功能是连接数字世界与模拟世界的桥梁。理解定时器时基与比较寄存器之间的关系,是配置任何 PWM 应用的基础。通过本文的示例,你可以轻松拓展到多通道舵机控制板、无刷电机 ESC、RGB LED 调光等场景。建议动手在开发板上验证,并将逻辑分析和示波器测量作为调试利器,才能深刻掌握 PWM 的精髓。