目录
电动机是获取动力的来源之一,在某些特殊的应用中,精确地控制电动机的转动角度是控制的难点,如想让电动机转动三周半,这对于普通的直流电动机来说是很难实现的,而步进电动机则不同,它只需要几个简单的控制脉冲即可轻而易举地实现这样的操控过程。步进电动机就是这样一种能“听话”的电动机,它在微处理器的控制下可以产生精确的角位移,使其在诸如打印机、传真机等精密设备中有广泛的应用。
常见的电动机都是连续转动的,而步进电动机的转动则是分步进行的,步进电动机也因此而得名。步进电动机是一种将电脉冲转化为角位移的执行机构,当步进驱动器接收到一个脉冲信号后,就会驱动电动机按设定的方向转动一个固定的角度,通过控制脉冲的个数来控制角位移量,从而达到准确定位的目的。另外,通过控制驱动脉冲的频率,还可以控制电动机转动的速度。
步进电动机在构造上有三种主要类型,即反应式、永磁式和混合式。
1)反应式(VR):定子上有线圈,转子由软磁材料组成。反应式步进电动机具有结构简单、成本低、步距角小的优点,但其动态性能差、效率低、发热量大、可靠性较难保证。
2)永磁式(PM):永磁式步进电动机的转子用永磁材料制成,转子的极数与定子的极数相同。永磁式步进电动机的特点是动态性能好、输出力矩大,但这种电动机精度差,步矩角大,一般为7.5°或15°。
3)混合式(HS):混合式步进电动机综合了反应式和永磁式的优点,其定子上有多相线圈,转子采用永磁材料,转子和定子上均有多个小齿以提高步距精度。混合式步进电动机的特点是输出力矩大、动态性能好、步距角小,但其结构复杂、成本相对较高。
第一部分:28BYJ-48
工作原理
【B站】STM32控制ULN2003驱动板-驱动28BYJ48步进电机
这种电动机的转子上有六个凸齿,但没有线圈缠绕,而定子上有八个凸齿,每一个凸齿上均绕有一组线圈。定子线圈的连接方式是在对称齿上的两个线圈进行反相连接,八个齿构成四对,所以称为四相步进电动机。
步进电动机的工作原理:
1)当步进电动机的A相线圈被激励时,磁通从定子的正相齿,经过软铁心的转子,以最短的路径流向负相齿。为使磁通路径最短,在磁场力的作用下,转子被强迫移动,使最近的一对齿与被激励的一相对准,其状态如图a所示。
2)在这个位置的基础上,驱动器再次对B相进行激励,这时离B相最接近的转子上的凸齿被吸引,转子会逆时针转动15°,其状态如图b所示。此时若是D相被激励,则转子会顺时针转动15°,其状态如图c所示。
3)按照驱动的顺序,当C相被激励时,转子会按照前一步的运动方向继续转动15°。
通过上面的介绍,我们对步进电动机的驱动已经有一个基本的理解了。通过控制不同相的驱动顺序,可以改变步进电动机的转动方向,驱动脉冲的个数会决定电动机转动的角度,而两个驱动脉冲的时间间隔决定了电动机的转动速度。
主要参数
步进电动机的静态技术指标主要有以下几项:
1.相数:产生不同对N、S极磁场的激磁线圈的对数。
2.拍数:完成一个磁场周期性变化所需脉冲数或转过一个齿距角所需脉冲数。
3.步距角:单个脉冲信号驱动电动机转子转动的角位移量。
步距角的计算方法
电动机步距角(步长)是步进电动机的主要性能指标之一,不同的应用场合对步距角大小的要求不同,步距角越小,步进电动机的转动控制就越精确。改变步进电动机的相数(线圈数)或转子的极数(齿数)可以改变步距角的大小。它们之间的相互关系可由下式计算:
步距角=360 ÷(相数×齿数)
以图中所示的步进电动机为例,相数为4、齿数为6,经计算步距角为15°,电动机转一圈需要24步。
在以上众多参数中,单片机软件开发时,最需要了解的参数即为 步距角。
步距角=5.625°/64,其意思就是每64个脉冲步进电机就会转5.625度
。因此可以很容易的得出旋转角度与脉冲之间的计算公式:pulse=(angle/5.625) * 64(
划重点
)。例如让电机转一圈有360°,那么转一圈的脉冲数为 (360 / 5.625) * 64 =
4096
个脉冲。
已知:步距角为5.625/64,即内轴64个
脉冲(8步8圈)外轴旋转5.625度;
已知:外轴1圈有360度,得 360/5.625=64
(也就是一圈需要64个5.625度);
因此得:64 * 64 = 4096, 即内轴4096个脉冲,外轴旋转1圈。
已知:8步就是8个脉冲;
又已知:8次循环,内轴电机旋转1圈;
已知:8 * 8 = 64,64个脉冲内轴电机旋转1圈;64个脉冲,外轴旋转5.625度。
还已知:减速比为1:64,内轴旋转64圈,外轴旋转1圈;
求:外轴旋转1圈需要多少个脉冲?
什么是28BYJ-48?
28BYJ-48 – 5V 步进电机 28BYJ-48 是一款小型步进电机,适用于多种应用。
- 28:电机的直径;
- B: 表示步进电机;
- Y:表示永磁体;
- J:表示减速电机;
- 4:表示4相;
- 8:表示8拍;
28BYJ-48 主要参数
第二部分:驱动
在自动控制中会有很多被控器件,如继电器、电动机、电磁阀、压缩机等,这些设备通常由CPU控制,由于控制系统不能直接驱动被控器件,需要由功率驱动电路来扩展输出电流,以满足被控器件的控制需求。ULN2003高压大电流达林顿晶体管阵列就属于这类可控大功率器件,特别是在步进电动机的驱动上有广泛的应用。
ULN2003A 驱动芯片
ULN2003是高耐压、大电流达林顿阵列,由七个硅NPN达林顿管组成,具有电流增益高、工作电压高、温度范围宽、带负载能力强等特点,适应于各类要求高速大功率驱动的系统。
ULN2003引脚功能:
下图是《迷人的8051单片机》 17.1 步进电机的特点里面的芯片内部结构电路,续流二极管方向有错误
!!
ULN2003是反向驱动电路,即当 输入端为高电平 时 输出端为低电平,其内部由七个硅NPN达林顿管组成,每一组达林顿复合管都串联了一个2.7kΩ的基极电阻,在5V的工作电压下能与TTL和CMOS电路直接相连,而无需桥接数据缓冲器。输出端采用集电极开路输出,输出电流可达500mA,内部集成了保护二极管,可以用来驱动继电器等感性负载。COM端是内部7个二极管负极的公共端,各二极管的正极分别接各达林顿管的集电极。用于感性负载时,该引脚接负载电源正极,以实现续流作用。
(20250514补充)
上图是TI ULN2003ADR规格书里面的电路。
ULN2003的驱动电路
通常使用单片机驱动ULN2003时,需要将I/O口上拉到VCC,上拉电阻选用2kΩ较为合适。另外,由于ULN2003是集电极开路输出,为了让这个二极管起到续流作用,必须将COM引脚接在负载的供电电源上,只有这样才能够形成续流回路。由ULN2003构成的步进电动机驱动电路如图。
加油站: 弱上拉和强上拉
上拉是指通过一个连接在I/O口和电源之间的电阻将不确定为高电平或高电平驱动能力弱的电位控制在高电平。上拉电阻越小,驱动能力越强,功耗也越大。上拉电阻的取值为1~10kΩ。一般来说当上拉电阻较小
时,可以称之为强上拉
,反之称其为弱上拉。
第三部分: 程序
MCU(程序) + 驱动芯片 + 28BYJ-48
【B站】STM32控制ULN2003驱动板-驱动28BYJ48步进电机
步进电机驱动方式:
1)1相励磁。这种励磁方式也称为单4拍工作方式,是指在某一驱动瞬间,步进电动机只有一相导通,驱动器每发送一个励磁信号,步进电动机就旋转一个步距角。这种驱动方式的特点是电能消耗小,但输出力矩小,振动大。
2)1-2相励磁。这种励磁方式也称为单双8拍工作方式,是指在某一驱动瞬间,步进电动机的某一相或某两相交替导通,驱动器每发送一个励磁信号,步进电动机只旋转半个步距角。这种驱动方式的特点是转动精度高、运行平稳,是大多数步进电动机理想的工作方式。
总结: 對
力矩
要求高使用 2相勵磁 (4相4拍),對精度
要求高使用 1-2相勵磁 (4相8拍)。
【B站】步进电机28BYJ-48的结构、原理及控制(Arduino和ULN2003)
参考程序
Motor.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4|GPIO_Pin_5 | GPIO_Pin_6; //PA3,PA4,PA5,PA6控制步进驱动引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//1相励磁函数,A-B-C-D
void Motor_One(uint16_t speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
}
//2相励磁函数 AB-BC-CD-DA
void Motor_two(uint16_t speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_4);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
}
//1.2混合励磁函数 A-AB-B-BC-C-CD-D-DA
void Motor_one_two(uint16_t speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_4);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
}
//1.2混合励磁函数 (反转) A-AB-B-BC-C-CD-D-DA DA-D-CD-C-BC-B-AB-A
void Motor_one_two2(uint16_t speed)
{
GPIO_SetBits(GPIOA,GPIO_Pin_6|GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_5);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_4);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_5);
GPIO_ResetBits(GPIOB,GPIO_Pin_3|GPIO_Pin_4 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5);
GPIO_ResetBits(GPIOA,GPIO_Pin_3 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4);
GPIO_ResetBits(GPIOA,GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
GPIO_SetBits(GPIOA,GPIO_Pin_3);
GPIO_ResetBits(GPIOA,GPIO_Pin_4|GPIO_Pin_5 | GPIO_Pin_6);
Delay_ms(speed);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "Motor.h"
#include "Key.h"
uint8_t n;
uint16_t num=0;
int main(void)
{
Motor_Init();
Key_Init();
while (1)
{
if(num==1)
{
for(int i=0;i<=(8*64);i++) //8次循环电机旋转一周,外轴减速比1:64,还需要*64
{
// Motor_One(5); // 1相励磁
// Motor_two(5); // 2相励磁 精度低 力矩大 震动大 耗时短
Motor_one_two(5); // 1-2相励磁 精度高 力矩小 震动小 耗时长
// Motor_one_two2(5); //反转
}
num=0;
GPIO_ResetBits(GPIOA,GPIO_Pin_3|GPIO_Pin_4 |GPIO_Pin_5| GPIO_Pin_6);
}
}
}
void EXTI0_IRQHandler(void) //按键中断,修改标志位 (实现按键按一次,电机旋转1周)
{
if (EXTI_GetITStatus(EXTI_Line0) == SET)
{
EXTI_ClearITPendingBit(EXTI_Line0);
if(num==1)
return;
else
num=1;
}
}
待优化… 定时器非阻塞驱动…
(20250514)
以下示例采用“全步”驱动(双相励磁),可改为半步或波驱动。
// 对应于 PA1‑PA4 的电平映射,依次为 1001, 1100, 0110, 0011
const uint8_t stepSeq[4] = {0x09, 0x0C, 0x06, 0x03};
// 设置 GPIO 输出状态
void setStep(uint8_t idx) {
uint8_t mask = stepSeq[idx];
GPIO_WriteBit(GPIOA, GPIO_Pin_1, (mask & 0x01) ? Bit_SET : Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_2, (mask & 0x02) ? Bit_SET : Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_3, (mask & 0x04) ? Bit_SET : Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_4, (mask & 0x08) ? Bit_SET : Bit_RESET);
}
// 转动指定步数(正向/反向)
void stepRotate(int steps, uint16_t speed_ms, bool clockwise) {
int idx = 0;
for (int i = 0; i < steps; i++) {
setStep(idx);
delay_ms(speed_ms);
if (clockwise) idx = (idx + 1) & 0x03;
else idx = (idx + 3) & 0x03;
}
}
// 主循环示例
int main(void) {
SystemInit();
MX_GPIO_Init();
MX_TIM2_Init();
while (1) {
// 顺时针 512 步(1/4 圈)
stepRotate(512, 5, true);
delay_ms(500);
// 逆时针 512 步
stepRotate(512, 5, false);
delay_ms(500);
}
}
成果展示
STM32F103x 驱动步进电机 28BYJ-48
资料下载
参考资料
-
1 【微信读书】《迷人的8051单片机》 17.1 步进电机的特点
-
4 【台部落】24BYJ48電機的使用,帶驅動程序
-
5 【YouTube】Modifying Cheap Stepper Motor To Be More Powerful (28BYJ-48)
-
8【iotword】物联网开发笔记(34)- 使用Micropython开发ESP32开发板之控制步进电机(28BYJ-48)
-
9【程序员小哈】STM32在定时器中驱动28BYJ-48步进电机
-
[10]【microcontrollerslab.com】STM32 Blue Pill with Stepper Motor (28BYJ-48 and ULN2003 Motor Driver)
(20250514补充)
ULN2003 芯片结构中输入与地之间为什么有个反向的二极管,输出与地之间为什么有个反向的二极管?为什么要用达林顿管阵列驱动继电器和步进电机?
在 ULN2003 芯片中,输入端与地之间的反向二极管主要用于抑制由外部噪声或静电放电产生的负向电压,从而保护内部晶体管基极;而输出端与地(COM)之间的反向二极管(又称续流或回击二极管)则用于钳制感性负载断电时产生的高压反向脉冲,防止对达林顿晶体管对造成损害。采用达林顿管阵列来驱动继电器和步进电机,能够将 MCU 的低电流逻辑信号转换为高电流、大电压的驱动能力,同时集成限流电阻和保护二极管,大幅简化外部电路设计并提高可靠性。
ULN2003 内部反向二极管设计
1. 输入端的反向二极管
- 抑制负向电压:输入端串有 2.7 kΩ 限流电阻,并并联一个反向二极管,用于在输入引脚受到负向瞬态或静电放电(ESD)时,将电流引至地,保护基极–发射极结不被击穿。此结构等价于增加了一个内部钳位电路,确保逻辑输入安全(德州仪器)。
- 提高噪声容限:部分 ULN2003A 还集成了 RC 抗扰电路,可与反向二极管共同抑制高速开关或环境干扰引起的毛刺信号(德州仪器)。
2. 输出端的续流(回击)二极管
- 钳制感性负载反向电压:当继电器线圈或步进电机断电时,线圈会产生与断电前电流方向相反的大电压尖峰。ULN2003 在每个集电极(OUTPUT)与公共 COM 引脚之间集成了反向二极管,将该尖峰电流导回到电源(VMOT),避免高压冲击损坏达林顿晶体管阵列(TI E2E, Electrical Engineering Stack Exchange)。
- 简化外部保护电路:有了内部续流二极管,通常无需在每个线圈两端再外接二极管;只需将 COM 引脚并联到负载供电正极即可实现完整的续流路径(Onsemi, allelcoelec.com)。
为什么使用达林顿管阵列驱动继电器和步进电机
1. 高电流和高电压驱动能力
- 大电流输出:单个达林顿管对输出电流可达 500 mA,峰值更可并联使用以提升驱动能力,能够直接驱动小型继电器、步进电机等感性负载(Instructables, Onsemi)。
- 高电压耐受:每通道可承受至 50 V 的集电极–发射极电压,满足多种工业场景的电压要求(Instructables, allelcoelec.com)。
2. 逻辑兼容与集成保护
- TTL/CMOS 兼容:输入端集成 2.7 kΩ 限流电阻,能够直接与 5 V TTL 或 CMOS 逻辑电平接口,无需外部限流器件(Instructables)。
- 保护元件内置:限流电阻、输入钳位二极管、输出续流二极管、输入下拉电阻等均已集成,简化了 PCB 布局和外部元器件选型,提高可靠性并节省成本(德州仪器, allelcoelec.com)。
3. 设计与应用的便利性
- 多通道集中驱动:一个 16‑pin 封装即可提供七路独立驱动,适合多继电器或四相步进电机同时控制,大幅减少器件数量和焊点(Instructables)。
- 快速原型与可靠性:常见的 Arduino、STM32 等单片机生态大量使用 ULN2003 驱动模块,社区资源丰富,调试与维护门槛低(Arduino Forum, Instructables)。
以上即为ULN2003 内部二极管设计原理及达林顿管阵列用于继电器和步进电机驱动的优势解析。
ULN2003V12 驱动LED可以做流水灯控制:
ULN2003V12 内部结构