Visita mi canal de youtube

domingo, 16 de julio de 2017

Proyecto: tetris en la tv




Breve descripción

El proyecto es un juego basado en color "Tetris" compatible con NTSC TV.
Resumen y motivación

El proyecto básicamente utiliza un chip Mega32, junto con un convertidor RGB-NTSC y un generador de sincronización para producir color en un estándar NTSC TV. El código para un juego tipo "Tetris" se escribe con el entorno AVR CodeVision C.
La principal razón para escoger este proyecto fue poder experimentar con el color. Una asignación de laboratorio implicó usar un televisor blanco y negro para mostrar un juego. Sin embargo, implementar el color era más desafiante. Llegó a nuestra atención que otro grupo había hecho un juego de video basado en color en el último año. Por lo tanto, decidimos producir un juego de color en un televisor, y finalmente decidimos clonar "Tetris" por motivos de simplicidad.
Visión de conjunto

Este proyecto consta de dos partes diferentes: el hardware y el software. La parte de hardware consiste en conectar las entradas y salidas correctas para combinar el Mega32, el convertidor AD724 RGB-NTSC y el generador de sincronización ELM304.

El núcleo del diseño de hardware es el convertidor AD724 RGB-NTSC. Este chip toma una señal RGB y una sincronización compuesta para producir una señal de TV que puede ser reconocida por cualquier TV estándar NTSC. La entrada RGB son los 3 bits que son retransmitidos por el Mega32. Dado que el Mega32 debe ser capaz de comunicarse con el AD724, debe activar parte de su código a la misma frecuencia de reloj que el AD724. Por lo tanto, la entrada de sincronización horizontal AD724 y la señal de disparo de interrupción Mega32 es la misma señal que es suministrada por el ELM304.

La parte más difícil de implementar el color es poder sincronizar el software con los impulsos de sincronización verticales y horizontales. El diseño del software debe ser capaz de detectar y distinguir las señales de sincronización vertical y horizontal. Una vez hecho esto, el resto del código de juego se puede implementar usando los controles que fueron implementados por la porción de sincronización de señal del código.

Hemos elegido implementar 8 colores, con la inclusión de blanco y negro, por simplicidad. Estos 8 colores son simplemente combinaciones binarias de los 3 bits de color. El negro representa el fondo mientras que los otros colores representan las diversas piezas en el juego.


Diseño de Hardware





Idea básica

Este proyecto consta de dos partes fundamentales.
La primera parte, y la más lenta, es el diseño del hardware. El gran problema es: ¿cómo se genera el color y se visualiza en la pantalla? La señal NTSC estándar consiste en todos los niveles de color, distribuidos de una manera que permite al televisor distinguir entre ellos. Como es visible en la imagen de señal de vídeo NTSC a continuación, la señal de vídeo comprende el comienzo de la sincronización. Una vez que este principio se ve, se envía una ráfaga de señal, seguida por el color. En este caso, el negro corresponde al nivel de voltaje más bajo mientras que el blanco es el más alto.

Es relativamente sencillo obtener una señal en blanco y negro ya que todo lo que uno tiene que hacer es producir una ráfaga y simplemente seleccionar niveles lógicos altos o bajos para emitir con la señal de TV. Sin embargo, la señal de color es más complicada. Dado el tiempo y los límites de memoria del chip, sería bastante difícil producir color. La solución simple es utilizar un convertidor RGB-NTSC.

Optamos por utilizar el chip Analog Devices AD724JR para este propósito. El chip utiliza conexiones analógicas y digitales independientes de alimentación y tierra (hay que recordar usar condensadores de acoplamiento con ambas entradas de alimentación, los detalles están en la hoja de datos AD724). También requiere una señal de sincronización, que le permite sincronizarse a la señal RGB. El chip debe tener señales de sincronización horizontales y verticales separadas. Además, tiene tres entradas que corresponden a rojo, verde y azul. Esta señal RGB es generada por el Mega32 con temporización especial. Una vez que el chip recibe esta señal RGB, la combina con los impulsos de sincronización horizontal y vertical para crear una señal de color NTSC. Esta señal es relativamente alta, en términos de tierra. Por lo tanto, debe reducirse utilizando un condensador y una resistencia para que el televisor pueda diferenciar entre aspectos críticos de la señal de video. Además, esta señal no es una señal NTSC perfecta y, por lo tanto, conduce a distorsión perceptible / meneo en la pantalla. La señal de salida se envía a través de un cable de vídeo RCA estándar al televisor en color.




