Conecte el joystick de Dendy a Raspberry pi

Una vez, habiendo visto suficiente de todo tipo de "Mientras todos juegan", también quería jugar mi Raspberry pi. Sí, no solo jugar, sino jugar usando un dispositivo real. ¿Por qué en el metro por 150 rublos se le compró un joystick a Dandy (bueno, no a un dandy, sino a Simbas Junior). Aquellos que estén interesados ​​en lo que surgió, pueden hacer clic en el botón a continuación. Al final del artículo habrá un enlace a la prueba.



Nuestros amigos chinos, lo reunieron con el lema: no hay calidad, pero espera. Inmediatamente se solda un cable nativo con una sección transversal de conductores de 2 micras cuadradas, y se reemplaza con un cable de algún convertidor de interfaz industrial, heredado después de la próxima puesta en servicio, solo tenía 5 cables.

Antes de realizar cambios en el código, tenía que descubrir cómo funciona el gamepad. El gamepad tiene un registro de desplazamiento. El gamepad tiene 5 cables - 2 - alimentación, 3 información - Latch (Strobe), reloj (Pulse) y datos. Cuando se suministra una unidad lógica a Latch, el estado de las entradas del registro de desplazamiento se guarda, y en la salida - datos, el estado del botón "A" está disponible de inmediato, y cuando el nivel lógico en la línea del reloj en la salida - datos cambia, aparecen los niveles de voltaje correspondientes al estado de los otros siete botones. en forma secuencial El botón presionado corresponde a - 0, no presionado - 1. Además, para el juego todo es exactamente lo contrario, es necesario hacer la inversión. La siguiente figura muestra el diagrama del gamepad.

imagen

Luego siguió la elección del emulador. La elección recayó en la antigua versión de fceu 0.98.12, ya que tiene una excelente modularidad y emula la arquitectura de la consola con bastante precisión, y está escrita en C. Esto fue seguido por la elección de una biblioteca para trabajar con GPIO. Elegí bcm2835 de Mike McCauley, que también está escrito en C y tiene un buen rendimiento.

Como soy un novato en programación, tuve que recurrir a una de las celebridades del mismo "Mientras todos juegan", con una solicitud para comentar las secciones del código. Y mete la nariz en las funciones que son responsables de transmitir el estado de los botones al juego. Me explicaron en un idioma accesible qué y cómo. Y así, el archivo input.c es responsable de emular la entrada, y aquí se producirán los principales cambios con él. Hay varias funciones que se encargan de simular un gamepad: FCEU_UpdateInput, ReadGP y DECLFW (4016), de hecho, hay más, estas son las principales. Además de input.c, tuve que hacer cambios en file.c y fceu.c. En el primer caso, hubo errores en el archivo file.c, pero este problema es google, hay un parche para este archivo, y en el archivo fceu.c agregué la inicialización de la biblioteca bcm2835 en la función int FCEUI_Initialize (void):

bcm2835_init();

Pre-agregando su archivo de encabezado

#include	<bcm2835.h>

Ahora input.c, también agregué el archivo de encabezado de biblioteca bcm2835 (similar a fceu.c) y el archivo de encabezado de biblioteca <unistd.h> para trabajar con usleep. A continuación, anuncié los puertos GPIO que estarán involucrados:

    #define LATCH RPI_V2_GPIO_P1_11
    #define CLK RPI_V2_GPIO_P1_13
    #define DATA RPI_V2_GPIO_P1_15

En la función void InitializeInput (void), agregué un código en el que registré el modo de operación de cada puerto GPIO e inmediatamente reinicié los puertos responsables de Latch (Strobe) y el reloj a 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);

Ahora a las funciones:

Y entonces DECLFW (4016) - es responsable de simular la señal Latch (Strobe). Como se dijo, para leer el estado de los botones, debe aplicar a Latch - 1 por un tiempo. Hay una variable Laststrobe en la que se escribe el último valor escrito en este registro. Si Laststrobe era 0, entonces se escribe 1 lógico, respectivamente, y el pin GPIO, que se designa Latch, también se alimenta 1 y después de 1 μs se restablece a 0. Y si Laststrobe era 1, entonces esta sección del código se ignora.

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)))
        {
	 /* This strobe code is just for convenience.  If it were
	    with the code in input / *.c, it would more accurately represent
	    what's really going on.  But who wants accuracy? ;)
	    Seriously, though, this shouldn't be a problem.
	 */
	 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;
}

Bueno, ahora el sondeo del joystick en sí, anula FCEU_UpdateInput (anulado): en esta función, los datos se leen desde los controladores de entrada que se seleccionaron cuando se configuró el emulador, o cuando se inició ingresando ciertas teclas, por ejemplo, un gamepad, terpad, pistola de luz, etc. ., todo lo que podría estar conectado a la consola. Genera bytes del estado de los botones de los gamepads joy [0] ... joy [3], en una cantidad de 2 a 4, ya que puede habilitar la emulación del Pribluda para conectar 2 gamepads más. Aquí es donde tuvieron lugar los principales cambios. Como no necesito usar la capacidad de trabajar con 4 gamepads y recibir datos de otros controladores, tiré todo el código e ingresé el mío:

    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);
	}

Además, inmediatamente formo dos bytes, respectivamente, del primer y segundo joystick. Dado que muchos juegos leen el estado de los botones de 2 puertos al mismo tiempo, para ellos no existe el concepto de un puerto prioritario. Pero hay juegos para los que existe ese concepto, por ejemplo, todos Mario, Kirby, Terminator 2, etc. Es decir, leen el estado de los botones solo desde el primer puerto (en Mario para el primer jugador, para el segundo solo desde el segundo), es decir, desde el registro 4016. También es importante asignar un valor de cero cuando se llama a esta función, de lo contrario el valor anterior se guardará en ellos, y nuevo ya se superpondrá en ellos. En principio, era posible dejar el byte para el segundo joystick igual a cero, pero hice posible jugar a Mario juntos.

ReadGP: ya extrae bits de bytes joy [0] ... joy [3], y la variable ret devuelve el estado del botón específico en el momento del juego, el número de botón se establece mediante la variable joy_readbit [w], donde w es el número de puerto del joystick, primero o segundo Pero en esta función no hice ningún cambio. Izquierda como es.

Para una compilación exitosa, en el Makefile (formado después de ejecutar el comando Configurar), que se encuentra en el directorio src, agregue -lbcm2835 -lm -lrt al lugar donde se escriben las dependencias de la biblioteca. Línea:

LIBS =

Y en general, todo funcionó. Dejé las bases si de repente decidí comprar un segundo joystick para jugar juntos en los mismos tanques.

» Enlace a la prueba
» Se utilizaron datos del sitio
»Un agradecimiento especial a esta persona que me ayudó a comprender el código del emulador

Source: https://habr.com/ru/post/es398285/


All Articles