うわっ…PCA9685の内蔵クロックの精度、低すぎ…?

PWMサーボの制御のために、16chのPWM出力IC PCA9685を使用した。使いやすい基板が Adafruit や 秋月から出ており、Arduino用ライブラリも Adafruit から提供されている。

ところが、サーボ用に周波数50Hz (周期20msec) のPWMパルスを出力させてオシロで測定してみると、周期は 54.73Hz だった。10%近い誤差である。

どうもPCA9685の内蔵クロックは精度が低いらしい。

PCA9685には外部クロックを入力することもできるが、Adafruitの基板では外部クロック端子(EXTCLK, 25番ピン)がGNDに落とされている。そこでパターンカットして25番ピンにポリウレタン線をハンダ付けして引き出し、マイコンから外部クロックを供給できるように改造した。

PCA9685のクロック分周比は最小が 4 (プリスケーラ設定値 3)なので、50Hzのパルスのために必要な最小のクロック周波数は、50Hz × 4096 × 4 = 819,200Hz である。

ESP32でこのようなクロックを出力するコードを以下に示す。

#define LEDC_CHANNEL    0
#define LEDC_FREQUENCY  (50 * 4096 * 4)
#define LEDC_RESOLUTION 4      // 4 bit resolution
#define OUTPUT_PIN      22

void setup() {
  ledcSetup(LEDC_CHANNEL, LEDC_FREQUENCY, LEDC_RESOLUTION);
  ledcAttachPin(OUTPUT_PIN, LEDC_CHANNEL);
  ledcWrite(LEDC_CHANNEL, 8); // 50% duty with 4-bit resolution
}

void loop() {
}

また、Adafruitのライブラリで、外部クロックを使用して分周比を4に設定するには下記のようにする。

#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver Pwm;

void setup() {
  Pwm.begin(4-1); // use external clock, prescaler = (4 -1)
  
// Pwm.begin(); // use internal clock
// Pwm.setPWMFreq(50);  // cycle = 20msec
}

以上の修正で、十分な精度の50Hzのパルスが出力できた。

なお、一度外部クロック使用に設定すると、一旦電源をオフするかソフトウェアリセットしないと内蔵クロック使用には戻らないことに注意。