1.MG996R舵机的性能参数
参数 | 数值 |
---|---|
产品型号 | MG995/MG996R |
产品重量 | 55 g |
工作扭矩 | 13 kg·cm |
反应速度 | 53-62 R/M |
使用温度 | -30°C ~ +55°C |
死区设置 | 4 微秒 |
插头类型 | JR、FUTABA 通用 |
转动角度 | 180°(左90°,右90°) |
舵机类型 | 数码舵机 |
使用电压 | 3.0 - 7.2 V |
工作电流 | 100 mA |
结构材质 | 部分铜齿、空心杯电机 |
线长 | ≈ 25 cm |
适用范围 | 双足机器人、机械手、遥控船、适合 50-90 级甲醇固定翼飞机、26-50 CC 汽油固定翼飞机等模型 |
2.MG996R 接线方式
2.1实物图

2.2接线方式
-
橙色线------信号线
-
黑色线------GND
-
红色线------VCC(一般接5V)
3. 单片机驱动 MG996R 舵机原理
MG996R 舵机是一种常见的 数字舵机 ,它的转动角度是通过 PWM(脉宽调制信号) 来控制的。单片机(比如 STM32)只要能输出符合要求的 PWM 信号,就可以精确控制舵机转动到指定角度。
3.1 什么是 PWM 信号?
-
PWM 全称 Pulse Width Modulation(脉宽调制)。
-
简单理解:就是输出一个"高低电平交替"的方波信号。
-
控制舵机的关键点在于:脉宽的长短决定舵机转的角度。
3.2 舵机对 PWM 的基本要求
MG996R 使用 标准 PWM 控制,主要参数如下:
-
周期(Period):20 ms(也就是 50 Hz 的频率)。 👉 每隔 20 ms,舵机会"读一次"信号,更新自己要到的角度。
-
占空比(Duty Cycle):在 20 ms 的周期里,高电平的持续时间不同,舵机角度也不同。
脉宽 (高电平持续时间) | 对应角度 |
---|---|
0.5 ms | 0° |
1.0 ms | 45° |
1.5 ms | 90° |
2.0 ms | 135° |
2.5 ms | 180° |
也就是说:
-
当 PWM 高电平只有 0.5ms,舵机会转到最左边(0°);
-
当 PWM 高电平是 1.5ms,舵机会正中间(90°);
-
当 PWM 高电平到 2.5ms,舵机会转到最右边(180°)。
3.3 STM32 控制舵机的原理
STM32 单片机内部的 定时器(Timer) 可以产生 PWM 信号。使用 HAL 库配置时,关键步骤是:
-
设置定时器时钟 确保能产生精确的 20 ms 周期。
-
配置 PWM 模式 把定时器的某个通道设为 PWM 输出,并把引脚映射到外部(比如 PA5、PB0 等)。
-
改变占空比 通过修改定时器的比较寄存器(CCR 值),就能调整 PWM 脉宽,从而控制舵机角度。
举个例子:
-
CCR = 500 → 脉宽约 0.5 ms → 舵机转到 0°
-
CCR = 1500 → 脉宽约 1.5 ms → 舵机转到 90°
-
CCR = 2500 → 脉宽约 2.5 ms → 舵机转到 180°
4.STM32 HAL库驱动MG996R舵机
4.1STM32CUBEMX初始化
HAL 库下 PWM 初始化步骤
①确定定时器时基
假设使用 84 MHz 主频,希望得到 20 ms 周期(50 Hz):
// 84 MHz / (Prescaler+1) / (Period+1) = 50 Hz
Prescaler = 84-1; // 72 MHz / (71+1) = 1 MHz
Period = 20000-1; // 1 MHz / (19999+1) = 50 Hz
注:这里我用到的是 STM32F407VET6 的 TIM12、TIM13 和 TIM14。它们都挂在 APB1 总线上。在主频 168 MHz 的情况下,APB1 时钟是 84 MHz,所以定时器相关的计算就以 84 MHz 为基准。
②CubeMX 初始化定时器 打开 CubeMX → Pinout & Configuration → Timers → 选择一个支持 PWM 的定时器。
Clock Source配置为Intenal Clock
设定:
-
Prescaler = 84-1
-
Counter Period = 20000-1
-
Channel 模式设为 PWM Generation CHx
-
把对应引脚设置为 PWM 输出。

