PWM呼吸灯电机转速实验
136.5.8PWM呼吸灯电机转速实验
/*
* 整体目的:启动 TIM3 两路 PWM,并用同一组三角波比较值演示 LED 呼吸和电机驱动占空比变化。
* 设计思路:PWM_Value 在 0~199 循环,再折算成 0→100→0 的 LED_PWM_Value;
* 每 25 ms 更新一次 TIM3_CH1、CH2 的 CCR,完整往返约为 5 秒。
* 硬件依据:PB4/TIM3_CH1 应接电机驱动器的 PWM/EN 输入,不能直接接电机;
* PB5/TIM3_CH2 接板载低电平有效 LED,因此亮度方向与高有效电机 EN 相反。
* 代码范围:摘自 CubeIDE/07PWM 的 main.c;TIM3 初始化见上一节,HAL 库内部不展开。
*/
#include "main.h" /* 引入 HAL 公共定义和系统错误处理接口。 */
#include "tim.h" /* 引入 htim3、TIM_CHANNEL_x 和 PWM 控制宏。 */
#include "gpio.h" /* 引入工程 GPIO 初始化函数。 */
uint8_t PWM_Value = 0U; /* 保存 0~199 的相位计数,用于构造上升再下降的三角波。 */
uint8_t LED_PWM_Value = 0U; /* 保存最终写入 CCR1、CCR2 的 0~100 比较值。 */
int main(void) /* 程序入口,初始化并持续更新 PWM 占空比。 */
{ /* main() 函数体开始。 */
HAL_Init(); /* 初始化 HAL 与 SysTick,HAL_Delay() 依赖此时基。 */
SystemClock_Config(); /* 配置 72 MHz 系统时钟和定时器所需总线时钟。 */
MX_GPIO_Init(); /* 初始化普通 GPIO,并按工程配置释放 PB4 的 JTAG 占用。 */
MX_TIM3_Init(); /* 配置 TIM3 为 100 Hz PWM,ARR=99。 */
HAL_TIM_PWM_Start( /* 启动第一路 PWM 输出。 */
&htim3, /* 选择 TIM3 句柄。 */
TIM_CHANNEL_1); /* 启动映射到 PB4 的 CH1,供电机驱动 EN 使用。 */
HAL_TIM_PWM_Start( /* 启动第二路 PWM 输出。 */
&htim3, /* 两路通道共享 TIM3 的 PSC、ARR 和计数器。 */
TIM_CHANNEL_2); /* 启动映射到 PB5 的 CH2,驱动板载 LED。 */
while (1) /* 主循环不断产生并写入新的占空比。 */
{ /* 一次 PWM 更新循环开始。 */
PWM_Value++; /* 相位每轮加 1。 */
PWM_Value = PWM_Value % 200U; /* 到 200 时回到 0,使相位保持在 0~199。 */
if (PWM_Value > 99U) /* 100~199 属于三角波下降段。 */
{ /* 下降段计算开始。 */
LED_PWM_Value = 200U - PWM_Value; /* 把 100~199 映射为 100~1。 */
} /* 下降段计算结束。 */
else /* 0~99 属于三角波上升段。 */
{ /* 上升段计算开始。 */
LED_PWM_Value = PWM_Value; /* 直接得到 0~99。 */
} /* 上升段计算结束。 */
HAL_Delay(25U); /* 每步保持约 25 ms,200 步完整往返约 5 秒。 */
__HAL_TIM_SET_COMPARE( /* 更新 TIM3 通道 1 的捕获比较寄存器 CCR1。 */
&htim3, /* 选择 TIM3。 */
TIM_CHANNEL_1, /* 选择 PB4/TIM3_CH1。 */
LED_PWM_Value); /* 写入 0~100,使高有效电机 EN 的占空比随之变化。 */
__HAL_TIM_SET_COMPARE( /* 更新 TIM3 通道 2 的 CCR2。 */
&htim3, /* 仍选择共享时基的 TIM3。 */
TIM_CHANNEL_2, /* 选择 PB5/TIM3_CH2。 */
LED_PWM_Value); /* 使用相同高电平占空比;低有效 LED 的亮度变化方向相反。 */
} /* 本次更新结束,返回循环顶部继续改变占空比。 */
} /* main() 语法边界;正常运行不会返回。 */
一、定时器
1.1 定时器分类
STM32F103 的定时器可分为基本、通用和高级控制定时器。PWM 输出必须使用带捕获/比较通道的通用或高级定时器。本实验使用 TIM3_CH1、TIM3_CH2 输出两路 PWM,并用 TIM4_CH1 输出舵机控制脉冲。
一个 PWM 通道的关键寄存器关系是:
flowchart LR
CLK[定时器时钟] --> PSC[PSC预分频]
PSC --> CNT[CNT计数]
ARR[ARR周期上限] --> CNT
CNT --> CMP{与CCR比较}
CCR[CCRx比较值] --> CMP
CMP --> MODE[OCxM选择PWM1或PWM2]
MODE --> POL[CCxP输出极性]
POL --> PIN[通道引脚]
1.2 设备配置
TIM3 配置为 100 Hz、100 个占空比步进,TIM4 约为 50 Hz、100 μs 的脉宽分辨率。GPIO 使用复用推挽输出:
| 定时器通道 | 引脚 | 用途 |
|---|---|---|
| TIM3_CH1 | PB4(部分重映射) | 可接电机驱动使能端 |
| TIM3_CH2 | PB5(部分重映射) | 板载低电平点亮 LED |
| TIM4_CH1 | PB6 | 舵机控制信号 |
PB4 被 JTAG 占用时必须关闭 JTAG、保留 SWD。所有外部驱动模块必须与 STM32 共地。
1.3 定时器初始化代码
MX_TIM3_Init() 和 MX_TIM4_Init() 不会自动让 PWM 波形出现在引脚上。初始化只配置时基、输出比较模式、通道参数和 GPIO,随后还要启动各通道。
1.3.1 定时器的启动
1.3.1.1 示例代码
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
启动函数使能对应 CCER.CCxE 通道输出,并在需要时使能 CR1.CEN 计数器。返回值应在正式项目中检查,便于发现参数、状态或硬件初始化问题。
1.3.1.2 示例代码
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, LED_PWM_Value);
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, LED_PWM_Value);
该宏写入对应的 CCRx。若开启比较预装载,新值通常在下一个更新事件统一生效,可避免周期中途修改导致波形不完整。
1.3.1.3 示例代码
PWM1 与 PWM2 的差别可先用向上计数、有效高极性理解:
| 模式 | 周期内参考有效区间 |
|---|---|
| PWM1 | CNT < CCR |
| PWM2 | CNT ≥ CCR |
输出极性 CCxP 还可以再反相一次。最终负载是亮、灭、正转还是制动,必须继续结合外部电路分析。
二、呼吸灯效果
2.1 呼吸灯原理
代码用 0~199 的变量构造三角波:
PWM_Value++;
PWM_Value %= 200;
if (PWM_Value > 99) {
LED_PWM_Value = 200 - PWM_Value;
} else {
LED_PWM_Value = PWM_Value;
}
PWM_Value 在 0~199 间循环,折算后的比较值按 0→1→…→99→100→99→…→1→0 往复变化。ARR=99 时,CCR=100 正好表示整个周期均为有效电平,因此这个序列覆盖 0%~100% 的端点,没有从 100% 突然跳回 0%。每步延时 25 ms,200 步完整往返约需 5 s。
板载 LED 是低电平点亮,PWM1 的高电平占空比越大,LED 导通时间越短。所以数值增大时实际亮度下降,数值减小时亮度上升;虽然方向与高电平有效负载相反,仍然能形成呼吸效果。
2.2 呼吸灯效果展示
人眼不会分辨 100 Hz 的单次明灭,而是看到平均亮度变化。占空比与视觉亮度并非严格线性:LED 光通量、眼睛感知都具有非线性。若希望呼吸更自然,可采用伽马校正表,而不是让 CCR 只做线性增减。
HAL_Delay(25) 会阻塞主循环,适合演示;系统还要处理通信、按键或控制任务时,应改用 HAL_GetTick()、定时器中断或任务调度,避免整个程序被延时卡住。
三、电机转速控制
3.1 电机控制方法
直流电机需要 H 桥或专用驱动芯片:
PWM ───────> EN(调节平均能量)
方向GPIO ──> IN1/IN2(改变桥臂导通方向)
电机电源 ──> VM
公共地 ────> STM32 GND = 驱动GND
OUT1/OUT2 ─> 电机两端
PWM 高电平有效时,占空比增大通常会让平均电枢电压、电流和转速上升,但负载变化、静摩擦、驱动压降和反电动势都会造成非线性。低占空比时电机还可能无法起转。
不要把电机接在 PB4、PB5 或 PB6 与地之间。GPIO 只负责控制驱动器逻辑端。电机电源入口附近应配置合适的去耦电容,布线应缩短大电流回路并远离单片机复位、晶振和模拟信号。
3.2 电机控制代码
若 TIM3_CH1 的 PB4 接驱动器 EN,下面的比较值直接控制其有效高电平占空比:
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, LED_PWM_Value);
TIM3_CH2 的 PB5 同时控制低电平点亮 LED 时,会出现“电机加速、LED 变暗”或相反的视觉关系,因为驱动 EN 高电平有效,而板载 LED 低电平有效。若希望二者同向显示,可给 LED 使用 ARR + 1 - motor_ccr 的互补比较值,或调整 PWM 模式/输出极性。
两个电机要独立调速时,应分别使用两个通道和两个比较变量;给两个通道写同一个比较值,只能获得相同占空比。