三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

STM32F103用CubeMX配置PWM驱动舵机,从TIM3通道配置到MDK代码实战

STM32F103用CubeMX配置PWM驱动舵机,从TIM3通道配置到MDK代码实战

STM32F103用CubeMX配置PWM驱动舵机:从TIM3通道配置到MDK代码实战

在机器人控制和自动化项目中,舵机是最常见的执行器之一。无论是机械臂的关节控制、摄像云台的精准定位,还是智能小车转向机构,都离不开PWM信号对舵机的精确驱动。对于STM32开发者来说,利用CubeMX快速配置PWM输出并实现多路舵机控制,是嵌入式开发中的一项基本功。

本文将带您从CubeMX的基础配置开始,逐步实现TIM3四通道PWM输出,并重点讲解如何将这些技术应用于实际舵机控制场景。不同于简单的PWM波形生成,我们会深入探讨参数设置的工程意义,分享避免舵机抖动的实战技巧,最终呈现一个完整的舵机角度控制解决方案。

1. 理解舵机控制原理与PWM参数设计

舵机的控制核心在于PWM信号的脉宽调制。标准舵机通常要求20ms的周期信号,其中高电平持续时间在0.5ms到2.5ms之间对应0°到180°的角度变化。这种控制方式虽然简单,但参数设置不当会导致舵机抖动、响应迟钝甚至损坏。

1.1 关键参数计算

在STM32中,PWM的周期由定时器的**ARR(自动重装载值)PSC(预分频器)**共同决定。假设我们使用72MHz的系统时钟:

PWM周期 = (ARR + 1) × (PSC + 1) / 时钟频率

对于20ms的舵机控制周期,我们可以这样配置:

// 时钟72MHz,预分频71,ARR为19999 PWM周期 = (19999 + 1) × (71 + 1) / 72000000 = 0.02秒(20ms)

对应的占空比设置范围:

角度脉宽(ms)比较值(CCR)
0.5500
90°1.51500
180°2.52500

注意:比较值超过2500可能导致舵机超出机械限位,长期使用会损坏齿轮组

1.2 多通道同步设计

TIM3提供四个独立的PWM通道(CH1-CH4),每个通道有自己的比较寄存器(CCRx)。通过合理配置,我们可以实现:

  • 四个舵机独立控制
  • 统一的20ms周期
  • 各通道不同的占空比(角度)

这种设计特别适合机械臂等需要多关节协同的场景。

2. CubeMX工程配置详解

2.1 基础环境搭建

  1. 新建STM32F103工程,选择对应型号(如RCT6)
  2. 配置系统时钟:
    • HSE选择"Crystal/Ceramic Resonator"
    • 时钟树配置为72MHz
  3. 开启TIM3时钟

2.2 TIM3 PWM通道配置

在CubeMX的图形界面中:

  1. 选择TIM3
  2. Clock Source选择"Internal Clock"
  3. 配置Channel1-Channel4为"PWM Generation CHx"
  4. 参数设置:
    • Prescaler (PSC): 71
    • Counter Mode: Up
    • Counter Period (ARR): 19999
    • Pulse: 初始占空比(如1500对应90°)
    • CH Polarity: High

关键配置截图示意:

[图示位置] TIM3 Configuration ├── Clock Source: Internal Clock ├── Channel1: PWM Generation CH1 ├── Channel2: PWM Generation CH2 ├── Channel3: PWM Generation CH3 └── Channel4: PWM Generation CH4 Parameter Settings ├── Prescaler: 71 ├── Counter Period: 19999 └── Pulse: 1500 (各通道可不同)

2.3 生成工程代码

  1. 设置工程名称和位置
  2. 选择Toolchain为MDK-ARM
  3. 生成代码前勾选"Generate peripheral initialization as a pair of .c/.h files"

3. MDK代码实现与优化

3.1 基础PWM输出

生成的代码已经包含TIM3初始化配置,我们只需启动PWM通道:

/* 启动所有PWM通道 */ HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3); HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_4);

3.2 角度控制函数实现

为方便控制,我们可以封装一个角度设置函数:

/** * @brief 设置舵机角度 * @param htim: 定时器句柄 * @param Channel: PWM通道 * @param angle: 目标角度(0-180) * @retval None */ void Servo_SetAngle(TIM_HandleTypeDef *htim, uint32_t Channel, uint8_t angle) { // 角度限幅 angle = angle > 180 ? 180 : angle; // 角度转比较值(500-2500对应0-180°) uint16_t ccr = 500 + angle * (2000 / 180); // 设置占空比 __HAL_TIM_SET_COMPARE(htim, Channel, ccr); }

使用示例:

// 设置通道1为45°,通道2为135° Servo_SetAngle(&htim3, TIM_CHANNEL_1, 45); Servo_SetAngle(&htim3, TIM_CHANNEL_2, 135);

3.3 平滑运动控制

直接跳变角度会导致舵机抖动,我们可以实现缓动效果:

void Servo_SmoothMove(TIM_HandleTypeDef *htim, uint32_t Channel, uint8_t start_angle, uint8_t end_angle, uint16_t duration) { int16_t step = (end_angle > start_angle) ? 1 : -1; uint16_t delay = duration / abs(end_angle - start_angle); for(uint8_t angle = start_angle; angle != end_angle; angle += step) { Servo_SetAngle(htim, Channel, angle); HAL_Delay(delay); } Servo_SetAngle(htim, Channel, end_angle); }

4. 实战案例:机械臂控制

假设我们控制一个4自由度机械臂,每个关节对应一个舵机:

// 定义关节枚举 typedef enum { BASE_JOINT = TIM_CHANNEL_1, SHOULDER_JOINT = TIM_CHANNEL_2, ELBOW_JOINT = TIM_CHANNEL_3, GRIPPER_JOINT = TIM_CHANNEL_4 } JointType; // 初始化位置 void Arm_Init(void) { Servo_SetAngle(&htim3, BASE_JOINT, 90); Servo_SetAngle(&htim3, SHOULDER_JOINT, 45); Servo_SetAngle(&htim3, ELBOW_JOINT, 135); Servo_SetAngle(&htim3, GRIPPER_JOINT, 0); } // 执行抓取动作 void Arm_GrabObject(uint8_t x_pos) { // 底座转向目标 Servo_SmoothMove(&htim3, BASE_JOINT, 90, x_pos, 1000); // 手臂下探 Servo_SmoothMove(&htim3, SHOULDER_JOINT, 45, 30, 800); Servo_SmoothMove(&htim3, ELBOW_JOINT, 135, 150, 800); // 夹持器闭合 Servo_SmoothMove(&htim3, GRIPPER_JOINT, 0, 180, 500); // 复位 HAL_Delay(1000); Arm_Init(); }

4.1 常见问题排查

  1. 舵机无反应

    • 检查PWM信号是否输出(可用示波器观察)
    • 确认舵机电源充足(建议单独供电)
    • 验证GPIO配置是否正确
  2. 舵机抖动

    • 确保PWM周期稳定在20ms
    • 检查电源是否稳定(加装大电容滤波)
    • 避免占空比突变,使用平滑移动函数
  3. 角度不准确

    • 校准舵机中立点(通常1500μs)
    • 检查机械结构是否卡顿
    • 考虑使用反馈舵机或额外加装电位器检测

5. 进阶技巧与性能优化

5.1 使用DMA自动更新占空比

对于需要频繁更新角度的应用,可以配置DMA减轻CPU负担:

// CubeMX中启用TIM3 CH1的DMA // 配置为Memory to Peripheral, 循环模式 uint16_t ccr_values[4] = {1500, 1500, 1500, 1500}; void Servo_UpdateByDMA(void) { HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *)ccr_values, 4); } // 需要更新时直接修改ccr_values数组

5.2 多舵机同步控制

通过定时器中断实现精确的同步更新:

// 在TIM3初始化后启用更新中断 __HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE); // 中断回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM3) { static uint8_t step = 0; // 按步骤更新各通道角度 switch(step++ % 4) { case 0: Servo_SetAngle(&htim3, TIM_CHANNEL_1, new_angle1); break; case 1: Servo_SetAngle(&htim3, TIM_CHANNEL_2, new_angle2); break; case 2: Servo_SetAngle(&htim3, TIM_CHANNEL_3, new_angle3); break; case 3: Servo_SetAngle(&htim3, TIM_CHANNEL_4, new_angle4); break; } } }

5.3 低功耗考虑

对于电池供电设备:

  • 在舵机静止时降低PWM频率(如从50Hz降到10Hz)
  • 使用HAL_TIM_PWM_Stop关闭不使用的通道
  • 选择数字舵机(如MG996R)替代模拟舵机,功耗更低

在实际项目中,我发现为每个舵机添加0.1μF的陶瓷电容和100μF的电解电容组合,能显著减少电源噪声带来的抖动问题。特别是在使用廉价舵机时,电源滤波对稳定性的提升非常明显。

← 返回列表