STM32模拟量控制核心:PWM技术全解析,从原理到电机调速实战

一、PWM 是什么?用数字信号实现 “模拟控制” 的魔法

在嵌入式系统中,很多外设需要 “连续可调的控制信号”—— 比如让 LED 亮度渐变、让电机转速平滑变化、让舵机旋转到指定角度。但 STM32 作为数字芯片,只能输出高低两种电平(0 和 1),如何用数字信号实现 “模拟量控制”?答案就是PWM(Pulse Width Modulation,脉冲宽度调制)

PWM 的核心原理是 “高频脉冲的占空比调节”:通过周期性输出高低电平,控制高电平在一个周期内的占比(占空比),从而改变信号的 “平均电压”。举个直观的例子:

  • 占空比 100%:持续输出高电平,平均电压 = 电源电压(如 3.3V),LED 最亮;

  • 占空比 50%:高低电平各占一半时间,平均电压 = 1.65V,LED 亮度中等;

  • 占空比 0%:持续输出低电平,平均电压 = 0V,LED 熄灭。

由于 PWM 频率通常远高于人眼或电机的响应速度(如 1kHz 以上),外设会 “感知” 到平均电压的变化,从而实现平滑的模拟量控制。这就像快速开关水龙头:虽然开关是离散的,但水流的平均大小可通过开关时间占比调节。

STM32 的 PWM 功能主要由通用定时器(TIM2-TIM5)高级定时器(TIM1、TIM8) 实现,其中通用定时器可输出 4 路独立 PWM,高级定时器支持带死区控制的互补 PWM,满足从简单调光到复杂电机驱动的各类需求。

二、PWM 的核心参数与 STM32 实现原理

要精准控制 PWM 信号,必须先理解其三大核心参数,以及 STM32 定时器如何生成 PWM 波形。

1. PWM 三大核心参数
参数名称定义对控制效果的影响典型取值范围
频率(Frequency)每秒输出的 PWM 周期数(f=1/T)频率过低会导致外设抖动(如 LED 闪烁、电机异响);频率过高会增加功耗100Hz~100kHz
占空比(Duty Cycle)一个周期内高电平持续时间占比直接决定平均电压,是控制外设状态的核心参数(如亮度、转速)0%~100%
分辨率(Resolution)占空比可调节的最小精度分辨率越高,控制越平滑(如 10 位分辨率支持 1024 级调节)8 位(256 级)~16 位(65536 级)
2. STM32 PWM 生成原理(基于定时器)

STM32 的 PWM 本质是 “定时器输出比较功能的应用”,其生成过程依赖定时器的四大核心组件(时钟源、预分频器、计数器、比较寄存器),具体流程如下:

  1. 时钟源与预分频:定时器时钟经预分频器分频后,得到计数时钟(如 72MHz 时钟经 7199 分频,得到 10kHz 计数时钟)。

  2. 计数器计数:计数器从 0 开始,每收到一个计数时钟就加 1(向上计数模式),直到达到 “自动重装载寄存器(ARR)” 的值后清零,完成一个周期。

  3. 比较匹配:计数器的值实时与 “比较寄存器(CCR)” 的值对比:

  • 当计数器 < CCR 时:PWM 输出高电平(PWM 模式 1);

  • 当计数器 ≥ CCR 时:PWM 输出低电平。

  1. 波形输出:通过重复上述过程,定时器的 PWM 输出引脚(如 TIM2_CH1 对应 PA0)就会产生连续的 PWM 波形。

关键公式推导

  • PWM 周期 T = (预分频器值 + 1) × (ARR 值 + 1) / 定时器时钟频率

  • PWM 频率 f = 定时器时钟频率 / [(预分频器值 + 1) × (ARR 值 + 1)]

  • 占空比 = (CCR 值 + 1) / (ARR 值 + 1) × 100%

  • 分辨率 = ARR 值 + 1(ARR 越大,分辨率越高)

示例:定时器时钟 72MHz,预分频器 = 71,ARR=999,CCR=499

  • 频率 f = 72000000 / [(71+1)×(999+1)] = 72000000 / 72000 = 1000Hz(1kHz)

  • 占空比 = (499+1)/(999+1)×100% = 50%

  • 分辨率 = 1000 级(支持 0.1% 精度调节)

3. 两种常用 PWM 模式

STM32 定时器支持多种 PWM 模式,新手需重点掌握以下两种:

  • PWM 模式 1:向上计数时,计数器 < CCR 输出高电平,≥ CCR 输出低电平(最常用,高电平优先);

  • PWM 模式 2:向上计数时,计数器 < CCR 输出低电平,≥ CCR 输出高电平(低电平优先,适用于特定反相控制场景)。

三、STM32 PWM 输出的硬件基础:引脚与定时器对应关系

PWM 信号需通过特定的 GPIO 引脚输出,这些引脚是定时器的 “复用功能引脚”,不同定时器通道对应固定的 GPIO 引脚(以 STM32F103C8T6 为例):

定时器通道 1(CH1)通道 2(CH2)通道 3(CH3)通道 4(CH4)
TIM1PA8PA9PA10PA11
TIM2PA0PA1PA2PA3
TIM3PA6PA7PB0PB1
TIM4PB6PB7PB8PB9

