1. 项目概述
SG90舵机作为最常用的微型舵机之一,在机器人、遥控模型、智能家居等领域有着广泛应用。但很多初学者在使用STM32驱动SG90时,常常会遇到角度不准、抖动严重甚至烧毁舵机的问题。本文将基于STM32F103C8T6开发板,从PWM原理到实际代码实现,手把手教你如何稳定驱动SG90舵机。
我曾在多个机器人项目中深度使用SG90舵机,遇到过各种"坑":从电源选择到信号线处理,从死区设置到机械结构优化。本文将分享这些实战经验,帮你避开我踩过的那些坑。
2. 硬件准备与连接
2.1 所需材料清单
- STM32开发板(以STM32F103C8T6为例)
- SG90舵机(注意区分模拟和数字型号)
- 5V/2A电源(重要!USB供电通常不足)
- 杜邦线若干
- 1000μF电容(可选,用于电源滤波)
2.2 电路连接要点
SG90舵机有三根线:
- 红色:接5V电源正极
- 棕色:接GND
- 橙色:接STM32的PWM输出引脚(如PA8)
重要提示:切勿直接使用STM32的3.3V引脚给舵机供电!必须使用独立5V电源,且GND要与STM32共地。
2.3 电源方案选择
实测数据对比:
| 电源方案 | 空载电压 | 带载电压 | 舵机表现 |
|---|---|---|---|
| USB供电 | 5.0V | 4.2V | 抖动严重 |
| 独立5V/1A | 5.0V | 4.8V | 轻微抖动 |
| 独立5V/2A+电容 | 5.0V | 5.0V | 运行平稳 |
建议使用LM2596等DC-DC模块将输入电压降至5V,并并联1000μF电容滤除纹波。
3. PWM信号配置详解
3.1 SG90的PWM要求
SG90的控制信号是50Hz(周期20ms)的PWM波,其中:
- 0.5ms脉宽 → 0度
- 1.5ms脉宽 → 90度
- 2.5ms脉宽 → 180度
3.2 STM32定时器配置
以TIM1_CH1(PA8)为例:
c复制// 时钟配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
// 时基配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Prescaler = 72 - 1; // 72MHz/72=1MHz
TIM_TimeBaseStruct.TIM_Period = 20000 - 1; // 1MHz/20000=50Hz
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStruct);
// PWM输出配置
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_Pulse = 1500; // 初始1.5ms(90度)
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_ENABLE;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM1, &TIM_OCInitStruct);
// 使能预装载和主输出
TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
3.3 角度控制函数
c复制void SetServoAngle(uint16_t angle) {
// 角度限幅
if(angle > 180) angle = 180;
// 计算脉宽(500-2500us对应0-180度)
uint16_t pulse = 500 + angle * (2000 / 180);
// 更新CCR值
TIM_SetCompare1(TIM1, pulse);
}
4. 软件优化技巧
4.1 死区处理
舵机在到达目标位置时会产生反向电流,可能导致抖动。解决方法:
- 在角度变化后添加50ms延时
- 使用软件滤波:
c复制#define FILTER_SAMPLES 5
uint16_t angle_buffer[FILTER_SAMPLES] = {0};
uint16_t FilteredAngle(uint16_t new_angle) {
// 滑动窗口滤波
static uint8_t index = 0;
angle_buffer[index] = new_angle;
index = (index + 1) % FILTER_SAMPLES;
uint32_t sum = 0;
for(uint8_t i=0; i<FILTER_SAMPLES; i++) {
sum += angle_buffer[i];
}
return sum / FILTER_SAMPLES;
}
4.2 多舵机控制
使用定时器多个通道同时控制多个舵机:
c复制// 初始化TIM1的4个通道
TIM_OC1Init(TIM1, &TIM_OCInitStruct); // PA8
TIM_OC2Init(TIM1, &TIM_OCInitStruct); // PA9
TIM_OC3Init(TIM1, &TIM_OCInitStruct); // PA10
TIM_OC4Init(TIM1, &TIM_OCInitStruct); // PA11
// 分别设置角度
void SetMultiServo(uint16_t angle1, uint16_t angle2, uint16_t angle3, uint16_t angle4) {
TIM_SetCompare1(TIM1, 500 + angle1 * 2000 / 180);
TIM_SetCompare2(TIM1, 500 + angle2 * 2000 / 180);
TIM_SetCompare3(TIM1, 500 + angle3 * 2000 / 180);
TIM_SetCompare4(TIM1, 500 + angle4 * 2000 / 180);
}
5. 常见问题与解决方案
5.1 舵机抖动严重
可能原因及解决方法:
- 电源功率不足 → 换用2A以上电源
- 信号干扰 → 缩短信号线长度,加磁环
- 机械负载过大 → 检查是否卡死,减轻负载
5.2 角度不准确
校准步骤:
- 发送1500us脉宽,此时应为90度
- 如有偏差,机械调整舵盘位置
- 在代码中修正角度映射关系:
c复制// 实测校准值
#define MIN_PULSE 480 // 实际0度对应脉宽
#define MAX_PULSE 2480 // 实际180度对应脉宽
uint16_t pulse = MIN_PULSE + angle * (MAX_PULSE - MIN_PULSE) / 180;
5.3 舵机发热严重
温度测试数据:
| 状态 | 电流 | 温度上升 |
|---|---|---|
| 空载 | 10mA | <5°C |
| 堵转 | 700mA | >30°C/分钟 |
解决方法:
- 避免长时间堵转
- 增加散热片
- 使用金属齿轮舵机
6. 进阶应用实例
6.1 舵机缓动动画
实现平滑运动效果:
c复制void SmoothMove(uint16_t start_angle, uint16_t end_angle, uint16_t duration) {
uint16_t steps = duration / 20; // 每20ms一步
float delta = (float)(end_angle - start_angle) / steps;
for(uint16_t i=0; i<steps; i++) {
SetServoAngle(start_angle + delta * i);
Delay_ms(20);
}
}
6.2 遥控舵机控制
通过串口接收指令:
c复制// 接收格式:"ANG 90\r\n"
void USART1_IRQHandler(void) {
static char cmd[10];
static uint8_t index = 0;
if(USART_GetITStatus(USART1, USART_IT_RXNE)) {
char c = USART_ReceiveData(USART1);
if(c == '\n') {
cmd[index] = '\0';
if(strncmp(cmd, "ANG ", 4) == 0) {
uint16_t angle = atoi(cmd + 4);
SetServoAngle(angle);
}
index = 0;
} else if(index < sizeof(cmd)-1) {
cmd[index++] = c;
}
}
}
6.3 位置反馈实现
加装电位器检测实际角度:
c复制// 配置ADC读取电位器电压
uint16_t GetActualAngle(void) {
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);
uint16_t adc_value = ADC_GetConversionValue(ADC1);
return adc_value * 180 / 4095; // 12位ADC
}
在机械结构设计方面,我发现使用3D打印的舵机支架如果刚性不足,会导致明显的回差。建议使用尼龙材料打印,并在关键受力点添加金属轴承。对于需要精确控制的应用,最好选用数字舵机,虽然价格贵一些,但定位精度和保持扭矩要好很多。