做六足、做桌面臂、做那种会摇头晃脑的小摆件,最先卡住人的往往不是结构,而是舵机位不够。Arduino 自带的 Servo 库每路都要占一个引脚,还得靠 CPU 反复打断口来保证脉宽,接上七八个舵机之后既不剩引脚,主控也开始发飘。PCA9685 这块板就是冲着这个问题来的:一块板给 16 路独立 PWM,只占 SDA、SCL 两根线,而且脉宽是芯片自己吐的,主控这边几乎不用出力。

Adafruit 官方品名写的是 Adafruit 16-Channel 12-bit PWM/Servo Driver - I2C interface,芯片本体就是 PCA9685,Product ID 815。官网标价按量分三档:买 1 到 9 片是 14.95 美元,10 到 99 片降到 13.46 美元,100 片以上 11.96 美元,页面当时显示缺货,可以留邮箱等补货提醒。
官方给的两个核心数字记牢就行:16 路输出、12 位分辨率,也就是每路的占空比被切成 0 到 4095 共 4096 个刻度;同一条 I2C 总线上最多能挂 62 块板,理论能管到 992 个舵机。日常做 6 自由度的小臂或者六足,一块板用完都绰绰有余,真正需要级联的是那种一条腿三四个舵机、还要对开的机器狗。
引脚别接错:VCC 和 V+ 是两回事

板子两侧的控制引脚是完全一样的,用哪边都行,级联的时候两边插排针反而更方便。电源三个脚里最容易出事的是 VCC 和 V+:VCC 是逻辑电,上限 3 到 5V,它还兼着给 SDA、SCL 上那两个 10K 上拉电阻供电,所以电平要跟主控一致,3.3V 的单片机就别往 5V 上接;V+ 是专门给舵机配电的那一路,用舵机时按 5 到 6V 给,实在凑不出可以到 12V,但把 VCC 接到 V+ 上就等着换板子。
输出侧每路都是 V+、GND、PWM 三针,信号和舵机身上的三线端子直接对插。这里有个官方特意点明的限制:16 路的 PWM 频率必须统一,不能一半给 LED 跑 1.0kHz、另一半给舵机跑 60Hz。另外每针最大只给到 25mA,PWM 输出串了 220 欧电阻,输出逻辑电平跟着 VCC 走,拿它点 LED 的话这几个数都要算进去。
控制脚里 OE 是输出使能,低电平使能、高电平全关,板子内部已经默认拉低,所以不接也照样能跑——要是想做一个急停按钮,把这个脚接出来最省事。默认 I2C 地址是 0x40,改地址的办法放在后面级联那一节。
四根线连 Arduino:三种板子三种接法

因为它走的是 I2C,连到 Arduino 只要四根线。老款 Uno 之类的经典接法是 +5V 接 VCC、GND 接 GND、Analog 4 接 SDA、Analog 5 接 SCL;更早一批 Mega 要换成 Digital 20 接 SDA、Digital 21 接 SCL;R3 及之后的 Uno、Mega、Leonardo 在靠近 USB 口那一排已经独立引出了 SDA、SCL,直接对插就行。

接线时被反复强调的一条:这里的 VCC 只是给芯片自身供电,板子上那句警示写得很直白——只要你打算用 V+ 给它外设供电,就必须把 V+ 也接上。V+ 可以比 VCC 高(官方举例是 VCC 跑 3.3V 时 V+ 仍可到 6V,芯片本身 5V 耐压),官方建议从板子顶端那个蓝色接线端子灌进去,理由是端子做了防反接的极性保护,从反面插排针进去没有这层保护。
舵机供电才是真正的大坑
绝大多数舵机标的是 5V 或者 6V,问题从来不在电压而在于电流。官方给的量级很实在:就算是微型舵机,动起来也要几百毫安;大扭矩舵机带载时单只能超过 1A。十几个舵机同时走的瞬间,电源输出不稳那一抖动,整条 I2C 总线跟着出问题。
所以有一条红线千万别踩:不要用 Arduino 板上的 5V 引脚给舵机供电。大电流拉出来的电气噪声和电压跌落会让主控行为异常、反复复位甚至发热。官方列的几种可选供电路子是:5V 2A 开关电源(这一档页面上显示缺货)、5V 4A 开关电源(14.95 美元)、5V 10A 开关电源、装碱性电池时 6V 的 4 节 AA 电池盒(换镍氢充电电池是 4.8V),以及模型店里 4.8V 或 6V 的 RC 充电电池包。按同时动的舵机数量留一倍以上余量,这个钱省不得。
板上还留了一个直插电解电容的位置。要不要补这个电容取决于你的供电抖不抖,官方给的起步值是 n 乘 100µF,n 是舵机数量——五个舵机就是 470µF 起步,往上加。因为跟舵机电流、负载扭矩、电源特性都挂钩,官方明确说不存在万能值,套件里因此也不配电容。
舵机插头这边没什么技术含量,标准三线母头直接插到排针上,注意把地线(一般是黑色或棕色)对准下面那一排、信号线(黄色或白色)朝上就行。单个舵机插 PWM 0 号口,十六个以内都插同一块板。


