As férias estão chegando ao fim, o que significa que é hora de lamentar o fígado e virar a cabeça. Então outra ideia me veio à mente. Depois de conectar o gamepad da Dendy (ele é um joystick, ele é um controlador, ele é um joystick, ele é um console de jogos etc.) geektimes.ru/post/281520 , pensei em conectar um segundo ao Raspberry pi. Eu não queria comprar o segundo lixo com botões bloqueados, e, a propósito, eles o jogaram nas prateleiras do Nintendo Classic Mini, bem, quando você o jogava fora, comprava rábano. O objetivo não era comprar um emulador para 4K, mas eu decidi comprar um gamepad. Felizmente, eu consegui comprá-lo, foi o último na loja. Quem estiver interessado no que aconteceu, pode clicar no botão abaixo.Aqui está um link direto para a prova , se o normal não estiver ativado.
Não há segredo de que este gamepad possa ser conectado ao Wii e ao Wii U, o que significa que pode ser interrogado pela interface i2c. O que é encorajador é que o gamepad é alimentado por 3,3 V, o que significa que você não precisa se preocupar com os níveis de tensão correspondentes. Para conectar o gamepad ao Raspberry pi, decidi comprar conectores para o wiimote no aliexpress, que são soldados no tabuleiro dele. Embora houvesse 2 conectores no pacote e eles estivessem em uma bolha comum, um deles ficou em mau estado. Nenhuma lata não pode fazer aqui. Depois que tudo foi soldado, foi necessário verificar se isso funciona. Para isso, decidi usar os utilitários do pacote i2c-tools. A idéia era determinar o dispositivo no endereço - 52. Depois de lançar o i2cdetect, vi o tesouro:i2cdetect -y 1
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: 03 04 05 06 07 -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: -- -- 52 -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --
Meu coração imediatamente ficou mais quente (como se uma garota desconhecida sorrisse para você em uma rua na primavera), por isso funciona. Em seguida, você teve que pesquisar no Google sobre a conexão de periféricos ao Wii. Encontrei um exemplo de como um nunchak do Wii se conecta a um pi Raspberry. Neste exemplo, descobri que o gamepad deve ser inicializado escrevendo zero no registro 0x40 e, em seguida, antes de ler todas as vezes, basta escrever um zero e ler os primeiros 6 bytes.Nesse caso, eu também enfrentei a escolha de uma biblioteca para o i2c. Como não obtive sucesso com a biblioteca bcm2835, decidi usar a biblioteca usada no exemplo - esta é a biblioteca WirinPi. Tudo deu certo com ela. Empiricamente, descobri que as informações do botão estão contidas em 4 e 5 bytes (se você contar bytes a partir de zero). As informações sobre os botões selecionar, iniciar, baixo, direito estão no quarto byte, e as informações sobre os botões A, B, cima, esquerdo estão no quinto byte. Além disso, as informações sobre os botões de seleção, para baixo e para a direita estão nos 4 bits mais altos do byte, e as informações sobre o botão Iniciar estão nas mais baixas. O mesmo vale para o 5º byte, as informações sobre os botões A, B estão nos 4 bits mais altos do byte e as informações sobre os botões para cima, à esquerda, estão nos inferiores. Aqui estão os códigos dos botões: A - 0xcf, B - 0xbf, cima - 0xf0, esquerda - 0xf1, selecione - 0xcf, início - 0xf3, baixo - 0xbf, direita - 0x7f.O resultado do pressionamento conjunto de botões, informações sobre as quais estão em um byte e nos mesmos bits, é um "AND" lógico de seus códigos. Por exemplo, pressionando os botões A e B juntos, obtém o valor 0x8f.Aqui está um link para a tabela:
Em seguida, escrevi um pequeno programa de teste para leitura de botões, abaixo vou listá-lo na íntegra e explicar o que está acontecendo com ele:#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
#include <errno.h>
char i, a, b, c, d, state;
char bytes[6];
int main(void) {
wiringPiSetup();
int fd = wiringPiI2CSetup(0x52);
wiringPiI2CWriteReg8(fd, 0x40, 0x00);
delayMicroseconds(20);
while(1)
{
state = 0;
wiringPiI2CWrite(fd, 0x00);
delayMicroseconds(100);
for (i=0; i<6; i++)
{
bytes[i] = wiringPiI2CRead(fd);
}
a = bytes[5] >> 4;
b = bytes[5] << 4;
c = bytes[4] >> 4;
d = bytes[4] << 4;
if (a == 0xc)
state ^= (1 << 0);
if (a == 0xb)
state ^= (1 << 1);
if (c == 0xc)
state ^= (1 << 2);
if (d == 0x30)
state ^= (1 << 3);
if (b == 0x00)
state ^= (1 << 4);
if (c == 0xb)
state ^= (1 << 5);
if (b == 0x10)
state ^= (1 << 6);
if (c == 0x7)
state ^= (1 << 7);
printf("%x \n", state);
}
return 0;
}
No início, as variáveis são declaradas, com a ajuda da qual o estado de um botão específico será determinado. É importante que as variáveis sejam do tipo char, caso contrário, com um deslocamento bit a bit para a esquerda, apenas 4 zeros serão adicionados, e é isso. A interpretação estará errada. Não importa o quão engraçado isso pareça, você deve usar o feitiço .Em seguida, no corpo principal do programa, a biblioteca WiringPi é inicializada - fiaçãoPiSetup (); e o endereço i2c do dispositivo é definido como 0x52. Em seguida, o controlador é inicializado:
wiringPiI2CWriteReg8(fd, 0x40, 0x00);
delayMicroseconds(20);
Bem, então, já no corpo do loop, 6 bytes são lidos e, antes da leitura, zero é gravado a cada vez. E depois disso, o botão pressionado é determinado. Em geral, todo esse código foi migrado para os arquivos do emulador.Na última vez, a inicialização da biblioteca é registrada no arquivo fceu.c:
int FCEUI_Initialize(void)
{
if(!FCEU_InitVirtualVideo())
return 0;
memset(&FSettings,0,sizeof(FSettings));
FSettings.UsrFirstSLine[0]=8;
FSettings.UsrFirstSLine[1]=0;
FSettings.UsrLastSLine[0]=231;
FSettings.UsrLastSLine[1]=239;
FSettings.SoundVolume=100;
FCEUPPU_Init();
X6502_Init();
wiringPiSetup();
return 1;
}
Bem, além disso, todas as alterações dizem respeito apenas ao arquivo input.c. Inicialmente, na função int FCEUI_Initialize (void), são definidos os modos de operação da porta gpio para trabalhar com o gamepad antigo (do Simba) e o gamepad é inicializado.
pinMode (0, OUTPUT);
pinMode (2, OUTPUT);
pinMode (3, INPUT);
digitalWrite (0, HIGH);
digitalWrite (2, LOW);
fd = wiringPiI2CSetup(0x52);
wiringPiI2CWriteReg8(fd, 0x40, 0x00);
usleep(20);
No começo, você também precisa declarar variáveis que determinarão o pressionamento do botão.Na função estática DECLFW (B4016), um estroboscópio é fornecido para o gamepad antigo, o novo estroboscópio não é necessário:
if (LastStrobe==0)
{
digitalWrite (0, LOW);
}
Ao criar um programa de teste, não consegui resolver o problema de identificação de botões pressionados juntos, informações sobre quais estão em um byte e em um bit. No final, apliquei a construção de caixa de opção em vez de se não.Bem, a função em si é uma função para ler o estado dos botões:
void FCEU_UpdateInput(void)
{
joy[0] = 0;
joy[1] = 0xff;
wiringPiI2CWrite(fd, 0x00);
usleep(100);
for (i = 0; i <= 7; i++)
{
bytes[i] = wiringPiI2CRead(fd);
joy[1] ^= digitalRead(3) << i;
digitalWrite (2, HIGH);
delayMicroseconds (20);
digitalWrite (2, LOW);
delayMicroseconds (20);
}
a = bytes[5] >> 4;
b = bytes[5] << 4;
c = bytes[4] >> 4;
d = bytes[4] << 4;
switch (a){
case 0xc:
joy[0] ^= (1 << 0);
break;
case 0xb:
joy[0] ^= (1 << 1);
break;
case 0x8:
joy[0] ^= (1 << 0);
joy[0] ^= (1 << 1);
break;
}
switch (b){
case 0x00:
joy[0] ^= (1 << 4);
break;
case 0x10:
joy[0] ^= (1 << 6);
break;
case 0x20:
joy[0] ^= (1 << 4);
joy[0] ^= (1 << 6);
break;
}
switch (c){
case 0xc:
joy[0] ^= (1 << 2);
break;
case 0xb:
joy[0] ^= (1 << 5);
break;
case 0x7:
joy[0] ^= (1 << 7);
break;
case 0x8:
joy[0] ^= (1 << 2);
joy[0] ^= (1 << 5);
break;
case 0x4:
joy[0] ^= (1 << 2);
joy[0] ^= (1 << 7);
break;
case 0x3:
joy[0] ^= (1 << 5);
joy[0] ^= (1 << 7);
break;
}
switch (d){
case 0x30:
joy[0] ^= (1 << 3);
break;
}
digitalWrite (0, HIGH);
}
Em um ciclo, combinei a leitura dos botões dos dois gamepads, bem, acho que são lidos 2 bytes extras, isso não importa, mas tudo acontece ao mesmo tempo. E não há atrasos.Em geral, está tudo bem.PS: Pelo que me lembro, amanhã no trabalho, já distorcido.PPS Esqueci de adicionar isso para compilação bem-sucedida, no Makefile (formado após a execução do comando Configure), localizado no diretório src, é necessário adicionar -lwiringPi -lm -lrt ao local em que as dependências da biblioteca são gravadas. Linha:LIBS =