Desarrollo y creación desde cero de una máquina arcade para cuatro jugadores.

imagen

En noviembre de 2019, dejé mi trabajo y decidí dedicar varios meses a aprender una nueva habilidad que siempre había querido aprender. En ese momento trabajaba como desarrollador web. Antes de eso, estudié desarrollo de software. E incluso antes, cuando era niño, experimentaba constantemente con electrónica y programas. Por lo tanto, me sentí lo suficientemente seguro en la creación de software.

Sin embargo, siempre hubo una cosa mágica llamada "hierro" , que uso a diario, pero no tengo idea de cómo funciona realmente. Un desarrollador de software (y especialmente un desarrollador web) no necesita comprender el hardware. Dado que todo el código de desarrollo web es de muy alto nivel, rara vez resulta que el problema con nuestro software está relacionado con el hardware.

Por lo tanto, nunca trabajé en "hierro" y no tenía ningún conocimiento general sobre ingeniería eléctrica, excepto los que nos dieron en la escuela secundaria. Y quería cambiar esta situación. Me puse una meta absurda, que en ese momento parecía muy lejos de mi conocimiento. Y luego ... empecé a experimentar. Planeaba comprobar hasta dónde podía llegar antes de que me quedara sin motivación. No esperaba lograr mi objetivo, pero de alguna manera lo logré.

Aquí está mi resultado:


Mi increíble objetivo era crear una consola de juegos arcade para cuatro jugadores desde cero. Utiliza un microcontrolador como cerebro y una tira de LED como pantalla. Usando un joystick y cuatro botones para cada jugador ubicado en los bordes de la mesa, los jugadores controlan un juego que se carga dinámicamente en el sistema desde un chip de memoria. Gracias a esto, el juego en el dispositivo se parece a las viejas consolas. Solo que ahora no necesitamos soplar el cartucho para eliminar los errores.

Para mi sorpresa ... Realmente pude completar el proyecto. Me tomó cerca de tres meses completos de trabajo para ciclos de experimentos, fracasos, lectura de documentación, desesperación y reintentos. Sería necesario fijar el número de horas de trabajo para saber cuánto tiempo pasé en esto. Supongo que alrededor de 800-900 horas. (Suponiendo que trabajé 6/7 días a la semana durante 8-10 horas al día, lo que está bastante cerca de la verdad).

Traté de documentar el proceso de mis experimentos de principio a fin, tomando fotos y grabando videos. Con esta publicación documentaré el proyecto y mi experiencia por mí mismo. Espero que para ti se convierta en una fuente de inspiración si también decides emprender un proyecto igualmente increíble.

Llegando al trabajo


Como dije al comienzo del artículo, cuando comencé el proyecto, no tenía ningún conocimiento de electrónica, excepto por la fórmula incorrectamente recordada de la ley de Ohm. Por lo tanto, en primer lugar, necesitaba estudiar cómo funciona la electrónica en la práctica, y solo luego tratar de armar una consola de juegos. Ni siquiera sabía cómo encender el LED usando el microcontrolador. Entonces, mi primer paso: ordenar una electrónica simple y un microcontrolador para experimentos. Para comenzar, intentaré encender el LED. Estoy casi seguro de que mi primer intento de encender el LED provocó su quema. Pero esto no fue documentado en la cámara, así que supongamos que no.


Experimentos con un microcontrolador, resistencias, un transistor y un LED.

Este fue el primer hito importante. Ahora sabemos cómo crear código que se ejecute en el microcontrolador. También aprendimos a usar transistores como un simple interruptor eléctrico para encender un LED. ¡Asombroso!

Aventura con EEPROM



Higo. 1. Uso de registros de desplazamiento para controlar 16 líneas de salida con solo 3 pines de salida.

Parte del proyecto fue la capacidad de escribir la lógica del juego en chips de memoria (EEPROM). Deberían haberse leído cuando se inserta un cartucho con un reproductor en el sistema.

A la larga, me dolió que no aprendiera suficiente información sobre qué tipo de EEPROM debería usarse en este proyecto. Me apasionó tanto que compré los viejos chips EEPROM paralelos (W27C512).

Para escribir o leer de ellos, necesitaba la capacidad de establecer una señal alta o baja en las 16 líneas. Si no quisiera ocupar todos los contactos de salida del microcontrolador, sería lógico aprender a usar los registros de desplazamiento.


