舵机是一种根据输入PWM信号占空比来控制输出角度的装置。
一、舵机基本参数
舵机(Servo Motor)是一种根据输入PWM(脉冲宽度调制)信号占空比来控制输出轴角度的精密执行装置。它内部包含直流电机、减速齿轮组、位置反馈电位器和控制电路,通过闭环控制实现精确的角度定位。
1、以舵机SG90为例:
SG90是一款微型舵机,广泛应用于机器人、航模、智能小车等场景。其核心电气参数如下:
- 工作电压:4.8V ~ 6.0V(典型5V,3.3V无驱动力)
- 工作电流:100mA ~ 250mA(空载~堵转)
- 扭矩:1.2kg·cm ~ 1.8kg·cm(电压不同)
- 响应速度:0.1s/60°(4.8V)
PWM控制信号规范:
- PWM周期固定:20ms(频率=50Hz,不能改变);
- 高电平脉宽范围:0.5ms ~ 2.5ms(对应输出角度0° ~ 180°);
- 典型对应关系 :
- 0.5ms → 0°(最左位置)
- 1.5ms → 90°(中间位置)
- 2.5ms → 180°(最右位置)
2、参数计算(基于STM32F103C8T6,系统时钟72MHz)
目标:生成周期20ms、分辨率满足0.5ms~2.5ms可调的PWM信号。
步骤分解:
- 确定计数频率:系统时钟72MHz,预分频器PSC设为7200-1,则计数器时钟频率 = 72MHz / 7200 = 10kHz(即每个计数周期0.1ms)。
- 确定自动重装载值ARR:PWM周期 = (ARR+1) × 计数周期。周期20ms对应200个计数周期(20ms ÷ 0.1ms = 200),故ARR = 200-1 = 199。
- 计算CCR值(捕获/比较寄存器) :CCR值决定高电平脉宽,与角度呈线性关系。
- 0°(0.5ms):CCR = 0.5ms ÷ 0.1ms = 5
- 90°(1.5ms):CCR = 1.5ms ÷ 0.1ms = 15
- 180°(2.5ms):CCR = 2.5ms ÷ 0.1ms = 25
通用公式:CCR = (Angle / 180.0) × (25 - 5) + 5,其中Angle为0~180°。
3、接线示意图
SG90舵机通常有三根线:
- 棕色线(Brown):GND(接地)
- 红色线(Red):VCC(电源,+5V)
- 橙色线(Orange):信号线(PWM输入)
连接STM32开发板时:
- 信号线 → PA1(TIM2通道2)
- VCC → 5V电源(注意电流需足够)
- GND → 共地

注意:若驱动多个舵机,建议使用外部独立电源供电,避免开发板LDO过载。
1、以舵机SG90为例:
PWM周期固定:20ms(频率=50Hz);
高电平范围:0.5~2.5ms(对应0~180°);
0.5ms→0°;1.5ms→90°(中间位置);2.5ms→180°;
2、参数计算
系统时钟72MHz,周期20ms。
设:PSC+1 =7200,计数频率10kHz(0.1ms计数一次),ARR+1 =200;
CCR值,线性关系:
CCR=5→0.5ms(0°);
CCR=15→1.5ms(90°);
CCR=25→2.5ms(180°);
3、接线

二、代码部分
练习:以TIM2为例,通道1→PA1,实现0°→180→0°,循环往复。
最经典且最简单的做法:在whiel()里放2个For循环,一个负责正转(0→180°),另一个负责反转(180→0)。
1、PWM初始化
cpp
void PWM_Init(uint16_t ARR,uint16_t PSC)
{
TIM_TimeBaseInitTypeDef TIM_BaseInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef OC_Config;
//开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//初始化GPIO
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//初始化时基单元
TIM_BaseInitStructure.TIM_ClockDivision =TIM_CKD_DIV1;
TIM_BaseInitStructure.TIM_CounterMode =TIM_CounterMode_Up;
TIM_BaseInitStructure.TIM_Period =ARR;
TIM_BaseInitStructure.TIM_Prescaler =PSC;
TIM_TimeBaseInit(TIM2,&TIM_BaseInitStructure);
//配置OC
TIM_OCStructInit(&OC_Config);
OC_Config.TIM_OCMode =TIM_OCMode_PWM1;
OC_Config.TIM_OutputState = TIM_OutputState_Enable;
OC_Config.TIM_OCPolarity = TIM_OCPolarity_High;
OC_Config.TIM_Pulse = 15;
TIM_OC2Init(TIM2,&OC_Config);
TIM_OC1PreloadConfig(TIM2,TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM2,ENABLE);
TIM_Cmd(TIM2,ENABLE);
}
2、舵机(Servo)初始化及设置角度函数
cpp
#include "stm32f10x.h"
#include "PWM.h"
void Servo_Init()
{
PWM_Init(200,7200-1);
}
void SetMyAngle(uint16_t Angle)
{
uint16_t Val;
if(Angle>180)
{
Angle =180;
}
Val =(uint16_t)((25.0-5.0)/180.0)*Angle+5);
TIM_SetCompare2(TIM2,Val);
}
3、舵机(Servo)头文件
cpp
#ifndef __SERVO_H
#define __SERVO_H
void Servo_Init();
void SetMyAngle(uint16_t Angle);
#endif
4、main函数
cpp
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
uint16_t ang;
int main()
{
OLED_Init();
Servo_Init();
SetMyAngle(0);//先归零
Delay_ms(1000);//等待舵机物理归位
OLED_ShowString(1,1,"Angle:");
while(1)
{
//0→180
for(ang =0;ang <=180;ang++)
{
SetMyAngle(ang);
Delay_ms(10); //延时决定转动速度
OLED_ShowNum(1,7,ang,3);
}
Delay_ms(1000); //到头停止1s
//180→0
for(ang =180;ang >0;ang--)
{
SetMyAngle(ang);
Delay_ms(10);
OLED_ShowNum(1,7,ang,3);
}
Delay_ms(1000); //到头停止1s
}
}
注意:
1)平滑度控制:Delay_ms(10)这个参数决定了舵机转动的丝滑程度。
如果设为Delay_ms(1),舵机会转动非常快,会因为惯性产生抖动。如果设为10ms,转动会比较丝滑。这个过程中耗时约1.8s。可多次试验几次,调整参数。
2)数据类型:angle使用了uint16_t(无符号16位整数),在反向循环中,条件写的是angle>0,而不是angle>=0,因为无符号数减到0后再减1,会发送下溢,变成65535(一直大于条件>=0),导致死循环。
本文完!