1. STM32寻迹小车电机驱动方案设计
在嵌入式硬件开发中,电机驱动是智能小车项目的核心模块之一。我最近完成了一个基于STM32的寻迹小车项目,其中PWM驱动电机模块的设计让我积累了不少实战经验。这个方案最大的特点是通过"同侧电机并联"的方式,用最少的硬件资源实现了四轮独立控制的效果。
1.1 同侧电机并联原理
所谓同侧电机并联,就是把位于小车同一侧的两个电机并联为一组。具体来说:
- 左前和左后电机并联为左侧组
- 右前和右后电机并联为右侧组
这种连接方式的优势非常明显:
- 简化控制:4个独立电机简化为2组,每组内的两个电机同步运转
- 节省资源:PWM通道需求从4路减少到2路
- 降低成本:减少电机驱动芯片的使用数量
注意:并联后总电流会翻倍,必须确保电源和驱动板能承受这个电流。例如,单个电机工作电流1A时,并联后组电流将达到2A。
1.2 硬件选型考量
在硬件选择上,我经过多次对比测试后确定了以下配置:
- 主控芯片:STM32F103C8T6,性价比高,资源丰富
- 电机驱动:L298N双H桥驱动模块,支持最大2A电流
- 电源系统:18650锂电池组(7.4V)+ LM2596降压模块(5V给单片机供电)
这里特别要强调的是,电机绝对不能直接连接单片机引脚。我曾经在一次调试中不小心直接连接,结果瞬间烧毁了GPIO口。L298N这类驱动板不仅提供电流放大,还能实现正反转控制,是必不可少的中间环节。
2. PWM驱动实现细节
2.1 STM32的PWM输出配置
STM32的PWM功能是通过定时器的输出比较功能实现的。在我的方案中,使用了TIM2定时器的两个通道:
- PA0(TIM2_CH1):左侧电机组PWM
- PA1(TIM2_CH2):右侧电机组PWM
配置过程主要分为三个步骤:
- GPIO初始化:将PA0和PA1设置为复用推挽输出模式
c复制GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
- 定时器基础配置:设置PWM频率和计数模式
c复制TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 分频系数
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
- PWM通道配置:设置输出比较模式和极性
c复制TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_Pulse = 0; // 初始占空比0%
TIM_OC1Init(TIM2, &TIM_OCInitStructure); // 通道1
TIM_OC2Init(TIM2, &TIM_OCInitStructure); // 通道2
2.2 PWM参数计算技巧
PWM频率的选择对电机控制效果影响很大。经过多次测试,我发现10kHz左右是比较理想的频率范围。计算过程如下:
系统时钟72MHz,预分频71,自动重装载值999:
code复制PWM频率 = 72MHz / (71+1) / (999+1) = 10kHz
占空比调节通过修改TIM_Pulse值实现,范围0-999对应0%-100%。例如设置50%占空比:
c复制TIM_SetCompare1(TIM2, 500); // 左侧电机50%速度
TIM_SetCompare2(TIM2, 500); // 右侧电机50%速度
3. 方向控制实现方案
3.1 方向控制引脚配置
除了PWM速度控制外,每组电机还需要两个GPIO控制方向。在我的设计中:
- PB1、PB2:左侧电机方向控制
- PB3、PB4:右侧电机方向控制
配置为普通推挽输出模式:
c复制GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
3.2 电机转向逻辑实现
L298N驱动模块的方向控制真值表如下:
| IN1 | IN2 | 电机状态 |
|---|---|---|
| 1 | 0 | 正转 |
| 0 | 1 | 反转 |
| 0 | 0 | 刹车 |
| 1 | 1 | 刹车 |
对应的控制函数示例:
c复制void Motor_SetDirection(GPIO_TypeDef* GPIOx, uint16_t IN1_Pin, uint16_t IN2_Pin, uint8_t dir)
{
if(dir == FORWARD) {
GPIO_SetBits(GPIOx, IN1_Pin);
GPIO_ResetBits(GPIOx, IN2_Pin);
}
else if(dir == BACKWARD) {
GPIO_ResetBits(GPIOx, IN1_Pin);
GPIO_SetBits(GPIOx, IN2_Pin);
}
else { // BRAKE
GPIO_ResetBits(GPIOx, IN1_Pin);
GPIO_ResetBits(GPIOx, IN2_Pin);
}
}
4. 系统集成与调试经验
4.1 硬件连接注意事项
在实际组装过程中,有几个容易出错的地方需要特别注意:
-
电源隔离:单片机电源和电机电源要分开供电,共地即可。我曾经因为共电源导致单片机复位不稳定。
-
走线规范:电机驱动线要尽量短而粗,避免长距离平行走线引入干扰。
-
散热处理:L298N在长时间大电流工作时发热严重,必须加装散热片。
-
保护电路:在电机两端并联续流二极管,防止反电动势损坏驱动芯片。
4.2 软件调试技巧
在软件开发过程中,我总结了以下调试方法:
-
分步验证法:
- 先单独测试PWM输出是否正常(用示波器观察波形)
- 再测试方向控制GPIO是否正确
- 最后整合测试电机实际运转
-
安全防护代码:
c复制// 上电默认所有电机停止
GPIO_ResetBits(GPIOB, GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4);
TIM_SetCompare1(TIM2, 0);
TIM_SetCompare2(TIM2, 0);
- 参数微调经验:
- 电机启动时需要稍高的占空比(约30%)
- 低速时PWM频率可适当降低(5kHz左右更平稳)
- 加入加速度控制可以避免急启急停
4.3 常见问题排查
在实际项目中遇到的一些典型问题及解决方法:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 电机不转 | 电源未接通 | 检查电池连接和开关 |
| 只有一个电机转 | 接线错误 | 检查对应相的接线 |
| 电机转动不稳定 | PWM频率不合适 | 调整定时器分频系数 |
| 驱动芯片发烫 | 电流过大或散热不良 | 检查负载,加装散热片 |
| 单片机复位 | 电源干扰 | 加强电源滤波,分开供电 |
5. 性能优化与扩展
5.1 闭环速度控制
基础的开环控制虽然简单,但在负载变化时速度会波动。我后来加入了编码器反馈实现闭环控制:
- 在电机轴上安装光电编码器
- 通过定时器输入捕获测量转速
- 使用PID算法动态调整PWM占空比
c复制// 简易PID实现示例
float PID_Update(PID_TypeDef* pid, float error)
{
pid->integral += error;
float derivative = error - pid->prev_error;
pid->prev_error = error;
return pid->Kp * error +
pid->Ki * pid->integral +
pid->Kd * derivative;
}
5.2 运动控制算法
对于寻迹小车,我开发了差速转向算法:
- 根据传感器反馈计算偏离程度
- 动态调整左右轮速差
c复制void SetMotorsSpeed(int baseSpeed, int turnOffset)
{
int leftSpeed = baseSpeed - turnOffset;
int rightSpeed = baseSpeed + turnOffset;
// 限幅处理
leftSpeed = constrain(leftSpeed, 0, MAX_SPEED);
rightSpeed = constrain(rightSpeed, 0, MAX_SPEED);
TIM_SetCompare1(TIM2, leftSpeed);
TIM_SetCompare2(TIM2, rightSpeed);
}
5.3 能耗优化技巧
在电池供电场景下,我通过以下方式延长续航:
- 在直线行驶时降低PWM频率(减少开关损耗)
- 刹车时启用能量回馈(需要支持此功能的驱动芯片)
- 空闲时自动进入低功耗模式
经过实际测试,这些优化措施能让小车的续航时间提升30%以上。