舵机会把 Arduino 拉到掉电重启,是因为它们要从一条只按毫安计的电源轨上抽走好几安培 —— 而 PCA9685 的解法,就是让舵机的供电变成一条板子根本不经手的独立电源。两条 I2C 线负责下命令,一个螺丝端子负责扛电流,16 路输出每一路都有自己的脉冲,用 12 位刻度一路一路校准。
为什么接上第三颗舵机,Arduino 就重启?
Arduino Uno 上那支 5 V 引脚不是电源,它只是电源的末端。用 USB 供电的时候,板子交出来的每一毫安都已经先穿过一颗 500 mA 的自恢复保险丝(Bourns MF-MSMF050-2),它就串在 USB 的 5 V 线上 —— 何况 USB 2.0 端口本来就只保证给到 500 mA。
再拿一颗舵机来对一对账。9 g 的 SG90 静止时大约 10 mA,动起来是 100–250 mA,堵转实测 360 mA。一颗,很轻松。两颗,还撑得住 —— 我们的摇杆舵机云台教程正好用两颗,而那一篇已经在伸手拿电池了。四颗同时动起来要 400 mA 到 1 A,而舵机最难熬的一刻是每次动作的头一毫秒:电机几乎还没转起来,那一瞬间它对电源轨来说几乎就是一条短路。
你看到的是一串连锁,不是单一故障:电源轨塌下去,ATmega328P 的欠压检测把芯片按在复位状态,程序在动作中途重跑;万一塌到 USB 那一侧,主机会把串口丢掉,IDE 里的 COM 口就凭空消失。跟我们那篇 SIM800L 掉电重启是同一家人:没有任何东西坏掉,只是你在跟一条电源要它从来就没被设计去给的电流。
零件清单 —— 四颗舵机跑在自己的电源上
值得一起加
5 V 3 A 火牛是四颗舵机变八颗、或者换成拉电流更大的金属齿舵机之后的桌面升级款。它旁边一定要配那条 5.5x2.1 母头圆孔转接线:火牛的插头插进母座,转接线红黑两条裸线就锁进驱动板的螺丝端子,跟电池线的接法一模一样。至于那条备用 A 转 B 数据线,就是拿来给 Uno 烧录程序的。
PCA9685 怎么把舵机电流挡在 Arduino 外面?
PCA9685 驱动板上有两路永不相接的供电,而这个分隔就是这块板子的全部价值。VCC 在那排六针的弯针排上,只喂 PCA9685 芯片本身和它的 I2C 上拉电阻 —— 毫安等级,Uno 供一整天都不会喘。V+ 在板子顶端那个绿色螺丝端子上,一条粗铜走线直通十六路通道排针中间那一针(红色)。舵机电流走的是电池 → 螺丝端子 → 红针 → 舵机再回来,全程不经过芯片。端子旁边那颗黑色胖电容跨在同一条电源上,专门缓冲每次动作的头几毫秒,把最陡的那道浪头削掉,电池线才不会直接吃到。
芯片要做的只有一件事:把第三针 —— 黄色的信号针 —— 在 0 V 和 VCC 之间来回切。舵机的信号输入只吃微安,所以 PWM 芯片自己的活儿一直都很轻,而隔壁那条铜皮上正拖着十六颗电机的安培级电流。
接地还是必须共通,而这里本来就已经共通了。Uno 的 GND 落在六针排上,电池的黑线落在螺丝端子里,两边在板子内部落到同一片接地铜层 —— 这块板子本身就是那个共地点,你不必再补一条跳线。这件事之所以要紧,是因为 PWM 脉冲讲的是相对于地的电压差:让舵机的零点和 Arduino 的零点坐在不同电位上,舵机量到的脉冲高度就跟驱动板送出去的不一样,出来的是抖动 —— 而你会去怪自己的代码。