Para nuestros propósitos, una señal de sincronización vertical externa no es necesaria ya que nuestra sincronización genera es capaz de producir una sincronización compuesta. Por lo tanto, la entrada de sincronización vertical en el AD724 se deja alta. Elegimos el Elm Electronics 304 Sync Generator para entregar nuestra señal de sincronización. Este chip también requiere un condensador de acoplamiento a la entrada de potencia. Produce una señal aproximada de 15KHz que se puede entregar a la entrada de sincronización horizontal del AD724.

Tanto el AD724 como el ELM304 funcionan con relojes de 3.58MHz. Esto es necesario para adherirse a los estándares de generación de señales de video en color.

El Mega32 se interconecta con el AD724 a través de PORTC. Tres de los pines de este puerto están designados como rojo, verde y azul, que sirven como entradas al AD724. El Mega32 funciona con un reloj de 16MHz. Sin embargo, para poder comunicarse con el AD724, el software Mega32 también debe tener una referencia de 3.58MHz. Para este propósito, se diseñó una interrupción para activar con la señal ELM304 y producir una señal RGB. PORTD está reservado para esta interrupción. PORTA del Mega32 se utiliza para la interfaz con el controlador serial Sega Genesis. Este puerto sirve como entrada y salida para el controlador donde las entradas aceptan señales de pulsación de botón y las salidas suministran energía y tierra.

Un simple controlador de Sega Genesis se utilizó para controlar el juego en la televisión. Este controlador fue elegido sobre algunos otros controladores en el mercado ya que no tiene lógica síncrona y los botones simplemente conexiones cortas para dar una señal alta o baja. Las señales lógicas bajas se interpretan como pulsaciones de botones. Como teníamos dos controladores, cortamos el enchufe de uno de ellos para determinar qué cables llevaban las señales para cuáles botones.




Otros aspectos del diseño

Inicialmente, intentamos confiar en el diseño de hardware del proyecto del año anterior. Sin embargo, dado que ese diseño era un poco diferente al de los nuestros e incluía algunos errores, decidimos basar todo nuestro diseño en hojas de datos y recursos de Internet.

Había mucha confusión con respecto a la señal de sincronización. De acuerdo con la hoja de datos AD724, la entrada de sincronización vertical sólo puede mantenerse lógica si se está proporcionando una señal de sincronización compuesta a la entrada de sincronización horizontal. Sin embargo, no estábamos seguros de si el ELM304 proporcionaba una verdadera señal compuesta. En lugar de seguir el diseño sugerido en la hoja de datos ELM304, que requiere el uso de todas las salidas ELM304 para componer una sincronización compuesta, simplemente usamos la primera salida ya que parecía tener la mayor cantidad de información. Las otras dos salidas contenían simplemente otra información que no era necesaria para nuestro proyecto.

También se sugiere que el diseño inicial se haga en protoboards y no dirigido soldado en un tablero de prototipo - especialmente si es la primera vez que uno está experimentando con el color. Hay suficiente espacio para errores y es mucho más fácil ser capaz de eliminar simplemente unos pocos cables en lugar de desoldarlos, para depurar.




Diseño de software





Idea básica

El diseño del software es la segunda parte de este proyecto. Aunque no pasamos casi tanto tiempo en esta parte en comparación con el diseño de hardware; Sin embargo, esta es la parte más crítica del diseño, ya que permite el control de dónde se muestra el color en una pantalla.

El juego se implementó en pasos.

