28BYJ-48 步进电机接 ULN2003:第一次转动,与 2038 步的真相

Cartoon of a silver 28BYJ-48 stepper motor with a blue gearbox wired through a green ULN2003 driver board to a blue Arduino Uno with a sensor shield

把 ULN2003 驱动板的 IN1–IN4 接到 Arduino 的 8、9、10、11 脚,给板子 5V,然后这样声明电机:Stepper(2038, 8, 10, 9, 11)——中间两个参数是故意对调的。输出轴转一整圈是 2038 个可数的步,而不是几乎每份 sketch 都写的 2048,正是这十步的差距,让新手的电机慢慢跑偏。

28BYJ-48 套装里有什么?驱动板为什么接不上 Uno?

28BYJ-48 套装其实是一个袋子里的两样东西。电机的五条线——蓝、粉、黄、橙,加一条红色的公共线——末端是一个带防呆缺口的插头,正好插进绿色的 ULN2003 驱动板。那块板上真正要认的只有四处:IN1–IN4 那排针(IN5–IN7 只有裸孔)、丝印 5–12V 下面的 + 两个脚、紧挨着它们、负责通断电机供电的那颗跳线帽,以及四颗 LED。

接线图从来不画的那个坑:驱动板上每一个接点都是公针,而 Arduino Uno 上每一排都是母座。公对母杜邦线可以直接解决。本文改用一块 Sensor Shield V5.0 扩展板,把 Uno 的每个脚重新引出成一组公针 G/V/S 三联,于是普通的母对母线就能把公针接到公针。

千万不要把驱动板按进面包板。它的两排针互相垂直,横跨在孔条上的那一排,每个针都会被短接进同一条五孔通道——IN1–IN4 全部并在一起,或者 + 直接短到 −。掰一小段公针排针插进 Uno 的母座,是更便宜也真的管用的做法。

选配:独立的 5V 电源

ItemPriceQty
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (5V2A)Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (5V2A)PSA0502RM9.95
5.5x2.1mm DC Power Male Connector Plug Jack Adapter for Arduino DIY Electronics Projects - 5.5x2.1 DC CONNECTOR (F)5.5x2.1mm DC Power Male Connector Plug Jack Adapter for Arduino DIY Electronics Projects - 5.5x2.1 DC CONNECTOR (F)5521DCFRM1.20

一路专用的 5V 供电,适合让电机长时间连续运转,或者再加一颗电机。请先读完供电那几段——接上之前,扩展板的 SEL 跳线帽必须拔掉。

卡通对比图:绿色驱动板上的公针排针无法和同样是公头的杜邦线对插,而母对母杜邦线则把驱动板接到了蓝色扩展板上
两个公头对不上,所以普通的公头杜邦线在驱动板上完全没用——母对母线加上扩展板的公针三联,不用烙铁就能解决。

ULN2003 板怎么接线、怎么供电?

Sensor Shield V5.0 把数字脚 11、10、9、8 排成一个四列的区块,每列都是 G(地)、V(5V)、S(信号)三个针——所以光这一个区块就装得下这个项目的全部连线,六条母对母杜邦线就能收工。

ULN2003 驱动板 Sensor Shield 扩展板 背后对应的 Uno 脚
IN1 “8” 那一列的 S 针 D8
IN2 “9” 那一列的 S 针 D9
IN3 “10” 那一列的 S 针 D10
IN4 “11” 那一列的 S 针 D11
+(丝印 5–12V) 该区块任意一列的 V 针 5V
−(丝印 5–12V) 该区块任意一列的 G 针 GND
IN5、IN6、IN7 不接——那里只有空焊孔
白色 5 针插座 电机的白色防呆插头

扩展板上有个细节能替你省下一小时:丝印 POWER / RESET 3V3 5V GND VIN 的那一排是裸的镀金孔,不是针。5V 和 GND 请从数字三联的 V 排和 G 排取。另外,驱动板 − 和 + 旁边那颗小小的银色跳线帽要留着——它是电机供电的开关,所以一块看起来接得没错却毫无动静的板子,往往就是那颗跳线帽不见了。

