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