La primera tarea era poder mostrar el color en la pantalla. Por lo tanto, la primera tarea fue diseñar la salida RGB para el AD724. Dado que el Mega32 se ejecuta en un reloj de 16MHz y el AD724 se sincroniza con un reloj de 3,58MHz, una interrupción era necesaria. Esta interrupción se activaría en la señal de sincronización generada por el ELM304.

La idea es poder distinguir los impulsos de sincronización vertical de los impulsos de sincronización horizontal. Los pulsos de sincronización verticales marcan el comienzo de la pantalla mientras que los impulsos de sincronización horizontales iteran sobre las líneas imprimibles en la pantalla, hasta que se encuentre la siguiente sincronización vertical.

La sincronización vertical es quizás la más difícil de detectar. La sincronización vertical está marcada distintivamente por la señal de sincronización. Parece una sincronización horizontal invertida. Esta señal tiene impulsos que son aproximadamente 5.7us de ancho. A continuación, la señal se mantiene baja durante unos 20-25us. El truco es detectar este patrón. Se empleó una interrupción externa, a saber, EXT_INT0 que corresponde al tercer pin de PORTD (PORTD.2). Esta interrupción se activaría en la señal de 15KHz del chip ELM304. Para detectar el patrón de sincronización vertical, hay que contar el número de impulsos de sincronización vertical, por supuesto. Por lo tanto, se utilizó un contador que cuenta como máximo cinco pulsos de sincronización verticales. Básicamente, la interrupción se inicia cuando se está disparando el flanco ascendente. Una vez que se detecta un impulso de sincronización vertical, se introduce la interrupción en el flanco ascendente, momento en el que se reinicia un temporizador - TCNT0 -. A continuación, la interrupción se conmuta para convertirse en la caída del borde activado. Esto se hace modificando los bits 0 y 1 de la MCUCR. Por lo tanto, ahora cuando el pulso de sincronización vertical termina, la interrupción puede detectar el borde descendente. Mientras tanto, el contador seguía contando. Por lo tanto, se puede comparar este valor de contador con el ancho conocido del pulso de sincronización vertical (~ 6us). Este comportamiento se puede utilizar para detectar la sincronización vertical ya que los impulsos de sincronización horizontal son mucho más amplios. Por lo tanto, utilizando esta técnica, uno debe contar alrededor de cinco pulsos de sincronización verticales que marcan el comienzo de la pantalla.

Una vez detectada la sincronización vertical, debe iniciarse la sincronización horizontal para iterar sobre la pantalla. Es evidente que la porción de escritura de la pantalla no incluye las 30 primeras líneas y las últimas 10-15 líneas (dependiendo del tamaño de la interrupción). Así que una vez que el recuento de líneas horizontales esté dentro del alcance, podemos empezar a escribir en la pantalla. Esto se logra simplemente asignando PORTC a un color predefinido. Dado que también hay una parte a la izquierda de la pantalla que no puede ser visible si se escribe, debe haber un breve retraso antes de que uno pueda comenzar a escribir. La longitud horizontal de cada línea depende de la cantidad de retraso encontrado desde el comienzo de la declaración de asignación a PORTC, hasta otra declaración de asignación similar con un color diferente. En nuestra experiencia, un retardo de 1us corresponde a un segmento de línea de 3/4 de pulgada en la pantalla. Una vez que hayamos terminado de escribir en una línea, podemos salir de la interrupción y escribir otra línea cuando la interrupción siguiente entra. Hay que tener cuidado de no incluir demasiados cálculos de tiempo / memoria intensivos dentro de la interrupción. Esto es necesario para asegurar líneas uniformes en la pantalla.

El resto de la interrupción implica código relacionado con TET. Para nuestra conveniencia, decidimos definir una matriz de bloques de 10 bloques x 18 que consideramos el área jugable. Aquí, las dimensiones verticales y horizontales de cada bloque TET son 11 líneas de sincronización horizontal y 1us retardos, respectivamente. Hemos asignado una matriz de tamaño 200 para representar la pantalla. Aunque sólo se escriben 180 elementos, los otros 20 elementos son para fines de depuración para las partes superior e inferior de la pantalla. La interrupción simplemente lee cada línea, o 10 elementos de la matriz, a la vez e imprime los mismos datos durante un período de 11 líneas. A continuación, este contador de línea se restablece y la siguiente "fila" de la matriz se puede leer y emitir en la pantalla.
El resto del código define el juego TET. Existen 7 formas básicas en "Tetris", que, junto con sus imágenes rotadas, constituyen aproximadamente 23 formas diferentes. Decidimos escribir 23 segmentos de código diferentes para representar cada una de estas formas. Cada función define un área donde se escribirá un bloque. Además, comprueba y actualiza la información sobre dónde es seguro escribir un bloque. Por ejemplo, el bloque no puede moverse hacia abajo, hacia la izquierda o hacia la derecha si hay otros bloques presentes en esas ubicaciones. Además, dado que cada forma puede girar a izquierda y derecha, cada función define si es seguro girar en cualquier dirección, dada la posición actual de la forma así como las piezas circundantes. Cada función toma como parámetros la posición que se va a dibujar en la pantalla (el elemento de la matriz lineal) y la dirección en la que debe moverse. Cada función también reserva un color para la pieza específica. Esto funciona bien ya que hay 7 piezas y 8 colores diferentes que podemos producir con los 3 bits rojo, verde y azul. El color negro está reservado para el fondo.

En el corazón del código TET está una máquina de estado que realiza un seguimiento de la pieza que se está dibujando y cómo se está dibujando. Siempre y cuando la función de la pieza actual haya declarado que es seguro moverse en una posición deseada, esta máquina de estado vuelve a llamar a esa función con una posición actualizada. Es trivial mover el mismo tipo de pieza dentro de la pantalla, ya que la función correspondiente tiene las posiciones de borrado y reescritura predefinidas. Sin embargo, es menos trivial borrar y volver a escribir cuando cambia de un tipo de pieza a otra. Por ejemplo, si se está girando una pieza en T, se debe borrar la pieza en T más antigua y se debe extraer la nueva pieza girada. Para ello, existe un indicador "draw" que permite a la función correspondiente saber si debe borrar o volver a dibujar su pieza designada. Por lo tanto, mientras gira piezas, la máquina de estado activa la variable 'rotar' y determina qué pieza debe ser borrada con la bandera de trazado y qué pieza debe dibujarse con un indicador de trazado de reajuste. La máquina de estado también verifica si el juego ha terminado. Una vez que todas las piezas han alcanzado la parte superior de la matriz de pantalla, no más piezas se les permite caer y el juego se considera que ha terminado. Además, la máquina de estado llama a la rutina 'clear_line' cada vez que una pieza aterriza. Esto es para comprobar si una línea se ha llenado completamente de color y para borrar esa línea mientras que cae el resto de la pantalla hacia abajo por una línea.

La interfaz del controlador es gestionada por la función 'control' que comprueba qué botones se están presionando y modifica las variables de velocidad, rotación y movimiento en consecuencia.

Las piezas despliegan la pantalla a una velocidad predefinida, que es mucho más lenta que la velocidad a la que se llama "control". Esto es para permitir al usuario más libertad con el comportamiento de las piezas dentro de cada línea de caída. La velocidad del juego se incrementa linealmente a medida que el usuario borra un cierto número de líneas.
Resultados
Problemas en el diseño


Debido a la naturaleza de este proyecto, es bastante difícil sincronizar con precisión el hardware y el software. En consecuencia, siempre hay un movimiento en las piezas de color en la pantalla. Esto es simplemente porque las llamadas de interrupción no están correctamente sincronizadas con el resto del software. También hay retraso moderado dentro de la interrupción que puede causar porciones no deseadas de color en la pantalla. Una solución a esto es escribir el código de interrupción en el ensamblaje e intentar reducir el tiempo de ciclo no deseado en las interrupciones de entrada y salida. Sin embargo, eso no fue un problema significativo para nuestro proyecto.


