单片机
返回首页

STM32实战教程:SG90舵机控制原理深度解析及代码详解

2025-09-28 来源:cnblogs

知识点1【SG90的简述】

SG90是一款微型舵机(Micro Servo),由TowerPro等厂商提供,广泛用于机器人,舵机云台,舵机控制教学等项目中。

1、基本参数

2、工作原理

SG90内部有电机,齿轮组,电位器和控制板。通过单根线输入(相对舵机)PWM控制信号

3、外观展示

4、接线说明

线颜色说明接法说明
棕色GND接单片机的地线
红色VCC(5V)建议接外部5V供电
橙色PWM信号接 STM32 的 PWM 输出引脚

知识点2【SG90分类】

SG90有180°舵机和360°舵机

  • 需要角度控制 → 选 180° 定位舵机 (Standard SG90)

  • 需要可变速度 → 选 360° 连续旋转舵机 (Continuous‑Rotation SG90)

  • 区别如下图:

    特性180° 定位舵机360° 连续旋转舵机
    控制参数意义脉宽→角度脉宽→速度方向
    是否定位
    适合应用定点定位、角度扫描速度驱动、轮式驱动
    代码实现区别设置脉宽一次,持续输出;持续输出脉宽做速度控制;
    停止方式断开 PWM 脉冲脉宽回 1.5 ms 或断开

    知识点3【180°舵机原理解析】

    控制方式:PWM(脉冲宽度调制)

    1、舵机通过PWM控制旋转角度

    2、通常使用50Hz(周期20ms)的PWM信号。

    3、控制信号的高电平时间(脉宽)决定转角,如下图表:

    脉宽(ms)角度(大约值)
    0.5 ms
    1.5 ms90°
    2.5 ms180°

    知识点4【360°舵机原理解析】

    工作流程:

    1. 接收同样的 50 Hz PWM 脉冲

    2. 驱动电路不再做角度锁定,而是将脉宽映射为“速度与方向”控制

    3. 输出轴持续旋转,直到 PWM 脉冲停止或脉宽回到中立值

    脉宽旋转方向与速度
    < 1.5 ms反向旋转,脉宽越短速度越快
    ≈ 1.5 ms停止(无转矩输出)
    > 1.5 ms正向旋转,脉宽越长速度越快

    知识点5【注意事项】

    1、电源要求

    SG90的推荐工作电压时5V,不要直接使用STM32板的3.3V供电,否则容易抖动,或不工作。

    2、不要强行转动舵机输出轴

    容易破坏内部齿轮或位置反馈电位器。

    知识点4【代码演示】

    我是用的是STM32F10x系列的,TIM2CH1。


main.c


#include 'stm32f10x.h'

#include 'stm32f10x_conf.h'

#include 'usart.h'

#include 'tim.h'


int main(void)

{

//有限级组的配置

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

TIM2_CH1_Init();

TIM2_CH1_GPIO_Init();

Usart1_Init(9600);

TIM3_Init();

while(1)

{

}

}



tim.c


#include 'tim.h'

#define MAX_SPEED 60.0f

const u16 period = 1000;

u16 pulse = 0;

int speed = 0;

int state = 30;

void TIM2_CH1_Init(void)

{

TIM_TimeBaseInitTypeDef TIM2_TimeBaseStruct;

TIM_OCInitTypeDef TIM2_OCStruct;

NVIC_InitTypeDef NVIC_TIM2Struct;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);

TIM_TimeBaseStructInit(&TIM2_TimeBaseStruct);

TIM2_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;

TIM2_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;

TIM2_TimeBaseStruct.TIM_Period  = period - 1;

TIM2_TimeBaseStruct.TIM_Prescaler = 1440 - 1;

TIM2_TimeBaseStruct.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM2,&TIM2_TimeBaseStruct);

pulse = speed * (2/180) *50 + 25;

TIM_OCStructInit(&TIM2_OCStruct);

TIM2_OCStruct.TIM_OCMode = TIM_OCMode_PWM1;

TIM2_OCStruct.TIM_OCPolarity = TIM_OCPolarity_High;

TIM2_OCStruct.TIM_OutputState = TIM_OutputState_Enable;

TIM2_OCStruct.TIM_Pulse = pulse;