上电之前有两点安全提醒。PCB 上印着 5–12V,是因为这个电压就是驱动板直通给绕组的电机供电——ULN2003 本身耐压 50V,所以板上没有任何东西在保护电机。套装里配的是 5V 型号,12V 会把它的绕组烧热。还有,板子带电时绝对不要拔电机的插头:切断一个正在通电的线圈,那股感应反冲会自己挑地方去。

这个项目用 USB 供电就够了。每一相绕组实测约 50 Ω,5V 下就是 5 ÷ 50 = 0.1 A,而驱动时序同时点亮两相——转动时大约 200 mA,对比 USB 口的 500 mA 和 Uno 自身的 40 mA,余量充足。不过请从 USB 取电,别用 Uno 的圆孔电源座:喂 12V 进去,Uno 的稳压器只能把多出来的电压烧成热,大约 7 V × 0.24 A = 1.7 W。

再加一颗电机,或者让电机长时间保持通电,这点余量就不够了。选配那一行给它一路独立的电源:5V 适配器插进带线的 DC 母座,两条裸线头拧进扩展板上那个蓝色的螺丝端子——接圆孔中心脚的那一根进 V/VCC 螺丝,另一根进 G/GND。适配器是中心正极,所以上电前用万用表的通断档确认哪根是哪根;两根接反就是给驱动板反向供电。先把黄色的 SEL 跳线帽拔掉。SEL 的作用是让数字区块的 V 针由 Uno 自己的 5V 供电,留着它就等于把适配器短接到 Uno 的 5V 轨、以及它后面的 USB 口上,两者都可能损坏。G 轨在任何情况下都保持共地,这正是 Uno 的逻辑电平对驱动板仍然有意义的原因。另外,绝对不要拆开市电适配器,也不要改动它的插头。

每圈 2048 步是怎么来的?

28BYJ-48 的转子在进减速箱之前,每个整步转 11.25°——内部转一圈是 32 个整步。半步驱动把每一步再切成两半,于是得到 datasheet 上写的 5.625° 步距角,一圈 64 个半步。减速箱再按标称的 64:1 减速,所以输出轴上是 32 × 64 = 2048 个整步(或者 4096 个半步)。

只是这个减速箱其实并不是 64:1。有人拆开数过齿:那是一套四级齿轮,精确比值是 (31 × 32 × 26 × 22) ÷ (11 × 10 × 9 × 9) = 63.68395:1。同样的算式,换成诚实的数字:32 × 63.68395 = 2037.9 步。

项目 标称(64:1) 实测(63.68395:1)
输出轴每圈的整步数 2048 2037.9
输出轴每圈的半步数 4096 4075.8
每个整步转过的角度 0.1758° 0.1767°

这个差距不是学术问题。命令它走 2048 步,轴实际转过的是 2048 ÷ 2037.9 × 360° = 361.8°——每圈多转 1.8°,而且永远朝同一个方向偏,二十圈之后你的指针就差了 36°。这恰恰是步进电机的意义所在:位置是数出来的,所以这个数必须对。

卡通剖面图:银色步进电机的转子带动蓝色减速箱里的四组白色塑料齿轮,最后驱动一根刻着细密刻度的慢速输出轴
一个 32 步的高速内转子,四级塑料齿轮,再到一根以极小的、可数的增量缓缓转动的输出轴。

为什么引脚顺序是 8、10、9、11,而不是 8、9、10、11?

28BYJ-48 的四相线圈按接头顺序排在定子的一圈上,而它公布的驱动时序点亮的是相邻的一对:IN1+IN2,然后 IN2+IN3、IN3+IN4、IN4+IN1。每一步都和上一步共用一个线圈,正是这个重叠让磁场绕着这一圈往前走。

Arduino 自带的 Stepper library 驱动它那四个脚,用的却是 1010、0110、0101、1001 这个模式——也就是依次点亮 (arg1,arg3)(arg2,arg3)(arg2,arg4)(arg1,arg4)。先配成一对的是第 1 和第 3 个参数,不是第 1 和第 2 个。

