EUR | USD

Plaidoyer pour la manipulation directe des ports

Alors que la programmation est de plus en plus accessible au grand public, certains bits de code à la fois élémentaires et essentiels, comme ceux pour la manipulation directe des ports (DPM), risquent d'être abandonnés ou de tomber entièrement dans l'oubli. Selon certains programmeurs, ces commandes sont difficiles à lire et peuvent désorienter les néophytes. Par conséquent, ils estiment qu'elles ne devraient pas être mélangées aux structures plus familières. Pourquoi ne pas utiliser tous les outils disponibles pour que le code reste concis et efficace ?

Par exemple, chaque port d'un microcontrôleur 8 bits nécessite trois registres 8 bits pour contrôler l'activité du port. Ces registres sont DDRx pour la direction entrée/sortie, PORTx pour le contrôle de niveau logique de broche et PINx qui maintient les états actuels des broches du port. Pour développer notre exemple, étudions le port C. Lors de la configuration, un programmeur peut définir les broches du port C en tant que sorties avec un état initial BAS. Souvent, un élément ordonné « pour boucle » est rédigé, ce qui nécessite 3 ou 4 lignes de code répété et appliqué aux broches zéro à sept pour un total de 32 lignes de code rédigé exécuté. L'utilisation de fonctions de bibliothèque dans la boucle, comme pinMode() et digitalWrite(), fait appel à davantage d'activités « en coulisse », ce qui entraîne une grande quantité de code exécuté supplémentaire.

Finalement, les huit bits de registre de direction pour le port C (DDRC) sont définis sur un état HAUT et les huit bits de registre de niveau logique pour le port C (PORTC) sont définis sur un état BAS. La même action peut être accomplie en définissant les bits sur un état HAUT ou BAS avec les commandes de registre de port directes suivantes :

DDRC = 0xFF; //Set port C pins as OUTPUTS (en binaire, DDRD = 0b11111111;)

PORTC = 0x00; //Set port C pins LOW (en binaire, PORTC = 0b00000000;)

Pour définir le port A en tant qu'entrées, utilisez la commande suivante :

DDRA = 0x00; //Set port A pins as INPUTS (en binaire, DDRA = 0b00000000;)

Enfin, pour définir le port D à la fois en tant qu'entrée et sortie, utilisez la commande suivante :

DDRD = 0x0F; //Set the port D upper four bits as INPUTS and the lower as OUTPUTS (en binaire, DDRD = 0b00001111;)

L'utilisation d'une valeur hexadécimale comme 0xFF permet de mieux comprendre l'affectation de valeurs de bits dans le registre. « 0x » étant l'indicateur hexadécimal, le premier nombre « F » représente les quatre bits supérieurs d'un registre 8 bits et le deuxième nombre « F » représente les quatre bits inférieurs.

Toutes les combinaisons possibles de bits définis sur un état HAUT ou BAS peuvent être représentées par un littéral (nombre) hexadécimal ou binaire. L'utilisation de littéraux binaires offre une meilleure visibilité, car les huit bits sont tous visibles lors de l'écriture du code. Les deux formats sont acceptables s'ils sont pris en charge par le compilateur. Toutefois, la forme hexadécimale est plus courte et a un aspect convivial.

Remarque : l'utilisation de littéraux binaires n'est pas une norme universelle dans le langage C/C++.

Développons davantage cet exemple

Une fois que la configuration des ports A et C est effectuée, seules quelques lignes de code supplémentaires sont nécessaires, en utilisant la manipulation DPM dans la boucle principale, pour lire des dispositifs numériques et en faire fonctionner d'autres. En pratique, deux moteurs pas-à-pas avec des interrupteurs de fin de course contrôlés par un joystick numérique peuvent facilement être programmés avec très peu de commandes de registre et une table de correspondance. La configuration matérielle pour ce scénario est illustrée à la Figure 1.

