niubit dev

Un espacio de contenidos "tech" dedicado a los desarrolladores más inquietos.

Vemos cómo montar una consola de videojuegos retro con Arduino y unos pocos cables. Con sonido y salida de video para televisor.

Introducción

Después de la construcción de las dos consolas arcade para emulación de videojuegos retro que vimos en un artículo pasado, comprobamos que la pantalla en modo analógico (vídeo compuesto) que montamos en una de ellas no estaba a la altura en cuanto a la calidad de imagen que Raspberry Pi puede ofrecer. Pensando en utilizar esta consola de un modo alternativo, se nos ocurrió que sería una buena plataforma para experimentar con Arduino y las posibilidades que tiene de generar una señal de video. Veamos como...

Conexiones

Antes de empezar tenemos que preparar la conexión de la salida de video compuesto. La librería TVOut que vamos a utilizar, y que describiremos más adelante, emite la señal de sincronismo en el pin D9 y la de luminosidad de los píxeles en el pin D7 de Arduino. La documentación nos explica cómo construir un sencillo mezclador y conversor digital-analógico con dos simples resistencias de 470Ω y 1kΩ conectadas de la siguiente forma:

Lo que traducido al mundo real queda así (ojo a la perspectiva de la foto que desalinea las conexiones de las resistencias; están pinchadas en las columnas de la breadboard correspondientes a los pines D7 y D9 de Arduino). Como vemos en la foto, hemos utilizado un Arduino Nano, pero serviría cualquiera de los Arduino basados en ATmega m168 o m328:

TVOut

Para producir la señal de video en Arduino, vamos a utilizar la fantástica librería TVOut desarrollada por Myles Metzler. La documentación se encuentra en el sitio donde se distribuyó originalmente la librería, es decir aquí. Pero para empezar lo mejor es echar un vistazo a los ejemplos que hay en el propio repositorio como éste para el estándar PAL. Cargando el sketch DemoPAL en el Arduino y conectándolo como hemos visto antes a un televisor, podemos ver lo siguiente:

Vídeo compuesto

Producir una señal de video compuesto en blanco y negro es relativamente sencillo. Sólo hay que generar una señal de 1V de amplitud que señalice los principios de frame y de línea (sincronismo vertical y sincronismo horizontal respectivamente) y que dibuje las líneas traduciendo directamente la luminosidad en un nivel de señal más alto o más bajo para representar los píxeles más o menos brillantes.

Fijándonos en el código de la demo anterior, hacemos un pequeño sketch que inicializa la librería en modo PAL y que dibuja la mitad izquierda de la pantalla en blanco y la mitad derecha en negro, cosa que nos facilitará el estudio de la señal. La inicialización en modo PAL por defecto, supone utilizar una matriz de píxeles de 128 en horizontal por 96 en vertical para la escena. Así pues nuestro sketch dibujará un rectángulo de 64x96 píxeles comenzando desde la esquina superior izquierda de la pantalla (0, 0):

#include <TVout.h>

TVout TV;

void setup() {
  TV.begin(PAL);
  TV.draw_rect(0, 0, 64, 96, WHITE, WHITE);
}

El resultado en pantalla es el siguiente:

Hemos conectado un osciloscopio a la misma salida de video compuesto que introducimos en el televisor. Vamos a analizar sobre una captura del osciloscopio las partes de que se compone:

Lo que estamos viendo aquí corresponde a dos lineas completas en pantalla. La señal PAL se compone de 625 de estas lineas. Como vemos en el televisor, una gran cantidad de estas señales serán distintas al principio y al final ya que formarán parte de los bordes superior e inferior. Aquí vamos a centrarnos en un par de lineas de la parte central que contengan parte de blanco. Hemos marcado 4 zonas que pasamos a explicar:

  • A: La señal empieza haciendo un pulso negativo de aproximadamente -0,3V. Esto es la señal de sincronismo horizontal, lo que le indica al televisor que tiene que pasar a dibujar la siguiente línea horizontal. Es el equivalente en el mundo del vídeo al retorno de carro en los ficheros de texto o en las antiguas máquinas de escribir. Hemos desplazado la señal hacia abajo de manera que el siguiente escalón que representa el valor de luminosidad mínimo (negro) se asimile a 0V ya que resulta más intuitivo así, pero los televisores sólo tienen en cuenta las diferencias relativas, por lo que es indiferente dónde situemos la referencia del 0.
  • B: Entramos en la linea base de la señal, la que hemos asimilado al valor 0V. Esto equivale a la luminosidad mínima, es decir, al color negro. Vemos negro al principio de cada linea porque la representación de la matriz de píxeles o escena que maneja TVOut incluye un borde negro alrededor. Así la zona B corresponde al borde izquierdo.
  • C: La señal se eleva hasta los 0,7V (de manera que entre pico y pico - A y C - tengamos alrededor de 1V). Esto corresponde al valor de luminosidad máximo, el color blanco. El tiempo que la señal permanece en este valor máximo se corresponde con la longitud de la parte de la linea que dibujamos en blanco.
  • D: Volvemos a negro. Aquí estamos viendo en realidad dos partes de la imagen que se confunden. Primero la parte de la matriz de píxeles que corresponde a la linea negra (mitad derecha de la pantalla), y después el borde derecho, que también es negro.