装库和跑第一段代码

先在 IDE 里打开库管理器,搜 adafruit pwm,装 Adafruit_PWMServo 这一个,写进代码时头文件叫 Adafruit_PWMServoDriver。装完之后关掉所有 IDE 窗口再重开,从 File 到 Examples 到 Adafruit_PWMServoDriver 找到 Servo 这个例程。
点亮之前先核对供电:裸板的话要同时给 Vin(3 到 5V 逻辑)和 V+(5V 舵机电)上电,绿灯亮;盾板形态要连 V+ 接线柱一起供 5V,红灯和绿灯两个都得亮;FeatherWing 形态同样要给接线柱供 5V,绿灯亮。一盏不对就别往下烧程序。

官方例程这一段可以直接原样拿去跑,八个舵机轮流扫,里面几个宏定义后面标定还要用:
#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"
// called this way, it uses the default address 0x40
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();
// you can also call it with a different address you want
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x41);
// you can also call it with a different address and I2C interface
//Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40, Wire);
// Depending on your servo make, the pulse width min and max may vary, you
// want these to be as small/large as possible without hitting the hard stop
// for max range. You'll have to tweak them as necessary to match the servos you
// have!
#define SERVOMIN 150 // This is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX 600 // This is the 'maximum' pulse length count (out of 4096)
#define USMIN 600 // This is the rounded 'minimum' microsecond length based on the minimum pulse of 150
#define USMAX 2400 // This is the rounded 'maximum' microsecond length based on the maximum pulse of 600
#define SERVO_FREQ 50 // Analog servos run at ~50 Hz updates
// our servo # counter
uint8_t servonum = 0;
void setup() {
Serial.begin(9600);
Serial.println("8 channel Servo test!");
pwm.begin();
/*
* In theory the internal oscillator (clock) is 25MHz but it really isn't
* that precise. You can 'calibrate' this by tweaking this number until
* you get the PWM update frequency you're expecting!
* The int.osc. for the PCA9685 chip is a range between about 23-27MHz and
* is used for calculating things like writeMicroseconds()
* Analog servos run at ~50 Hz updates, It is importaint to use an
* oscilloscope in setting the int.osc frequency for the I2C PCA9685 chip.
* 1) Attach the oscilloscope to one of the PWM signal pins and ground on
* the I2C PCA9685 chip you are setting the value for.
* 2) Adjust setOscillatorFrequency() until the PWM update frequency is the
* expected value (50Hz for most ESCs)
* Setting the value here is specific to each individual I2C PCA9685 chip and
* affects the calculations for the PWM update frequency.
* Failure to correctly set the int.osc value will cause unexpected PWM results
*/
pwm.setOscillatorFrequency(27000000);
pwm.setPWMFreq(SERVO_FREQ); // Analog servos run at ~50 Hz updates
delay(10);
}
void loop() {
// Drive each servo one at a time using setPWM()
Serial.println(servonum);
for (uint16_t pulselen = SERVOMIN; pulselen < SERVOMAX; pulselen++) {
pwm.setPWM(servonum, 0, pulselen);
}
delay(500);
for (uint16_t pulselen = SERVOMAX; pulselen > SERVOMIN; pulselen--) {
pwm.setPWM(servonum, 0, pulselen);
}
delay(500);
// Drive each servo one at a time using writeMicroseconds(), it's not precise due to calculation rounding!
// The writeMicroseconds() function is used to mimic the Arduino Servo library writeMicroseconds() behavior.
for (uint16_t microsec = USMIN; microsec < USMAX; microsec++) {
pwm.writeMicroseconds(servonum, microsec);
}
delay(500);
for (uint16_t microsec = USMAX; microsec > USMIN; microsec--) {
pwm.writeMicroseconds(servonum, microsec);
}
delay(500);
servonum++;
if (servonum > 7) servonum = 0; // Testing the first 8 servo channels
}注意 setup 里那句 pwm.setOscillatorFrequency(27000000)。PCA9685 内部振荡器标称 25MHz,实际每颗芯片落在 23 到 27MHz 之间,这个值会直接影响 setPWMFreq 和 writeMicroseconds 的换算结果,官方建议的做法是挂示波器到 PWM 输出脚,调到实际更新频率等于 50Hz 为止。嫌麻烦的话先按 27000000 跑,扫到端点再说。
超过 16 路:级联与改地址

