Une fois, après avoir vu assez de toutes sortes de "Pendant que tout le monde joue", je voulais aussi jouer mon Raspberry pi. Oui, pas seulement jouer, mais jouer avec un vrai appareil. Pourquoi dans le métro pour 150 roubles, un joystick a été acheté chez Dandy (enfin, pas chez un dandy, mais Simbas Junior). Ceux qui sont intéressés par ce qui en est arrivé peuvent cliquer sur le bouton ci-dessous. À la fin de l'article, il y aura un lien vers la preuve.Nos amis chinois l'ont rassemblé avec la devise - il n'y a pas de qualité, mais vous tenez bon. Immédiatement, un câble natif a été soudé avec une section de conducteurs de 2 microns carrés et remplacé par un câble provenant d'un convertisseur d'interface industrielle, qui, après la prochaine mise en service, n'avait que 5 fils.Avant d'apporter des modifications au code, vous deviez comprendre comment fonctionne la manette de jeu elle-même. La manette de jeu a un registre à décalage. La manette de jeu a 5 fils - 2 - alimentation, 3 informations - Latch (Strobe), horloge (Pulse) et données. Lorsqu'une unité logique est fournie à Latch, l'état des entrées du registre à décalage est enregistré et à la sortie - données, l'état du bouton «A» est immédiatement disponible, et lorsque le niveau logique sur la ligne d'horloge à la sortie - les données changent, les niveaux de tension correspondant à l'état des sept autres boutons apparaissent sous forme séquentielle. Le bouton enfoncé correspond à - 0, non enfoncé - 1. De plus, pour le jeu tout est exactement le contraire, il faut faire l'inversion. La figure ci-dessous montre le schéma de la manette de jeu.
Cela a été suivi d'un choix d'émulateur. Le choix s'est porté sur l'ancienne version de fceu 0.98.12, car elle a une excellente modularité et émule l'architecture de la console assez précisément, et elle est écrite en C. Cela a été suivi par le choix d'une bibliothèque pour travailler avec GPIO, j'ai choisi bcm2835 de Mike McCauley, qui est également écrit en C et a de bonnes performances.Comme je suis un noob en programmation, j'ai dû me tourner vers l'une des célébrités du même "Pendant que tout le monde joue", avec une demande de commenter les sections de code. Et enfoncez votre nez dans les fonctions qui sont responsables de la transmission de l'état des boutons au jeu. Ils m'ont expliqué quoi et comment dans une langue accessible. Et donc, le fichier input.c est responsable de l'émulation de l'entrée, et ici les principaux changements se produiront avec lui. Il existe plusieurs fonctions qui sont responsables de la simulation de la manette de jeu - FCEU_UpdateInput, ReadGP et DECLFW (4016), en fait, il y en a plus, ce sont les principales. En plus de input.c, j'ai dû apporter des modifications à file.c et fceu.c. Dans le premier cas, il y avait des erreurs dans le fichier file.c, mais ce problème est google, il y a un patch pour ce fichier, et dans le fichier fceu.c j'ai ajouté l'initialisation de la bibliothèque bcm2835 dans la fonction int FCEUI_Initialize (void):bcm2835_init();
Pré-ajouter son fichier d'en-tête#include <bcm2835.h>
Maintenant, input.c, j'ai également ajouté le fichier d'en-tête de bibliothèque bcm2835 (similaire à fceu.c) et le fichier d'en-tête de bibliothèque <unistd.h> pour fonctionner avec usleep. Ensuite, j'ai annoncé les ports GPIO qui seront impliqués: #define LATCH RPI_V2_GPIO_P1_11
#define CLK RPI_V2_GPIO_P1_13
#define DATA RPI_V2_GPIO_P1_15
Dans la fonction void InitializeInput (void), j'ai ajouté un code dans lequel j'ai enregistré le mode de fonctionnement de chaque port GPIO, et j'ai immédiatement réinitialisé les ports responsables du verrouillage (stroboscope) et de l'horloge à 0. bcm2835_gpio_fsel(LATCH, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(CLK, BCM2835_GPIO_FSEL_OUTP);
bcm2835_gpio_fsel(DATA, BCM2835_GPIO_FSEL_INPT);
bcm2835_gpio_set_pud(DATA, BCM2835_GPIO_PUD_UP);
bcm2835_gpio_write(CLK, LOW);
bcm2835_gpio_write(LATCH, LOW);
Passons maintenant aux fonctions:Et donc DECLFW (4016) - est responsable de la simulation du signal Latch (Strobe). Comme cela a été dit, pour lire l'état des boutons, vous devez appliquer à Latch - 1 pendant un certain temps. Il existe une variable Laststrobe dans laquelle la dernière valeur écrite dans ce registre est écrite. Si Laststrobe était égal à 0, le 1 logique est écrit, respectivement, et la broche GPIO, qui est appelée Latch, est également alimentée 1 et après 1 μs est réinitialisée à 0. Et si Laststrobe était égal à 1, cette section de code est ignorée.static DECLFW(B4016)
{
if (FCExp)
if (FCExp->Write)
FCExp->Write(V & 7);
if (JPorts[0]->Write)
JPorts[0]->Write(V & 1);
if(JPorts[1]->Write)
JPorts[1]->Write(V&1);
if((LastStrobe&1) && (!(V&1)))
{
if(JPorts[0]->Strobe)
JPorts[0]->Strobe(0);
if(JPorts[1]->Strobe)
JPorts[1]->Strobe(1);
if(FCExp)
if(FCExp->Strobe)
FCExp->Strobe();
}
if (LastStrobe==0)
{
bcm2835_gpio_write(LATCH, HIGH);
usleep(1);
bcm2835_gpio_write(LATCH, LOW);
}
LastStrobe=V&0x1;
}
Eh bien, maintenant l'interrogation du joystick lui-même, void FCEU_UpdateInput (void) - dans cette fonction, les données sont lues à partir des pilotes d'entrée qui ont été sélectionnés lorsque l'émulateur a été configuré, ou quand il a été démarré en entrant certaines touches, par exemple, une manette de jeu, un terpad, une arme à feu, etc. ., tout ce qui pourrait être connecté à la console. Dans celui-ci, les octets de l'état des boutons des manettes de jeu joy [0] ... joy [3] sont formés, dans une quantité de 2 à 4, car vous pouvez activer l'émulation du Pribluda pour connecter 2 gamepads supplémentaires. C'est là que les principaux changements ont eu lieu. Comme je n'ai pas besoin d'utiliser la possibilité de travailler avec 4 manettes de jeu et de recevoir des données d'autres pilotes, j'ai jeté tout le code et entré le mien: joy[0] = 0;
joy[1] = 0;
for (i = 0; i <= 7; i++)
{
joy[0] ^= bcm2835_gpio_lev(DATA) << i;
joy[0] ^= (1 << i);
joy[1] ^= bcm2835_gpio_lev(DATA) << i;
joy[1] ^= (1 << i);
bcm2835_gpio_write(CLK, HIGH);
usleep(1);
bcm2835_gpio_write(CLK, LOW);
usleep(1);
}
De plus, je forme immédiatement deux octets, respectivement, du premier et du deuxième joystick. Étant donné que de nombreux jeux lisent l'état des boutons de 2 ports en même temps, pour eux, il n'y a pas de concept de port prioritaire. Mais il existe des jeux pour lesquels un tel concept existe - par exemple, tous Mario, Kirby, Terminator 2, etc. Autrement dit, ils ne lisent l'état des boutons que depuis le premier port (dans Mario pour le premier joueur, pour le second uniquement depuis le second), c'est-à-dire depuis le registre 4016. Il est également important d'attribuer une valeur de zéro lorsque cette fonction est appelée, sinon la valeur précédente sera enregistrée en eux, et du nouveau leur sera déjà superposé. En principe, il était possible de laisser l'octet du deuxième joystick égal à zéro, mais j'ai permis de jouer à Mario ensemble.ReadGP - il extrait déjà des bits d'octets joy [0] ... joy [3], et la variable ret renvoie l'état du bouton spécifique au moment du jeu, le numéro du bouton est défini par la variable joy_readbit [w], où w est le numéro de port du joystick, premier ou deuxième. Mais dans cette fonction, je n'ai fait aucun changement. Laissé tel quel.Pour une compilation réussie, dans le Makefile (formé après l'exécution de la commande Configure), qui se trouve dans le répertoire src, vous devez ajouter -lbcm2835 -lm -lrt à l'endroit où les dépendances de bibliothèque sont écrites. Ligne:LIBS =
Et en général, tout fonctionnait. J'ai quitté le terrain si je décidais soudain d'acheter un deuxième joystick pour jouer ensemble dans les mêmes tanks.» Lien vers la preuve
» Les données du site ont été utilisées
»Un merci spécial à cette personne qui m'a aidé à comprendre le code de l'émulateur