把两张表对齐,映射关系就自己浮出来了:arg1 = IN1、arg3 = IN2、arg2 = IN3、arg4 = IN4——写成脚号就是 Stepper(2038, 8, 10, 9, 11)。这不是一个被大家抄来抄去的笔误,而是 library 的配对约定和这颗电机线圈顺序之间的一次翻译。

如果传进直觉上的 8、9、10、11,每一对都会落错位置。第 1 步和第 3 步变成 IN1+IN3 和 IN2+IN4——定子上正对面的两相,互相拉扯,几乎合不出净磁场。第 2 步和第 4 步给出 IN2+IN3 和 IN1+IN4:这是真实存在的位置,但两者相隔 180°。于是磁场变成:没有、一边、没有、另一边,转子就在两个点之间来回摇,而不是绕着圈走。十秒钟就能验证:把中间两个参数对调,然后听声音。

ULN2003 到底在做什么?

ULN2003AN 在一颗 16 脚芯片里塞进了七组一模一样的达林顿管——这就是为什么板上丝印一直印到 IN7,实际却只有 IN1–IN4 焊了针。最关键的那个词是灌电流(sink):每一路只能把自己的输出拉低到地,永远不会往外供电。所以电机那根红线才要做成接在 +5V 的公共中心抽头——电流从 5V 轨出发,穿过半个线圈,进芯片,再流到地。

每一路其实就是一个低边开关,和 IRF520 模块用一颗 MOSFET 做的是同一件事——所以线圈的半边里,电流永远不会反向。这就是单极性(unipolar)驱动:任何一个瞬间都有一半的铜线闲着,用扭矩换来了简单。

这也解释了那四颗 LED:它们接在输出上,所以哪个线圈通电,哪一颗就正好亮——等于送你一台逻辑分析仪。LED 在跑马、轴却不动,说明是机械卡住或者插头没插到底;LED 根本不跑马,问题就在你的引脚或者 sketch。

只要输入一直保持 HIGH,达林顿管就一直导通,所以一次移动结束之后,最后那两个线圈仍然通着电。这就是保持扭矩,也是电机停着不动却会发热的原因。空闲时把四个脚全部写成 LOW,另外绝对不要让一颗堵转的电机通着电、还没人看着。

卡通图:电流从正电源轨经红色公共线流进铜线圈,再向下流入黑色 ULN2003 芯片到地,四颗 LED 中有两颗亮着
红线把两个线圈都吊在 +5V 上;ULN2003 只会把另一端拉低到地,而 LED 就跟着当前通电的那一对亮起来。

完整的 sketch 长什么样?

Stepper library 随 Arduino IDE 一起附带,所以没有任何库要装。这个 sketch 分四次各转四分之一圈、每次之间停一下,然后一步到位回到原点——它全程按绝对步数计算,所以舍入误差不会累积。

#include <Stepper.h>

// ULN2003 驱动板 -> Arduino Uno(经过 Sensor Shield 的 S 针)
const int IN1 = 8;
const int IN2 = 9;
const int IN3 = 10;
const int IN4 = 11;

// 减速箱标称 64:1,但真实齿轮组实测是 63.68395:1,
// 所以输出轴一圈是 32 * 63.68395 = 2037.9 步。
// 2038 才是诚实的数字;2048 每圈会多转大约 1.8 度。
const int STEPS_PER_REV = 2038;

// 重要:参数顺序是 IN1、IN3、IN2、IN4 —— 不是 8、9、10、11。
// library 点亮的配对是 (arg1,arg3)、(arg2,arg3)、(arg2,arg4)、
// (arg1,arg4);把中间两个对调,这些配对才会落在定子上
// 彼此相邻的线圈上。
Stepper motor(STEPS_PER_REV, IN1, IN3, IN2, IN4);

long currentStep = 0;   // sketch 认为轴现在停在哪里

// 只要输入是 HIGH,达林顿管就导通,所以移动结束后线圈
// 仍然通电(并且发热)。空闲时就放开它们。
void releaseCoils() {
  digitalWrite(IN1, LOW);
  digitalWrite(IN2, LOW);
  digitalWrite(IN3, LOW);
  digitalWrite(IN4, LOW);
}

// 移动到从起点算起的一个绝对位置。
// 以原点为基准、而不是不断累加增量,
// 舍入误差就永远不会堆积起来。
void moveTo(long target) {
  motor.step((int)(target - currentStep));
  currentStep = target;
  releaseCoils();

  Serial.print(F("step "));
  Serial.print(currentStep);
  Serial.print(F("  ="));
  Serial.print(currentStep * 360.0 / STEPS_PER_REV, 1);
  Serial.println(F(" deg"));
}

void setup() {
  Serial.begin(9600);
  motor.setSpeed(10);   // rpm;5V 下这颗电机大约超过 15 就会堵转
  releaseCoils();
  Serial.println(F("28BYJ-48 ready"));
}

void loop() {
  // 四次四分之一圈,中间停一下,方便你看清每一次到位
  for (int quarter = 1; quarter <= 4; quarter++) {
    moveTo((long)STEPS_PER_REV * quarter / 4);
    delay(600);
  }

  delay(1000);
  moveTo(0);            // 一步直接回到原点
  delay(2000);
}

打开串口监视器,波特率选 9600:计数应该依次走过 509、1019、1528、2038,再回到零,同时轴干净利落地转完四个四分之一圈,最后一整圈回家。如果它只是嗡嗡响却不转,先检查参数顺序。另外要注意 step() 是阻塞的——轴在转的时候,loop() 里其它任何事都不会执行。

Nikodem Bartnik 的新手实拍演示,用的正是同一颗电机和同一块驱动板:

真实客户最常犯的错误

28BYJ-48 上我们见得最多的错误,而且遥遥领先的,是把引脚按顺序写——从通用例子里抄来的 Stepper(2048, 8, 9, 10, 11)。它能编译,LED 甚至会闪,轴就在那儿嗡嗡地不动。

其次是在要连跑几个小时的场合里留着 2048,然后把结果叫做丢步。其实什么都没丢:一个偏大 0.5% 的计数表现为稳定的单向蠕动,而真正的丢步是随机的跳变。

再往后是:一见模块就往面包板上插的条件反射;因为丝印写着 5–12V 就上 12V;以及一颗“坏掉”的电机,最后发现只是插头插了一半。

FAQ

每圈到底该用 2048 还是 2038 步?

凡是位置需要长时间保持准确的场合——时钟、指针仪表、相机滑轨——都用 2038。实测的减速比是 63.68395:1 而不是 64:1,所以 2048 每圈会多转大约 1.8°,而且永远偏同一个方向。如果只是来回转着玩的演示,两个数字看起来毫无分别。

电机明明没在动,为什么会发热?

因为只要你的 Arduino 引脚还是 HIGH,ULN2003 就一直导通,所以移动结束后最后那两个线圈仍然通着电。这就是保持扭矩——需要轴停在原地不被推动时很有用,否则就在两次移动之间把四个脚全写成 LOW。

我的项目该用步进电机还是舵机?

如果你要的是在大约 180° 之内保持一个角度、而且自带位置反馈,选舵机;如果你要的是可以无限圈旋转、位置靠计数得出,并且能接受“丢了一步没人告诉你”,选步进电机。另一边的取舍可以看我们的舵机 180 vs 270 vs 360 指南

ULN2003 是对的驱动吗?要不要换 L298N 或 A4988?

套装里配的 ULN2003 就是对的——28BYJ-48 是带公共中心抽头的单极性电机,正好是达林顿灌电流阵列该驱动的对象。像 L298N 那样的 H 桥(我们的循线小车指南里用的就是它)适合双极性步进电机和有刷直流电机;A4988 这类斩波驱动则适合 NEMA-17 步进电机。它们是不同类别的电机,不是升级关系。

最后更新:2026年8月。有问题?WhatsApp 联系我们

Leave a Reply

Your email address will not be published. Required fields are marked *