1. PWM控制电机转速的核心原理
第一次用单片机控制电机时,我盯着转动的电机看了足足十分钟。那种通过几行代码就能精确控制物理世界的感觉,至今难忘。PWM(脉宽调制)技术正是实现这种控制的关键,它的精妙之处在于用数字信号模拟出了模拟量的效果。
直流电机转速与供电电压成正比这个特性,是PWM控制的基础。但单片机GPIO只能输出0V或3.3V这样的固定电平,无法直接提供可变电压。这里PWM就像个聪明的"电压骗子"——通过快速开关GPIO,制造出不同比例的高低电平,让电机"感受"到不同的平均电压。
关键理解:电机作为惯性负载,对电压变化的响应存在滞后性。当PWM频率足够高时,电机的机械惯性会自然"平滑"这些脉冲,表现出与平均电压对应的转速特性。
2. PWM参数详解与电机控制实践
2.1 PWM三大核心参数解析
2.1.1 周期与频率的选择
周期(T)和频率(f)的关系是f=1/T。对于常见的直流有刷电机:
- 1kHz-5kHz:基础频率范围,可能产生可闻噪音
- 16kHz以上:超出人耳听觉范围,推荐工作频率
- 20kHz:常见无噪音控制频率
我在实际项目中测试发现,使用24V供电的370电机在20kHz时,既保证了控制精度,又完全消除了高频啸叫。
2.1.2 占空比的精确控制
占空比(Duty Cycle)计算公式为:
code复制占空比 = (高电平时间 / 周期时间) × 100%
通过STM32的PWM模块,我们可以实现0.1%级的占空比调节精度。以下是典型占空比对应的电机状态:
| 占空比 | 电机状态 | 备注 |
|---|---|---|
| 0% | 完全停止 | GPIO持续低电平 |
| 30% | 低速运转 | 启动临界点 |
| 50% | 半速运转 | 线性区间中点 |
| 75% | 接近全速 | 效率最佳区间 |
| 100% | 全速运转 | GPIO持续高电平 |
2.2 硬件电路设计要点
2.2.1 驱动电路的必要性
单片机GPIO通常只能提供20mA左右的驱动电流,而即便是小型直流电机,启动电流也可能达到100mA以上。必须使用电机驱动电路,常见方案有:
-
晶体管驱动方案
- NPN三极管+续流二极管
- 成本低,适合单个电机控制
- 典型电路:GPIO → 基极电阻 → NPN → 电机
-
MOSFET驱动方案
- IRF540N等功率MOS管
- 导通电阻小,效率高
- 需要栅极驱动电路
-
专用驱动IC
- L298N双H桥驱动器
- TB6612FNG等现代驱动芯片
- 集成保护电路,支持正反转
重要提示:无论哪种方案,都必须并联续流二极管(通常选用1N5819或1N4007),用于泄放电机线圈断电时产生的反向电动势。
2.2.2 实际电路示例
这是我在一个智能小车项目中验证过的可靠电路:
c复制// STM32 GPIO配置示例
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
// PWM定时器配置
TIM_HandleTypeDef htim3;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 84-1; // 84MHz/84 = 1MHz
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 50-1; // 1MHz/50 = 20kHz
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
TIM_OC_InitTypeDef sConfigOC = {0};
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0; // 初始占空比0%
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);
3. 软件实现与优化技巧
3.1 基础PWM生成方法
不同单片机平台生成PWM的方式各有特点:
1. 定时器直接配置法(STM32 HAL库示例)
c复制// 设置占空比
void Set_Motor_Speed(uint8_t percentage)
{
if(percentage > 100) percentage = 100;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, (htim3.Init.Period * percentage)/100);
}
2. Arduino平台简易实现
arduino复制const int motorPin = 9; // 必须选择带PWM功能的引脚
void setup() {
pinMode(motorPin, OUTPUT);
// 设置PWM频率为20kHz(仅部分板型支持)
#if defined(__AVR_ATmega2560__)
TCCR1B = (TCCR1B & 0b11111000) | 0x01;
#endif
}
void loop() {
analogWrite(motorPin, 128); // 50%占空比
}
3.2 高级控制技巧
3.2.1 软启动保护
电机从静止到运动需要克服静摩擦力,直接施加高占空比可能导致:
- 电流冲击损坏驱动电路
- 机械应力损伤传动部件
- 电源电压跌落影响系统稳定
实现软启动的代码示例:
c复制void Soft_Start(uint8_t target_speed, uint16_t duration_ms)
{
uint16_t steps = duration_ms / 10;
uint8_t increment = target_speed / steps;
for(uint16_t i=0; i<steps; i++){
Set_Motor_Speed(increment * i);
HAL_Delay(10);
}
Set_Motor_Speed(target_speed);
}
3.2.2 转速闭环控制
开环PWM控制存在负载变化导致转速不稳的问题。增加编码器反馈可实现闭环控制:
c复制// 伪代码示例
void Motor_PID_Control(float target_rpm)
{
static float integral = 0;
static float last_error = 0;
float current_rpm = Read_Encoder_Speed();
float error = target_rpm - current_rpm;
integral += error * dt;
float derivative = (error - last_error) / dt;
float output = Kp*error + Ki*integral + Kd*derivative;
Set_Motor_Speed(constrain(output, 0, 100));
last_error = error;
}
4. 常见问题与解决方案
4.1 电机不转的排查流程
-
电源检查
- 测量电机两端电压是否正常
- 确认电源功率足够(启动电流可能是额定值的3-5倍)
-
信号路径检查
- 用示波器确认PWM信号到达驱动芯片
- 检查所有接线端子是否牢固
-
软件配置检查
- 确认定时器时钟使能
- 验证PWM通道映射正确
- 检查GPIO复用功能配置
4.2 典型异常现象处理
现象1:电机抖动不转
- 可能原因:PWM频率过低
- 解决方案:提高至16kHz以上
现象2:驱动芯片发热严重
- 可能原因:死区时间不足导致上下管直通
- 解决方案:调整驱动芯片的死区时间设置
现象3:转速不稳定
- 可能原因:电源内阻过大
- 解决方案:靠近电机端并联大容量电解电容(如1000μF)
经验分享:我在调试四轴飞行器时发现,使用开关电源直接供电时,PWM变化会导致电源电压波动。解决方法是在电源端增加LC滤波电路(100μH电感+470μF电容)。
5. 进阶应用实例
5.1 多电机同步控制
在机械臂控制中,需要协调多个关节电机。通过主从定时器同步技术,可以确保所有PWM信号相位一致:
c复制// STM32定时器同步配置示例
void TIM_Synchronization_Init(void)
{
// 主定时器配置
htim1.Instance = TIM1;
htim1.Init.RepetitionCounter = 0;
HAL_TIM_PWM_Init(&htim1);
// 从定时器配置
htim3.Instance = TIM3;
HAL_TIM_PWM_Init(&htim3);
// 设置定时器同步
__HAL_TIM_SET_MASTER_MODE(&htim1, TIM_MASTERSLAVEMODE_ENABLE);
__HAL_TIM_SET_SLAVE_MODE(&htim3, TIM_SLAVEMODE_TRIGGER);
__HAL_TIM_SET_TRIGGER_SOURCE(&htim3, TIM_TS_ITR1);
}
5.2 正弦波驱动技术
对于需要平稳运行的应用,可以使用PWM生成正弦波驱动:
c复制// 生成正弦PWM波
void Generate_Sine_PWM(uint16_t amplitude, uint16_t frequency_hz)
{
const uint16_t samples = 100;
static uint16_t index = 0;
float radian = 2 * PI * index / samples;
uint16_t value = amplitude * (1 + sin(radian)) / 2;
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, value);
index = (index + 1) % samples;
HAL_Delay(1000/(frequency_hz * samples));
}
在实际使用中,我发现将PWM分辨率提高到16位(如STM32的HRTIM),可以显著改善低速时的转矩平稳性。特别是在需要精密控制的CNC设备上,这种技术可以将步进电机的振动降低到几乎不可察觉的程度。