Hexapode (Partie 3: électronique)

Choix des composants

Voici un tableau récapitulatifs des pièces électroniques utilisées:

Pièce Nb pièces Lien
Raspberry Pi 3 1 lien
16 channel PWM PCA9685 2 lien
DC buck converter 5A 3 lien
Servomoteurs SG92R 18 lien

J’ai choisi d’utiliser une Raspberry Pi 3 pour pouvoir expérimenter le multi-threading.  Ainsi il est possible de faire tourner par exemple la caméra sur un core, le calcul de position et la mise à jour de position sur un autre en temps réel et le reste du système sur les autres core, tout ça sans perdre en fluidité.

Le multithreding n’est pas aussi efficace sur Rpi zero car ce dernier ne possède qu’un seul core physique d’où ce choix.

Les cartes PCA 9685 seront reliées à la liaison I2C de la RaspberryPi avec deux adresses différentes, ce qui permettra de leur envoyer les données via cette liaison.

Fonctionnement du PCA9685

Voici la doc technique du composant :  lien

L’objectif est de piloter des servomoteurs. Nous devons donc utiliser un PWM à 50Hz avec une période comprise entre 1ms et 2ms comme nous pouvons le voir sur l’image suivante:

On va détailler ici la documentation technique du PCA9685.

Brochage

On commence par le brochage du composant:

  • AX: il s’agit des broches utilisées pour modifier l’adresse du composant. Il y a 5 broches, donc 2^6 adresses possibles. On a donc la possibilité de brancher par exemple 64  PCA9685 différents sur la même liaison I2C, dont on devra retrancher un certain nombre d’addresses réservées à des usages spécifiques.
  • LEDXX: Ce sont les broches sur lesquelles on va brancher les servomoteurs. Ce sont d’elles que sortent les signaux de commande PWM.
  • VDD: C’est la borne d’alimentation positive.
  • VSS: C’est la masse.
  • SCL: C’est l’horloge de la liaison I2C.
  • SDA: C’est le signal de la liaison I2C.
  • EXTCLK: Il est possible de brancher une horloge externe pour augmenter la fréquence du contrôleur. Le composant a une horloge interne qui est bien assez précise pour ce qu’on va en faire.
  • OE: permet d’activer les sorties.

Quant au composant en lui même, il s’agit d’une puce cadencée à 25MHz. Elle est capable de générer les  PWM à des fréquences variant entre 24 et 1526 Hz. Le rapport cyclique varie entre 0 et 100% avec une définition de 4096 pas. Pour ce qui nous concerne on a besoin d’un PWM à 50Hz (T=20ms) et qui varie avec un rapport cyclique de 5% et 10% (entre 1ms et 2ms) ce qui donne des valeurs allant environs du pas 205 (0°) au pas 409 (180°). On a donc une définition de 205 pas pour 180°, ce qui nous donne une précision inférieure au degré.

Coté liaison série, on peut et on  va communiquer avec la puce via une liaison I2C à 400kHz.

Trame

Pour dialoguer avec la puce, il est nécéssaire de lui envoyer des trames. Il est possible aussi de recevoir des infos, en l’occurence les valeurs qui sont stockées dans les registres via cette même liaison I2C, mais cela ne nous sera pas utile tout de suite. Le RaspberryPi sera le maître, les PCA seront les esclaves. On va principalement écrire dans les registres du composant dans un premier temps.

Une trame I2C pour une écriture dans un registre peut se présenter comme ceci:

Un bit de start, un octet pour l’adresse du composant (slave address), un pour l’adresse du registre (control register) et un pour les données à écrire dans le registre ciblé (data for register). L’envoi de trame se termine par un bit de stop. On a un bit de confirmation qui est transmis par le PCA entre chaque octets dans le but de confirmer ou non que la trame a été bien reçue.

Commençons par le premier octet:

Ce premier octet détermine à qui la trame va être envoyé et si une réponse est attendue de l’esclave.

  • Le premier bit est un 1 fixe.
  • Les 6 bits suivant correspondent à l’adresse de l’esclave sur la liaison I2C.
  • Le dernier bit est utilisé pour indiquer à l’esclave si une réponse à cette trame est attendue. Si le bit est à 0 (écriture), on n’attends pas de réponse et on écrit dans le registre. Si le bit est à 1 (lecture), on attends la valeur du registre adressé en réponse.

On va maintenant regarder ce qu’il se passe au niveau des registres.

Les registres des PWM:


On a ici les 4 registres pour la sortie LED2, mais il y en a 15*4 autres identiques, 4 par sorties PWM. Comme l’échantillonage de la période se fait sur 12 bits, on a besoin de 1 octet et demi pour stocker la valeur de l’instant auquel on fera monter le PWM et autant pour la valeur de l’instant auquel on fera descendre le PWM. On peut noter que les valeurs écrites dans les cases entre D7 et D0 donnent l’adresse des registres correspondant.

  • Le registre marqué LEDX_ON_L est le registre dans lequel sont stockés les 8 bits de poids faible de l’instant de montée du signal PWM, tandis que LEDX_ON_H stocke les 4 bits de poids fort restant. Dans notre cas, on fera toujours monter le PWM à l’instant 0. Il y aura donc écrit 0000 0000 dans les registres LEDX_0N_L et XXXX 0000 dans LEDX_ON_H.
  • Le registre marqué LEDX_OFF_L est le registre dans lequel sont stockés les 8 bits de poids faible de l’instant de descente du signal PWM, tandis que LEDX_ON_H stocke les 4 bits de poids fort restant. Dans notre cas, on fera varier l’instant de descente du PWM entre t=205 et t=409.

Les autres registres:



Les registres de type ALL_LED_XXX_X

On peut en effet contrôler tous les registres des sorties Led en écrivant dans un seul registre.

Le registre PRE_SCALE

C’est dans ce registre que l’on va définir la fréquence du PWM.

La doc nous dit de calculogiqueler la valeur à entrer dans le registre avec cette formule:

prescale\_value=round\left (\frac{osc\_clock}{4096 \times update\_rate}\right )-1

Ce qui donne dans notre contexte:

prescale\_value=round\left (\frac{25 000 000}{4096 \times 50}\right )-1

prescale\_value=round\left (\frac{25 000 000}{4096 \times 50}\right )-1

On peut corriger cette formule grâce à la classe Adafruit_PWMServoDriver écrite pour Arduino et qui nous informe que la formule nécéssite une modification de la valeur de la fréquence du PWM.

prescale\_value=round\left (\frac{25 000 000}{4096 \times 50 \times 0.9}\right )-1

prescale\_value=135

Le registre TestMode

Il  ne faut en aucun cas écrire dans ce registre. Ce registre est réservé et y écrire quelque-chose engendrerai des résultats imprédictibles.

Le registre MODE1

Chaque bits du registre MODE1 a sa propre fonction:


On va détailler ici la fonction de chaque bits:

Bit 0 (ALLCALL): à 0, il n’est pas possible d’utiliser la fonction ALLCALL, à 1 on peut l’utiliser.

Bit 1, 2 et 3 (SUBADRX): Permet d’activer ou de désactiver la fonction SUBADR (voir plus bas). Actif à 1, inactif à 0.

Bit 4 (SLEEP): un passage à 1 permet de désactiver l’oscillateur interne afin d’économiser du courant. La consommation du contrôleur étant négligeable devant celle des servomoteurs, nous ne nous en servirons pas.

Bit 5(AI): Si le bit est à 1 permet d’activer la fonction d’auto incrémentation.

Bit 6 (EXTCLK): Ce bit restera toujours à 0 car on n’utilisera que l’oscillateur interne.

Bit 7 (RESTART): un passage à 1 permet de sortir du mode SLEEP et de modifier le bit 4 en lui mettant la valeur 0.

Le registre MODE2

Le registre MODE2 a un fonctionnement similaires à celles de MODE1.

Détail des bits:

Bit 7 à 5: Ces bits sont réservés. On n’y touchera donc pas.

Bit 4 (INVRT): Bit utile lorsqu’il y a un transistor en commutation en sortie. Il sera toujours à 0 dans notre cas.

Bit 3 (OCH): bit qui décide à quel moment les PWM sont mis à jour sur les sorties. Dans notre cas, ce qui nous intéresse est de charger toutes les sorties au même moment. On choisira donc d’entrer la valeur 0 pour que les PWMs se mettent à jour au moment de la réception du bit de stop.

Bit 2 (OUTDRV): permet de changer la structure des transistors en sortie. Dans notre cas cela n’aura pas d’importance car l’entrée du driver des servomoteurs que l’on va utiliser possède une entrée en haute impédence. Comme cela consomme très peu de courant, les deux structures sont valables. On choisira la structure à collecteur ouvert, et on mettra donc le bit à 0.

Bits 1 et 0(OUTNE): Permet de définir le comportement de la puce lorsque le pin Output Enable est à 1. On n’utilisera pas cette sortie car on souhaite avoir un controle permanent sur le PWM en sortie, et l’utilisation de cette fonction nécéssite de couper le PWM pendant au moins un cycle. On leur donnera la valeur 00.

