Tutorial · Mittelstufe · 18 Min. Lesezeit

Servos mit dem PCA9685 ansteuern (I²C-PWM-Treiber)

Viele Servos mit einem PCA9685 über I²C treiben: PWM-Frequenz setzen, einen Winkel in kalibrierte Impulsbreite verwandeln und die Servos sicher versorgen.

Ein Mikrocontroller hat nur eine Handvoll Hardware-PWM-Kanäle. Für ein, zwei Servos reicht das – doch ein Roboterarm hat vier, ein Hexapode achtzehn, und dem Arduino gehen die Timer aus, lange bevor dir die Gelenke ausgehen. Der PCA9685 löst das: Eine kleine Platine erzeugt sechzehn unabhängige PWM-Signale in Hardware, und alle befiehlt man über nur zwei I²C-Drähte. So fährt man jedes Gelenk des Roboterarmsimulators auf echter Hardware.

Beschriftetes Diagramm einer PCA9685-16-Kanal-PWM-/Servotreiberplatine: sechzehn dreipolige Servostecker oben, der PCA9685-Chip in der Mitte, eine fünfpolige I²C-Steuerleiste links, Adresslötjumper und eine V+-Schraubklemme für Servostrom rechts.
Sechzehn PWM-Kanäle in Hardware, getrieben über zwei I²C-Pins; Servostrom kommt aus der separaten V+-Klemme, nie aus dem Mikrocontroller. SVG herunterladen

Warum die Servos nicht einfach an den Arduino verdrahten?

Zwei Gründe. Erstens Pins: Dem Uno reichen die Timer für wenige Servo-Objekte, bevor sich die Bibliothek selbst im Weg steht. Zweitens, und wichtiger, Leistung: Ein Servo kann blockiert 500–700 mA ziehen, und vier gemeinsam fahrende mehrere Ampere. Dieser Strom darf nicht aus dem 5-V-Pin des Arduino kommen – er lässt das Board per Brownout zurücksetzen, mitten in der Bewegung. Der PCA9685 behebt beides: Er lagert die PWM-Erzeugung auf einen eigenen Chip aus und führt Servostrom aus seiner eigenen V+-Klemme – hoher Strom bleibt damit vollständig von deiner Logik fern.

Die PWM-Frequenz setzen

Jedes Hobbyservo erwartet ein 50-Hz-Steuersignal – einen Impuls alle 20 ms. Der PCA9685 setzt eine Frequenz für den ganzen Chip – also einmal in setup() setzen:

#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);  // default I²C address

void setup() {
  pwm.begin();
  pwm.setPWMFreq(50);   // 50 Hz — the standard servo refresh rate
}

Alle sechzehn Kanäle pulsen nun mit 50 Hz. Welchen Winkel jedes Servo hält, setzt die Breite seines Impulses, nicht die Frequenz – das steuert man pro Kanal.

Winkel zu Impulsbreite – die Kalibrierung, auf die es ankommt

Ein Servo liest die Impulsbreite als Positionsbefehl: grob 1 ms für 0°, 1,5 ms für Mitte, 2 ms für 180°, fünfzigmal pro Sekunde wiederholt. Das ist derselbe Impulsbreite-als-Winkel-Code, den der SG90 nutzt; der PCA9685 erzeugt ihn nur in Hardware.

Servosteuerungsdiagramm: ein 50-Hz-PWM-Signal, dessen Impulsbreite den Servowinkel setzt. Ein 1-Millisekunden-Impuls befiehlt 0 Grad, 1,5 Millisekunden befehlen 90 Grad und 2 Millisekunden befehlen 180 Grad, dargestellt auf Winkelmesserskalen.
Das Servo liest die Impulsbreite, nicht die Spannung: 1 ms sind 0°, 1,5 ms Mitte, 2 ms Vollweg – fünfzigmal pro Sekunde wiederholt. SVG herunterladen

Der PCA9685 nimmt Millisekunden nicht direkt. Er teilt jede 20-ms-Periode in 4096 Zählwerte (12-Bit-Auflösung) und man sagt ihm, wie viele Zählwerte der Impuls high bleibt. Bei 50 Hz sind eine volle Periode 4096 Zählwerte = 20 ms, also:

  • 1 ms ≈ 205 Zählwerte (0°)
  • 1,5 ms ≈ 307 Zählwerte (90°)
  • 2 ms ≈ 410 Zählwerte (180°)

