步进电机驱动芯片 · STM32F103C8T6 · HAL库开发
ULN2003A 高压大电流达林顿晶体管阵列驱动芯片
ULN2003A 是一款高压、大电流达林顿晶体管阵列芯片,内部集成了 7 路 NPN 达林顿晶体管对。每路达林顿管由两个三极管级联组成,电流放大系数可达千倍以上,能够将微控制器输出的弱电流信号放大为足以驱动步进电机线圈的强电流信号。芯片内置 2.7kΩ 串联基极电阻,使其可直接与 TTL 或 5V CMOS 逻辑电平接口,无需额外驱动电路。每路输出端均集成 续流钳位二极管,当驱动感性负载(如步进电机线圈)时,可有效抑制线圈断电瞬间产生的反向感应电动势,保护芯片和电路安全。
7路独立达林顿晶体管对,单通道持续电流500mA,峰值电流600mA,可直接驱动步进电机、继电器等感性负载。
每路输出内置钳位续流二极管,共阴极接COM端,有效抑制步进电机线圈换向时产生的反向感应电动势。
内置2.7kΩ串联基极电阻,输入端可直接连接STM32的GPIO推挽输出,兼容TTL和5V CMOS电平,无需电平转换。
配合28BYJ-48步进电机,通过4路GPIO输出8拍半步励磁序列,实现4096步/圈的精确角度控制,支持正反转。
| 引脚号 | 引脚名称 | 功能描述 | STM32连接 |
|---|---|---|---|
| 1 | IN1 | 输入1 — 驱动A相线圈 | PB6 |
| 2 | IN2 | 输入2 — 驱动B相线圈 | PB7 |
| 3 | IN3 | 输入3 — 驱动C相线圈 | PB8 |
| 4 | IN4 | 输入4 — 驱动D相线圈 | PB9 |
| 5-7 | IN5-IN7 | 输入5-7 — 备用通道(本例未使用) | — |
| 8 | GND | 地 — 逻辑与功率共地 | GND |
| 9 | COM | 钳位二极管公共端 — 接电机电源正极 | 5V |
| 10 | OUT7 | 输出7 — 备用(本例未使用) | — |
| 11 | OUT4 | 输出4 — 驱动D相线圈 | 电机D端 |
| 12 | OUT3 | 输出3 — 驱动C相线圈 | 电机C端 |
| 13 | OUT2 | 输出2 — 驱动B相线圈 | 电机B端 |
| 14 | OUT1 | 输出1 — 驱动A相线圈 | 电机A端 |
| 15-16 | NC | 未连接 | — |
步距角(输出轴)
减速比
半步模式步数/圈
线圈直流电阻
ULN2003A 驱动板与 28BYJ-48 步进电机实物外观
电路原理图设计与PCB封装布局
STM32F103C8T6 · HAL库 · 步进电机驱动程序源码解析
系统时钟配置为72MHz(HSE 8MHz × PLL 9倍频)。主程序初始化GPIO、TIM1定时器、OLED显示屏和步进电机驱动,在主循环中通过方向标志位Flag控制电机正转到达MOTOR_STEP(4096步)后反转回到0,每50ms更新一次OLED显示当前方向和转动百分比。TIM1定时器中断每1ms调用一次StepperMotorTick()驱动步进电机走一步。
/* 全局变量 */ uint32_t Time_Now; _Bool Flag; // 转动方向标志位 int main(void) { HAL_Init(); SystemClock_Config(); // 72MHz系统时钟 MX_GPIO_Init(); // GPIO初始化 MX_TIM1_Init(); // TIM1定时器初始化 HAL_TIM_Base_Start_IT(&htim1); // 启动TIM1中断 OLED_Init(); // OLED显示屏初始化 StepperMotor_Init(); // 步进电机引脚初始化 while (1) { if(Flag == 0) { if(Motor_Num < MOTOR_STEP) StepperMotorCTL(FORWARD); // 正转 else Flag = 1; // 切换为反转 } else { if(Motor_Num > 0) StepperMotorCTL(REVERSE); // 反转 else Flag = 0; // 切换为正转 } if(HAL_GetTick() - Time_Now >= 50) // 50ms更新一次OLED { OLED_Clear(); // 清屏 if(getStepperMotor() == FORWARD) OLED_CenterPrintf(12, "正转"); else if(getStepperMotor() == REVERSE) OLED_CenterPrintf(12, "反转"); OLED_CenterPrintf(36, "%d%%", (int)(Motor_Num/(float)(MOTOR_STEP)*100)); OLED_Update(); // 更新显示 Time_Now = HAL_GetTick(); // 更新时间戳 } } } /* TIM1定时器中断回调函数 — 每1ms执行一次 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == htim1.Instance) { StepperMotorTick(); // 驱动步进电机走一步 } }
使用4路GPIO(PB6-PB9)以推挽输出方式驱动ULN2003A的4路输入。采用8拍半步励磁序列,电机走4096步完成一圈(360°)。步进序列数组 Motor_Buf[8] 定义了8个励磁状态,通过 Motor_Num 对8取模循环输出。定时器每1ms调用一次 StepperMotorTick(),电机每步1ms,完成一圈约需4.096秒。
// 步进电机引脚定义 #define STEPPER_IN1_Port GPIOB #define STEPPER_IN1_Pin GPIO_PIN_6 #define STEPPER_IN2_Port GPIOB #define STEPPER_IN2_Pin GPIO_PIN_7 #define STEPPER_IN3_Port GPIOB #define STEPPER_IN3_Pin GPIO_PIN_8 #define STEPPER_IN4_Port GPIOB #define STEPPER_IN4_Pin GPIO_PIN_9 #define MOTOR_STEP 512*8 // 4096步 = 一圈 // 方向枚举 typedef enum { STOP, // 电机停止 FORWARD, // 电机正转 REVERSE // 电机反转 } Dir;
// 8拍半步励磁序列:A→AB→B→BC→C→CD→D→DA uint8_t const Motor_Buf[8] = {0x01, 0x03, 0x02, 0x06, 0x04, 0x0c, 0x08, 0x09}; // GPIO引脚驱动函数 — 按位写入4路线圈状态 void MotorGpioWrite(uint8_t data) { HAL_GPIO_WritePin(STEPPER_IN1_Port, STEPPER_IN1_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(STEPPER_IN2_Port, STEPPER_IN2_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(STEPPER_IN3_Port, STEPPER_IN3_Pin, GPIO_PIN_RESET); HAL_GPIO_WritePin(STEPPER_IN4_Port, STEPPER_IN4_Pin, GPIO_PIN_RESET); if (data & 0x01) HAL_GPIO_WritePin(STEPPER_IN1_Port, STEPPER_IN1_Pin, GPIO_PIN_SET); if (data & 0x02) HAL_GPIO_WritePin(STEPPER_IN2_Port, STEPPER_IN2_Pin, GPIO_PIN_SET); if (data & 0x04) HAL_GPIO_WritePin(STEPPER_IN3_Port, STEPPER_IN3_Pin, GPIO_PIN_SET); if (data & 0x08) HAL_GPIO_WritePin(STEPPER_IN4_Port, STEPPER_IN4_Pin, GPIO_PIN_SET); } // 定时器驱动步进函数 — 每1ms调用一次 void StepperMotorTick(void) { if(Motor_Status == 1 && Motor_Num < MOTOR_STEP) Motor_Num++; // 正转步数+1 if(Motor_Status == 2 && Motor_Num) Motor_Num--; // 反转步数-1 if(Motor_Num == 0 || Motor_Num == MOTOR_STEP) Motor_Status = 0; // 到达极限停止 if(Motor_Status) MotorGpioWrite(Motor_Buf[Motor_Num % 8]); // 输出对应励磁状态 }
TIM1定时器配置为基本定时器模式,预分频器 Prescaler=71,自动重装值 Period=999。STM32F103C8T6的TIM1挂载在APB2总线上,时钟频率为72MHz。定时器中断频率 = 72MHz / (71+1) / (999+1) = 1kHz,即每1毫秒产生一次更新中断,作为步进电机的步进时钟源。
TIM_HandleTypeDef htim1; void MX_TIM1_Init(void) { htim1.Instance = TIM1; htim1.Init.Prescaler = 71; // 72MHz/(71+1) = 1MHz htim1.Init.CounterMode = TIM_COUNTERMODE_UP; htim1.Init.Period = 999; // 1MHz/(999+1) = 1kHz = 1ms htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; HAL_TIM_Base_Init(&htim1); // 在MspInit中启用TIM1更新中断 HAL_NVIC_SetPriority(TIM1_UP_IRQn, 0, 0); HAL_NVIC_EnableIRQ(TIM1_UP_IRQn); } /* 定时器中断频率计算: 时钟源 = 72MHz (APB2) 计数频率 = 72MHz / (71+1) = 1MHz 中断频率 = 1MHz / (999+1) = 1kHz 中断周期 = 1ms → 每步1ms */
8拍半步励磁序列每个状态对应不同的线圈通电组合,逐步切换实现电机平滑转动。Motor_Num 对8取模循环输出对应状态。
| 步数 (Motor_Num%8) | 序列值 | 二进制 | IN1 (PB6) | IN2 (PB7) | IN3 (PB8) | IN4 (PB9) | 励磁相 |
|---|---|---|---|---|---|---|---|
| 0 | 0x01 | 0001 | ● | ○ | ○ | ○ | A |
| 1 | 0x03 | 0011 | ● | ● | ○ | ○ | AB |
| 2 | 0x02 | 0010 | ○ | ● | ○ | ○ | B |
| 3 | 0x06 | 0110 | ○ | ● | ● | ○ | BC |
| 4 | 0x04 | 0100 | ○ | ○ | ● | ○ | C |
| 5 | 0x0c | 1100 | ○ | ○ | ● | ● | CD |
| 6 | 0x08 | 1000 | ○ | ○ | ○ | ● | D |
| 7 | 0x09 | 1001 | ● | ○ | ○ | ● | DA |
系统初始化 → 主循环(正反转控制 → OLED更新) → TIM1中断驱动步进
步进电机驱动仿真 — 实时模拟ULN2003A驱动28BYJ-48步进电机
左侧为ULN2003A驱动板与28BYJ-48步进电机仿真。4个LED指示灯对应IN1-IN4(PB6-PB9)的GPIO输出状态,按8拍半步励磁序列循环点亮。电机转子按励磁序列逐步旋转,正转至100%后自动切换为反转。右侧OLED模拟屏显示当前方向(正转/反转)和转动百分比,仪表盘以弧形进度条实时显示步数进度(0-4096步=0%-100%)。定时器中断周期1ms/步,OLED更新间隔50ms。