Arcade Shield v2

Dans l’article précédent, j’ai conçu une carte shield qui permet de remplacer par un Arduino Leonardo, la carte USB Joystick fournie avec le kit de joystick et de boutons achetés sur Aliexpress.

Arcade Shield v1

Cela fonctionne très bien, mais j’aimerais aussi contrôler les LED, par exemple pour voir quand on appuie sur les boutons, faire une petite animation lors du branchement la manette, déboguer le programme etc…
J’ai donc décidé d’améliorer la carte Arcade Shield.

PCBWay, le sponsor de la vidéo

L’article et la vidéo sont sponsorisés par PCBWay. Les PCB m’ont été envoyés gratuitement pour que je teste leurs services et partage mon expérience.

Le site Internet de PCBWay

Nombre de de pins disponible

Pour contrôler directement les 10 LED (des 10 boutons), il faudrait 10 sorties. Malheureusement, il n’en reste pas suffisamment puisque le joystick et les boutons en utilisent 14. Si on enlève les pins 0 et 1 (utilisées pour communiquer avec l’ordinateur) il reste seulement les pins 13, 21, 22 et 23. On pourrait éventuellement utiliser les pins 14, 15 et 16 mais cela nécessiterait l’ajout d’un connecteur supplémentaire (ICSP, en bas du schéma).

Arduino Leonardo Pinout

Il serait tout à fait possible de réduire nombre de boutons, parce que 6 boutons (Start, Select, A, B, X et Y) sont suffisants pour jouer à la plupart des jeux. Et les 4 boutons restants seraient placés sur les bords de la manette (devant ou sur les cotés) mais reliés aux autres boutons. Cela permettrait par exemple de jouer au flipper dans de bonnes conditions.

On pourrait également utiliser des matrices pour brancher les boutons (comme sur les claviers) et les LED. Mais les branchements sont plus compliqués, il reste peu de place pour ajouter tous les composants électroniques nécessaires (diodes et contrôleur de LED).

Le registre à décalage

Finalement, j’ai choisi d’utiliser le registre à décalage MC74HC595. Cette puce électronique permet de contrôler 8 LED avec seulement 3 pins de l’Arduino. Il faut relier les LED au registre à décalage et ensuite indiquer dans le programme quelle LED doit être allumée.

shiftOut(21, 23, LSBFIRST, B00000000);

Par exemple, pour éteindre toutes les LED, il faut envoyer la valeur (binaire) B00000000 au registre à décalage, pour allumer seulement le 2ème LED, la valeur (binaire) B01000000 etc..

Fonctionnement du registre à décalage MC74HC595

Si vous souhaitez plus d’informations, sachez que j’explique tout ça dans mon livre sur l’Arduino.

Malheureusement, mon registre à décalage peut contrôler seulement 8 LED (sur 10). Il serait possible de relier ensemble plusieurs puces MC74HC595 pour augmenter le nombre de LED. Mais ça complique quand même un peu le code et les branchements. J’ai donc décidé de faire un compromis en reliant seulement les 8 boutons de jeu (B1 à B8) au registre à décalage et en laissant les 2 boutons Start en Select allumés en permanence. Voilà le schéma de branchement :

Schéma de branchement de la carte Arcade Shield v2

Et le code complet :

// Arcade_Shield_v2

#include <Joystick.h>

Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID,JOYSTICK_TYPE_GAMEPAD,
  10, 0,                  // Button Count, Hat Switch Count
  true, true, false,     // X and Y, but no Z Axis
  false, false, false,   // No Rx, Ry, or Rz
  false, false,          // No rudder or throttle
  false, false, false);  // No accelerator, brake, or steering

// Registre_a_decalage MC74HC595AN
// D21 pour l'Arduino Leonardo et D17 pour l'Arduino Uno
const int MEMORISATION = 23;  // SER
// D22 pour l'Arduino Leonardo et D18 pour l'Arduino Uno
const int AFFICHAGE = 22;  // RCLK
// D23 pour l'Arduino Leonardo et D19 pour l'Arduino Uno
const int BIT = 21; // SRCLK

// Mesure du temps
unsigned long tempsEcoule = millis();

