Uma vez, tendo visto o suficiente de todos os tipos de "Enquanto todo mundo está tocando", eu também queria tocar meu Raspberry Pi. Sim, não apenas jogue, mas jogue usando um dispositivo real. Por que no metrô, por 150 rublos, um joystick foi comprado de Dandy (bem, não de um dândi, mas de Simbas Junior). Quem estiver interessado no que aconteceu, pode clicar no botão abaixo. No final do artigo, haverá um link para a prova.Nossos amigos chineses reuniram-se com o lema - não há qualidade, mas você aguenta. Imediatamente um cabo nativo foi soldado com uma seção transversal de condutores de 2 mícrons quadrados e substituído por um cabo de algum conversor de interface industrial, obtido após o próximo comissionamento, e possuía apenas 5 fios.Antes de fazer alterações no código, você tinha que descobrir como o próprio gamepad funciona. O gamepad possui um registro de turno. O gamepad possui 5 fios - 2 - energia, 3 informações - Trava (Strobe), relógio (Pulso) e dados. Quando uma unidade lógica é fornecida ao Latch, o estado das entradas do registro de turno é salvo e, na saída - dados, o estado do botão "A" fica imediatamente disponível e, quando o nível lógico na linha do relógio na saída - dados muda, os níveis de tensão correspondentes ao estado dos outros sete botões são exibidos. em forma seqüencial. O botão pressionado corresponde a - 0, não pressionado - 1. Além disso, para o jogo tudo é exatamente o oposto, é necessário fazer a inversão. A figura abaixo mostra o diagrama do gamepad.
Isto foi seguido por uma escolha de emulador. A escolha recaiu na versão antiga do fceu 0.98.12, uma vez que possui excelente modularidade e emula a arquitetura do console com muita precisão, e está escrita em C. Isso foi seguido pela escolha de uma biblioteca para trabalhar com o GPIO. Escolhi o bcm2835 de Mike McCauley, também escrito em C e com bom desempenho.Como sou um noob em programação, tive que recorrer a uma das celebridades do mesmo “Enquanto todo mundo toca”, com um pedido para comentar em seções de código. E enfie o nariz nessas funções que são responsáveis por transmitir o estado dos botões ao jogo. Eles me explicaram em uma linguagem acessível o que e como. E assim, o arquivo input.c é responsável pela emulação de entrada, e aqui as principais alterações ocorrerão com ele. Existem várias funções responsáveis pela simulação de um gamepad - FCEU_UpdateInput, ReadGP e DECLFW (4016); na verdade, existem mais, são as principais. Além do input.c, tive que fazer alterações no arquivo.c e fceu.c. No primeiro caso, houve erros no arquivo file.c, mas esse problema é o google, existe um patch para esse arquivo e, no arquivo fceu.c, adicionei a inicialização da biblioteca bcm2835 na função int FCEUI_Initialize (void):bcm2835_init();
Pré-adicionando seu arquivo de cabeçalho#include <bcm2835.h>
Agora, input.c, também adicionei o arquivo de cabeçalho da biblioteca bcm2835 (semelhante ao fceu.c) e o arquivo de cabeçalho da biblioteca <unistd.h> para trabalhar com o usleep. Em seguida, anunciei as portas GPIO que estarão envolvidas: #define LATCH RPI_V2_GPIO_P1_11
#define CLK RPI_V2_GPIO_P1_13
#define DATA RPI_V2_GPIO_P1_15
Na função void InitializeInput (void), adicionei um código no qual registrei o modo de operação de cada porta GPIO e redefinii imediatamente as portas responsáveis por Latch (Strobe) e o relógio para 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);
Agora, para as funções:E então o DECLFW (4016) - é responsável por simular o sinal de trava (Strobe). Como foi dito, para ler o estado dos botões, você precisa aplicar o Latch - 1 por um tempo. Há uma variável Laststrobe na qual o último valor gravado neste registro é gravado. Se Laststrobe era 0, então 1 lógico é gravado, respectivamente, e o pino GPIO, designado como Trava, também é alimentado 1 e após 1 μs é redefinido para 0. E se Laststrobe era 1, essa seção do código é ignorada.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;
}
Bem, agora o polling do joystick em si, void FCEU_UpdateInput (void) - nesta função, os dados são lidos dos drivers de entrada que foram selecionados quando o emulador foi configurado ou quando foi iniciado digitando determinadas teclas, por exemplo, um gamepad, terpad, pistola leve etc. ., tudo o que poderia ser conectado ao console. Nele, os bytes do estado dos botões dos joys gamepad [0] ... joy [3] são formados, em uma quantidade de 2 a 4, pois é possível ativar a emulação do Pribluda para conectar mais 2 gamepads. É aqui que as principais mudanças ocorreram. Como não preciso usar a capacidade de trabalhar com 4 gamepads e receber dados de outros drivers, joguei fora todo o código e digitei o meu: 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);
}
Além disso, eu formo imediatamente dois bytes, respectivamente, do primeiro e do segundo joystick. Como muitos jogos leem o estado dos botões de 2 portas ao mesmo tempo, para eles não há conceito de porta prioritária. Mas existem jogos para os quais esse conceito existe - por exemplo, todos Mario, Kirby, Terminator 2, etc. Ou seja, eles leem o estado dos botões apenas na primeira porta (em Mario para o primeiro jogador, e na segunda apenas na segunda), ou seja, no registro 4016. Também é importante atribuir um valor zero quando essa função é chamada, caso contrário, o valor anterior será salvo neles, e o novo já será sobreposto a eles. Em princípio, era possível deixar o byte para o segundo joystick igual a zero, mas tornei possível jogar Mario juntos.ReadGP - ele já extrai bits dos bytes joy [0] ... joy [3] e a variável ret retorna o estado do botão específico no momento para o jogo, o número do botão é definido pela variável joy_readbit [w], em que w é o número da porta do joystick, primeiro ou segundo. Mas nessa função eu não fiz nenhuma alteração. Deixado como está.Para uma compilação bem-sucedida, no Makefile (formado após a execução do comando Configure), localizado no diretório src, adicione -lbcm2835 -lm -lrt ao local em que as dependências da biblioteca são gravadas. Linha:LIBS =
E, em geral, tudo funcionou. Eu deixei as bases se de repente decidisse comprar um segundo joystick para jogar juntos nos mesmos tanques.» Link para a prova
» Foram utilizados dados do site
»Agradecimentos especiais a essa pessoa que me ajudou a entender o código do emulador