Higo. 2. El circuito utilizado en un intento fallido de leer / escribir datos en un chip EEPROM paralelo.

Con solo un par de contactos, podemos controlar por separado las 16 líneas.

¡Ahora apliquemos este conocimiento a EEPROM! Si pero no. No pude escribir datos de manera estable en EEPROM. Este tipo de chip requiere 12-14 V para eliminar o escribir bytes. Traté de proporcionarlos utilizando transistores como interruptores para suministrar mayor voltaje. Y como lo entiendo, DEBE funcionar. Pero no funcionó. Todavía no entiendo cuál es exactamente el problema. En la Fig. La Figura 2 muestra los circuitos que intenté usar para leer / escribir bytes desde / hacia EEPROM.


Higo. 3. Un circuito que ha logrado leer / escribir bytes en un chip EEPROM serie.

Finalmente, después de estudiar más información sobre este tema, aprendí que hoy en día las EEPROM paralelas no se usan con tanta frecuencia. Por lo general, los desarrolladores prefieren EEPROM serie I2C y, por lo tanto, es mucho más fácil encontrar tutoriales sobre ellos. (Por ejemplo, este increíble tutorial de Sparkfun ).

En la Fig. La Figura 3 muestra mi esquema para leer y escribir datos en EEPROM seriales ... Es MUCHO más simple y mucho más confiable. Esto me enseñó que no debería estar muy contento de pedir piezas e intentar desarrollar el proyecto demasiado rápido. Pasamos alrededor de 1 a 2 semanas en EEPROM paralelas, y finalmente no las usamos en absoluto. Sin embargo, en el proceso, aprendimos mucho en electrónica y especificaciones de lectura (que en sí mismo es una habilidad). Los esfuerzos no se desperdiciaron por completo.

Entonces, ¿cómo lo hacemos de todos modos?


Ahora podemos leer y escribir en los chips de memoria. ¿Cuál será el próximo paso? En esta etapa, será lógico describir mi plan de consola con más detalle utilizando varios dibujos como ejemplo.


Plan General. Esta será una consola de juegos con controladores para cuatro jugadores. Cada jugador tiene un joystick y varios botones.

El círculo rojo marca el tapón del cartucho del juego. Este conector tendrá cuatro pines metálicos que la consola usa para leer EEPROM seriales para cargar juegos en la memoria.

El verde muestra cómo planeamos crear una pantalla de tiras de LED. Queríamos que el píxel de la pantalla fuera un parche de color cuadrado y luminoso, no un punto que se mezclara con otros puntos.

Para lograr el efecto deseado, utilizamos dicho esquema para separar las fuentes de luz entre sí. Al mismo tiempo, dispersa la luz que cae sobre la pantalla. (Las rayas blancas debajo de la rejilla indican la tira de LED)

Decidí hacer una pantalla LED de 42x42. Es decir, en total se obtienen 1764 LED. Elegí el número 42, porque gracias a esto, el tamaño de la mesa será el adecuado para cuatro personas.

Encender tantos LED es una tarea desalentadora en sí misma. Al máximo brillo, el LED RGB consume 60 mA de corriente. (que da blanco con brillo máximo). Si multiplicamos por el número total de LED, ¡obtenemos el consumo de corriente máximo de 105.84 A a 5 V! Para acercarnos a esta corriente, compramos una fuente de alimentación SE-600–5 MEAN WELL. Puede suministrar corriente hasta 100 A. Lo cual es menos de lo teóricamente posible 105 A. Pero no busco mostrar pantallas completamente blancas con brillo máximo en los juegos. Además, podemos limitar el brillo mediante programación. También agregaremos fusibles para evitar exceder accidentalmente este límite de 100 A.


Higo. 4. Prototipo de cartón para una pantalla LED de 3x3.

Volveré al montaje de esta pantalla más tarde. Primero necesitas hacer prototipos. Veamos si podemos hacer que una pequeña pantalla LED funcione.

Este prototipo 3x3 está compuesto por varias piezas de cartón que separan los píxeles y un papel normal para impresoras que dispersa la luz. ¡Funcionó muy bien! Hemos recibido una confirmación maravillosa de la capacidad de trabajo del concepto.

