STM32F103C8T6定时器中断驱动28BYJ-48步进电机(ULN2003模块)保姆级教程

📅 2026/7/1 5:42:08
STM32F103C8T6定时器中断驱动28BYJ-48步进电机(ULN2003模块)保姆级教程
STM32F103C8T6定时器中断驱动28BYJ-48步进电机实战指南硬件准备与基础原理28BYJ-48步进电机配合ULN2003驱动模块的组合因其低廉的价格和稳定的性能成为嵌入式入门学习的热门选择。这套系统主要由三个核心部件构成STM32F103C8T6最小系统板Blue Pill、28BYJ-48四相五线步进电机以及ULN2003达林顿阵列驱动模块。关键部件特性对比部件名称工作电压接口类型主要参数STM32F103C8T63.3VGPIO/定时器72MHz主频16KB SRAM28BYJ-485V4相5线步距角5.625°64步/转ULN20035-15V8引脚DIP500mA驱动能力注意虽然STM32工作电压为3.3V但ULN2003模块内部有光耦隔离可以直接用3.3V信号控制5V电机电路。步进电机的工作原理基于电磁铁的顺序激磁。28BYJ-48采用四相八拍驱动方式即按照A-AB-B-BC-C-CD-D-DA的顺序循环通电。每个脉冲使转子转动一个步距角通过控制脉冲频率可以精确控制转速。硬件连接与CubeMX配置物理连接示意图正确的硬件连接是项目成功的第一步。参考以下接线方式电源部分STM32的3.3V引脚连接开发板供电ULN2003模块的5V接独立5V电源切勿与STM32共用3.3V电源确保STM32与ULN2003模块共地信号线连接STM32 PB5 - ULN2003 IN1 STM32 PB6 - ULN2003 IN2 STM32 PB7 - ULN2003 IN3 STM32 PB8 - ULN2003 IN4CubeMX定时器配置使用STM32CubeMX可以快速生成初始化代码以下是关键配置步骤打开CubeMX选择STM32F103C8Tx系列芯片在Pinout视图中配置PB5-PB8为GPIO_Output定时器配置选择TIM3Clock Source选择Internal ClockPrescaler设为7172MHz/(711)1MHzCounter Period设为9991MHz/10001kHz中断频率启用TIM3全局中断生成代码时注意Toolchain/IDE选择适合的开发环境MDK-ARM/IAR/TrueStudio勾选Generate peripheral initialization as a pair of .c/.h files/* TIM3初始化代码片段 */ static void MX_TIM3_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig {0}; TIM_MasterConfigTypeDef sMasterConfig {0}; htim3.Instance TIM3; htim3.Init.Prescaler 71; htim3.Init.CounterMode TIM_COUNTERMODE_UP; htim3.Init.Period 999; htim3.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(htim3); sClockSourceConfig.ClockSource TIM_CLOCKSOURCE_INTERNAL; HAL_TIM_ConfigClockSource(htim3, sClockSourceConfig); sMasterConfig.MasterOutputTrigger TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode TIM_MASTERSLAVEMODE_DISABLE; HAL_TIMEx_MasterConfigSynchronize(htim3, sMasterConfig); }中断服务程序与电机控制逻辑步进电机驱动时序28BYJ-48采用四相八拍驱动方式每个步进周期包含8个状态。我们可以用位模式表示各相导通状态const uint8_t stepPattern[8] { 0x09, // 1001 (AD) 0x01, // 0001 (A) 0x03, // 0011 (AB) 0x02, // 0010 (B) 0x06, // 0110 (BC) 0x04, // 0100 (C) 0x0C, // 1100 (CD) 0x08 // 1000 (D) };定时器中断服务函数在stm32f1xx_it.c中添加TIM3中断处理void TIM3_IRQHandler(void) { static uint8_t step 0; static uint32_t pulseCount 0; HAL_TIM_IRQHandler(htim3); if(__HAL_TIM_GET_FLAG(htim3, TIM_FLAG_UPDATE) ! RESET) { __HAL_TIM_CLEAR_FLAG(htim3, TIM_FLAG_UPDATE); // 控制电机转动 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5, (stepPattern[step] 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6, (stepPattern[step] 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_7, (stepPattern[step] 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, (stepPattern[step] 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); // 步进计数 if(direction CW) { step (step 1) % 8; } else { step (step 0) ? 7 : (step - 1); } // 脉冲计数 if(pulseCount targetPulses) { HAL_TIM_Base_Stop_IT(htim3); motorRunning 0; } } }实际调试与性能优化步数计算与实测差异28BYJ-48电机的理论步数为步距角 5.625° 每转步数 360° / 5.625° 64步 由于采用半步驱动(8拍/周期) 实际每转脉冲数 64 × 64 4096但在实际测试中许多开发者发现2048个脉冲即可完成完整旋转。这主要由于电机内部齿轮减速比为1:64转子转64圈输出轴转1圈部分批次电机可能采用不同的减速比驱动模式选择全步/半步影响实际步数调试建议先用较低速度如100rpm测试实际步数逐步调整至所需精度。速度控制与加速度曲线直接以高速启动可能导致失步推荐采用梯形加速度曲线void setMotorSpeed(uint16_t rpm) { // 计算定时器重载值 // 脉冲频率 (rpm * 4096) / 60 uint32_t pulseFreq (rpm * 4096UL) / 60; uint32_t timerClk 1000000; // 1MHz (72MHz/72) uint32_t arrValue (timerClk / pulseFreq) - 1; __HAL_TIM_SET_AUTORELOAD(htim3, arrValue); } void accelerate(uint16_t startRpm, uint16_t endRpm, uint16_t durationMs) { uint16_t steps durationMs / 10; // 每10ms调整一次 float increment (float)(endRpm - startRpm) / steps; for(int i0; isteps; i) { setMotorSpeed(startRpm (uint16_t)(increment * i)); HAL_Delay(10); } }进阶应用与扩展功能位置闭环控制增加限位开关可实现简单的位置闭环// 初始化限位开关GPIO void initLimitSwitches(void) { GPIO_InitTypeDef GPIO_InitStruct {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1; GPIO_InitStruct.Mode GPIO_MODE_INPUT; GPIO_InitStruct.Pull GPIO_PULLUP; HAL_GPIO_Init(GPIOA, GPIO_InitStruct); } // 在中断中检查限位 if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) GPIO_PIN_RESET) { // 碰到左限位 direction CW; // 改变方向 }串口命令控制通过串口接收控制指令的示例void processUartCommand(uint8_t* cmd) { if(strncmp(cmd, RUN, 3) 0) { sscanf(cmd3, %hu,%hu, targetPulses, targetRpm); direction CW; motorRunning 1; HAL_TIM_Base_Start_IT(htim3); } else if(strncmp(cmd, STOP, 4) 0) { HAL_TIM_Base_Stop_IT(htim3); motorRunning 0; } }实际项目中遇到的一个典型问题是电机偶尔会失步。通过示波器检查发现当快速改变方向时ULN2003的输出存在约100us的切换延迟。解决方法是在方向改变时插入5ms的延时或者降低最高运行速度约20%。