Le module PCA9685 est un contrôleur 16 canaux qui permet de piloter 16 sorties PWM via la communication I2C. Il permet entre autre de libérer des entrées sorties de votre microcontrôleur et piloter jusqu’à 16 LED ou servomoteurs (ou tout autre module prenant en entrée un signal PWM) à l’aide de deux broches (SCL et SDA) tout en conservant les broches de votre microcontrôleur pour d’autres modules comme des capteurs.
Prérequis: Piloter un servomoteur avec Arduino
Matériel
- Ordinateur
- Arduino UNO
- Câble USB A Mâle/B Mâle
- PCA9685 Module
Principe de fonctionnement
Le module est basé sur le contrôleur PCA9685 qui permet de piloter des sorties PWM à l’aide de la communication I2C et d’une horloge intégrée. Ce module comporte 6 ponts permettant de sélectionner l’adresse de la carte et ansi de placer sur le même bus jusqu’à 62 contrôleurs pour un total de 992 servomoteurs (Adresses disponibles 0x40 à 0x7F).
Il permet de piloter des sorties PWM avec une fréquence ajustable et avec une résolution de 12 bits. Le module est compatible avec les microcontrôleurs 5V et 3.3V.
Schéma
Le module est muni d’un bus I2C et d’un entrée de puissance. Le bus I2C est branché comme suit:
- Broche A5 ou SCL à la broche SCL du module
- Broche A4 ou SDA à la broche SDA du module
- Broche 5V à la broche Vcc du module
- Broche GND à la broche GND du module
Dans ce tutoriel, nous utilisons la carte Arduino UNO mais il peut être adapté à d’autre microcontrôleur. Il suffit pour cela d’adapter les broches I2C disponibles sur le microcontrôleur en question et éventuellement le code.
N.B.: Dans le cas de plusieurs servomoteurs ou d’un servomoteur puissant, il faut brancher une alimentation externe à votre système sur les broches V+ et GND tout en s’assurant que les masses de l’alimentation et de l’Arduino sont reliées.
Code
Pour utiliser le module PCA9685, nous utilisons la librairie Adafruit_PWMServoDriver.h. Les largeurs de PWM généralement données en microsecondes sur une période de 20ms (50Hz) mais ces valeurs peuvent changer d’un servomoteur à l’autre et entre fournisseur. Il vous faudra modifier les valeurs du code pour les adapter à votre servomoteur.
Dans notre cas, nous utilisons le servomoteur MG90S dont les plages sont 400-2400µs sur 20ms.
Pour définir la commande PWM, la librairie présente deux fonctions setPWM() et writeMicroseconds(). La fonction writeMicroseconds() nous permet d’utiliser les valeurs constructeur directement. Pour la fonction setPWM, il nous faut trouver la largeur d’impulsion correspondante sur 4096 (2^12, 12bits).
Exemple: La fréquence est de 50Hz donc une période de 20ms soit 20000µs.
- pwmvalmin=400/20000*4096=81.92-> 90 en arrondissant avec une marge de sécurité
- pwmvalmax=2400/20000*4096=491.52 -> 480 en arrondissant avec une marge de sécurité
Ce qui nous donne l’équivalence suivante:
- pca.writeMicroseconds(i,400) équivaut à pca.setPwm(i,0,90)
- pca.writeMicroseconds(i,2400) équivaut à pca.setPwm(i,0,480)
A vous d’utiliser la fonction qui vous arrange le plus.
//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; }
Applications
- Piloter le robot Quadrina6
Sources
- 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
Retrouvez nos tutoriels et d’autres exemples dans notre générateur automatique de code
La Programmerie
Bonjour,
Ayant 64 servos à commander, j’ai monté 4 modules en cascade, en ajustant les adresses comme il se doit.
Avec votre programme, j’arrive bien à commander un module ou un autre en changeant l’adresse au niveau de l’instruction: Adafruit_PWMServoDriver pca= Adafruit_PWMServoDriver(0x40) //0x41, 0x42…;
Mais je voudrais les commander à partir de leur numéro 0-63.
Étant à peu près nul en C, pouvez-vous m’indiquer le processus à suivre?
Merci!
J’ai quand même écrit ceci:
Serial.print (« servo « ); Serial.println(S,DEC); // numéro du servo
Serial.print (« module « ); Serial.println(S>>4,DEC); // numéro du module
Serial.print (« numero « ); Serial.println(S&15,DEC); // numéro dans le module
et changé la dimension des tables.
—
Bonjour,
chouette projet! A quoi vont servir tous ces servomoteurs?!
Je pense que vous avez la bonne logique avec votre bout de code. Il ne vous reste plus qu’à placer les objets pca dans un tableau et vous avez la solution.
Adafruit_PWMServoDriver pca[4]= {Adafruit_PWMServoDriver(0x40),Adafruit_PWMServoDriver(0x41),Adafruit_PWMServoDriver(0x42),Adafruit_PWMServoDriver(0x43)};
A appeler de cette manière:
pca[i>>4].writeMicroseconds(i&15,1500);delay(2);
N’hésitez pas à nous dire si ça fonctionne
Bonjour à tous, je m’appelle JP, je cherche de l’aide pour terminer un projet, ma question,je cherche un bout de programme juste pour faire fonctionner 5 servos avec un arduino (Uno ou Nano) et une carte PCA9685, je trouve bien des exemples, mais jamais avec la possibilité de faire varier la vitesse de chaque servo et cela dans les deux sens (aller rapide et retour plus lent, par exemple)
Merci pour votre aide
JP
Bonjour JP, très bon commentaire!
Si vous souhaitez piloter les servo les uns après les autres, vous pouvez définir des incréments différents STEP_UP et STEP_DOWN pour chaque servo et selon la direction.
for(int pos=CURRENT_IMP[i];pos
Salut à tous et merci bien pour le tuto, bon moi j’aimerais savoir si l’on peut connecter jusqu’à deux modules et les programmés et commandés à partir d’une carte Arduino
Bonjour, oui il est possible de brancher deux modules en série et changer l’adresse I2C du deuxième en soudant une piste
Merci beaucoup, ça fonctionne très bien, et ça fait un code beaucoup plus élégant.
À quoi ça sert? Il y aura 31 servos pour commander des aiguillages (de train miniature), et 27 relais pour commuter le courant de voie, commandés eux aussi par des 9685. Les boutons, inters et LEDs sont connectés directement à l’Arduino.
Bonjour,
J’ai mis du temps à comprendre pourquoi la fonction jointToImp que vous proposez en exemple me donnait des résultats aberrants.
Je pense que les résultats intermédiaires du calcul dépassent la capacité des entiers (et même des longs?).
En la ré-écrivant comme ceci:
int imp =( (x-MIN_ANG[i]) / (MAX_ANG[i]-MIN_ANG[i]) * (MAX_IMP[i]-MIN_IMP[i])) + MIN_IMP[i];
ça passe mieux.
Qu’en pensez-vous?
Merci!
Merci beaucoup d’avoir relevé cette erreur.
J’ai testé votre solution avec un Arduino UNO et je trouve encore des incohérences. Le mieux est de faire un cast sur la variable x avec un double.
Oui, j’ai noté ces incohérences, aussi j’utilise la fonction map.
Bonjour,
J’ai besoin que les sorties de certains modules soient à l’état haut au moment de la mise sous tension.
(La raison en est que ces sorties commandent des modules de relais « actif bas », et il ne faut pas qu’ils soient excités au démarrage, même fugitivement).
Peut-on programmer les module en conséquence? Ou faut-il jouer avec la ligne OE? Comment?
Merci!
Bonjour,
la broche OE sert à activer ou désactiver toutes les sorties du module pca9685 mais il ne permet pas de définir l’état haut ou bas de la sortie.
La solution qui me vient en tête actuellement est de retarder la mise sous tension des modules « actif bas » par rapport au microcontrôleur. Avec un condensateur, par exemple.
Ou inverser la logique en sortie du PCA9685 avec un transistor passant à l’état bas.
Bonjour,
Depuis quelques semaines je mets au point mon application avec un Arduino Mega et tout se passe bien.
Mais je cherche maintenant à utiliser un autre Arduino Mega, d’origine différente, et là lorsqu’on envoie une commande sur le port I2C, la carte se plante complètement. Pourtant cette seconde carte fonctionne très bien avec un autre périphérique I2C (afficheur).
Pouvez-vous m’aider svp? Merci!
J-P.M.
Bonjour,
Mon projet est maintenant bien avancé. Cependant je constate que si le programme fonctionne parfaitement avec un Arduino Mega venant de Chine, il se plante sur un Arduino Mega made in Italy.
On peut vérifier ça sans aucune connexion externe, simplement avec les cartes nues.
Par ailleurs le bus I2C fonctionne normalement sur les deux cartes avec des PCF8574 ou des afficheurs.
Avez-vous une idée sur l’origine du problème et sa solution? Merci.
Bonjour, si je comprends bien vous avez développé une application avec une Arduino Mega chinoise (Marque?) et vous souhaitez la remplacer par une Arduino Mega italienne. Pourriez-vous me donner plus de détail par rapport au « plantage » de la carte made in Italy.
J’ai trouvé le problème. Il y avait un bug dans mon programme: j’initialisais une table bien plus grande que ce que j’avais déclaré. Ensuite, lorsque le programme tentait d’envoyer des commandes sur le bus I2C, ça plantait la carte.
Cependant je ne comprends pas pourquoi ce bug se manifestait sur un Arduino et pas sur l’autre.
Mais, étant débutant, je n’exclus pas m’être simplement « mélangé les pinceaux »…
J’ai deux cartes parce que je compte en installer une au local de mon association et utiliser l’autre chez moi pour peaufiner le programme.
L’Arduino « chinois » est un Geekcreit® Mega 2560 R3 ATmega2560-16AU. Je l’ai choisi parce qu’il ne nécessite pas le driver CH340, qui refuse obstinément de s’installer sur mon PC, (apparemment parce que je suis sous W8.1).
Cordialement,
J-P.
Content que vous ayez trouvé le problème.
Bonjour,
Votre montage est très intéressant et je compte l’utiliser pour commander une trentaine d’aiguillages sur mon réseau HO. J’aimerais savoir commander ce montage à partit de mon ESU.
Je vous remercie de me venir en aide,
Louis
Bonjour,
Pour utiliser le module PCA9685, il faut un bus I2C. Quelle est la référence de votre ESU?
bonjour , a partir de combien de servo branchés dessus il faut une alimentation externe a l’arduino ?
Dès le premier!
Pour une application, il vaut mieux utiliser une alimentation externe. Pour tester un seul petit servomoteur(9g) à vide, on peut utiliser la broche 5V ou Vin.
et il faut mettre une alimentation de combien de V et A pour un montage avec 4 servos ? (Caractéristiques:
Dimension: 22 mm x 11,5 mm x 22,5 mm
Vitesse de fonctionnement: 0,12 seconde / 60 degrés (4,8 V sans charge)
Couple de décrochage (4,8 V): 17,5 oz / po (1 kg / cm)
Plage de température: -30 à +60
Largeur de bande morte: 7usec
Tension de fonctionnement: 3,0 V ~ 7,2 V)
tension de fonctionnement 3 à 7V. Je compterais 500mA pour chaque servomoteur donc 2A. Mais cela dépend du modèle de servomoteur et de la charge qu’ils doivent bouger.
je souhaite piloter 32 servos avec 32 boutons pour 32 aiguillages, avec arduino mega , 2 pca 9685 , bien sur je debute ,pouvez vous m aider sur ce projet ( a part televerser du code !!! ) merci ded votre aide.
Alain
Bonjour,
chouette projet. En plus des driver I2C PCA9685, je vous conseille aussi d’utiliser un multiplexeur ou registre à décalage pour les boutons, cela vous permettra d’utiliser une carte moins imposante.
Je vous conseille de créer une boucle for sur l’ensemble des boutons et des tables pour tout vos paramètres
btnPin[32]
servoId[32]
servoOnPos[32]
servoOffPos[32]
vous pouvez ensuite développer votre code dans la boucle en indiquant la position du servo en fonction du bouton correspondant
Un exemple similaire pour vous aider
https://www.aranacorp.com/fr/gerer-boutons-et-leds-avec-un-registre-a-decalage/
Bonsoir, J’ai en cours de réalisation un projet de motorisation d’un réseau à base de servos et de nano associés à des cartes pca9685. Les aiguillages sont à mouvement lents commandés par des interrupteurs afin de pouvoir jouer facilement avec mon petit-fils. Actuellement j’ai un montage qui me donne satisfaction avec 6 interrupteurs. mais dans une partie du réseau j’ai 7 aiguillages proches à commander et je n’arrive pas à passer de 6 à 7 servos. La carte PCA9685 pouvant piloter 16 servos je tourne en rond…
Pourriez vous m’indiquer comment vous avez réalisé votre système?
Merci d’avance!
Bonjour,
il faut certainement rajouter une alimentation externe à votre système pour alimenter les servomoteurs sur les bornes GND et V+ du pc9685.
Veillez à ce que les masses de l’arduino et de l’alim soient reliées.
Bonjour Sauf erreur on peut chainer plusieurs PCA9685 par le connecteur d’extension.
Toutefois si l’on observe le schéma de la carte on remarque les deux résistances de mise au 5v sur SCL et SDA. Résistances qui sont préconisées dans le câblage du bus I2C mais me semble-t-il à n’implémenter qu’une seule fois. Donc la question doit-on supprimer les résistances des PCA mis en extension ou peut-on les conserver sans soucis.
Merci d’avance pour votre réponse
Roland utilisition en modélisme ferroviaire
Bonjour,
vous pouvez les connecter ensemble sans retirer les résistances. Par contre, il faut souder les bornes A0-A5 pour modifier l’adresse I2C.
Merci de votre promptitude