4.2核心代码
1.servo_app.c代码
#include "servo_app.h"
// ================== 舵机参数 ==================
// MG996R 是常用的舵机,它通过 PWM 脉宽来控制角度。
// 一般来说:
// 0.5ms 脉宽 = 0°
// 1.5ms 脉宽 = 90°
// 2.5ms 脉宽 = 180°
// 所以我们先定义最小/最大脉宽值,后续角度计算会用到。
#define SERVO_DEFAULT_MIN_PULSE 500 // 对应 0° 时的脉宽 (单位: us,对应0.5ms)
#define SERVO_DEFAULT_MAX_PULSE 2500 // 对应 180° 时的脉宽 (单位: us,对应2.5ms)
// 定义四个舵机对象(左右两边各两个)
Servo_MG996R_t MG996R_LEFT1;
Servo_MG996R_t MG996R_LEFT2;
Servo_MG996R_t MG996R_RIGHT1;
Servo_MG996R_t MG996R_RIGHT2;
// ================== 舵机初始化 ==================
void Servo_Init(Servo_MG996R_t* servo, TIM_HandleTypeDef* htim, uint32_t channel)
{
// 保存定时器和通道信息
servo->htim = htim;
servo->channel = channel;
servo->min_pulse = SERVO_DEFAULT_MIN_PULSE;
servo->max_pulse = SERVO_DEFAULT_MAX_PULSE;
// 启动对应定时器通道的 PWM 输出
HAL_TIM_PWM_Start(servo->htim, servo->channel);
}
// ================== 设置舵机角度 ==================
void Servo_SetAngle(Servo_MG996R_t* servo, float angle)
{
// 1. 限制角度范围在 [0°, 180°] 之间
if (angle < 0) angle = 0;
if (angle > 180) angle = 180;
// 2. 根据角度 -> 换算为脉宽
// 角度 0° = min_pulse
// 角度 180° = max_pulse
uint16_t pulse = servo->min_pulse + (uint16_t)((servo->max_pulse - servo->min_pulse) * angle / 180.0f);
// 3. 把换算出来的脉宽值,交给 PWM 输出
Servo_SetPulse(servo, pulse);
}
// ================== 设置舵机 PWM 脉宽 ==================
void Servo_SetPulse(Servo_MG996R_t* servo, uint16_t pulse)
{
// 1. 限制脉宽在 [min_pulse, max_pulse] 范围
if (pulse < servo->min_pulse) pulse = servo->min_pulse;
if (pulse > servo->max_pulse) pulse = servo->max_pulse;
// 2. 直接写入定时器的比较寄存器 (CCR)
// 这会改变 PWM 占空比,从而改变舵机角度
__HAL_TIM_SET_COMPARE(servo->htim, servo->channel, pulse);
}
// ================== 校准舵机脉宽范围 ==================
void Servo_Calibrate(Servo_MG996R_t* servo, uint16_t min_pulse, uint16_t max_pulse)
{
// 用户可以根据自己舵机的实际情况调整
servo->min_pulse = min_pulse;
servo->max_pulse = max_pulse;
}
// ================== 初始化所有舵机 ==================
void Servo_Init_All(void)
{
// 左边两个舵机挂在 TIM12 (CH1, CH2)
Servo_Init(&MG996R_LEFT1, &htim12, TIM_CHANNEL_1);
Servo_Init(&MG996R_LEFT2, &htim12, TIM_CHANNEL_2);
// 右边两个舵机挂在 TIM13 (CH1) 和 TIM14 (CH1)
Servo_Init(&MG996R_RIGHT1, &htim13, TIM_CHANNEL_1);
Servo_Init(&MG996R_RIGHT2, &htim14, TIM_CHANNEL_1);
}
// ================== 演示任务:让舵机转到不同角度 ==================
void Servo_Task(void)
{
// 左1 = 0°
Servo_SetAngle(&MG996R_LEFT1, 0);
// 左2 = 45°
Servo_SetAngle(&MG996R_LEFT2, 45);
// 右1 = 90°
Servo_SetAngle(&MG996R_RIGHT1, 90);
// 右2 = 135°
Servo_SetAngle(&MG996R_RIGHT2, 135);
}
2.servo_app.h代码
#ifndef __SERVO_APP_H__
#define __SERVO_APP_H__
#include "MyDefine.h"
typedef struct {
TIM_HandleTypeDef* htim; // 定时器句柄
uint32_t channel; // 定时器通道
uint16_t min_pulse; // 最小脉宽(0度位置)
uint16_t max_pulse; // 最大脉宽(180度位置)
} Servo_MG996R_t;
// 初始化舵机
void Servo_Init(Servo_MG996R_t* servo, TIM_HandleTypeDef* htim, uint32_t channel);
// 设置舵机角度 (0-180度)
void Servo_SetAngle(Servo_MG996R_t* servo, float angle);
// 设置舵机原始脉宽值
void Servo_SetPulse(Servo_MG996R_t* servo, uint16_t pulse);
// 校准舵机最小和最大脉宽
void Servo_Calibrate(Servo_MG996R_t* servo, uint16_t min_pulse, uint16_t max_pulse);
void Servo_Init_All(void);
void Servo_Task(void);
#endif
3.main.c 示例代码
/* USER CODE BEGIN Includes */
#include "servo_app.h" // 引入你写的舵机驱动头文件
/* USER CODE END Includes */
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM12_Init(); // 配置 TIM12
MX_TIM13_Init(); // 配置 TIM13
MX_TIM14_Init(); // 配置 TIM14
/* USER CODE BEGIN 2 */
// === 初始化所有舵机 ===
Servo_Init_All();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
// === 演示舵机动作 ===
Servo_Task(); // 设置 0°、45°、90°、135°
HAL_Delay(2000); // 停 2 秒,方便观察动作
// 也可以手动设置某个舵机的角度,例如:
Servo_SetAngle(&MG996R_LEFT1, 180); // 左1转到 180°
HAL_Delay(1000);
Servo_SetAngle(&MG996R_LEFT1, 90); // 左1转回 90°
HAL_Delay(1000);
}
/* USER CODE END WHILE */
}