Les registres SUBADR1, 2 et 3

Permet de définir des groupes d’adresse lorsque l’on utilise plusieurs puce PCA9685 dans le but de dialoguer avec elles avec une même adresse en leur écrivant la même chose dans leur registres. On ne s’en servira donc pas car chaque servo aura besoin d’avoir des valeurs bien spécifiques dans leur registres.

Software Reset

Il est possible de déclencher un reset de tous les PCA9685 sur la liaison I2C en envoyant cette trame:

Câblage

à venir

Utilisation du PCA9685

De manière générale

Nous allons utiliser chaque PCA9685 pour piloter 9 servomoteurs. Pourquoi 9? Dans le but d’utiliser les broches d’alimentation intégrée, et de répartir la charge de courant de manière égale entre les deux cartes.

Le courant maximal que peux tirer un servomoteur est de 250mA, et donc dans le pire des cas, on aurait une charge de 250*9, soit 2,25A. Sachant que les buck converter peuvent fournir chacun 5A, on ne les surcharge pas, ce qui est une bonne chose.

Les positions devront être mises à jour de manière relativement simultanées pour permettre un mouvement fluide. La liason série permet de mettre à jour la position de tous les servomoteurs reliées  à un PCA9685 avec la fonction un trame de 72 octets auquel on devra ajouter un bit de start, 72 bits d’acknowledgement, et 1 bit de stop. Cela nous donne donc 72*8+72+1+1=650 bits. Si l’on considère que la liaison I2C travaille à un débit de 400kbits/s, on a donc une information qui est transmise en 650/400000=1,625ms. Il est donc correct de penser que si l’on adresse le même genre de trame aux deux cartes, alors les positions seront mises à jour en moins de 4 ms. Si l’on considère alors qu’il faut 4ms pour mettre à jour une position, on aura la capacité changer la position des servomoteurs 250 fois par secondes.

Les trames

On va ici décrire la procédure que l’on va suivre pour piloter les sorties PWM comme on le souhaite.

Tout d’abord, après le démarrage de l’hexapode, on va récupérer le contenu d’un certain nombre de registres dont on ne souhaite pas modifier le contenu, ou alors seulement certains bits bien précis. En effectuant des opérations de OR de AND bit à bit, nous pourons alors modifier les valeurs qui nous intéressent sans avoir à toucher aux autres ni même à les connaitre.

Il s’agit des registres MODE1, MODE2, SUBADR1, SUBADR2, SUBADR3 et ALLCALLADR.

Pour les consulter, on enverra une trame composée de l’adresse de l’esclave (de type 0bXXXXXXX1), puis de l’adresse du registre que l’on souhaite consulter. L’esclave devrait répondre en nous envoyant le contenu de son registre.

On conservera les valeurs des registres SUBADR et ALLCALLADR dans des variables sans modification.

On effectuera cette opération sur le registre MODE1 avant de la conserver dans une variable:  MODE1 = MODE1 | 0b00100000
Ou encore en hexa: MODE1 = MODE1 | 0x20

Idem pour le registre MODE 2:  MODE2 = MODE2 & 0b11100000
En hexa:  MODE2 = MODE2 & 0xE0

Première trame à envoyer en écriture donc, modifier la fréquence du PWM en modifiant le registre PRE_SCALE. On y écrira la valeur trouvée plus haut: 135 qui en hexadécimal s’écrit: 0x87

Pour écrire dans un registre spécifique, on utilise une trame de la même forme que celle détaillée au début de cet article.

Une fois la fréquence mise à jour, on peut commencer à envoyer une trame en auto-incrémentation:

Comme on peut le voir sur cette image, pour envoyer une telle trame, il suffit d’adresser l’adresse du PCA en écriture, d’adresser le premier registre qui correspond à MODE1, de set au moins le bit chargé de l’auto incrémentation à 1 puis d’envoyer à la suite et dans l’ordre toutes les valeurs que l’on souhaite écrire dans les registres suivants.

On aura donc une trame qui resemblera à cela:

ADR_PCA, 0x00, MODE1, MODE2, SUBADR1, SUBADR2, SUBADR3, ALLCALLADR, LED_0_ON_L, LED_0_ON_H, … … , LED_15_OFF_L, LED_15_OFF_H.

L’alimentation: batterie et buck converter

à venir

Article suivant

Références:

datasheet PCA9685

Lib arduino du composant

Sommaire du projet:

Partie 1
Partie 2
Partie 3
Partie 4
Partie 5
Partie 6
Partie 7

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *