PWM呼吸灯电机转速实验

Tutorial: 电子电路 Category: 嵌入式 Published: 2026-10-08 01:26:37 Views: 1 Likes: 0 Comments: 0

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_CH1PB4(部分重映射)可接电机驱动使能端
TIM3_CH2PB5(部分重映射)板载低电平点亮 LED
TIM4_CH1PB6舵机控制信号

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 的差别可先用向上计数、有效高极性理解:

模式周期内参考有效区间
PWM1CNT < CCR
PWM2CNT ≥ 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 模式/输出极性。

两个电机要独立调速时,应分别使用两个通道和两个比较变量;给两个通道写同一个比较值,只能获得相同占空比。