STM32CubeMX实战:基于霍尔编码器与L298N的直流电机闭环调速系统
1. 项目背景与硬件选型
直流电机闭环调速系统在智能小车、工业自动化等领域应用广泛。这次我选择STM32F103作为主控芯片,搭配L298N电机驱动模块和霍尔编码器,构建一个完整的调速系统。这种组合性价比高,特别适合学生和电子爱好者入门学习。
L298N模块最大支持46V电压、2A持续电流,内置双H桥电路,能同时驱动两台直流电机。我在实际测试中发现,当驱动电压超过24V时,模块发热明显增加,建议工作电压控制在7-12V范围内。霍尔编码器采用磁感应原理,相比光电编码器更耐灰尘干扰,虽然精度稍低(通常每圈13-15个脉冲),但对于常规速度控制完全够用。
硬件连接有个关键细节:必须确保STM32、L298N和编码器三者共地。我曾因忘记共地导致电机转速异常,排查了半天才发现问题。建议用万用表先测量各模块GND之间的导通性。
2. STM32CubeMX工程配置
打开CubeMX新建工程时,建议直接搜索"STM32F103RB"选择对应型号。时钟配置采用默认的8MHz晶振,通过PLL倍频到72MHz主频。这里要注意在RCC配置中正确选择"HSE(外部高速时钟)"作为时钟源。
定时器配置是核心部分:
- TIM1通道1配置为PWM输出,用于电机调速
- TIM2配置为编码器接口模式,捕获AB相信号
- TIM3配置为10ms定时中断,用于速度采样
编码器模式有个易错点:CubeMX默认的"Encoder Mode"会自动启用四倍频。比如霍尔编码器每圈13个脉冲,实际会计数13×4=52个脉冲。我在首次测试时没注意这个细节,导致速度计算错误。
3. 电机驱动电路详解
L298N的接线需要特别注意电源配置:
- 驱动电压(12V端子):接7-12V电源
- 逻辑电压(5V端子):当驱动电压≥7V时,保留跳线帽使用板载5V
- 使能端(ENA/ENB):拔掉跳线帽改用PWM控制
实测中发现一个有趣现象:当PWM占空比低于30%时,电机可能出现启动困难。解决方法是在代码中加入最小占空比限制,或者改用更先进的PID启动算法。
电机方向控制逻辑:
// 正转 HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_SET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_RESET); // 反转 HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, GPIO_PIN_SET);4. 编码器速度测量实现
霍尔编码器的AB相需要接到定时器的CH1和CH2引脚。在CubeMX中配置TIM2为"Encoder Mode"后,HAL库会自动处理脉冲计数。速度测量采用M法(频率法),即在固定时间间隔内统计脉冲数。
速度计算公式:
转速(rpm) = (ΔCount × 60) / (PPR × 采样周期(s) × 倍频数)其中PPR(每转脉冲数)为13,倍频数为4。
代码实现关键点:
// 在定时器中断中读取编码器值 int32_t current_count = __HAL_TIM_GET_COUNTER(&htim2); int32_t delta = current_count - last_count; // 处理计数器溢出 if(delta > 32767) delta -= 65536; else if(delta < -32767) delta += 65536; last_count = current_count; // 计算转速 float speed_rpm = (delta * 60.0f) / (13 * 0.01 * 4);5. PID调速算法实现
采用位置式PID算法,参数整定经验:
- Kp:从0.1开始逐步增加,直到出现轻微振荡
- Ki:设为Kp/10,消除静差
- Kd:通常设为0,除非系统响应很慢
实际代码实现:
typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; float PID_Update(PID_Controller* pid, float error, float dt) { float proportional = pid->Kp * error; pid->integral += pid->Ki * error * dt; pid->integral = constrain(pid->integral, -100, 100); // 积分限幅 float derivative = pid->Kd * (error - pid->prev_error) / dt; pid->prev_error = error; return proportional + pid->integral + derivative; }调试技巧:先用纯比例控制,等电机能基本跟随目标速度后再加入积分项。我在实验室测试时发现,加入微分项反而容易引起高频振荡,所以大多数情况下PI控制就足够了。
6. 系统集成与调试
将所有功能集成到主循环中:
- 每10ms读取编码器值计算速度
- 用PID算法计算PWM占空比
- 更新PWM输出
- 通过串口打印调试信息
常见问题排查:
- 电机不转:检查使能端是否接PWM,IN1/IN2电平组合
- 速度测量不准:确认编码器PPR和倍频设置
- 电机振动:降低PID参数或增加死区补偿
一个实用的调试技巧:先用固定占空比让电机运转,确认编码器读数正常后再启用闭环控制。我在开发过程中就曾因编码器接线错误,导致PID控制完全失效。
7. 性能优化技巧
经过多次测试,总结了几个提升系统性能的方法:
- 在TIM2的编码器中断中加入防溢出处理:
if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) { __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); if(encoder_dir == 1) overflow_count++; else overflow_count--; }- 使用移动平均滤波平滑速度采样:
#define FILTER_SIZE 5 float speed_buffer[FILTER_SIZE]; float filtered_speed = 0; // 在中断中更新 for(int i=FILTER_SIZE-1; i>0; i--) { speed_buffer[i] = speed_buffer[i-1]; } speed_buffer[0] = current_speed; filtered_speed = 0; for(int i=0; i<FILTER_SIZE; i++) { filtered_speed += speed_buffer[i]; } filtered_speed /= FILTER_SIZE;- 增加PWM死区补偿:当目标速度很小时,直接关闭电机驱动,避免"嗡嗡"声。
8. 扩展应用与改进方向
这个基础框架可以扩展更多实用功能:
- 加入速度曲线规划,实现平滑加减速
- 通过蓝牙或Wi-Fi接入无线控制
- 增加电流检测实现力矩控制
我在最近的项目中尝试加入了模糊PID控制,相比传统PID在变负载情况下表现更好。具体做法是根据速度误差和误差变化率动态调整PID参数。
对于更高精度的应用,可以考虑:
- 改用光电编码器(每圈数百脉冲)
- 使用FOC驱动方案替代L298N
- 升级到STM32F4系列,运行更复杂算法
这套系统虽然简单,但涵盖了嵌入式开发的完整流程:硬件选型、外设配置、算法实现和调试优化。建议初学者可以先用示波器观察PWM波形和编码器信号,加深对系统工作原理的理解。