驱动板的 I2C 排针是预焊好的弯针公头,Uno 的 A4/A5 是母座,所以这里每一条线都是公对母杜邦线 —— 不用面包板,也没有任何要焊的地方(我们的杜邦跳线教程把这笔账算过一遍)。
| 从 | 接到 | 为什么 |
|---|---|---|
| 驱动板 GND | Uno GND | 信号脉冲共用同一个零点 |
| 驱动板 SCL | Uno A5 | I2C 时钟 —— 要用有母座的那一支,不是那块光孔 |
| 驱动板 SDA | Uno A4 | I2C 数据 |
| 驱动板 VCC | Uno 5 V | 只给芯片用的逻辑供电,几毫安而已 |
| 驱动板 OE | 空着不接 | 低电平有效的输出使能,板上已经拉低 —— 出厂状态输出就是开的;把它拉高就能一次关掉全部十六路 |
| 电池红线 | 螺丝端子 V+ | 所有舵机电流从这里进 |
| 电池黑线 | 螺丝端子 GND | 从这里回;在板内跟 Uno 的 GND 汇合 |
| 舵机插头 | 通道 0–3 | 棕线对黑针,红线对红针,橙线对黄针 |
那些三针排针没有任何防呆,所以舵机插头反过来插一样插得进去 —— 而且因为红线不管怎么插都落在中间那一针,插错了看起来还挺正常。会对调的是棕线和橙线:舵机的接地回流跑到一支只额定几毫安的 PCA9685 输出脚上,逼着电机电流穿过这块板子存在的意义所在的那颗芯片,而信号线则坐在地上,一辈子等不到脉冲。请确认每一路通道上棕线都落在最下面那排黑针,而且所有舵机都插好之后,才打开电池盒的开关。
Uno 靠近顶边的地方还有一块 3 × 4 的未焊镀孔,丝印写着 SCL/SDA/5V/GND。那些是光秃秃的孔,不是插座 —— 跳线插进去什么也碰不到。模拟区那两支 A4 和 A5 就是同样这两路 I2C 信号,而且已经是母座了。
V+ 螺丝端子该喂什么?
PCA9685 的 V+ 端子要的是 5–6 V 直流,这是 Adafruit 为插着舵机的 PCA9685 指定的范围,而这个窗口是舵机定的,不是板子定的。玩具级舵机是照着 4.8–6 V 造的:低于大约 4.5 V 扭力就塌,舵机自己的控制器也开始掉电;高过 6 V 则是让一颗齿轮是尼龙的电机超速运转,折寿。
四节 AA 电池盒是串联的,所以落在哪里由电池的化学体系决定。四节标称 1.2 V 的镍氢充电电池给你 4.8 V,稳稳落在窗口里,而且镍氢在放电过程的大半段都守得住那条平台。四节碱性电池标称 6.0 V,但新的时候每节接近 1.6 V,所以一套新电池空载可能坐在 6.4 V 上下 —— 高过窗口上缘,要等第一颗舵机把它拉下来。碱性电池能用;镍氢才是那个不需要加注解的答案,而电压只讲了一半的原因。另一半是内阻:碱性 AA 是为手电筒造的,不是为四颗一起起动的电机,它的内阻是镍氢电池的好几倍 —— 同一道涌浪镍氢耸耸肩就过去了,碱性电池却会被明显拉垮。那就是同一场掉电重启,只是从 Arduino 挪到了电池身上。
电池盒的红黑两条线末端是裸线,正好是螺丝端子要的东西 —— 红进 V+,黑进 GND,而且要锁在铜线上,不是锁在绝缘皮上。等到四颗舵机变八颗,或者你换成拉电流更大的金属齿 MG90S 舵机(这个选择我们的舵机角度对比教程讲过),就把电池换成 5 V 3 A 火牛加母头圆孔转接线:3 A 大概等于八颗 9 g 舵机同时堵转的余量。通道是免费的,电流不是:十六颗 SG90 一起动是 4 A 的账单,所以电源要按你同时驱动的舵机数量来挑,不是按通道数。绝对不要从 Uno 的 5 V 引脚拉一条跳线过去接 V+。那等于把舵机电流又还给这块板子本来要保护的那条电源轨;而如果端子上同时还接着电池,这条跳线就把两路电源绑在一起 —— 一套新的碱性电池于是坐在 6.4 V 上,直接压在芯片自己的 VCC 引脚,同时倒灌回 Uno,好端端一块驱动板就是这样烧掉的。
接线端子和一整队舵机的实作,用工作台上的速度走一遍:
12 位刻度怎么变成舵机角度?
PCA9685 根本不知道舵机是什么。它只知道怎么把一个重复的周期切成 4096 等份,然后让某一路输出维持高电平若干份。调用 setPWMFreq(50),周期就变成 20 ms 长,于是一份 —— 一个 tick —— 就是 20000 µs ÷ 4096 = 4.88 µs。整个转换层就只有这一道除法。
所以 setPWM(channel, 0, N) 会在周期开头把引脚拉高,N 个 tick 之后放掉,给出一道 N × 4.88 µs 的脉冲。1000 µs 的脉冲是 205 个 tick,1500 µs 是 307,2000 µs 是 410。请用微秒去想,然后只换算一次 —— 舵机的行程本来就是用微秒标定的。
这也是为什么照抄 150 和 600 当端点会出事 —— 那是每一份示例代码都带着的两个数字。用精确的 tick 值 20000 µs ÷ 4096 = 4.8828 µs 乘回去:150 个 tick 是 732 µs,600 个 tick 是 2930 µs。这个跨度比大多数 9 g 舵机走得动的还宽,所以两端摇臂都会顶到机械限位,而内部控制器还盯着一个它修不掉的误差,电机就一直吃着接近堵转的电流,舵机在那里嗡嗡叫。那两个数字是刻意放宽的占位值,不是规格。
有两件事算术看不见,眼睛一看就明白:同一道脉冲下,每一颗舵机会差上好几度;而且 PCA9685 的分频系数是从芯片自己的内部振荡器算出来的,Adafruit 自己的库就注明那颗振荡器一颗一颗之间落在 23–27 MHz 这个带子里,而不是刚好压在数据手册写的 25 MHz 上,所以真实的周期未必正好是 20 ms。
所以要用机械的方式校准。舵机出厂是不带摇臂的,那就撕一条美纹纸胶带贴在输出花键上当旗子。先送 1500 µs,然后一次 25 µs 往外走,边走边看。旗子不动的那一刻 —— 或者舵机开始嗡嗡叫、开始发热 —— 你就到限位了。往回退 50 µs,把这个端点记下来,另一个方向再来一次,每一路通道都来一次。