void setup() {
  // Boutons
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
  pinMode(12, INPUT_PULLUP);
  // D18 pour l'Arduino Leonardo et D14 pour l'Arduino Uno
  pinMode(18, INPUT_PULLUP);
  // D19 pour l'Arduino Leonardo et D15 pour l'Arduino Uno
  pinMode(19, INPUT_PULLUP);
  // D20 pour l'Arduino Leonardo et D16 pour l'Arduino Uno
  pinMode(20, INPUT_PULLUP);

  //Registre_a_decalage
  pinMode(BIT, OUTPUT);
  pinMode(AFFICHAGE, OUTPUT);
  pinMode(MEMORISATION, OUTPUT);

  // Par sécurité, si bouton Start appuyé au démarrage
  // la manette n'est pas activée
  if (digitalRead(6) == 0) {
    // Boucle infinie
    while(digitalRead(6) == 0) { 
      clignoteTout(400);
    }
  }

  // Animation (qui allume toutes les LED)
  animationDebut(3000);

  // Initialize Joystick Library
  Joystick.begin();
  Joystick.setXAxisRange(-1, 1);
  Joystick.setYAxisRange(-1, 1);
}

int currentButtonState;
// Last state of the buttons
int lastButtonState[14] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0};

// Leonardo
int buttonMap[14] = {2,3,4,5,6,7,8,9,10,11,12,18,19,20};
// Uno
// int buttonMap[14] = {2,3,4,5,6,7,8,9,10,11,12,14,15,16};

// ButtonMap = 0, Pin 2 = UP
// ButtonMap = 1, Pin 3 = RIGHT
// ButtonMap = 2, Pin 4 = DOWN
// ButtonMap = 3, Pin 5 = LEFT

// ButtonMap = 4, Pin 6 = Start
// ButtonMap = 5, Pin 7 = Select
// ButtonMap = 6, Pin 8 = Button 1
// ButtonMap = 7, Pin 9 = Button 2
// ButtonMap = 8, Pin 10 = Button 3
// ButtonMap = 9, Pin 11 = Button 4
// ButtonMap = 10, Pin 12 = Button 5
// ButtonMap = 11, Pin A0 (18 ou 14) = Button 6
// ButtonMap = 12, Pin A1 (19 ou 15) = Button 7
// ButtonMap = 13, Pin A2 (20 ou 16) = Button 8