Das sind die Nennwerte. Echte Servos streuen – dasselbe SG90 kann bei 130 unten anschlagen und bei 490 oben. Das ist die eine Zahl, die man kalibrieren muss. Pro Servo Min und Max speichern und den Winkel in diesen Bereich mappen:

// Calibrate these two per servo by eye — see "Gotchas" below.
const int SERVO_MIN = 130;   // counts at the servo's 0° hard stop
const int SERVO_MAX = 490;   // counts at the servo's 180° hard stop

int angleToCount(int deg) {
  return map(deg, 0, 180, SERVO_MIN, SERVO_MAX);
}

Kalibrierung auszulassen ist der Grund, warum ein „90°“-Befehl an einem Gelenk bei 78° und am anderen bei 96° landet – und warum ein Arm auf unkalibrierten Servos die Pose nie ganz erreicht, die der Inverskinematik-Löser verlangte.

Verdrahtung: zwei Stromschienen, eine Masse

  • Logik – VCC, GND, SDA, SCL vom Arduino. Das versorgt nur den Chip.
  • Servostrom – eine separate 5–6-V-Versorgung an die V+-Schraubklemme, dimensioniert auf den Blockierstrom aller Servos zugleich, nicht ihren Leerlauf.
  • Gemeinsame Masse – die Versorgungsmasse und die Arduinomasse müssen verbunden sein, sonst hat das PWM-Signal keine Referenz und die Servos zucken oder ignorieren dich. Dasselbe gilt für die I²C-Leitungen, die die Befehle tragen.

Einen Kondensator über V+ legen, der die Stromspitze beim Servoanlauf schluckt; das PCA9685-Breakout hat genau dafür eine Bestückfläche.

Der Code: ein Gelenk bewegen

#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

const int SERVO_MIN = 130;
const int SERVO_MAX = 490;

int angleToCount(int deg) {
  return map(deg, 0, 180, SERVO_MIN, SERVO_MAX);
}

void setServo(uint8_t channel, int deg) {
  pwm.setPWM(channel, 0, angleToCount(deg));  // on at count 0, off at the angle count
}

void setup() {
  pwm.begin();
  pwm.setPWMFreq(50);
  setServo(0, 90);   // base to centre
  setServo(1, 45);   // shoulder
  setServo(2, 120);  // elbow
  setServo(3, 10);   // gripper open
}

void loop() {}

Jedes setPWM(Kanal, 0, Zählwert) sagt: „Diesen Kanal am Periodenbeginn einschalten und nach Zählwert Ticks ausschalten.“ Ein Gelenk durchfahren, indem man den Winkel in einer Schleife mit kurzem delay() zwischen Schritten erhöht – damit das Servo Zeit zum Fahren hat statt zu springen.

Fallstricke

  • SERVO_MIN / SERVO_MAX pro Servo kalibrieren. 0° befehlen, notieren, wo das Horn wirklich sitzt, und den Zählwert nachführen, bis es stimmt. Dasselbe bei 180°. Zehn Minuten hier retten jede nachgelagerte Pose.
  • V+ nie aus dem Arduino treiben. Ein Servo mag es überleben; vier setzen das Board per Brownout zurück.
  • Die Versorgung auf Blockieren auslegen, nicht Leerlauf. Eine unterdimensionierte Versorgung sackt, wenn mehrere Servos gemeinsam fahren, und das ganze Board zittert.
  • Die Adresse prüfen. Standard ist 0x40. Die Lötjumper brücken, um Platinen zu verketten, und die neue Adresse dem Konstruktor übergeben.
  • Sanft bewegen. Zum Ziel schreiten schlägt springen – es senkt die Stromspitze und ist freundlicher zu Kunststoffzahnrädern.
  • Der Greiferkanal ist der, der gart. Jedes andere Gelenk erreicht seinen Winkel und ruht; ein Greifer, der ein Objekt hält, ist ein dagegen blockiertes Servo, das dauerhaft etwa ein halbes Ampere zieht. Öffnen, sobald das Teil abgelegt ist – und siehe Servogreiferkraft, Blockieren und Nachgiebigkeit, warum feinere PWM-Auflösung keine feinere Kraft kauft.

Antwortet jedes Gelenk erst auf einen Winkel, ist der Arm bereit für die Geometrie: aus einem Zielpunkt die Gelenkwinkel machen, die ihn erreichen – genau wofür Inverskinematik eines Zweigelenkarms und der Roboterarmsimulator da sind.

Den Graphen erkunden

Teil dieser Bauprojekte

Projekte und Lernpfade, die dieses Tutorial enthalten.

Weiterführende Literatur

Quellen