从0°到180°,STM32玩转MG996R舵机

发布于:2025-09-11 ⋅ 阅读:(21) ⋅ 点赞:(0)

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
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 库配置时,关键步骤是:

  1. 设置定时器时钟 确保能产生精确的 20 ms 周期。

  2. 配置 PWM 模式 把定时器的某个通道设为 PWM 输出,并把引脚映射到外部(比如 PA5、PB0 等)。

  3. 改变占空比 通过修改定时器的比较寄存器(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 */
}
​


网站公告

今日签到

点亮在社区的每一天
去签到