void loop() {
  // Read pin values
  for (int index = 0; index < 14; index++) {
    currentButtonState = !digitalRead(buttonMap[index]);
    if (currentButtonState != lastButtonState[index]) {
      switch (index) {
        case 0: // UP
          if (currentButtonState == 1) {
            Joystick.setYAxis(-1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 1: // RIGHT
          if (currentButtonState == 1) {
            Joystick.setXAxis(1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 2: // DOWN
          if (currentButtonState == 1) {
            Joystick.setYAxis(1);
          } else {
            Joystick.setYAxis(0);
          }
          break;
        case 3: // LEFT
          if (currentButtonState == 1) {
            Joystick.setXAxis(-1);
          } else {
            Joystick.setXAxis(0);
          }
          break;
        case 4: // Start Button
          Joystick.setButton(0, currentButtonState);
          break;
        case 5: // Select Button
          Joystick.setButton(1, currentButtonState);
          break;
        case 6: // Button 1
          Joystick.setButton(2, currentButtonState);
          affichageLED(B01111111);
          break;
        case 7: // Button 2
          Joystick.setButton(3, currentButtonState);
          affichageLED(B10111111);
          break;
        case 8: // Button 3
          Joystick.setButton(4, currentButtonState);
          affichageLED(B11011111);
          break;
        case 9: // Button 4
          Joystick.setButton(5, currentButtonState);
          affichageLED(B11101111);
          break;
        case 10: // Button 5
          Joystick.setButton(6, currentButtonState);
          affichageLED(B11110111);
          break;
        case 11: // Button 6
          Joystick.setButton(7, currentButtonState);
          affichageLED(B11111011);
          break;
        case 12: // Button 7
          Joystick.setButton(8, currentButtonState);
          affichageLED(B11111101);
          break;
        case 13: // Button 8
          Joystick.setButton(9, currentButtonState);
          affichageLED(B11111110);
          break;
      }
      lastButtonState[index] = currentButtonState;
    }
  }
  if ((millis() - tempsEcoule) >= 80){
    affichageLED(B11111111);
  }
  delay(10);
}

void animationDebut(int duree){
  affichageLED(B00000000);
  delay(duree/8);
  affichageLED(B10000000);
  delay(duree/8);
  affichageLED(B10100000);
  delay(duree/8);
  affichageLED(B10101000);
  delay(duree/8);
  affichageLED(B10101010);
  delay(duree/8);
  affichageLED(B10101011);
  delay(duree/8);
  affichageLED(B10101111);
  delay(duree/8);
  affichageLED(B10111111);
  delay(duree/8);
  affichageLED(B11111111);
}

void clignoteTout(int duree){
  affichageLED(B11111111);
  delay(duree/2);
  affichageLED(B00000000);
  delay(duree/2);
}

int affichageLED(int leds){
  digitalWrite(AFFICHAGE,LOW);
  shiftOut(BIT, MEMORISATION, LSBFIRST, leds);
  digitalWrite(AFFICHAGE,HIGH);
  tempsEcoule = millis(); //Mise à jour du temps
}

Arcade Shield v2

J’ai refais le schéma en remplaçant les connecteurs 2 pins par des connecteurs 3 pins afin d’alimenter individuellement chaque LED (avec le signal envoyé par le registre à décalage).

Le schéma version KiCad

J’ai routé toutes les pistes, le circuit passe de 2 à 4 couches.

Le routage des pistes avec KiCad

L’aperçu 3D de la carte semble correcte…

L'aperçu 3D de la carte Arcade Shield V2

Et j’ai envoyé le fichier Gerber à PCBWay.

Commande du PCB sur le site PCBWay

Si vous n’êtes pas trop pressé, je vous conseille de choisir le mode de transport Global Standard Shipping parce qu’avec DHL ou FedEx, vous risquez d’avoir des frais de douanes + les frais administratifs du transporteur + la TVA sur les frais administratifs…

Choix du mode de transport

J’ai reçu les PCB un peu plus d’une semaine plus tard. J’ai soudé les connecteurs et j’ai commencé à tester la carte shield.

Arcade Shield v2

Et j’ai découvert un gros problème : Lorsqu’on déplace le joystick sur la droite, cela appuie sur le 3ème bouton.

Test de la manette et des boutons (il y a un problème)

Effectivement, on voit clairement dans l’éditeur de PCB que la piste du joystick (déplacement vers la droite) est en contact avec le bouton B1.

Faux contact sur le PCB

Si l’erreur était sur la couche supérieure ou inférieure, il serait certainement possible de rafistoler la carte en coupant la mauvaise piste au cutter et en ajoutant un petit fil pour rétablir la bonne connexion. Malheureusement Nous sommes sur une des 2 couches centrales…

Arcade Shield v2.1

J’ai fini la vérification de la carte shield (sans détecter d’autres problèmes) et j’ai corrigé mon erreur.

Correction du PCB

Et rebelotte, j’ai commandé le nouveau PCB sur le site de PCBWay.

Commande du PCB sur le site PCBWay

Je l’ai réceptionné une dizaine de jours plus tard (avec le transporteur Global Standard Shipping) et j’ai soudé les composants.

Arcade Shield v2.1

Ensuite, j’ai installé l’Arduino dans le boitier

L'Arcade Shield v2.1 installée dans la manette

Et testé la manette… Ouf, ça fonctionne parfaitement !

Test de la manette et des boutons (tout va bien)

Et maintenant les touches clignotent quand j’appuie dessus…

Test de la manette

Conclusion

Si j’ai un conseil à vous donner c’est de ne jamais faire directement une grosse commande (plusieurs dizaines de pièces ou plus). Mieux vaut commander un petit échantillon pour tester la carte et seulement si elle est validée, lancer la grosse commande.

Si cela vous intéresse, j’ai partagé le fichier Gerber sur le site de PCBWay


Laisser un commentaire