关键注意事项

  1. 同一引脚可复用为不同定时器的通道(如 PA0 可作 TIM2_CH1 或 GPIO),需通过 CubeMX 配置为 “复用推挽输出”;

  2. 高级定时器(TIM1、TIM8)的引脚支持互补 PWM 输出(如 TIM1_CH1 对应 PA8,互补通道 TIM1_CH1N 对应 PB13),适用于电机 H 桥驱动。

四、PWM 配置实操:从基础调光到高级调速

以 STM32F103C8T6 为例,分别演示 “LED 亮度渐变(基础 PWM)”“舵机角度控制(精准 PWM)”“直流电机调速(PWM 驱动)” 三大实战场景,覆盖 PWM 开发的核心要点。

1. 实战 1:LED 亮度渐变(TIM2_CH1 输出 PWM)
(1)硬件连接
  • PA0(TIM2_CH1):接 LED 阳极(串联 1kΩ 限流电阻,阴极接地)

  • 电源:3.3V(STM32 引脚输出电压)

(2)CubeMX 配置步骤
  1. 时钟配置:配置 APB1 总线时钟为 36MHz,TIM2 定时器时钟 = APB1 时钟 ×2=72MHz。

  2. 定时器配置

  • 选择 TIM2,在 “Mode” 中勾选 “PWM Generation CH1”;

  • 配置 “Parameter Settings”:

    • Prescaler(预分频器):71(72MHz/72=1MHz 计数时钟);

    • Counter Period(ARR):999(1MHz/1000=1kHz PWM 频率);

    • PWM Mode:PWM Mode 1;

    • Pulse(CCR 初始值):0(初始占空比 0%,LED 熄灭);

    • Auto-reload Preload:Enable(允许 ARR 自动重装载)。

  1. GPIO 配置:PA0 自动设为 “Alternate Function Push-Pull”(复用推挽输出)。

  2. 生成代码

(3)核心代码
// 定时器PWM初始化(CubeMX自动生成,tim.c)

TIM\_HandleTypeDef htim2;

TIM\_OC\_InitTypeDef sConfigOC = {0};

void MX\_TIM2\_Init(void) {

 TIM\_ClockConfigTypeDef sClockSourceConfig = {0};

 TIM\_MasterConfigTypeDef sMasterConfig = {0};

 htim2.Instance = TIM2;

 htim2.Init.Prescaler = 71;

 htim2.Init.CounterMode = TIM\_COUNTERMODE\_UP;

 htim2.Init.Period = 999;

 htim2.Init.ClockDivision = TIM\_CLOCKDIVISION\_DIV1;

 htim2.Init.AutoReloadPreload = TIM\_AUTORELOAD\_PRELOAD\_ENABLE;

 if (HAL\_TIM\_Base\_Init(\&htim2) != HAL\_OK) Error\_Handler();



 sClockSourceConfig.ClockSource = TIM\_CLOCKSOURCE\_INTERNAL;

 if (HAL\_TIM\_ConfigClockSource(\&htim2, \&sClockSourceConfig) != HAL\_OK) Error\_Handler();



 if (HAL\_TIM\_PWM\_Init(\&htim2) != HAL\_OK) Error\_Handler();



 sMasterConfig.MasterOutputTrigger = TIM\_TRGO\_RESET;

 sMasterConfig.MasterSlaveMode = TIM\_MASTERSLAVEMODE\_DISABLE;

 if (HAL\_TIMEx\_MasterConfigSynchronization(\&htim2, \&sMasterConfig) != HAL\_OK) Error\_Handler();



 sConfigOC.OCMode = TIM\_OCMODE\_PWM1;

 sConfigOC.Pulse = 0; // 初始CCR值

 sConfigOC.OCPolarity = TIM\_OCPOLARITY\_HIGH; // 高电平有效

 sConfigOC.OCFastMode = TIM\_OCFAST\_DISABLE;

 if (HAL\_TIM\_PWM\_ConfigChannel(\&htim2, \&sConfigOC, TIM\_CHANNEL\_1) != HAL\_OK) Error\_Handler();

}

// main函数实现亮度渐变

int main(void) {

 HAL\_Init();

 SystemClock\_Config();

 MX\_TIM2\_Init();



 HAL\_TIM\_PWM\_Start(\&htim2, TIM\_CHANNEL\_1); // 启动PWM输出

 uint16\_t ccr\_val = 0;

 uint8\_t dir = 1; // 1=亮度增加,0=亮度降低

 while (1) {

   HAL\_Delay(10); // 控制渐变速度

   if (dir) {

     ccr\_val++;

     if (ccr\_val >= 1000) dir = 0; // 占空比达100%反转

   } else {

     ccr\_val--;

     if (ccr\_val <= 0) dir = 1; // 占空比达0%反转

   }

   // 更新CCR值(两种方式)

   // 方式1:HAL库函数

   // TIM\_OC\_InitTypeDef sConfigOC = {0};

   // sConfigOC.Pulse = ccr\_val;

   // HAL\_TIM\_PWM\_ConfigChannel(\&htim2, \&sConfigOC, TIM\_CHANNEL\_1);

   // HAL\_TIM\_PWM\_Start(\&htim2, TIM\_CHANNEL\_1);

   // 方式2:直接操作寄存器(更高效)

   TIM2->CCR1 = ccr\_val;

 }

}
2. 实战 2:舵机角度控制(50Hz 标准 PWM)
(1)舵机工作原理

