El módulo PCA9685 es un controlador de 16 canales que puede controlar 16 salidas PWM a través de la comunicación I2C. Permite, entre otras cosas, liberar las entradas y salidas de su microcontrolador y manejar hasta 16 LEDs o servomotores (o cualquier otro módulo que tome una señal PWM como entrada) usando dos pines (SCL y SDA) mientras mantiene los pines de su microcontrolador para otros módulos como los sensores.
Prerrequisitos: Conducir un servomotor con Arduino
Hardware
- Computadora
- ArduinoUNO
- USB A Macho a B Cable macho
- PCA9685Módulo
Principio de funcionamiento
El módulo se basa en el controlador PCA9685, que permite controlar las salidas PWM mediante la comunicación I2C y un reloj integrado. Este módulo tiene 6 puentes para seleccionar la dirección de la placa y para colocar en el mismo bus hasta 62 controladores para un total de 992 actuadores (direcciones disponibles 0x40 a 0x7F).
Puede manejar salidas PWM con frecuencia ajustable y resolución de 12 bits. El módulo es compatible con microcontroladores de 5V y 3.3V.
Diagrama
El módulo está equipado con un bus I2C y una entrada de energía. El bus I2C está conectado de la siguiente manera:
- Pin A5 o SCL al pin SCL del módulo
- Pin A4 o SDA al pin SDA del módulo
- Pin 5V al pin Vcc del módulo
- Pin GND al pin GND del módulo En este tutorial usamos la placa Arduino UNO pero puede ser adaptada a otros microcontroladores. Para ello, sólo hay que adaptar los pines de I2C disponibles en el microcontrolador en cuestión y posiblemente el código.
Código
Para usar el módulo PCA9685, usamos la biblioteca Adafruit_PWMServoDriver.h. Los anchos de los PWM suelen darse en microsegundos durante un período de 20ms (50Hz), pero estos valores pueden cambiar de un actuador a otro y entre proveedores. Tendrá que modificar los valores del código para adaptarlos a su actuador.
En nuestro caso, utilizamos el actuador MG90S cuyos rangos son de 400-2400µs sobre 20ms.
Para establecer el comando PWM, la biblioteca proporciona dos funciones: setPWM() y writeMicroseconds(). La función writeMicroseconds() nos permite usar los valores del constructor directamente. Para la función setPWM, necesitamos encontrar el ancho de pulso correspondiente en el 4096 (2^12, 12bits).
Ejemplo: La frecuencia es de 50Hz por lo que un período de 20ms o 20000µs.
pwmvalmin=400/200004096=81.92-> 90 redondeo con un margen de seguridad pwmvalmax=2400/200004096=491.52 -> 480 redondeo con un margen de seguridad
Lo que nos da la siguiente equivalencia:
pca.writeMicrosegundos(i,400) es igual a pca.setPwm(i,0,90)
y pca.writeMicrosegundos(i,2400) es igual a pca.setPwm(i,0,480)
Depende de ti usar la función que más te convenga.
//Libraries #include <Wire.h>//https://www.arduino.cc/en/reference/wire #include <Adafruit_PWMServoDriver.h>//https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library //Constants #define nbPCAServo 16 //Parameters int MIN_IMP [nbPCAServo] ={500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500}; int MAX_IMP [nbPCAServo] ={2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500}; int MIN_ANG [nbPCAServo] ={0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; int MAX_ANG [nbPCAServo] ={180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180}; //Objects Adafruit_PWMServoDriver pca= Adafruit_PWMServoDriver(0x40); void setup(){ //Init Serial USB Serial.begin(9600); Serial.println(F("Initialize System")); pca.begin(); pca.setPWMFreq(60); // Analog servos run at ~60 Hz updates } void loop(){ pcaScenario(); } void pcaScenario(){/* function pcaScenario */ ////Scenario to test servomotors controlled by PCA9685 I2C Module for (int i=0; i<nbPCAServo; i++) { Serial.print("Servo"); Serial.println(i); //int middleVal=((MAX_IMP[i]+MIN_IMP[i])/2)/20000*4096; // conversion µs to pwmval //pca.setPWM(i,0,middleVal); for(int pos=(MAX_IMP[i]+MIN_IMP[i])/2;pos<MAX_IMP[i];pos+=10){ pca.writeMicroseconds(i,pos);delay(10); } for(int pos=MAX_IMP[i];pos>MIN_IMP[i];pos-=10){ pca.writeMicroseconds(i,pos);delay(10); } for(int pos=MIN_IMP[i];pos<(MAX_IMP[i]+MIN_IMP[i])/2;pos+=10){ pca.writeMicroseconds(i,pos);delay(10); } pca.setPin(i,0,true); // deactivate pin i } } int jointToImp(double x,int i){/* function jointToImp */ ////Convert joint angle into pwm command value int imp=(x - MIN_ANG[i]) * (MAX_IMP[i]-MIN_IMP[i]) / (MAX_ANG[i]-MIN_ANG[i]) + MIN_IMP[i]; imp=max(imp,MIN_IMP[i]); imp=min(imp,MAX_IMP[i]); return imp; }
Aplicaciones
- Control del robot Quadrina6
Fuentes
- http://adafruit.github.io/Adafruit-PWM-Servo-Driver-Library/html/class_adafruit___p_w_m_servo_driver.html#aa892432ed08e4b3892c8eb0478168dd8
- https://robojax.com/learn/arduino/robojax-PCA6985.pdf
- https://www.nxp.com/products/power-management/lighting-driver-and-controller-ics/ic-led-controllers/16-channel-12-bit-pwm-fm-plus-ic-bus-led-controller:PCA9685
- https://www.arduino.cc/en/reference/wire
- https://github.com/adafruit/Adafruit-PWM-Servo-Driver-Library
Reading your article helped me a lot and I agree with you. But I still have some doubts, can you clarify for me? I’ll keep an eye out for your answers.