STM32实战指南:SG90舵机控制原理与代码详解

知识点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 ms 90°
2.5 ms 180°

知识点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

cpp 复制代码
#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

cpp 复制代码
#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

cpp 复制代码
//串口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;
}

代码运行结果

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!

相关推荐
Geek__19924 分钟前
GD32 蓝牙模块调试
c语言·单片机·蓝牙
Miuney_MAX8 小时前
【单片机】之HC32F460中断向量选择
单片机·嵌入式硬件
XINVRY-FPGA11 小时前
XC3S1000-4FGG320I Xilinx AMD Spartan-3 SRAM-based FPGA
嵌入式硬件·机器学习·计算机视觉·fpga开发·硬件工程·dsp开发·fpga
猫猫的小茶馆14 小时前
【ARM】ARM的介绍
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件·物联网
猫猫的小茶馆14 小时前
【PCB工艺】数模电及射频电路基础
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·pcb工艺
点灯小铭14 小时前
基于单片机的智能药物盒设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
梓德原14 小时前
【基础】详细分析带隙型稳压电路的工作原理
单片机·嵌入式硬件·物联网
国科安芯15 小时前
航天医疗领域AS32S601芯片的性能分析与适配性探讨
大数据·网络·人工智能·单片机·嵌入式硬件·fpga开发·性能优化
小李做物联网16 小时前
【物联网毕业设计】60.1基于单片机物联网嵌入式项目程序开发之图像厨房监测系统
stm32·单片机·嵌入式硬件·物联网
贝塔实验室17 小时前
新手如何使用Altium Designer创建第一张原理图(三)
arm开发·单片机·嵌入式硬件·fpga开发·射频工程·基带工程·嵌入式实时数据库