Para controlar los LED utilizando un microcontrolador, utilizamos la biblioteca FastLED. Sin embargo, resultó que si los LED están controlados por un bus de datos, entonces es imposible actualizar 1764 LED a una velocidad suficiente para los juegos. Dada una frecuencia de 800 kHz, podemos lograr una velocidad de cuadro de aproximadamente 17 cuadros por segundo. No es exactamente por lo que nos esforzamos. Personalmente, me gusta jugar con al menos 60FPS. Afortunadamente, esto es posible. Es suficiente usar más de un bus de datos. La pantalla 42x42 se puede dividir convenientemente en 7 partes iguales. Cada una de estas partes se puede controlar por separado, con su propio bus de datos. Esto es posible gracias al uso de la función de salida paralela de la biblioteca FastLED en el microcontrolador Teensy 4 . ¡Al usar 7 buses de datos, podemos lograr una frecuencia de actualización máxima de aproximadamente 120FPS!

No planeé esto, pero por pura casualidad, al comienzo del proyecto, elegí este microcontrolador en particular. Me pareció que el buen funcionamiento de los juegos requeriría el rápido tiempo de procesamiento proporcionado por este MK. El prototipo en la Fig. 4 ya está controlado por la función de salida paralela. Sin embargo, en este prototipo usamos solo 2 buses de datos, solo para probar si todo realmente funcionará como se indica.

¿Cómo ejecutaremos el código con EEPROM?


Entonces, en esta etapa podemos leer y escribir en la EEPROM, y también tenemos una prueba del concepto de la pantalla LED. ¿Que sigue?

Solo quedan dos tareas técnicas serias: la organización del método de entrada para todos los jugadores (botones y joysticks). Y también necesitamos descubrir cómo ejecutar la lógica del juego desde EEPROM y mostrar los resultados de estas instrucciones en la pantalla.

Los botones y joysticks ordenados aún no han llegado. Por lo tanto, primero participaremos en la ejecución de la lógica del juego desde EEPROM.

Hay muchas formas de ejecutar instrucciones (código) desde la EEPROM. Sin embargo, primero debemos establecer ciertos requisitos:

1) El código debe ser fácil de escribir.

2) El código debe ser pequeño (tamaño del archivo).

3) El código debería poder interactuar con bibliotecas ya preparadas, por ejemplo, FastLED, para reducir la cantidad de mi trabajo.

4) El código debe poder cargarse en la RAM lo más rápido posible para reducir el tiempo de carga.

5) Necesita la capacidad de emular código en una PC normal para probar juegos.

6) Nuestra solución debe ser de alto rendimiento (proporcionar al menos 60 FPS).

7) Una solución no debería requerir la compra de una gran cantidad de equipo adicional.

8) La solución debería funcionar en Teensy 4.0 MK.

9) La solución debe ser fácil de implementar.

Se me ocurrieron cuatro formas diferentes de ejecutar código desde EEPROM:

- Escribimos en la EEPROM el código Arduino compilado habitual. Luego hacemos que el programador externo de Arduino lea el código compilado de la EEPROM y reprograme el Arduino sobre la marcha cada vez que se carga un nuevo juego.

- Cargamos el código del ensamblador en la RAM y lo ejecutamos desde allí.

- Utilizamos un intérprete de código ya preparado, por ejemplo, ArduinoBASIC o Bitlash .

- Escribimos nuestro propio intérprete de código.

Aquí hay una breve comparación de las ventajas y desventajas de las cuatro soluciones para nuestro proyecto:


(1) El tamaño del archivo para una solución con un programador externo es terrible. Junto con la lógica del juego, todas las bibliotecas de códigos que usamos en este proyecto deben almacenarse en EEPROM. Para mostrar el estado del juego, cada EEPROM debe tener su propia copia de la biblioteca FastLED. Esto no es genial en absoluto. Este problema probablemente se puede evitar agregando de alguna manera el código básico a un programador externo, que luego se combina con el código EEPROM antes de programar el Arduino. Esta sería una tarea de alto riesgo, porque no es tan fácil encontrar tutoriales en Internet. Probablemente pasaríamos demasiado tiempo en ello.

(2) Ejecutar ensamblador desde RAM es una gran opción. Por eso decidí considerarlo. La complejidad de escribir código puede reducirse utilizando un lenguaje de alto nivel, por ejemplo, C, que luego se compila en el código de ensamblador correcto. Sin embargo, no estaba claro lo fácil que sería lograr que interactúe con otras bibliotecas en Arduino, así que decidí abandonarlo.

(3) Usar un intérprete listo para usar también es una solución bastante buena. Sin embargo, hasta donde yo sé, todos estos intérpretes se ejecutan en base a cadenas de caracteres. Estas largas líneas deben escribirse en la EEPROM. Este no es un gran problema, pero definitivamente no es la mejor manera de minimizar el tamaño del archivo. Además, no estaba claro si estos intérpretes podían interactuar con las bibliotecas Arduino. Por lo tanto, decidí que usar intérpretes prefabricados no es una buena idea.

(4) Finalmente, tenemos la solución 4: crear nuestro propio intérprete de código. Satisface todos los requisitos, porque la forma en que se implementa el intérprete depende completamente de mí. Lo harátener alta velocidad si logro la alta velocidad del propio intérprete. Sí, el tamaño del archivo será pequeño y el código será fácil de escribir ... si lo proporciono yo mismo. En otras palabras, tendremos un control completo sobre todo. Y si hago un esfuerzo, todo saldrá perfectamente. El único inconveniente serio de tal solución será un largo tiempo de desarrollo. Puede recordar que pasé entre 800 y 900 horas en este proyecto. Es decir, es obvio que decidí elegir la solución 4: crear mi propio intérprete de código. El tiempo no fue un problema para mí. Aproveché esta oportunidad para aprender cómo se crean los lenguajes de programación. Descubriré por qué se tomó una u otra decisión arquitectónica en estos idiomas.

El nacimiento de ArcadableScript


El principio general del intérprete es bastante simple. No entraré en detalles de su estructura interna, porque al momento de escribir, estaba planeando reescribir completamente el intérprete para que sea más eficiente y le sea más fácil escribir código. Sin embargo, lo básico sigue siendo el mismo. El intérprete ejecutará un bucle de código simple para cada medida del juego:


Un gran inconveniente de este esquema es que la entrada y el estado del juego se verifican solo una vez por fotograma. Para juegos que no requieren una reacción rápida, esto es normal. Sin embargo, en otros juegos, el jugador no podrá reaccionar entre fotogramas. Resolveré este problema en la próxima versión del intérprete.


Higo. 5

En el diagrama con la fig. La Figura 5 muestra cómo planeé actualizar el intérprete en un futuro cercano, creando dos ciclos separados para el estado del juego y los marcos del juego. Esto nos permitirá actualizar el estado del juego cientos de veces por segundo y la pantalla, solo 60 veces por segundo.

Desafortunadamente, en Teensy 4.0 no es posible ejecutar código multiproceso de hardware. Por lo tanto, no podremos llevar a cabo estos dos ciclos en paralelo. Pero estoy seguro de que se me ocurrirá algo.


Después de un tiempo, logré escribir dos programas simples usando mi propio lenguaje de código de bytes inventado. Utilizan números sin procesar como entrada para minimizar el tamaño del archivo. Es decir, completé la escritura de estos dos programas, literalmente escribiendo listas de números que el intérprete puede entender. Para dar una idea de cuán legible es este código de bytes, demostraré las instrucciones reales utilizadas en el programa de ejemplo que mueve un punto por la pantalla:

// TODO: Write/read all untyped... data to/from ROM
  int untypedGamestate[] = {
    0, // Previous time, to keep track of game time/framerate.
    0, // Player position x.
    0, // Player position y.
    0, // Player R color value.
    0, // Player G color value.
    255, // Player B color value.
  };
  int untypedValues[][3] = {
    // ID, Type, Value
    {0, 0, 0}, // Move up button value 
    {1, 0, 1}, // Move right button value
    {2, 0, 2},  // Move down button value
    {3, 0, 3},  // Move left button value
    {4, 3, 1},  // True/1
    {5, 3, 0},  // False/0
    {6, 4, 0},  // Current millis since game start
    {7, 2, 0},  // Gamestate previous time
    {8, 2, 1},  // Gamestate player x
    {9, 2, 2},  // Gamestate player y
    {10, 2, 3}, // Gamestate player r color
    {11, 2, 4}, // Gamestate player g color
    {12, 2, 5}, // Gamestate player b color
    {13, 3, 1}, // Move player up after button press boundary check
    {14, 1, 0}, // System config screen width
    {15, 1, 1}, // System config screen height
    {16, 3, 3}, // Move player down after button press boundary check
    {17, 3, 5}, // Move player left after button press boundary check
    {18, 3, 7}, // Move player right after button press boundary check
  }
  int untypedCalculations[][5] = {
    // ID, ending, valueLeftID, calculationRightID, calculationOperator
    {0, 0, 9, 1, 1}, // Current player y position - 1
    {1, 1, 4, 0, 0}, // True/1
    {2, 1, 0, 0, 0}, // Up button
    {3, 1, 2, 0, 0}, // Down button
    {4, 1, 5, 0, 0}, // False/0
    {5, 1, 9, 0, 0}, // Current player y position
    {6, 0, 15, 1, 1} // screenheight - 1
    {7, 0, 9, 1, 0}, // Current player y position + 1
    {8, 1, 3, 0, 0}, // Left button
    {9, 1, 1, 0, 0}, // Right button
    {10, 1, 8, 0, 0}, // Current player x position
    {11, 0, 8, 1, 0}, // Current player x position + 1
    {12, 0, 8, 1, 1}, // Current player x position - 1
    {13, 0, 14, 1, 1} // screenwidth - 1
  }
  int untypedInstructions[][10] = {
    // ID, rootInstruction, conditionCalculationLeftID, conditionCalculationRightID, conditionOperator, conditionSuccesValueLeftID,
    // conditionSuccessCalculationRightID, hasFailedCondition, conditionFailedValueLeftID, conditionFailedCalculationRightID
    {0, 1, 2, 1, 0, 13, 0, 0, 0, 0}, // move player up when up button is pressed.
    {1, 0, 5, 4, 1, 9, 0, 0, 0, 0}, // move the player up when the boundary is not reached.
    {2, 1, 3, 1, 0, 16, 0, 0, 0, 0}, // move player down when down button is pressed. 
    {3, 0, 5, 6, 1, 9, 7, 0, 0, 0} // move the player down when the boundary is not reached.
    {4, 1, 8, 1, 0, 17, 0, 0, 0, 0}, // move player left when left button is pressed.
    {5, 0, 10, 4, 1, 8, 12, 0, 0, 0}, // move the player left when the boundary is not reached.
    {6, 1, 9, 1, 0, 18, 0, 0, 0, 0}, // move player right when right button is pressed.
    {7, 0, 10, 13, 1, 8, 11, 0, 0, 0}, // move the player right when the boundary is not reached.
  };

Si se toma el tiempo de estudiar detenidamente el código, puede comprender cómo funcionaba la versión anterior de este intérprete. El principio general de utilizar "valores", "cálculos" e "instrucciones" se conserva en la versión actual. Sin embargo, mucho ha cambiado. Ya no tenemos una lista fija de valores de gamestate, ahora son solo parte de la lista normal de valores. Además, separamos las "condiciones" de las instrucciones. Gracias a esto, podemos reutilizar las instrucciones en el código, lo que reduce el tamaño del archivo.

No entremos en los detalles del intérprete, porque en la versión rediseñada, todo esto cambiará pronto.

Ahora estamos en la etapa en que sabemos cómo leer / escribir en la EEPROM, y podemos ejecutar instrucciones basadas en matrices de números ordinarios. El siguiente paso lógico será eliminar las listas codificadas de instrucciones del código y escribirlas en la EEPROM. A partir de ahora, podremos intentar leer y ejecutar las instrucciones almacenadas en la EEPROM.


¡En esta etapa, tenemos todas las pruebas de la capacidad de trabajo de los conceptos que necesitamos para comenzar a crear el resultado final!

Obviamente, cada parte individual del prototipo que creamos todavía requiere mucho trabajo. Pero, en mi opinión, las tareas más difíciles ya se han resuelto. Todo lo que tenía que hacer era desarrollar lo que ya tenía. ¡Todo es simple!

Asamblea de pantalla LED



Tomó mucho trabajo ensamblar la pantalla. Mucho trabajo Comencé por tener que soldar cables a 42 tiras separadas. Cada tira requiere tres cables en un lado y dos en el otro. Es decir, solo unos 200 cables. Antes de este proyecto, no tenía mucha práctica en la soldadura, por lo que puede notar claramente que cuanto más lo hago, más fuerte es la calidad. Por supuesto, al final, tuve que rehacer muchas de las primeras tiras de LED, porque no me gustó el resultado. ¡Todo esto es parte del proceso de aprendizaje!


El siguiente paso: colocamos todas las tiras de LED en un gran panel de madera. Mirando hacia atrás, creo que quizás por el bien de una mejor conductividad térmica valió la pena usar una lámina de metal, porque después de una hora de funcionamiento, la pantalla ahora se calienta (40-50 ° C). Pero esto todavía no sucede con tanta frecuencia, por lo que no es un gran problema. Sin embargo, si decidiera repetir el proyecto, corregiría este aspecto.


A continuación, conectamos cables de mayor diámetro a los conductores positivo y negativo de las tiras de tiras de LED. Debemos asegurarnos de que los cables de gran diámetro soporten de manera confiable una corriente máxima de 100 A. Para protección adicional, también agregamos fusibles de 15 A a cada parte de la pantalla.

En esta etapa, estamos listos para hacer los primeros intentos de controlar la pantalla. Después de muchas fallas y manipulaciones con los parámetros y cables del software, finalmente logré mostrar un solo color en la pantalla sin distorsiones e interferencias. Tomó mucho tiempo, y el resultado aún estaba lejos de ser perfecto. Durante mucho tiempo, todavía tuve problemas con las señales distorsionadas hasta que publiqué una pregunta al respecto en electronics.stackexchange.com. Grandes personas de este sitio me ayudaron a diagnosticar el problema. Todavía no entendía completamente lo que era, pero al conectar los cables negativos directamente a lo largo de los buses de datos desde la tierra MK a la terminal de tierra cerca de la entrada de datos, pude resolverlo. Desde entonces no he tenido ningún problema con la distorsión en la pantalla.


Como vimos en la ilustración anterior, dos capas de material se superponen en las tiras de LED.

Como material para este recubrimiento, necesitamos algo fuerte, transparente, pero que difunda la luz. Sin embargo, no quería usar vidrio porque es costoso y frágil. Por lo tanto, decidí tomar un poliestireno transparente estándar. Para que él dispersara la luz, la traté con un papel de lija fino hasta el punto de que era imposible mirar a través. Todo es muy simple.

Se dedicó mucho más tiempo a crear una cuadrícula que se ubicaría directamente encima de las tiras. Pensé en pedir una parrilla con los parámetros necesarios para ordenar, pero me llamaron un precio de unos 500 euros. En mi opinión, ella no valía la pena. Por lo tanto, tuve que recogerlo nosotros mismos. Para esto necesitamos tiras de plástico blanco opaco, duradero (no más de 2 mm de espesor). Muchos requisitos. Por lo tanto, la selección del material correcto tomó mucho tiempo. Al final resultó que, las rayas de las persianas (laminillas) son ideales.


Primero, hicimos un pequeño soporte para hacer cortes uniformes, incluso en franjas anchas. En este caso, se obtuvieron pequeñas tiras de plástico. Luego tuvimos que hacer incisiones en el medio de cada tira exactamente en los lugares correctos. Esto es necesario para conectar las piezas de plástico y ensamblar la rejilla. Esto es lo que hago en la tercera foto. Antes de hacer esto, debe asegurarse de que el ancho de la sierra sea igual o ligeramente mayor que el ancho de la tira que cortó. De lo contrario, la rejilla no se puede ensamblar. En mi caso, resultó que una sierra para metales tiene un grosor ideal.

En la cuarta foto, el resultado de ensamblar todas las tiras en la cuadrícula es visible. Fue un trabajo muy molesto y monótono, pero la parrilla resultó ser muy duradera. En general, parece que todas las celdas son del mismo tamaño. No hay mucha variación entre los tamaños de celda, de lo contrario la pantalla se vería extraña, por lo que todo resultó bastante bien. Estoy satisfecho con esta parrilla.


Luego encerré la rejilla en un marco de madera que realizaba varias tareas. Primero, sostiene una lámina de poliestireno en la parte superior de la parrilla. Asegura que la rejilla no se mueva. Además, un marco de madera presiona todos los cables para que se sujeten de manera más segura y no se puedan soltar accidentalmente (lo que ocurrió un par de veces cuando moví la pantalla).


Esta fotografía muestra una lámina de poliestireno lijado sobre una rejilla de alambre. Tenga en cuenta que la parrilla ahora es apenas visible, que es lo que queríamos.


Después de pasar largas horas experimentando con la implementación correcta del control de visualización, finalmente logramos que funcione. Un gran problema fue la preservación de la integridad de las señales. Pero como dije anteriormente. ¡La comunidad de electronics.stackexchange me ayudó con esto!

La pantalla es brillante, mucho más brillante de lo que esperaba. Era necesario prever esto, ordenando una unidad de 100 A para su suministro de energía.


Como he dicho varias veces, utilizamos la biblioteca FastLED para controlar los LED. Pero no mencioné que también uso una versión modificada de la biblioteca FastLED-GFX de Jürgen Skrocki (que en sí mismo es un puerto de la Biblioteca Adafruit-GFX ) para la representación simple de figuras en la pantalla . Los cambios en esta biblioteca son menores, pero fueron necesarios para que el intérprete pudiera comunicarse con la biblioteca de manera conveniente.

Cerebros y ...


Una de las últimas tareas técnicas que deben resolverse para completar este proyecto es completar los cerebros de la consola. Debe convertirlos en un conjunto conveniente y compacto. Pero primero tenemos que descubrir cómo manejar todas las señales de entrada de los jugadores. Permíteme recordarte que cada jugador tiene 4 botones y un joystick. Eso es un total de 16 señales de entrada digital y 8 analógicas. MK nunca tiene suficientes contactos para tal entrada; Además, dos pines del bus de datos ya se utilizan para leer la EEPROM, y se necesitan 7 pines del bus de datos paralelos para controlar la pantalla.

Para resolver este problema, utilizamos un par de registros de desplazamiento para la entrada digital. Y para todas las entradas analógicas, tomaremos un multiplexor analógico.


Esquema para leer / escribir simultáneamente EEOPROM, procesar la entrada del reproductor y controlar la pantalla.

Y entonces comienza la confusión. Permítanme explicar lo que está sucediendo en esta foto, de izquierda a derecha. En la parte superior de la placa de pruebas izquierda hay 7 buses de datos utilizados para controlar la pantalla. A continuación se muestra el área donde se puede insertar la EEPROM y donde se lee.

En la parte superior de la placa del medio hay un multiplexor analógico. Este multiplexor puede recibir hasta 16 señales de entrada analógicas y, dependiendo de las señales de entrada en el multiplexor, conectar una de las señales analógicas al MK. Por lo tanto, solo necesitamos un pin de entrada analógica y un par de pines de entrada digital para procesar 16 entradas analógicas. Parece que no hay una entrada analógica conectada al multiplexor en esta foto.

Debajo del multiplexor está MK Teensy.

En el panel derecho, procesamos señales de entrada digital. En esta foto, 16 señales digitales simplemente se conectan a tierra. Todavía estaba esperando que llegaran los botones, así que probé las 16 señales digitales de esta manera. Gracias a los registros de desplazamiento, solo se necesitaban 4 pines para controlar todas las señales digitales.

Todo el sistema es un gran caos poco confiable. Es necesario hacerlo más compacto para que no se desmorone de una exhalación descuidada. ¡Por lo tanto, pedí varias placas de prototipos y comencé a soldar nuevamente!



¡Y eso resultó ser una tarea difícil! Sin embargo, esta fue una excelente ocasión para aprender a soldar con precisión.

Para llegar a este circuito, utilicé como ejemplo el caos de los cables en la placa de pruebas. Luego traté de encontrar un circuito más inteligente para que los componentes que deberían comunicarse entre sí estén lo más cerca posible. Así que traté de deshacerme de la necesidad de tirar demasiados cables. Ahora creo que podría usar algunas herramientas de modelado para diseñar el circuito. Pero aún así resultó bastante bien.

Decidí diseñar un circuito de dos capas. En la placa del prototipo inferior, se procesan todas las entradas. En cada una de las 4 conexiones de entrada, tengo 8 líneas de entrada (1 - tierra, 1 - positiva, 4 - entrada digital, 2 - entrada analógica). MK se comunica con la capa inferior utilizando cuatro conexiones al lado de los registros de desplazamiento y cuatro conexiones al lado del multiplexor.

La fuente de alimentación se encuentra en la capa superior, a la derecha hay 7 buses de datos de salida para la pantalla, y en la parte inferior hay contactos para leer la EEPROM. Y finalmente, en el centro, por supuesto, está el microcontrolador.


Entre los tableros de control principales y la entrada de los jugadores, coloqué tableros intermedios para reducir el número de cables requeridos en ambos lados de 12 a 8, porque no es necesario usar los 5 cables de tierra que van desde el tablero principal al tablero de entrada. Una conexión a tierra es suficiente para todos los botones y el joystick. Por lo tanto, hay cuatro de estos tableros intermedios. Me gusta que puedas notar una mejora en la calidad de cada próximo tablero que recolectes.