下面这份程序是那张工作台,不是一个演示:它从串口监视器接收微秒值、帮你换算、记住每一路的端点,然后让四颗舵机在你找到的数字之间来回扫。请先在 IDE 的库管理器里装好 Adafruit PWM Servo Driver Library。
// PCA9685 + Arduino Uno:先量出每颗舵机真正的端点,再让四颗一起扫。
// Uno A4 -> 驱动板 SDA Uno A5 -> 驱动板 SCL
// Uno 5V -> 驱动板 VCC Uno GND -> 驱动板 GND (逻辑侧,毫安级)
// 电池盒或 5 V 火牛 -> 驱动板 V+ 螺丝端子(舵机侧,安培级)
// 舵机插在通道 0-3 上。Uno 继续用 USB 供电。
//
// 串口监视器,9600 波特,行结尾选 "Newline"。命令:
// 0 1500 送 1500 us 到通道 0(一路走这个数字,直到摇臂不动为止)
// min 0 把当前脉冲记成通道 0 的下端点
// max 0 把当前脉冲记成通道 0 的上端点
// sweep 让四路通道各自在自己的端点之间来回扫
// show 打印校准表
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);
const uint8_t CHANNELS = 4;
const uint16_t PWM_HZ = 50;
const uint32_t PERIOD_US = 1000000UL / PWM_HZ; // 50 Hz 下每个周期 20000 us
// 这是起步的猜测值,不是安全限位:1000-2000 us 是 9 g 舵机的标称满行程
//(大约 0 度到 180 度),所以某一颗到了某一端可能已经在嗡嗡叫了。
// 校准的时候按通道各自收窄或放宽。
uint16_t loUs[CHANNELS] = {1000, 1000, 1000, 1000};
uint16_t hiUs[CHANNELS] = {2000, 2000, 2000, 2000};
uint16_t nowUs[CHANNELS];
// PCA9685 把 20 ms 的周期切成 4096 份,所以一份就是
// 20000 / 4096 = 4.88 us。整个换算就只有这个。
uint16_t usToTicks(uint16_t us) {
// 加上 PERIOD_US / 2 是为了四舍五入到最近的一份,而不是一律截断
return (uint16_t)((((uint32_t)us * 4096UL) + PERIOD_US / 2) / PERIOD_US);
}
void sendUs(uint8_t ch, uint16_t us) {
if (ch >= CHANNELS) return;
if (us < 500) us = 500; // 这是搜索范围,不是安全限位:9 g 舵机在
if (us > 2500) us = 2500; // 1000 和 2000 us 附近就已经顶到限位了
nowUs[ch] = us;
pwm.setPWM(ch, 0, usToTicks(us));
}
void show() {
for (uint8_t ch = 0; ch < CHANNELS; ch++) {
Serial.print(F("ch ")); Serial.print(ch);
Serial.print(F(" now ")); Serial.print(nowUs[ch]);
Serial.print(F(" us (")); Serial.print(usToTicks(nowUs[ch]));
Serial.print(F(" ticks) min ")); Serial.print(loUs[ch]);
Serial.print(F(" max ")); Serial.println(hiUs[ch]);
}
}
void sweep() {
for (uint16_t p = 0; p <= 100; p += 2) {
for (uint8_t ch = 0; ch < CHANNELS; ch++) {
sendUs(ch, loUs[ch] + (uint32_t)(hiUs[ch] - loUs[ch]) * p / 100);
}
delay(20);
}
for (int16_t p = 100; p >= 0; p -= 2) {
for (uint8_t ch = 0; ch < CHANNELS; ch++) {
sendUs(ch, loUs[ch] + (uint32_t)(hiUs[ch] - loUs[ch]) * p / 100);
}
delay(20);
}
Serial.println(F("sweep done"));
}
void setup() {
Serial.begin(9600);
Wire.begin();
pwm.begin();
// PCA9685 的内部振荡器纸面上是 25 MHz,实际却一颗一颗地落在
// 23-27 MHz 这个带子里,而分频系数正是从这个数字算出来的。
// 27000000 是 Adafruit 示例里用的数字,不是对「你手上这颗」的实测 —
// 这也正是为什么下面用眼睛找出来的端点,赢过任何从它算出来的数值。
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(PWM_HZ);
for (uint8_t ch = 0; ch < CHANNELS; ch++) sendUs(ch, 1500);
Serial.println(F("ready - try: 0 1500 / min 0 / max 0 / sweep / show"));
}
void loop() {
if (!Serial.available()) return;
String line = Serial.readStringUntil('\n');
line.trim();
if (line.length() == 0) return;
if (line == F("sweep")) { sweep(); return; }
if (line == F("show")) { show(); return; }
if (line.startsWith(F("min ")) || line.startsWith(F("max "))) {
uint8_t ch = line.substring(4).toInt();
if (ch >= CHANNELS) { Serial.println(F("no such channel")); return; }
if (line.startsWith(F("min "))) loUs[ch] = nowUs[ch];
else hiUs[ch] = nowUs[ch];
show();
return;
}
int8_t space = line.indexOf(' ');
if (space < 0) { Serial.println(F("?")); return; }
uint8_t ch = line.substring(0, space).toInt();
uint16_t us = line.substring(space + 1).toInt();
if (ch >= CHANNELS) { Serial.println(F("no such channel")); return; }
sendUs(ch, us);
show();
}
它编译出来是 10,226 字节,占 Uno 闪存的 31%。校准的时候一次只插一颗舵机、只校那一路,全部校完再把其余的插回去,然后输入 sweep。
两条线怎么带得动十六路?
PCA9685 挂在 I2C 上,而 I2C 是一条共享总线,不是一组专线。SDA 和 SCL 同时接到每一个设备上,每个设备各自应一个地址,所以 Uno 在开口之前会先点名它要哪一颗芯片 —— 这也是为什么一块 I2C LCD 可以跟它共用同样这两支引脚而不打架。PCA9685 出厂应的是 0x40。
板上那六组标着 A0–A5 的焊盘,丝印写着开 = 0、闭 = 1,就是拿来改地址的。每一组是加在基址上的一个偏移位:把 A0 用锡桥起来,板子就变成 0x41;只桥 A1 是 0x42;两个都桥就是 0x43。于是六十二块板子可以共用 Uno 的同一对引脚 —— 992 路通道。
这也是这里唯一需要动烙铁的活儿。级联输出那一侧,也就是右边缘那排镜像的六个孔,出厂是空的;只有第二块板子要加进来的时候,你才在那里焊一排排针。第一次搭这个作品,这张零件清单上没有任何东西需要焊接。

