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 */ }