El mayor obstáculo que encontramos fue un extraño parpadeo y líneas innecesarias en la pantalla. Parecía que las líneas estaban siendo empujadas hacia la derecha a intervalos constantes en la pantalla. Nuestra primera intuición fue considerarlo un problema de retraso. Hemos intentado varios métodos de reducir el retraso, pero todos los intentos fallaron. Sin embargo, pudimos darnos cuenta de que el problema era un conflicto entre dos interrupciones. Mientras se llamaba constantemente a la interrupción de escritura de la pantalla principal, también se estaba llamando otra interrupción, que servía para mantener temporizadores. Aparentemente se estaba dando preferencia a esta última interrupción, lo que causó una interrupción en la ejecución uniforme de la anterior interrupción. La solución era eliminar la última interrupción y mover su código en la interrupción principal. Esto eliminó todas las líneas no deseadas.


En cuanto a la seguridad y compatibilidad, el proyecto es compatible con cualquier TV en color. Además, el código es simplemente suficiente para que cualquiera sea capaz de entender. El código no es muy seguro, en términos de semántica. No hay grandes fugas de memoria, sin embargo, el compilador C de CodeVision no es muy bueno en comprobar los límites de índice de matriz. Algunos de nuestros accesos de matriz podrían haber sido fácilmente desbordante; Sin embargo, nos cercioramos de que ningunos tales casos afectaron el funcionamiento del juego.




Nuestra idea original era un juego tipo Super Mario Brothers, sin embargo, rápidamente nos dimos cuenta de que no sería capaz de lograr esto debido a la velocidad y las limitaciones de memoria de la AtMega32 y la cantidad de tiempo que un juego se llevaría a crear. Cambiamos a un juego tipo Tetris debido a sus requisitos de baja resolución, simplicidad de juego y su extraordinaria fuente de diversión en los juegos. Si tuviéramos que hacerlo de nuevo, probablemente habríamos prototipado el tablero, porque nos topamos con algunos problemas de hardware que se complicaron por soldar y desoldar. También habríamos estudiado las señales de televisión antes de tiempo en vez de aprender como fuimos, especialmente desde que usamos un PCB. Podríamos haber hecho un juego más complicado con un chip con más velocidad y memoria.

Los estándares de NTCS son muy estrictos e hicimos nuestro mejor esfuerzo para seguirlos. Hemos logrado mantener nuestra señal de interferir con la sincronización vertical para que la imagen es estable en la pantalla. Además, eliminamos la interrupción extra que estaba luchando con la sincronización horizontal externa causando una desagradable distorsión. Esto da una señal bastante clara y brillante; Sin embargo, la imagen experimenta un ligero "wiggle". No pudimos identificar la causa exacta de esto, sin embargo creemos que es causada por problemas de sincronización de herencia. La velocidad del AtMega32 junto con las imprecisiones en los osciladores de cristal evitó que la señal estuviera perfectamente sincronizada. Por lo tanto, no es nuestra culpa, lo juro!

Dibujamos pesadamente del proyecto de la serpiente psicodélica de la primavera de 2002 que utilizó un AtMega16. Aunque su esquema era incompleto, utilizamos muchas de las ideas de hardware allí y nos dieron idea para el generador Elm Sync y Analog Devices RGB a NTSC convertidor de ellos. También consultamos antiguos proyectos y varios sitios web sobre el diseño del controlador Sega Genesis. El juego Tetris es también un programa con derechos de autor, de ahí el nombre TET. A pesar de la turbulenta historia legal de Tetris, nos sentimos seguros de que la gente amable de The Tetris Company LLC no se sentirá amenazada.

Para saber todo acerca del pasado cuadriculado de Tetris de una fuente independiente haga clic aquí.

Este proyecto nos condujo en terreno templado ético sobre una base regular.
















0 comentarios:

Publicar un comentario