Figure 1 : Configuration matérielle joystick/moteur pas-à-pas avec les ports A et C du microcontrôleur mis en évidence. (Source de l'image : Digi-Key Electronics)

Matériel :

Un simple joystick numérique avec ses quatre contacts normalement ouverts (NO) connectés à une tension VCC et également connectés aux broches 0 à 3 du port A. Les sorties présentent un état BAS au niveau du microcontrôleur. Lorsque les contacts se ferment, les broches du port passent à l'état HAUT. Les contacts représentent des commandes UP (HAUT), DOWN (BAS), LEFT (GAUCHE) et RIGHT (DROITE), et sont configurés pour permettre à deux contacts adjacents de s'activer simultanément, ce qui donne huit combinaisons de sorties d'interrupteur possibles. Une neuvième sortie représente la position ALL STOP (ARRÊT TOTAL) qui se produit lorsque le joystick est centré et que tous les contacts sont ouverts.

Quatre interrupteurs de fin de course avec leurs contacts normalement ouverts (NO) connectés à la terre sont également connectés aux broches restantes du port A correspondant à la configuration UP (HAUT), DOWN (BAS), LEFT (GAUCHE), RIGHT (DROITE) du joystick. Les sorties des interrupteurs présentent un état HAUT au niveau du microcontrôleur. Lorsque les contacts se ferment, les broches passent à l'état BAS.

Les cartes de variateurs moteurs pas-à-pas créent un mouvement mécanique en faisant basculer trois broches de commande à l'état haut et bas pour faciliter les fonctionnalités de pas, de direction et de maintien. Pour cet exemple, les huit bits du port C sont tous dédiés au fonctionnement de deux variateurs, même si deux broches ne sont pas utilisées.

Programmation :

Pour générer la sortie appropriée vers les variateurs, une table de correspondance est utilisée pour transférer les quatre bits du joystick vers les huit bits des variateurs. Une seule ligne de code dans la boucle principale fait appel à la fonction « get_output() » et transmet le contenu du registre PINA à la fonction. La valeur renvoyée à partir de la fonction est écrite directement dans le registre PORTC :

PORTC = get_output(PINA);

Dans la fonction, la table de correspondance « lookup_output[ ] » est consultée et une valeur indexée est renvoyée. Mais il existe une autre opération à l'intérieur de cette fonction qui implique les interrupteurs de fin de course. La valeur d'index de la table de correspondance contenue dans les crochets de « lookup_output[ ] » est représentée par une expression de décalage et de masquage de bits. La variable d'index qui en résulte est la variable AND des quatre bits supérieurs du registre d'entrée (valeurs des interrupteurs de fin de course) et des quatre bits inférieurs (valeurs du joystick). Si l'un des contacts des interrupteurs de fin de course est fermé et qu'une valeur nulle existe dans l'un des quatre bits supérieurs, le bit inférieur correspondant est effacé.

Remarque : étant donné que quatre bits peuvent représenter 16 combinaisons de bits uniques, les sept positions d'index de la table de correspondance qui ne sont pas utilisées sont converties en 0x00 pour éviter les erreurs.

Copieruint8_t get_output(uint8_t porta_val) { return lookup_output[(porta_val >> 4) & (porta_val & 0x0F)]; } 

Exemple :

Le joystick dans la position UP (HAUT) et RIGHT (DROITE) avec tous les interrupteurs de fin de course ouverts entraîne une valeur de registre PINA binaire de 0b11111001 (ou hexadécimale de 0xF9) qui est transmise à la fonction. La fonction annule les quatre bits supérieurs à l'aide d'une valeur AND au niveau du bit de 0b00001111 (0x0F) qui entraîne une valeur 0b00001001 (0x09) en tant que valeur d'index de la table de correspondance. La comparaison de ce résultat à l'aide d'une autre valeur AND au niveau du bit présentant la valeur décalée de l'état de l'interrupteur de fin de course 0b00001111 (0x0F) laisse la valeur précédente inchangée, en renvoyant 0b00001001 (0x09) en tant que valeur d'index finale qui pointe vers 0b00100001 (0x21) dans la table de correspondance. Il s'agit de la conversion du variateur moteur pour la position UP (HAUT) et RIGHT (DROITE). Reportez-vous à la Figure 2.

Figure 2 : Interprétation des entrées du port A et du port C par le microcontrôleur lorsque le joystick est en position UP (HAUT) et RIGHT (DROITE). (Source de l'image : Digi-Key Electronics)

Si l'interrupteur de fin de course en position UP (HAUT) avait été atteint, avec pour conséquence un bit nul, la valeur décalée serait 0b00000111 (0x07) plutôt que 0b00001111 (0x0F), ce qui entraînerait l'annulation de la valeur correspondante du joystick en position UP (HAUT), avec pour conséquence une valeur d'index finale de 0b00000001 (0x01) au lieu de 0b00001001 (0x09). La valeur convertie pour 0b00000001 (0x01) est 0x26 dans la table de correspondance. Il s'agit de la conversion du variateur moteur pour la position RIGHT (DROITE) uniquement. Reportez-vous à la Figure 3.

Figure 3 : Interprétation des entrées du port A et du port C par le microcontrôleur lorsque le joystick est en position UP (HAUT) et RIGHT (DROITE) tandis que l'interrupteur de fin de course en position UP (HAUT) est déclenché. (Source de l'image : Digi-Key Electronics)

Conclusion

Qu'un programmeur choisisse d'utiliser ou non la manipulation DPM en tant qu'outil viable pour configurer, lire ou écrire des données de port, la possibilité de réduire considérablement le code est une bonne source de motivation. Les mêmes opérations en utilisant des fonctions de bibliothèque standard nécessitent beaucoup plus de code et peuvent utiliser une quantité considérable de la mémoire programme disponible. Le code fourni ci-dessous utilise moins de 1 % de la mémoire du microcontrôleur ATMEGA328P utilisé dans les tests de banc d'essai. Les commentaires bien rédigés au niveau du code constituent un élément clé pour comprendre la fonctionnalité DPM en code écrit et simplifient son utilisation pour le débogage, quel que soit le niveau du programmeur.

Exemples de matériel Digi-Key :

Moteurs pas-à-pas – https://www.digikey.fr/short/pdnfp4

Contrôleurs pas-à-pas – https://www.digikey.fr/short/pdnf4r

Joystick – https://www.digikey.fr/short/pdnf57

Interrupteurs de fin de course – https://www.digikey.fr/short/pdnfwm

Code d'exemple :

Copierconst uint8_t lookup_output[16] = { 0x09, //Index 0 All Stop. Apply hold current 0x26, // Index 1 Right 0x34, // Index 2 Left 0x00, // Index 3 Unused 0x36, // Index 4 Down 0x0C, // Index 5 Down/Right 0x31, // Index 6 Down/Left 0x00, // Index 7 Unused 0x24, // Index 8 Up 0x21, // Index 9 Up/Right 0x0E, // Index 10 Up/Left 0x00, // Index 11 Unused 0x00, // Index 12 Unused 0x00, // Index 13 Unused 0x00, // Index 14 Unused 0x00 // Index 15 Unused }; void setup() { // Set all bits in port A direction register as INPUTs; // Limits (up, down, left, right) Joystick (up, down, left, right) DDRA = 0x00; // Set all bits of port C direction register as OUTPUTs; // Motor control (Not Used, Mot_1, Dir_1, En_1, Not Used, Mot_2, Dir_2, En_2 DDRC = 0xFF; } void loop() { //Send the port A values to the function. Write the return value to port C. PORTC = get_output(PINA); } /***** Input Value Translation Function *******/ uint8_t get_output(uint8_t porta_val) { // Compare the limit switch and joystick values. Retrieve and return the translated value.
    return lookup_output[(porta_val >> 4) & (porta_val & 0x0F)]; } 

À propos de l'auteur

Image of Don Johanneck

Don Johanneck, développeur de contenu technique chez Digi-Key Electronics, travaille dans l'entreprise depuis 2014. Occupant ce poste depuis peu, il est responsable de la rédaction des descriptions vidéo et du contenu produit. Don a obtenu sa licence en sciences appliquées en technologies électroniques et systèmes automatisés du Northland Community & Technical College dans le cadre du programme de bourses de Digi-Key. Il aime le radiomodélisme, la restauration de machines anciennes et le bricolage.

More posts by Don Johanneck