DIY智能小车核心:STM32 HAL库驱动电机与编码器测速全攻略(含PCB与源码)
STM32智能小车电机控制与编码器测速实战指南引言在机器人爱好者和智能车竞赛选手的圈子里基于STM32的智能小车一直是热门项目。这类项目最核心的挑战之一就是如何精准控制电机转速并实时获取运动数据。不同于简单的让轮子转起来真正的难点在于实现闭环控制——这意味着我们需要同时掌握电机驱动和编码器反馈两套系统。本文将带你从零开始构建一个完整的智能小车运动控制模块。我们会使用STM32 HAL库来简化开发流程重点解决三个关键问题如何通过PWM精确调节电机转速、如何利用编码器获取实时速度反馈、以及如何将两者整合成闭环系统。不同于市面上泛泛而谈的教程这里会深入硬件连接细节、代码实现原理以及实际调试中可能遇到的坑。1. 硬件架构设计与选型1.1 电机驱动板的选择与原理市面上的电机驱动方案五花八门从简单的L298N到专业的DRV系列芯片。经过多次项目实践我强烈推荐使用集成度高的驱动板比如基于DRV8701的设计。这类板子通常具备以下优势双路LDO输出可为编码器和指示灯单独供电3.3V和4.8V电流可调通过改变驱动电流适配不同功率的MOS管隔离设计有效防止电机干扰影响控制电路典型引脚配置如下表引脚标识功能描述连接目标L_EN/R_ENPWM调速输入STM32定时器输出L_PH/R_PH方向控制STM32 GPIOL_A/L_B左编码器AB相STM32编码器接口R_A/R_B右编码器AB相STM32编码器接口1.2 STM32最小系统设计对于智能小车应用STM32F103C8T6俗称蓝 pill已经足够胜任。关键要注意// 典型时钟配置在CubeMX中设置 HCLK 72MHz PCLK1 36MHz PCLK2 72MHz提示务必启用外部晶振8MHz作为时钟源内部RC振荡器的精度无法满足编码器计数需求。2. 软件环境配置2.1 CubeMX基础设置使用STM32CubeMX可以大幅减少底层配置工作量。以下是关键步骤选择正确的芯片型号STM32F103C8Tx配置调试接口SWD模式设置时钟树达到72MHz主频启用必要的外设定时器1PWM生成定时器2编码器接口定时器3速度计算中断USART1调试输出2.2 定时器特殊配置PWM定时器TIM1配置要点预分频器(Prescaler)71 72MHz/(711)1MHz自动重装载值(Counter Period)99 1MHz/10010kHz PWM频率通道模式PWM Generation CHx编码器定时器TIM2配置要点编码器模式Encoder Mode TI1 and TI24倍频自动重装载值6553516位最大值滤波器(IC Filter)建议设为6-8以消除抖动3. 核心代码实现3.1 电机驱动控制电机控制主要涉及两个函数设置方向和调节速度。// 设置电机方向 void Set_Motor_Direction(Motor_Type motor, Direction_Type dir) { if(motor MOTOR_LEFT) { HAL_GPIO_WritePin(L_PH_GPIO_Port, L_PH_Pin, dir); } else { HAL_GPIO_WritePin(R_PH_GPIO_Port, R_PH_Pin, dir); } } // 设置电机速度0-100% void Set_Motor_Speed(Motor_Type motor, uint8_t speed) { uint16_t compare speed * 99 / 100; // 映射到0-99 if(motor MOTOR_LEFT) { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, compare); } else { __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_4, compare); } }3.2 编码器速度计算速度计算的核心在于定时中断中处理编码器脉冲。这里需要三个关键参数编码器线数电机转一圈产生的脉冲数如13线减速比电机到轮子的减速比例如30:1采样周期计算速度的时间间隔如10ms// 在tim.c文件中添加回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim3) { // 10ms定时器 static int16_t last_count 0; int16_t current_count __HAL_TIM_GET_COUNTER(htim2); int16_t delta current_count - last_count; // 处理计数器溢出 if(delta 32767) delta - 65536; if(delta -32767) delta 65536; // 计算实际转速转/分钟 float rpm (delta / 4.0 / ENCODER_LINES / GEAR_RATIO) * 6000; last_count current_count; // 此处可以添加PID计算等控制逻辑 } }注意__HAL_TIM_GET_COUNTER()返回的是有符号16位整数但STM32的计数器实际是无符号的。需要特殊处理溢出情况。4. 系统集成与调试技巧4.1 硬件布线建议电机电源与控制电源要分开供电编码器信号线建议使用双绞线所有GND最终要单点共地在PWM信号线上串联100Ω电阻可减少振铃4.2 常见问题排查电机不转检查nSLEEP引脚是否为高测量PWM信号是否到达驱动板确认PH方向信号电平正确编码器读数异常检查AB相是否接反尝试调整输入滤波器参数确认编码器供电电压稳定速度波动大检查机械连接是否牢固尝试增加PID控制器的微分项确认采样周期与PWM频率匹配5. 进阶优化方向当基础功能实现后可以考虑以下优化加入PID闭环控制使用编码器反馈实时调整PWM占空比实现运动轨迹记录通过编码器累计值计算行驶距离添加电流检测在驱动板上加入采样电阻监测电机电流开发上位机调试界面通过蓝牙或无线模块实时监控参数// 简单的PID实现示例 typedef struct { float Kp, Ki, Kd; float integral; float prev_error; } PID_Controller; float PID_Update(PID_Controller* pid, float setpoint, float actual) { float error setpoint - actual; pid-integral error; if(pid-integral 1000) pid-integral 1000; if(pid-integral -1000) pid-integral -1000; float derivative error - pid-prev_error; pid-prev_error error; return pid-Kp*error pid-Ki*pid-integral pid-Kd*derivative; }在实际项目中我发现机械结构的精度对编码器测量影响很大。曾经遇到过一个案例小车直线行驶时左右轮速差始终有5%排查后发现是其中一个联轴器存在轻微打滑。因此建议在软件调试前先确保机械部分足够可靠。