Como nunca recibí los botones pedidos, pedí otro juego a otro vendedor y después de un par de días lo recibí. Esto nos permitió finalmente comenzar a probar todos los botones.


En esta etapa, también escribí el primer juego que se puede jugar en la consola. Sí, ahora lo llamo la consola.

Tuve éxito porque en el proceso de trabajar en la pantalla mejoré simultáneamente el intérprete. En particular, logré mejorar el proceso de desarrollo en sí mismo al escribir el código. Este proceso no es tan interesante externamente, así que decidí no documentarlo particularmente.


Creé un entorno de desarrollo simple para escribir código que se puede ejecutar en la consola.

Este entorno funciona en un navegador y le permite escribir código para la consola sin meterse con las listas caóticas de números que vimos anteriormente. Además, creé un emulador de consola que se ejecuta en la misma aplicación web.

La capacidad de emular juegos para la consola en una aplicación web ahorró mucho tiempo, porque la depuración en una PC es mucho más fácil que en un MK.

El emulador no es perfecto. Hay algunos errores que distinguen la emulación de jugar en una versión real de la consola. Intentaré eliminarlos en el futuro cuando escriba una nueva versión del intérprete. La necesidad de admitir dos versiones del intérprete (una escrita en C ++, la otra en TS) fue un poco molesta, pero valió la pena.

Poniendolo todo junto


Todo está listo. Podemos mostrar imágenes basadas en la lógica del juego tomada de la EEPROM y procesar la entrada del jugador desde botones y joysticks. Ahora necesitamos combinar todo esto en un dispositivo duradero.


Comencé perforando agujeros en las placas de aluminio que mantendrán los botones y joysticks en su lugar. Entonces fue posible insertar estas placas en las ranuras de la caja de madera en la que estaban convenientemente empotradas. Gracias a esto, los jugadores no podrán arañar accidentalmente el metal.



Recopilamos todo alrededor de la pantalla y agregamos patas plegables confiables (para un fácil almacenamiento). Las placas de metal pintadas con pintura negra se ven muy hermosas. Existe la sensación de que el proyecto realmente está avanzando hacia su finalización.


Pero, por supuesto, me equivoqué al creer que el proyecto estaba casi completo. Así es como se veía la consola durante aproximadamente un mes.

Recopilamos todo, entendemos que el botón no funciona. Desmontamos de nuevo, solucionamos el problema con el botón. Todo parece funcionar. Entendemos que el joystick no funciona. Desmontar de nuevo. Tarde o temprano, todos los botones y joysticks comienzan a funcionar. Pero luego los LED dejan de funcionar. Para solucionarlos, debe desmontar toda la consola y volver a armarla desde el principio. Esto continuó durante aproximadamente un mes, hasta que finalmente todo funcionó.

Resultado




¡Todo está listo! Después de comenzar a trabajar en el proyecto, ya han pasado 3-4 meses. Por lo tanto, es lógico que mi motivación para seguir mejorando el intérprete, pintar el árbol y desarrollar nuevos juegos ya se haya agotado. Al momento de escribir esto, ya había pasado un mes, cuando estuve involucrado por última vez en el proyecto. Finalmente gané fuerzas para escribir esta publicación documental para mí y para ti, mi lector. Aunque no espero que alguien en su sano juicio lea realmente todo lo que escribí, espero que haya disfrutado viendo fotos del desarrollo gradual del proyecto.

Pero si lee todo esto, puede estar interesado en el repositorio de Github del proyecto. ¡Por supuesto, todo mi código es de código abierto! https://github.com/Arcadable

Planes de desarrollo de proyectos adicionales:

- En primer lugar, debes hacer un cartucho más bonito para jugar Pong para cuatro, que se muestra en el último video. Si bien es una pieza de cartón con piezas curvas de papel de aluminio, que se utilizan como superficies de contacto.

- Además, quiero pintar mejor la consola. Inicialmente, planeé pintarlo con amigos muy experimentados a quienes les gusta pintar. Sin embargo, ahora debido a la pandemia esto no es posible.

- Finalmente, como dije un par de veces, voy a reescribir el intérprete de una manera más eficiente. También mejoraré el entorno de desarrollo para facilitar los juegos. El diseño de aplicaciones web actualmente está obstaculizando la productividad.


Compensación por la grabación de videos verticales.

All Articles