一块最多十六路,不够就往上叠,官方给的上限是同一总线 62 块。板子两端都焊上排针之后,物理连接就是用一根 6 芯平行线从一块插到下一块。地址在右上角那排跳线上改:基准地址是 0x40,焊上去的二进制偏移量直接加上去——什么都不焊是 0x40;焊 A0 是偏移 00001,地址 0x41;焊 A1 是 00010,地址 0x42;A0 和 A1 一起焊是 00011,地址 0x43;只焊 A2 是 00100,地址 0x44,以此类推。
程序里每一块都要单独一个对象,各自 begin 一遍、各自设定频率,然后按对象操作挂在它上面的那路舵机:
#include "Wire.h"
#include "Adafruit_PWMServoDriver.h"
Adafruit_PWMServoDriver pwm1 = Adafruit_PWMServoDriver(0x40);
Adafruit_PWMServoDriver pwm2 = Adafruit_PWMServoDriver(0x41);
void setup() {
Serial.begin(9600);
Serial.println("16 channel PWM test!");
pwm1.begin();
pwm1.setPWMFreq(1600); // This is the maximum PWM frequency
pwm2.begin();
pwm2.setPWMFreq(1600); // This is the maximum PWM frequency
}这里有个官方 FAQ 里点出来的坑:PCA9685 芯片除了配好的地址之外,还有一个 0x70 的 All Call 地址,恰好跟 Adafruit 的 LED Matrix / 7Seg Backpack 默认地址撞车。同时用这两类板出现异常时,把 backpack 改到 0x71 或者别的地址就能解决。
顺带说一句 LED 的用法。这块板虽然是为舵机设计的,拿它点 LED 完全没问题,只是熄灭要写对——官方 FAQ 写明必须用:
pwm.setPWM(pin, 0, 4096);而不是写 4095,那样灯不会彻底灭。反过来的全亮写法是:
pwm.setPWM(pin, 4096, 0);底层两个函数:setPWMFreq 和 setPWM
真正干活的是这两个。setPWMFreq 决定每秒产生多少个完整脉冲,它的入参范围是 40 到 1600Hz,例如下面这句把频率设成 1000Hz:
pwm.setPWMFreq(1000)官方对频率为什么重要解释得很细:频率定得太高、占空比又很小时,上升沿的时间可能比信号有效时间还长,输出看上去被抹平,压根到不了 VCC,于是各种莫名其妙的问题就来了。舵机一般按 50 到 60Hz 走。
setPWM 则是指定某一路脉冲的高电平从哪个刻度开始、到哪个刻度结束,三个参数依次是通道、开刻度、关刻度,取值都是 0 到 4095。官方的例子是让 15 路在脉冲周期 25% 处翻高、75% 处翻低:
pwm.setPWM(15, 1024, 3072);标定:SERVOMIN、SERVOMAX 与角度换算
不同牌子甚至同一牌子的不同个体,靠那条模拟脉宽开环定位,差个十几二十微秒圆心就偏了,想跑准就得逐个标定。方法是用例程里的 SERVOMIN 慢慢逼近,让扫到的低点刚好停在行程的最小端,然后再用 SERVOMAX 逼近最大端。官方那句警告要放在心上:打到物理极限会扫齿,舵机就永久废了,所以宁可留一点余量。
标定完记下这两个数,之后要把 0 到 180 度的角度换算成脉宽刻度,直接用 Arduino 自带的 map 就行:
pulselength = map(degrees, 0, 180, SERVOMIN, SERVOMAX);这就是 6 自由度小臂、六足这类玩法的基础操作。一个轴的行程一旦标定好了,剩下五轴照着改个数就完事,比拿 Servo 库一路挂着九个舵机等着掉帧爽快得多。
几句掏心窝的判断
这块板的价值不在多稀奇,而是把「引脚不够用」和「主控算脉宽」这两个麻烦一起打包解决了,十几美元的代价换十六路,还要什么自行车。真正容易翻车的从头到尾只有三件事:VCC 和 V+ 接混、用 Arduino 5V 去喂舵机、多路同时动时电源撑不住。
所以顺序建议是这样的:焊排针的时候顺手在板上预留的那个孔位里补一颗电解电容,电源单独准备且留足余量,第一次上电先不插舵机、只用万用表确认 VCC 是逻辑电、V+ 是想要的舵机电,确认两盏灯的指示跟形态对得上,最后再烧程序。步子看着慢,比烧一次板连同舵机一起报废划算。


0x70 那个 All Call 撞地址的事第一次见有人写全,之前我板上挂着 backpack 一起动就抽风,一直以为是排线松了,回头把地址改到 0x71 试试。map 那行也收下了,之前自己写的角度换算还把方向写反了。
VCC 和 V+ 这一段讲得比我买的那个店家的发货说明清楚多了。我之前就是一路 5V 全焊到一起,插上两只 MG996R 一动作,板子发烫、主控跟着重启,折腾半天才发现两路供电压根没分开。
那个电解电容我踩过一次:六个舵机配 5V 4A,一开始没焊电容,六条腿同时起身那一瞬间就掉电重启,后来补了一颗 1000µF,同样的程序立刻稳了。n 乘 100µF 的起步值对小机子没区别,舵机一大就偏紧。
给路过的人提一句:SERVOMIN 和 SERVOMAX 别贪那点多出来的行程,我有一只 9g 舵机就是扫到尽头卡了半分钟,拿下来已经是磨齿的声音了。宁可留两度的余量。