TIM_OC1Init(TIM2,&TIM2_OCStruct);

TIM_Cmd(TIM2,ENABLE);

}


void TIM3_Init(void)

{

TIM_TimeBaseInitTypeDef TIM3_TimeBaseStruct;

NVIC_InitTypeDef NVIC_TIM3Struct;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

TIM_TimeBaseStructInit(&TIM3_TimeBaseStruct);

TIM3_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;

TIM3_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;

TIM3_TimeBaseStruct.TIM_Period  = 20000 - 1;

TIM3_TimeBaseStruct.TIM_Prescaler = 7200 - 1;

TIM3_TimeBaseStruct.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM3,&TIM3_TimeBaseStruct);

NVIC_TIM3Struct.NVIC_IRQChannel = TIM3_IRQn;

NVIC_TIM3Struct.NVIC_IRQChannelCmd = ENABLE;

NVIC_TIM3Struct.NVIC_IRQChannelPreemptionPriority = 1;

NVIC_TIM3Struct.NVIC_IRQChannelSubPriority = 1;

NVIC_Init(&NVIC_TIM3Struct);

TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);

TIM_Cmd(TIM3,ENABLE);

}


void TIM2_CH1_GPIO_Init(void)

{

GPIO_InitTypeDef GPIOA_InitStruct;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

GPIO_StructInit(&GPIOA_InitStruct);

GPIOA_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIOA_InitStruct.GPIO_Pin = GPIO_Pin_0;

GPIOA_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOA,&GPIOA_InitStruct);

}


void TIM3_IRQHandler(void)

{


if(TIM_GetITStatus(TIM3,TIM_IT_Update) == SET)

{

TIM_ClearITPendingBit(TIM3,TIM_IT_Update);

speed += state;

if(speed >= 60)

{

state = -30;

}

else if(speed <= -60)

{

state = 30;

}

//角度处理

pulse = (u16)(speed * (1.0f/20) *(period / MAX_SPEED) + 1.5f / 20 * 1000);

printf('Speed:%d r/s\n',speed);

TIM_SetCompare1(TIM2,pulse);

}

}



usart.c


//串口1初始化

void Usart1_Init(u32 Baud)

{

GPIO_InitTypeDef GPIOB_InitStruct;

USART_InitTypeDef USART1_InitStruct;

//时钟配置 USART1,TX:PA9,RX:PA10

RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);

//端口配置

GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;

GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_9;

GPIOB_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init(GPIOA,&GPIOB_InitStruct);

GPIOB_InitStruct.GPIO_Pin = GPIO_Pin_10;

GPIOB_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;

GPIO_Init(GPIOA,&GPIOB_InitStruct);

//串口初始化

USART1_InitStruct.USART_BaudRate = Baud;

USART1_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

USART1_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;

USART1_InitStruct.USART_Parity = USART_Parity_No;

USART1_InitStruct.USART_StopBits = USART_StopBits_1;

USART1_InitStruct.USART_WordLength = USART_WordLength_8b;

USART_Init(USART1,&USART1_InitStruct);

//使能串口

USART_Cmd(USART1,ENABLE);

}


//串口1发送数据函数发送数据

void USART1_Trans(u8 c)

{

while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);

USART_SendData(USART1,c);

while(USART_GetFlagStatus(USART1,USART_FLAG_TC) == RESET);

}


int fputc(int c,FILE *stream)

{

USART1_Trans((u8)c);

return c;

}


 代码运行结果

结束

代码重在练习!

代码重在练习!

代码重在练习!

进入单片机查看更多内容>>
相关视频
  • 【TI MSPM0 应用实战】智能小车+工业角度编码器+血氧仪+烟雾探测器!硬核参考设计详解!

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

  • 直播回放: Microchip Timberwolf™ 音频处理器在线研讨会

  • 基于灵动MM32W0系列MCU的指夹血氧仪控制及OTA升级应用方案分享

精选电路图
  • 1瓦线性调频增强器

  • 家用电器遥控器

  • 12V 转 28V DC-DC 变换器(基于 LM2585)

  • 红外开关

  • DS1669数字电位器

  • HA1377 桥式放大器 BCL 电容 17W(汽车音频)

    相关电子头条文章