STM32的Sg90舵机

1.舵机到底要的是什么信号?

想象舵机就像一个"听秒表的工人":

这个工人每隔 20ms 就抬头看看秒表一次。

秒表上的 高电平持续多久,他就把这个时间当成"指令角度"。

高 1ms → 转到最左(0°)

高 1.5ms → 转到中间(90°)

高 2ms → 转到最右(180°)

所以:舵机只关心"脉宽多少微秒",不关心占空比,也不关心电平是多少次。

它就像在量高电平的时间长度。

为什么舵机用 PWM 控制?

舵机的内部

常见 3 线舵机(信号、VCC、电源地)内部是:电机 + 减速齿轮 + 电位器反馈 + 控制器。

外部只给它一个周期固定、脉宽可变的控制脉冲,舵机内部会把"脉宽"当作目标角度,做闭环去转到位。

舵机"吃"的信号

帧周期:通常 20 ms(50 Hz)最通用;很多舵机允许 40--300 Hz,数字舵机还能更高,但50 Hz 是最安全默认。

脉宽:常见 1.0 ms ≈ 0°、1.5 ms ≈ 90°、2.0 ms ≈ 180°(具体要以舵机说明书/实测为准;有些是 0.5--2.5 ms)。

注意:这是"定周期的脉宽控制",不是单纯追求占空比。我们用定时器 PWM 正是为了精准到微秒地生成这个脉宽。

为什么用 PWM?

PWM(脉宽调制)就是 MCU 内部"定时器"自动帮我们生成一个方波信号:

周期固定:20ms 一次。

高电平可调:可以是 1000us、1500us、2000us......

不用自己 while(1) 里 delay 搞波形,定时器硬件自动输出,非常精准。

所以我们用 定时器 + PWM 模式,就是为了让 MCU 硬件替我们"定点报时"。

2.在 STM32 里怎么做?

MCU 的"定时器"就像一个秒表:

PSC(预分频器):决定秒表走得快还是慢。

ARR(自动重装载):决定多少数后清零 → 就是周期。

CCR(比较寄存器):决定在哪个数的时候翻转电平 → 就是脉宽。

举例(假设 STM32 主频 72MHz):

让秒表每 1 微秒加 1(PSC=71)。

设置 ARR=20000 → 秒表数到 20000 就清零 → 周期 20ms。

设置 CCR=1500 → 秒表数到 1500 的时候输出翻转 → 高电平 1.5ms。

这样我们就得到了一个"周期 20ms、高电平 1.5ms"的 PWM,舵机看到它,就会乖乖转到 90°。

把角度变成脉宽

角度和脉宽是线性映射:

脉宽(us)=1000+(角度/180)×(2000−1000),比如:

0° → 1000us

90° → 1500us

180° → 2000us

所以我们只要给定一个角度,就能算出对应的脉宽,然后设置到 CCR 寄存器。

为什么必须这样做?

因为舵机内部的电路就是按照"高电平时间长度"来解码的。

如果你给它随便一个 PWM(比如频率 1kHz,占空比 10%),它根本不懂,会乱抖。

只有20ms 周期 + 特定的脉宽,舵机才能正确理解。

举个直观的比喻

想象你和舵机在玩手电筒信号:每 20 秒我都给你亮一次手电筒。如果亮 1 秒,你就走到左边。如果亮 1.5 秒,你就走到中间。如果亮 2 秒,你就走到右边。舵机就是这么傻,但很稳定。

3.举例具体代码案例

主函数 main.c

cpp 复制代码
OLED_Init();       // 初始化 OLED 显示屏
Servo_Init();      // 初始化舵机(其实就是初始化 PWM)
Key_Init();        // 初始化按键

OLED_ShowString(1,1,"Angle:");

while (1)
{
    KeyNum = Key_GetNum();  // 读按键
    if(KeyNum == 1)
    {
        Angle += 30;        // 每按一下 +30°
        if(Angle > 180)
        {
            Angle = 0;      // 超过180°回到0°
        }
    }
    Servo_SetAngle(Angle);               // 把角度换成 PWM 脉宽
    OLED_ShowNum(1, 7, Angle, 3);        // 显示角度
}

按一次按键 → Angle += 30 → Servo_SetAngle(Angle) → 改 PWM 脉宽 → 舵机转动。

PWM_Init()