我们从真实客户身上看到的常见错误
从 Uno 的 5 V 引脚拉一条跳线到 V+。看起来很整齐,却把这块板子的意义整个抹掉 —— 舵机电流原路杀回自恢复保险丝,掉电重启一次不少。
只接 V+,没接 VCC。芯片没有逻辑供电,于是它在 I2C 上永远不应答,每一路都静悄悄。反过来那一种 —— 接了 VCC 没接 V+ —— 更难看出来:程序跑得好好的,库也不报错,舵机就只是抽两下或者干脆不动,因为根本没有东西在喂它们的红针。
从示例代码里照抄 150 和 600。那两个 tick 值跨的是 732–2930 µs,所以每一次扫动的两端都把舵机顶进它自己的限位。
把没电的碱性电池留在电池盒里。每节 1.1 V 的时候整盒只剩 4.4 V,而症状 —— 一带负载就顿挫、空载又好好的 —— 看起来跟代码出问题一模一样。
SDA 和 SCL 接反。A4 和 A5 就挨在一起,对调之后板子什么都不应 —— 跟芯片死了或者忘了接 VCC 是同一种沉默。在你动手 debug 任何别的东西之前,先跑一遍 IDE 自带的 File → Examples → Wire → i2c_scanner:只要 0x40 冒出来,接线就是对的,问题在你的脉冲里。
常见问题
可以直接用 Arduino 的 5V 引脚给舵机供电吗?
只有一两颗小的可以。用 USB 供电时,Uno 的 5 V 是穿过一颗 500 mA 自恢复保险丝出来的,而四颗 SG90 同时动要 400 mA 到 1 A —— 电源轨于是塌下去,欠压检测把芯片复位,串口也可能掉线。
PCA9685 的 V+ 端子该接多少电压?
插着舵机的时候是 5–6 V 直流。四节镍氢 AA 串联给 4.8 V 而且守得住;5 V 3 A 火牛是同一个答案,余量更大。排针上的 VCC 是另一回事 —— 那是 Uno 给的 5 V 逻辑供电,只喂芯片。
PCA9685 的默认 I2C 地址是多少?
0x40。A0–A5 那六组焊盘用锡桥起来之后,会在基址上加一个二进制偏移 —— A0 是 0x41,A1 是 0x42,两个都桥是 0x43 —— 所以最多 62 块板子能共用 Arduino 的同一对引脚。
舵机脉冲宽度怎么换算成 setPWM 的 tick?
50 Hz 下周期是 20000 µs,芯片把它切成 4096 份,所以一份是 4.88 µs。拿你的脉冲去除以 4.88:1000 µs 是 205 个 tick,1500 µs 是 307,2000 µs 是 410。
为什么同样的数值,两颗一模一样的舵机停在不同角度?
因为玩具级舵机是照着价格造的:里面的反馈电位器和机械限位每一颗都不一样。把每颗舵机的脉冲一格一格往外送,直到摇臂不动为止,再往回退一点,把这一路的一对端点存起来 —— 上面那份程序做的就是这件事。
最后更新:2026 年 8 月。有问题?WhatsApp 联系我们。



16 Channel PWM Servo Motor Driver PCA9685 16 PWM Output with I2C Communication For Arduino Robotic
Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
SG90 MG90 Tower Pro Micro Servo Motor 9g 180 360 Servo Motor for Arduino Robotic - SG90- 180°
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-F 20CM
AA Battery Holder with Cover On/Off Switch 2/3/4 Slots Battery AA Holder Red/Black Wire - AA BATTERY CASE (4S)
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (5V3A)
5.5x2.1mm DC Power Male Connector Plug Jack Adapter for Arduino DIY Electronics Projects - 5.5x2.1 DC CONNECTOR (F)
Data Cable Type-A Type-C MicroUSB Type-B 0.5m 1m 30cm 0.3m 100cm Data Transfer Upload Code - TYPE-A TO TYPE-B (1.0M)