把 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 电源
一路专用的 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°。这恰恰是步进电机的意义所在:位置是数出来的,所以这个数必须对。

为什么引脚顺序是 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,另外绝对不要让一颗堵转的电机通着电、还没人看着。

完整的 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 联系我们。


PWM Generator Module Motor Control Frequency Adjustable Control Board 1.8V - 15V Power Control
ESP8266 Motor Driver Expansion V2 Board L293D LUA ESP8266 Wifi Motor Driver Shield
MH Soil Moisture Sensor Module Hygrometer Detection Moisture Sensor for Arduino Plant Watering System
Arduino Compactable Pro Mini ATMEGA328P 5V/16M 
28BYJ-48 Stepper Motor + ULN2003 Motor Driver 5V DC Motor For Arduino Robotic
Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
Sensor Shield V5.0 Arduino Uno R3 Shield For Arduino Protoype Application
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE F-F 20CM
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (5V2A)
5.5x2.1mm DC Power Male Connector Plug Jack Adapter for Arduino DIY Electronics Projects - 5.5x2.1 DC CONNECTOR (F)
自动引用通知: DRV8833 vs L298N:电池小车选哪块驱动板