cpp 复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; // 复用推挽输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;       // TIM2_CH2 -> PA1
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

STM32 主频 72 MHz

PSC=72-1 → 72 分频 → 定时器时钟 = 1 MHz(1 tick = 1us)

ARR=20000-1 → 每数到 20000 清零 → 周期 = 20000us = 20ms 20ms 就是舵机需要的 PWM 周期。

cpp 复制代码
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   // PWM1 模式
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0;                  // CCR 初值
TIM_OC2Init(TIM2, &TIM_OCInitStructure);

输出 PWM1 模式:计数器 < CCR → 高电平

计数器 ≥ CCR → 低电平

这样一来,高电平的长度就取决于 CCR。

PWM_SetCompare2()

cpp 复制代码
void PWM_SetCompare2(uint16_t Compare)
{
    TIM_SetCompare2(TIM2, Compare);
}

直接改 CCR2,控制高电平时间。

比如 Compare=1500 → 高电平 1500us → 舵机转到 90°。

Servo_SetAngle()

cpp 复制代码
void Servo_SetAngle(float Angle)
{
    PWM_SetCompare2(Angle/180.0f*2000 + 500);
}

把角度(0°~180°)映射到脉宽:

Angle=0 → CCR=500 → 500us

Angle=180 → CCR=2500 → 2500us

这就是一个 500~2500us 的范围。

(有些舵机要求 1000~2000us,我这里用了 500~2500us,表示支持的角度范围更大,实际可能会超出舵机物理限制,要注意一下)。

总结一下

参数/函数 作用 对应舵机需求
PSC=72-1 定时器分频,得到 1MHz 计数频率 1 tick = 1us
ARR=20000-1 自动重装载,周期 20000us 20ms 周期
CCR2 捕获比较寄存器 控制高电平时间(us)
PWM1 模式 输出模式 高电平持续到 CCR,符合舵机信号
Servo_SetAngle() 角度→脉宽换算 0°=500us,180°=2500us
PA1 (TIM2_CH2) PWM 输出引脚 信号送到舵机

4.注意:普通舵机(角度型) vs 连续旋转舵机(360°型)

特性 普通舵机(角度舵机) 连续旋转舵机(360°舵机)
控制信号周期 20ms(50Hz) 20ms(50Hz)
PWM脉宽含义 表示目标角度 表示旋转方向和速度
典型控制范围 0.5ms ~ 2.5ms ≈ 0°~180° 1.0ms ~ 2.0ms = 速度控制(1.5ms停止)
1.0ms信号 接近 0°位置 反转(中速)
1.5ms信号 中间角度(≈90°) 停止
2.0ms信号 接近 180°位置 正转(中速)
反馈机制 内部电位器 → 有角度反馈 无反馈,类似直流电机
能否定位 可以,转到角度后会自动停 不可以,只能连续旋转
控制目标 绝对角度 转速 + 方向
主要用途 机械臂、云台、模型舵面 小车驱动轮、电机替代
代码实现公式 PWM = (Angle/180)*2000 + 500 PWM = 1500 + speed*5(speed=-100~100)

具体连续旋转舵机,之后会详细说明(这边可以留意一下)

相关推荐
kyle~29 分钟前
C/C++---浮点数与整形的转换,为什么使用sqrt函数时,要给参数加上一个极小的小数(如1e-6)
c语言·开发语言·c++
逼子格1 小时前
【Protues仿真】定时器
单片机·嵌入式硬件·51单片机·定时器·硬件工程师·硬件工程师真题·at89c52
用户6120414922133 小时前
C语言做的排队叫号系统
c语言·后端·敏捷开发
不爱学英文的码字机器4 小时前
[CS创世SD NAND征文] CS创世CSNP1GCR01-AOW在运动控制卡中的高可靠应用
人工智能·嵌入式硬件·物联网·iot
机器视觉知识推荐、就业指导5 小时前
STM32 外设驱动模块九:TB6612FNG 电机驱动模块
stm32·单片机·嵌入式硬件
XH华10 小时前
C语言第十一章内存在数据中的存储
c语言·开发语言
清风66666612 小时前
基于STM32单片机的二维码识别物联网OneNet云仓库系统
stm32·单片机·物联网·毕业设计·课程设计
3壹13 小时前
单链表:数据结构中的高效指针艺术
c语言·开发语言·数据结构
knd_max15 小时前
C语言:内存函数
c语言