STM32 智能垃圾桶项目笔记(四):PWM 回顾与舵机(SG90)控制实现

本系列笔记是笔者学习 B 站 up 主 "技术探索者" STM32 系列视频所作的记录,不理解的地方推荐观看视频~

目录

  • 一、前言
  • [二、PWM 快速回顾与舵机(SG90)控制](#二、PWM 快速回顾与舵机(SG90)控制)
    • [2.1 PWM 核心概念快速回顾](#2.1 PWM 核心概念快速回顾)
    • [2.2 舵机(SG90)工作原理与控制逻辑](#2.2 舵机(SG90)工作原理与控制逻辑)
    • [2.3 CubeMX 配置(定时器 2 生成 50Hz PWM)](#2.3 CubeMX 配置(定时器 2 生成 50Hz PWM))
    • [2.4 舵机驱动代码实现(driver_SG90)](#2.4 舵机驱动代码实现(driver_SG90))
    • [2.5 主函数测试与预期现象](#2.5 主函数测试与预期现象)
  • 三、下一篇预告:语音合成模块(SYN6288)
  • 四、总结

一、前言

大家好,我是 Hello_Embed。上一篇我们完成了蓝牙模块(JDY-31)的通信验证,实现了 "手机指令控制 LED"。本次笔记将聚焦智能垃圾桶的 "执行核心"------ 舵机 SG90,通过 PWM 信号控制舵机转动,为后续 "自动开盖" 功能铺垫。

舵机控制的核心是 特定频率的 PWM 信号,因此先快速回顾 PWM 的核心概念(具体细节可参考笔者之前的 "STM32HAL 快速入门" 系列笔记),再逐步讲解舵机的原理、配置与代码实现。

二、PWM 快速回顾与舵机(SG90)控制

2.1 PWM 核心概念快速回顾

PWM(脉宽调制)本质是 "高低电平周期性交替的矩形波",核心参数为频率占空比,是控制舵机、电机、LED 亮度的基础。

2.1.1 核心参数定义
  • 占空比 :高电平持续时间占整个周期的比例,公式为 占空比 = 高电平时间(t2) / 周期(t)× 100%

    例:5V 设备接入占空比 50% 的 PWM 信号,实际等效工作电压约为 5V × 50% = 2.5V,可通过调节占空比控制设备功率(如电机调速)。

  • PWM 频率 :单位时间内周期的数量,公式为 PWM 频率 = 时钟频率 / 预分频值 / (ARR + 1)(ARR 为自动重载值)。

2.1.2 PWM 输出原理

定时器计数器(CNT)从 0 向上累加,与捕获比较寄存器(CCR)实时比较:

  • CNT < CCR 时,输出高电平;
  • CNT ≥ CCR 时,输出低电平;
  • CNT = ARR 时,CNT 清零,开始下一个周期。

例:若 ARR=1000、CCR=800,则高电平占比 800/1000=80%(高电平有效)。

2.1.3 PWM 控制 LED 示例

以定时器 3 为例,配置预分频值 71、ARR=1000(频率 1kHz),代码启动 PWM 并设置占空比:

c 复制代码
// 启动定时器3 通道1 PWM 输出
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
// 设置 CCR1=500,占空比 50%(1000 周期内高电平 500)
TIM3->CCR1 = 500;

逻辑分析仪抓取的 50% 占空比波形如下:

若将 CCR1 改为 200,占空比则变为 20%。

2.2 舵机(SG90)工作原理与控制逻辑

2.2.1 舵机特性与原理

SG90 是 180° 模拟舵机,核心由 "信号调制芯片、电机、减速齿轮、电位器" 组成,工作原理如下:

  1. 接收 PWM 控制信号后,内部基准电路生成 20ms 周期、1.5ms 高电平的基准信号;
  2. 控制信号的高电平时间与基准信号比较,产生电压差;
  3. 电压差驱动电机正转 / 反转,通过减速齿轮带动电位器旋转;
  4. 当电位器电压与控制信号电压一致时,电压差为 0,电机停止,舵机稳定在目标角度。

舵机使用说明图如下:

2.2.2 舵机控制核心:PWM 时基脉冲

SG90 需 20ms 周期(频率 50Hz) 的 PWM 信号,高电平时间决定转动角度,180° 舵机的 "高电平时间 - 角度" 对应关系如下:

高电平时间 对应角度 占空比(20ms 周期)
0.5ms 2.5%
1.0ms 45° 5%
1.5ms 90° 7.5%
2.0ms 135° 10%
2.5ms 180° 12.5%

关键结论:只要生成 50Hz PWM 信号,并调节高电平时间在 0.5ms~2.5ms 之间,即可控制舵机转动到对应角度。

2.3 CubeMX 配置(定时器 2 生成 50Hz PWM)

由于定时器 1 无法配置通道 1 为 PWM 输出,选择 定时器 2 通道 1(PA0 引脚) 生成 50Hz PWM 信号,配置步骤如下:

2.3.1 参数计算(50Hz 频率)

已知系统时钟 72MHz,目标 PWM 频率 50Hz,根据频率公式推导参数:

  • 频率 = 72MHz / (预分频值 + 1) / (ARR + 1) = 50Hz
  • 选择预分频值 = 720 - 1(720 分频后,时钟频率 = 72MHz / 720 = 100kHz);
  • 则 ARR + 1 = 100kHz / 50Hz = 2000 → ARR = 2000 - 1 = 1999。
2.3.2 定时器 2 配置
  1. 进入 Timers → TIM2,模式选择 Internal Clock
  2. 参数配置:
    • Prescaler(预分频值):719(720 分频);
    • Counter Period(ARR):1999(2000 周期);
    • PWM 模式:通道 1 设为 PWM Generation CH1
    • 极性:默认 High(高电平有效,不影响舵机控制);
  3. 引脚:TIM2 通道 1 对应 PA0,无需手动修改;
  4. 配置截图如下:
2.3.3 生成工程

确认基础配置(HCLK=72MHz、Debug=Serial Wire)不变,点击 Generate Code 生成工程,编译确保无错误。

2.4 舵机驱动代码实现(driver_SG90)

新建 driver_SG90.cdriver_SG90.h 驱动文件,封装 "舵机初始化" 和 "角度设置" 函数,核心是将 "角度" 线性映射为 "CCR 值"。

2.4.1 驱动头文件(driver_SG90.h)

定义角度对应的 CCR 值宏(0° 对应 50、180° 对应 250,推导:2000 周期内 0.5ms 占 50 个计数单位,2.5ms 占 250 个计数单位):

c 复制代码
#ifndef __DRIVER_SG90_H
#define __DRIVER_SG90_H

#include "tim.h"  // 包含定时器句柄定义

/* 舵机角度-CCR 值映射宏(2000 周期,50Hz) */
#define SG90_0_DEGREE       50    // 0° 对应 CCR 值(0.5ms 高电平)
#define SG90_90_DEGREE      150   // 90° 对应 CCR 值(1.5ms 高电平)
#define SG90_180_DEGREE     250   // 180° 对应 CCR 值(2.5ms 高电平)

/* 函数声明 */
void SG90_Init(void);            // 舵机初始化(启动 PWM + 复位到 90°)
void SG90_SetAngle(uint8_t angle); // 设置舵机角度(0-180°)

#endif
2.4.2 驱动源文件(driver_SG90.c)

实现初始化和角度设置函数,包含角度范围限制和线性映射逻辑:

c 复制代码
#include "driver_SG90.h"

// 声明定时器2 句柄(CubeMX 自动生成在 tim.c 中)
extern TIM_HandleTypeDef htim2;

/**
 * @brief  舵机 SG90 初始化函数
 * @param  无
 * @retval 无
 * @note   启动定时器2 通道1 PWM,初始化舵机到中间位置(90°)
 */
void SG90_Init(void)
{
    // 启动 TIM2 通道1 PWM 输出(舵机信号线接 PA0)
    HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
    // 初始化角度为 90°(避免舵机上电后无规律转动)
    SG90_SetAngle(90);
}

/**
 * @brief  设置舵机 SG90 目标角度
 * @param  angle:目标角度(范围 0-180°,超过 180° 自动限制为 180°)
 * @retval 无
 * @note   线性映射:角度 → CCR 值(0°→50,180°→250)
 */
void SG90_SetAngle(uint8_t angle)
{
    uint16_t ccr_value = 0;  // 存储计算后的 CCR 值

    // 限制角度范围(避免超出舵机物理极限)
    if (angle > 180)
    {
        angle = 180;
    }

    // 线性映射公式:ccr = 最小CCR + (最大CCR-最小CCR) × 角度/180
    ccr_value = SG90_0_DEGREE + (uint16_t)((SG90_180_DEGREE - SG90_0_DEGREE) * angle / 180);

    // 设置 TIM2 通道1 的 CCR 值(更新 PWM 高电平时间)
    __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_1, ccr_value);
}

2.5 主函数测试与预期现象

main.c 中调用舵机驱动函数,实现 "每秒转动 45°" 的测试逻辑:

c 复制代码
#include "driver_SG90.h"

// 全局变量:舵机当前角度、计时变量
uint8_t sg90_current_angle = 0;
uint32_t sg90_tick = 0;

int main(void)
{
    SG90_Init();
    while (1)
    {
        // 500ms 定时(避免转动过快导致舵机卡顿)
        if (HAL_GetTick() - sg90_tick > 500)
        {
            sg90_tick = HAL_GetTick();  // 更新计时基准

            // 设置舵机到当前角度
            SG90_SetAngle(sg90_current_angle);

            // 角度累加 45°,超过 180° 重置为 0°
            sg90_current_angle += 45;
            if (sg90_current_angle >= 180)
            {
                sg90_current_angle = 0;
            }
        }
    }
}

三、下一篇预告:语音合成模块(SYN6288)

完成舵机控制后,下一篇将聚焦智能垃圾桶的 "语音提示" 功能 ------ 基于语音合成模块 SYN6288 实现 "开盖提示""关盖提示" 等语音输出。内容包括:

  1. SYN6288 模块特性与接线(串口通信);
  2. CubeMX 串口配置(与模块匹配的波特率);
  3. 语音合成代码实现(发送文本指令,模块转换为语音);
  4. 功能整合:舵机开盖时触发 "欢迎使用" 语音,关盖时触发 "谢谢使用" 语音。

四、总结

本次笔记围绕 "PWM 控制舵机" 展开,核心收获包括:

  1. 回顾 PWM 频率与占空比的计算逻辑,理解 "特定频率 PWM 信号" 对舵机控制的重要性;
  2. 掌握 SG90 舵机的控制原理(20ms 周期 PWM + 高电平时间 - 角度映射);
  3. 封装通用舵机驱动函数,实现 "角度 - CCR 值" 线性映射,为后续 "自动开盖" 功能提供可复用代码。

至此,智能垃圾桶的 "感知(超声波)- 通信(蓝牙)- 执行(舵机)" 三大核心模块已全部覆盖,下一篇将通过语音模块丰富项目交互体验。请关注 Hello_Embed,后续笔记持续更新!

相关推荐
QL.ql2 小时前
MOS管简单入门笔记(主讲NMOS,PMOS不常用)
笔记
想唱rap3 小时前
归并排序、计数排序以及各种排序稳定性总结
c语言·数据结构·笔记·算法·新浪微博
零一iTEM3 小时前
NS4168输出音频通过ESP32C3测试
c++·单片机·嵌入式硬件·mcu·音视频·智能家居
charlie1145141913 小时前
精读C++20设计模式:结构型设计模式:装饰器模式
笔记·学习·设计模式·程序设计·c++20·装饰器模式
charlie1145141913 小时前
精读C++20设计模式——行为型设计模式:解释器模式
c++·学习·设计模式·解释器模式·c++20
时空自由民.3 小时前
GD32 I2C外设详介绍
单片机
zhangxuyu11183 小时前
flex布局学习记录
前端·css·学习
AnsonNie4 小时前
Xrdp 远程桌面配置【笔记】
笔记