1. 项目概述
作为一名嵌入式开发工程师,我经常需要在项目中使用舵机实现精确的角度控制。今天我想分享一个基于STM32F103C8T6的PWM控制舵机方案,这是我在多个机器人项目中验证过的稳定方案。
STM32F103C8T6作为一款性价比极高的Cortex-M3内核MCU,其丰富的定时器资源非常适合舵机控制。通过本文,你将学会如何配置定时器产生精确的PWM信号,理解舵机工作原理,并掌握实际项目中的调试技巧。
2. 舵机控制原理深度解析
2.1 舵机PWM信号标准
舵机控制的核心在于理解其PWM信号规范。以常见的SG90和MG995舵机为例:
- 周期要求:固定20ms(50Hz),这是所有舵机控制的基础
- 脉宽范围:0.5ms-2.5ms对应0°-180°角度
- 控制精度:理论上每0.01ms脉宽变化对应约1°角度变化
注意:不同品牌舵机的脉宽范围可能略有差异,建议查阅具体型号的数据手册
2.2 舵机内部工作机制
舵机内部是一个典型的闭环控制系统:
- 控制板:解析PWM脉冲宽度
- 电位器:实时反馈当前角度
- 电机驱动:H桥电路驱动直流电机
- 减速齿轮组:提高扭矩并降低转速
工作流程如下:
mermaid复制graph TD
A[MCU输出PWM] --> B[舵机解析脉宽]
B --> C[转换为目标角度]
C --> D[与当前角度比较]
D -->|角度差| E[驱动电机转动]
E --> F[达到目标角度]
F --> G[锁定位置]
3. STM32硬件配置
3.1 定时器参数计算
STM32F103C8T6使用TIM2定时器产生PWM:
- 时钟源:72MHz(系统时钟)
- 预分频器(PSC):71
- 分频后时钟 = 72MHz/(71+1) = 1MHz
- 每个计数周期 = 1/1MHz = 1μs
- 自动重载值(ARR):19999
- 总周期 = (19999+1)*1μs = 20ms
配置代码示例:
c复制TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 19999;
TIM_TimeBaseStructure.TIM_Prescaler = 71;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
3.2 PWM输出配置
以TIM2_CH3(PA2引脚)为例:
c复制TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 1500; // 初始1.5ms(90°)
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_Cmd(TIM2, ENABLE);
4. 角度控制实现
4.1 脉宽与角度转换
角度控制函数实现:
c复制void SetServoAngle(uint16_t angle)
{
// 角度限幅(0-180)
angle = angle > 180 ? 180 : angle;
// 计算对应脉宽(单位:μs)
uint16_t pulse = 500 + angle * (2500-500)/180;
// 设置比较寄存器值
TIM_SetCompare3(TIM2, pulse);
}
4.2 多路舵机控制方案
当需要控制多个舵机时:
-
方案一:使用多个定时器
- 优点:各通道完全独立
- 缺点:占用硬件资源多
-
方案二:单定时器多通道
- 配置示例:
c复制// 初始化TIM2的CH1-CH4 TIM_OC1Init(TIM2, &TIM_OCInitStructure); TIM_OC2Init(TIM2, &TIM_OCInitStructure); TIM_OC3Init(TIM2, &TIM_OCInitStructure); TIM_OC4Init(TIM2, &TIM_OCInitStructure);
5. 实际项目经验分享
5.1 常见问题排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 舵机不响应 | 电源不足 | 确保5V/2A以上电源 |
| 角度抖动 | 信号干扰 | 缩短信号线,加滤波电容 |
| 发热严重 | 机械卡死 | 检查负载是否超限 |
5.2 性能优化技巧
-
电源处理:
- 舵机电源与MCU电源分开
- 每个舵机并联100μF电容
-
信号稳定性:
- 使用屏蔽线连接信号
- 信号线长度不超过50cm
-
软件优化:
c复制// 平滑移动算法示例 void SmoothMove(uint16_t targetAngle, uint8_t speed) { uint16_t current = GetCurrentPulse(); while(abs(current - targetAngle) > 2) { current += (targetAngle > current) ? speed : -speed; SetServoAngle(current); Delay_ms(20); } }
6. 进阶应用
6.1 舵机校准方法
-
机械零点校准:
c复制// 找到舵机机械零点 for(int i=0; i<180; i+=5){ SetServoAngle(i); Delay_ms(500); if(舵机停止转动) break; } -
软件补偿:
c复制// 角度补偿表 const uint8_t angleComp[180] = {...}; void SetCompensatedAngle(uint8_t angle) { SetServoAngle(angle + angleComp[angle]); }
6.2 舵机控制协议扩展
可扩展支持常见协议:
- 标准PWM
- 串口指令控制
- I²C总线控制
协议转换示例:
c复制void UART_CmdHandler(uint8_t cmd)
{
switch(cmd) {
case 0x01: SetServoAngle(0); break;
case 0x02: SetServoAngle(90); break;
case 0x03: SetServoAngle(180); break;
default: break;
}
}
7. 项目总结
通过这个项目,我总结了几个关键经验:
- 定时器配置必须精确,20ms周期误差应小于±0.1ms
- 机械结构设计要考虑舵机扭矩余量(至少30%)
- 多舵机系统要特别注意电源管理
一个完整的工程示例包含:
- 硬件原理图(电源电路、信号连接)
- 软件驱动(定时器配置、角度控制)
- 测试用例(角度精度测试、负载测试)
提示:开发时建议先用示波器验证PWM信号,再连接舵机