舵机是典型的 “PWM 受控设备”,其角度由 50Hz(周期 20ms)的 PWM 信号占空比决定:

  • 占空比 2.5%(0.5ms 高电平):对应 0°;

  • 占空比 7.5%(1.5ms 高电平):对应 90°(中位);

  • 占空比 12.5%(2.5ms 高电平):对应 180°。

(2)硬件连接
  • PA6(TIM3_CH1):接舵机信号引脚(通常为橙色 / 黄色线)

  • 舵机电源:5V(舵机电流较大,需独立供电,与 STM32 共地)

(3)CubeMX 配置要点
  • 定时器选择 TIM3,PWM 频率 50Hz:

    预分频器 = 7199(72MHz/7200=10kHz 计数时钟),

    ARR=199(10kHz/200=50Hz,周期 20ms)。

  • 初始 CCR 值 = 99(1.5ms 高电平,对应 90°)。

(4)核心控制代码
// 舵机角度控制函数(0°\~180°)

void Servo\_Set\_Angle(uint8\_t angle) {

 if (angle > 180) angle = 180;

 // 角度转CCR值:0°→10(0.5ms),180°→50(2.5ms),线性映射

 uint16\_t ccr\_val = (angle \* 40 / 180) + 10;

 TIM3->CCR1 = ccr\_val;

}

// main函数测试

int main(void) {

 HAL\_Init();

 SystemClock\_Config();

 MX\_TIM3\_Init(); // 配置50Hz PWM

 HAL\_TIM\_PWM\_Start(\&htim3, TIM\_CHANNEL\_1);

 while (1) {

   Servo\_Set\_Angle(0);   // 转到0°

   HAL\_Delay(1000);

   Servo\_Set\_Angle(90);  // 转到90°

   HAL\_Delay(1000);

   Servo\_Set\_Angle(180); // 转到180°

   HAL\_Delay(1000);

 }

}
3. 实战 3:直流电机调速(PWM+H 桥驱动)
(1)硬件设计

STM32 引脚输出的 PWM 电流(最大 25mA)无法直接驱动电机,需通过 H 桥驱动模块(如 L298N、TB6612)放大电流:

  • PA0(TIM2_CH1):接 L298N 的 ENA(电机 A 使能端);

  • PA1、PA2:接 L298N 的 IN1、IN2(控制电机转向);

  • 电机:接 L298N 的 OUT1、OUT2。

(2)核心控制逻辑
  • 转向控制:通过 GPIO 设置 IN1、IN2 电平(IN1=1、IN2=0→正转;IN1=0、IN2=1→反转);

  • 转速控制:通过调节 ENA 引脚的 PWM 占空比(占空比越高,转速越快)。

(3)核心代码
// 电机初始化(GPIO+PWM)

void Motor\_Init(void) {

 // PA1、PA2配置为推挽输出

 GPIO\_InitTypeDef GPIO\_InitStruct = {0};

 \_\_HAL\_RCC\_GPIOA\_CLK\_ENABLE();

 GPIO\_InitStruct.Pin = GPIO\_PIN\_1|GPIO\_PIN\_2;

 GPIO\_InitStruct.Mode = GPIO\_MODE\_OUTPUT\_PP;

 GPIO\_InitStruct.Pull = GPIO\_NOPULL;

 GPIO\_InitStruct.Speed = GPIO\_SPEED\_FREQ\_LOW;

 HAL\_GPIO\_Init(GPIOA, \&GPIO\_InitStruct);



 MX\_TIM2\_Init(); // 配置10kHz PWM

 HAL\_TIM\_PWM\_Start(\&htim2, TIM\_CHANNEL\_1);

}

// 电机控制函数(dir:1=正转,0=反转;speed:0\~100=占空比%)

void Motor\_Control(uint8\_t dir, uint8\_t speed) {

 if (speed > 100) speed = 100;

 uint16\_t ccr\_val = (speed \* 1000) / 100; // 1000级分辨率



 // 转向控制

 if (dir) {

   HAL\_GPIO\_WritePin(GPIOA, GPIO\_PIN\_1, GPIO\_PIN\_SET);

   HAL\_GPIO\_WritePin(GPIOA, GPIO\_PIN\_2, GPIO\_PIN\_RESET);

 } else {

   HAL\_GPIO\_WritePin(GPIOA, GPIO\_PIN\_1, GPIO\_PIN\_RESET);

   HAL\_GPIO\_WritePin(GPIOA, GPIO\_PIN\_2, GPIO\_PIN\_SET);

 }



 // 转速控制

 TIM2->CCR1 = ccr\_val;

}

// main函数测试

int main(void) {

 HAL\_Init();

 SystemClock\_Config();

 Motor\_Init();

 while\</doubaocanvas>
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值