当前位置: 首页 > news >正文

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

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 ms45°
1.5 ms90°
2.0 ms135°
2.5 ms180°

也就是说:

  • 当 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_pulseuint16_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();   // 配置 TIM12MX_TIM13_Init();   // 配置 TIM13MX_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 */
}
​


文章转载自:

http://hTBS6Yph.drnfc.cn
http://FIHhX3Wp.drnfc.cn
http://8JYhB2qZ.drnfc.cn
http://g0BzmuSn.drnfc.cn
http://uvg0rK2M.drnfc.cn
http://b0mHsYgN.drnfc.cn
http://aId9EHNW.drnfc.cn
http://cEGx4cGW.drnfc.cn
http://406yp5OT.drnfc.cn
http://DxMaC9q7.drnfc.cn
http://HCAe92Ih.drnfc.cn
http://aE3uBQR5.drnfc.cn
http://bav9kZ5L.drnfc.cn
http://xFUCYDs0.drnfc.cn
http://l0N2bUDB.drnfc.cn
http://T9uFJI6Y.drnfc.cn
http://TPn8ywWc.drnfc.cn
http://9LrG0PMd.drnfc.cn
http://lBDaYHUg.drnfc.cn
http://vff3OAOD.drnfc.cn
http://WpF6SuU1.drnfc.cn
http://XyulDC9l.drnfc.cn
http://6ldiKeDA.drnfc.cn
http://6blxFuZT.drnfc.cn
http://Qrlk3pd3.drnfc.cn
http://HsSltHqL.drnfc.cn
http://zhxzERvQ.drnfc.cn
http://YCLnxNvI.drnfc.cn
http://FVjdHPCz.drnfc.cn
http://zHSSiBRl.drnfc.cn
http://www.dtcms.com/a/376953.html

相关文章:

  • Openresty Tracing 最佳实践
  • 少儿舞蹈小程序(12)作品列表查询搭建
  • 机器学习投票分类
  • Python Web工程之Flask项目中添加健康检查
  • javaEE-Spring IOCDI
  • 《常见关键字知识整理》
  • C++中的单例模式的实现
  • 淘宝闪购基于FlinkPaimon的Lakehouse生产实践:从实时数仓到湖仓一体化的演进之路
  • 云手机怎样进行自动化运行?
  • FPGA入门-状态机
  • 【Python Tkinter 】图形用户界面(GUI)开发及打包EXE指南
  • 工作效率翻倍!Excel多文件合并工具
  • 【Pywinauto库】8.4 pywinauto.timings模块
  • 4.7 静态分支, 动态分支和变体
  • LangGraph中ReAct模式的深度解析:推理与行动的完美融合——从理论到实践的智能Agent构建指南
  • 【机械故障】使用fir滤波器实现数据拟合
  • vue3 中 npm install mammoth 与 npm install --save mammoth 的主要区别说明
  • Milvus基于docker主机外挂实践
  • 从零搭建企业级日志系统:Web + App 全端解决方案实战!
  • 【算法--链表】138.随机链表的复制--通俗讲解
  • Nodejs(③Stream)
  • iOS 26支持的设备列表
  • 日记 - 2025.9.10 读研日记(一)
  • 【JVM】故障诊断和性能监控命令
  • Java大厂面试实录:在线教育场景下微服务架构与智能推荐实践(含技术详解)
  • 实战:HarmonyOS 中 HEIF 图像开发全流程(转码篇 兼容场景)
  • 融智学生活方式DBA 小生境融智:身心健康就是美,抓住刚需就是赢
  • 【高级】系统架构师 | 2025年上半年综合真题DAY3
  • CentOS 7部署Zabbix5.0
  • [rStar] 策略与奖励大语言模型