1. 硬件准备与电路连接搞过步进电机驱动的朋友都知道20BYJ46这种小型4相步进电机虽然体积小但驱动起来还是需要点技巧的。我刚开始玩的时候直接用STM32的IO口去驱动结果电机纹丝不动后来才发现问题出在驱动电流不足上。这里给大家分享下我的实战经验。先说硬件搭配核心就三样STM32开发板我用的是NUCLEO-F767ZI、ULN2003驱动模块、20BYJ46步进电机。ULN2003这个芯片特别适合新手它相当于一个电流放大器把STM32微弱的控制信号转换成能驱动电机的强电流。实际接线时要注意几个关键点电源部分电机需要5V供电而STM32是3.3V系统。我建议用独立电源给电机供电但一定要把两个系统的GND连在一起不然信号无法正常传递。曾经因为忘记共地调试了半天才发现问题。信号连接把STM32的四个GPIO比如PD4-PD7接到ULN2003的输入端B1-B4输出端C1-C4接电机四相线。这里有个坑要注意不同厂家的20BYJ46线序可能不同我手头的这个电机线序是蓝(A)、黑(A-)、棕(B)、黄(B-)和网上常见的标记不一样。保护电路ULN2003的COM端一定要接电机电源正极这个引脚是用来泄放电机断电时产生的反向电动势的。有次我没接COM端连续烧了两个驱动芯片后来查资料才知道这是必须的。2. STM32CubeMX工程配置现在进入软件配置环节。我用的是STM32CubeIDE这个环境对新手特别友好图形化配置省去了很多底层代码编写的工作。打开CubeMX后跟着下面几步操作2.1 时钟配置首先配置系统时钟。F767ZI支持216MHz主频但实际使用不需要这么高。我一般设置为108MHz这样定时器分频更方便计算。在Clock Configuration标签页按照以下参数设置HCLK 108MHzPCLK1 27MHz (APB1)PCLK2 54MHz (APB2)2.2 GPIO设置接着配置控制引脚。在Pinout视图找到GPIOD把PD4-PD7设置为GPIO_Output模式初始电平设为低。这里有个小技巧在Configuration标签页的GPIO设置里把输出速度设为Very High这样可以确保信号切换更快速稳定。2.3 定时器配置精准控制步进电机的核心就是定时器。我用TIM1来做步进控制配置步骤如下选择TIM1模式选择Internal ClockPrescaler设为21599108MHz/(215991)5kHzCounter Period设为1995kHz/20025Hz开启定时器中断这样配置后定时器每40ms产生一次中断对应电机每秒走25步。实际项目中可以根据需要调整这些参数。2.4 串口配置可选为了方便调试我加上了USART3串口通信波特率设为115200。通过串口发送指令可以控制电机启停和转向这在调试阶段非常有用。3. 电机驱动代码实现配置好工程后开始编写核心驱动代码。HAL库的好处是封装了很多底层操作让我们的代码更简洁。3.1 定时器中断处理在stm32f7xx_it.c文件中找到TIM1中断服务函数添加步进控制逻辑。我采用8拍模式驱动比常见的4拍模式更平稳void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim1) { static uint8_t step 0; switch(step) { case 0: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_SET); break; case 1: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_SET); break; case 2: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_RESET); break; case 3: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_SET); break; case 4: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_RESET); break; case 5: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET); break; case 6: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_RESET); break; case 7: HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_RESET); break; } step (step 1) % 8; } }3.2 方向控制实现通过变量控制旋转方向只需要改变step的增减方向即可int direction 1; // 1正转-1反转 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim htim1) { static uint8_t step 0; // ... 同上一步的case处理 ... step (step direction 8) % 8; // 保证step始终在0-7范围内 } }3.3 速度控制技巧调整电机速度有两种方法改变定时器中断频率通过修改TIM1的Prescaler或Period值多步合并比如每两次中断才执行一步可以降低一半速度我推荐第一种方法因为步进电机对时序要求严格。在CubeMX里重新配置定时器参数或者在运行时动态修改void SetMotorSpeed(uint32_t speed) { htim1.Instance-ARR speed; // 修改自动重装载值 HAL_TIM_Base_Init(htim1); }4. 调试技巧与常见问题调试步进电机时遇到过不少坑这里分享几个实用经验4.1 电机不转的排查步骤先检查电源用万用表测量电机端电压是否达到5V测试信号通路用示波器看ULN2003输入输出信号检查线序尝试交换相线顺序有时电机厂家标注有误验证代码单独测试GPIO输出是否正常4.2 电机抖动问题处理如果电机转动不顺畅可能是以下原因供电不足尝试加大电源电流步进频率过高降低定时器中断频率机械负载过大20BYJ46扭矩较小不适合带太重负载4.3 发热问题优化电机或驱动芯片发热严重时降低驱动电压但不要低于额定电压改用半步或微步驱动模式增加散热片避免长时间堵转记得第一次做项目时电机运行半小时后烫得不能碰后来发现是程序bug导致电机持续堵转。加入堵转检测后问题就解决了。5. 进阶优化方向基础功能实现后可以考虑以下优化5.1 加减速曲线控制突然启停会导致电机失步我通常用S形加减速算法void UpdateSpeedProfile(void) { static uint32_t step 0; uint32_t new_arr CalculateScurveValue(step); htim1.Instance-ARR new_arr; }5.2 微步驱动虽然ULN2003不支持真正的微步但可以通过PWM模拟实现更精细的控制void SetMicrostep(uint8_t phase, uint8_t level) { // 用PWM占空比控制相电流大小 __HAL_TIM_SET_COMPARE(htim_pwm, phase_ch[phase], level); }5.3 位置闭环控制增加编码器反馈可以实现精准定位int32_t target_pos 1000; int32_t current_pos ReadEncoder(); void ControlLoop(void) { int32_t error target_pos - current_pos; if(abs(error) 10) { SetMotorDirection(error 0 ? 1 : -1); SetMotorSpeed(CalculateSpeed(error)); } }这些优化需要更多硬件支持但对提升系统性能很有帮助。我在一个机械臂项目中实现了0.1mm级的位置控制关键就是做好了加减速和闭环控制。