Existe al principio de cada frame otra señal que le indica al televisor que además de empezar una nueva linea (como lo que indica el pulso de sincronismo horizontal), tiene que empezar un nuevo frame, es decir volver al punto de partida que se encuentra arriba a la izquierda. Esto se hace con otra señal especial denominada sincronismo vertical que aquí no vamos a ver por no extendernos demasiado. Baste decir que consiste en una serie de pulsos parecidos al pulso de sincronismo horizontal pero más anchos y numerosos.

Si modificamos el programa para dibujar toda la escena de la pantalla en blanco (TV.draw_rect(0, 0, 128, 96, WHITE, WHITE);), la señal correspondiente a las líneas vista en el osciloscopio se modifica consecuentemente:

Vemos claramente cómo la zona C se ha hecho el doble de ancha, a costa de la zona D que ahora sólo incluye la zona de borde.

Como puede verse, generar una señal de video se reduce a situar unos valores de tensión determinados en los pines de Arduino respetando unos tiempos muy precisos para que la imagen tenga perfiles rectos y no produzca parpadeos o fallos de sincronismo. Internamente la librería TVOut desempeña esta tarea usando interrupciones, de manera que nos deja a nosotros el resto del tiempo para dibujar nuestra escena del juego en la matriz de píxeles que representa la pantalla.

Videojuegos

Una vez que hemos aprendido un poco sobre las posibilidades y el funcionamiento interno de Arduino y la librería TVOut, podemos programar sencillos juegos. La propia librería TVOut incluye soporte para sonido si conectamos un buzzer al pin D11 de Arduino. Para no hacer este post muy extenso y convertirlo en un taller sobre desarrollo de videojuegos, vamos a limitarnos a cargar juegos ya programados por otras personas por medio de esta misma librería. De todas formas, dejamos por aquí este enlace que además de a TVOut, hace referencia a una librería para gestionar controles de juego (gamepads) que sería otra parte importante para desarrollar un videojuego sobre Arduino. En el enlace anterior encontramos también otro enlace hacia una lista de juegos que pueden instalarse en el montaje hecho con Arduino presentado en este artículo. Allí podemos encontrar juegos como los siguientes:

Modificación EmuPi

Como hemos comentado en la introducción, todo esto empezó pensando en utilizar la segunda unidad de EmuPi construida (a las que dedicamos este artículo) con algo más sencillo que Raspberry Pi, es decir, que utilizara una salida de video compuesto de manera nativa. Así pues el paso final fue integrar el montaje anterior hecho con Arduino en una de las unidades EmuPi. Respecto a lo que hemos visto en el artículo, sólo quedaba mapear el stick y uno de los pulsadores para que se convirtieran en los controles de juego. Por defecto la librería Controllers que se encarga de gestionar el controlador de juego utiliza los siguientes pines:

  • Izquierda: D3
  • Derecha: D2
  • Arriba: D4
  • Abajo: D5
  • Disparo: D10

Por tanto sólo tuvimos que puentear los pulsadores correspondientes de la EmuPi a los pines de Arduino. La librería establece una resistencia pullup en los pines que utiliza, de manera que cuando el pin se conecta a GND es cuando se detecta la pulsación y se libera la pulsación cuando el pulsador se abre. Por tanto, los pulsadores los conectaremos con un cable al pin correspondiente de Arduino y con otro a GND.

El resultado final:

PD: Comentar para terminar que el artículo se denomina Arduino Pong porque el primer proyecto que encontramos que implementara un juego con salida de vídeo para Arduino se basaba en este tipo de juego y fue así como bautizamos nuestro propio proyecto.

Esta página web utiliza cookies para que sea posible mejorar tu experiencia de navegación.