controlar-16-servos-o-16-salidas-pwm-en-arduino-con-pca9685

Controlar 16 servos o 16 salidas PWM en Arduino con PCA9685

¿Qué es el PCA9685?

El PCA9685 es un controlador de PWM controlado por I2C que podemos conectar con un procesador como Arduino para aumentar el número de salidas PWM disponibles de forma sencilla.

El PCA9685 fue originalmente diseñado para generar señales PWM, principalmente para control de LED. Sin embargo, dado que los servos emplean una señal PWM, también es frecuente emplear el PCA9685 como controlador de servos.

El PCA9685 permite generar hasta 16 señales PWM, o controlar 16 servos, únicamente empleando 2 pines. La frecuencia del PWM es ajustable hasta a 1600 Hz, y la precisión de 12 bits.

La comunicación se realiza a través del bus I2C, por lo que es sencillo obtener los datos medidos. Dispone de 6 pines de dirección, lo que supone que puede direccionarse hasta 62 módulos Se pueden conectar hasta 62 módulos para generar un total de 992 salidas PWM.

Todas las salidas son configurables tanto como push-pull o drenaje abierto. Además de dispone de un pin de output enable, que permite desactivar rápidamente todas las salidas simultáneamente.

El PCA9685 dispone de un reloj propio y la electrónica necesaria para generar las señales PWM, es decir, no es necesario enviar constantemente la señal desde Arduino, que queda liberado para realizar otros procesos.

Precio

Podemos encontrar un módulo generador de señales PWM/controlador de servos basado en el PCA9685 de 16 canales por 1.40€, buscando en vendedores internacionales de eBay o AliExpress.

arduino-controlador-servos-pwm-pca9685-componente

Esquema montaje

La conexión es sencilla, simplemente alimentamos el módulo desde Arduino mediante GND y 5V y conectamos el pin SDA y SCL de Arduino con los pines correspondientes del sensor.

arduino-controlador-servos-pwm-pca9685-esquema

Mientras que la conexión vista desde el lado de Arduino quedaría así.

arduino-controlador-servos-PWM-PCA9685-conexion

Para la conexión de los servos disponemos de 16 columnas de 3 pines. La alimentación de los servos se realiza desde una fuente de alimentación independiente, ya que requieren una corriente superior a la que un procesador puede entregar. Para ello se dispone de una clema de conexión con protección contra polaridad inversa.

En Arduino Uno, Nano y Mini Pro, SDA es el pin A4 y el SCK el pin A5. Para otros modelos de Arduino consultar el esquema patillaje correspondiente.

Al usar varias fuentes de alimentación poner siempre referencia de tensión Gnd común.

Ejemplos de código

Para realizar la lectura del PCA9685 usaremos la librería desarrollada por Adafruit, disponible en este enlace.

La librería proporciona ejemplos de código, que resulta aconsejable revisar. Los siguientes ejemplos son modificaciones a partir de los disponibles en la librería

Generador de salidas PWM

El siguiente código muestra el uso del PCA9685 como generador de salidas PWM. En ella generamos una salida PWM de 1600 Hz en todos los canales, y ajustamos el duty de 0% a 100%.

El PWM se configura marcando el encendido y apagado en un número de ticks entre 0 a 4095. El ancho de un tick en milisegundos dependerá de la frecuencia elegida.

//SCL -> A5
//SDA -> A4

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

void setup() 
{
  pwm.begin();

  // Ajustamos la frecuencia al máximo (1600Hz)
  pwm.setPWMFreq(1600); 
}

void loop() 
{
  for (uint16_t duty = 0; duty < 4096; duty += 8)
  {
    for (uint8_t pwmNum = 0; pwmNum < 16; pwmNum++)
    {
      // Ajustar PWM con ON en tick=0 y OFF en tick=duty
      pwm.setPWM(pwmNum, 0, duty);
    }
  }
}

Controlador de servos

El siguiente ejemplo muestra el uso del PCA9685 como controlador de servos. En el ejemplo se emplearán servos de 50 Hz. Es necesario establecer el número de ticks equivalente a 0º y 180º.

//SCL -> A5
//SDA -> A4

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver servoController = Adafruit_PWMServoDriver(0x40); 

const uint8_t frequency = 50;    // Frecuencia PWM de 50Hz o T=20ms
const uint16_t ServoMinTicks = 102; // ancho de pulso en ticks para pocicion 0°
const uint16_t ServoMaxTicks = 512; // ancho de pulso en ticks para la pocicion 180°

void setup()
{
  servoController.begin();
  servoController.setPWMFreq(frequency );
}

void loop()
{
  for (uint16_t duty = ServoMinTicks; duty < ServoMaxTicks; duty++)
  {
    for (uint8_t n = 0; n<16; n++)
    {
      servoController.setPWM(n, 0, duty);
    }
  }
  delay(1000);

  for (uint16_t duty = ServoMaxTicks; duty > ServoMinTicks; duty++)
  {
    for (uint8_t n = 0; n<16; n++)
    {
      servoController.setPWM(n, 0, duty);
    }
  }
  delay(1000);
}

Si queremos trabajar con ms, en lugar de ticks, necesitaremos una función que realice la conversión. Sin embargo, siempre que sea posible, es preferible el código anterior en ticks dado que permite una mayor precisión.


//SCL -> A5
//SDA -> A4

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>

Adafruit_PWMServoDriver servoController = Adafruit_PWMServoDriver(0x40);

const uint8_t frequency = 50;    // Frecuencia PWM de 50Hz o T=20ms
const uint16_t ServoMinMs = 500;  // Ancho de pulso en ms para pocicion 0°
const uint16_t ServoMaxMs = 2500; // Ancho de pulso en ms para la pocicion 180°

void setup()
{
  servoController.begin();
  servoController.setPWMFreq(frequency);
}

void setServoPulse(uint8_t n, double pulseMs)
{
  double bitlength;
  // 1,000,000 us per second / frequency / 4096 (12 bits)
  bitlength = 1000000 / frequency / 4096;
  double duty = pulseMs / bitlength;
  servoController.setPWM(n, 0, pulseMs);
}

void loop()
{
  for (uint16_t pulseWidth = ServoMinMs; pulseWidth < ServoMaxMs; pulseWidth = pulseWidth + 10)
  {
    for (uint8_t n = 0; n<16; n++)
    {
      setServoPulse(n, pulseWidth);
    }
  }
  delay(1000);

  for (uint16_t pulseWidth = ServoMaxMs; pulseWidth > ServoMinMs; pulseWidth = pulseWidth - 10)
  {
    for (uint8_t n = 0; n<16; n++)
    {
      setServoPulse(n, pulseWidth);
    }
  }
  delay(1000);
}

Descarga el código

Todo el código de esta entrada está disponible para su descarga en Github. github-full