domingo, 26 de febrero de 2012

Pintando desde SRAM

Esta semana me ha llegado el módulo de MegaRAM que pedí en Rugged Circuits. He hecho una prueba muy sencilla, he creado un buffer bidimensional del mismo tamaño que el mapa de bits que tengo en memoria de programa en la primera posición de memoria del módulo y he copiado toda la información en él.

He creado otra función para pintar lineas como la función RenderLine, pero esta vez en vez de utilizar la instrucción de ensamblador LPM (Load Program Memory) he utilizado LD (Load Indirect). LPM utiliza 3 ciclos, mientras que LD sólo utiliza 2. El acceso a SRAM necesita un ciclo más, así que invertimos el mismo tiempo en acceder a memoria de programa con LPM que a memoria SRAM con LD.

El shield de memoria ocupa 2 pins del puerto C que necesito para pintar, tendré que modificar la placa, pero antes es necesario probar que funciona, así que por ahora pintaré en el puerto F.

No me enrollaré más, os dejo el circuito y el código (página de descargas) por si quereis probarlo.

Esquema de conexiones al monitor con el shield MegaRAM.

Ahora sólo queda hacer un buen motor de tiles para generar mapas de juego y animaciones con sprites.

lunes, 20 de febrero de 2012

Framebuffer en SRAM

Por ahora he estado utilizando un framebuffer almacenado en memoria de programa (FLASH). El problema es que la memoria de programa es de sólo lectura y voy a necesitar poder escribir el buffer para generar movimiento. Si la resolución utilizada es 208x144 y cada pixel ocupa 1 byte, necesito un espacio de memoria de 29.25Kb para almacenar la escena que tiene que salir por pantalla.

208 * 144 * 1 = 29952 bytes
19952 / 1024 = 29.25 Kb


Arduino Mega dispone de 128Kb (-4Kb del bootloader) y sólo 8Kb de SRAM. Necesito una solución. Pues bien, como siempre, googleando se encuentran soluciones. He encontrado varios módulos de RAM para Arduino:


El primero, de Andy Brown, es un shield de 512Kb especial para Arduino Mega. Dispone de una librería fácil de utilizar, pero he contactado con el autor y no tiene stock:

512Kb SRAM expansion for the Arduino Mega (Andy's Workshop)
http://andybrown.me.uk/ws/2011/08/28/512kb-sram-expansion-for-the-arduino-mega-build/

 El segundo que he encontrado, de Rugged Circuits, está basado en el primero, además utiliza la misma librería. También es de 512Kb:

QuadRAM (Rugged Circuits)
http://ruggedcircuits.com/html/quadram.html

La misma empresa tiene otro shield más sencillo, de 128Kb, de momento este es el que he pedido para hacer pruebas, pero me gustaría probar con más.


MegaRAM (Rugged Circuits)
http://ruggedcircuits.com/html/megaram.htm

El cuarto que he encontrado, de Adam Ward, también de 512Kb, y sólo utiliza 15 pins.

Open Source SRAM Memory Board (Wardy's Projects)
http://wardyprojects.blogspot.com/p/open-source-sram-memory-board.html


Y el último, de Science Prog, es algo más sencillo, el ejemplo tiene una memoria de 8Kb y puede llegar hasta 64Kb.

Adding external memory to Atmega128 (Science Prog)
http://www.scienceprog.com/adding-external-memory-to-atmega128/

Mientras llega el pedido iré desarrollando un software de edición de tiles en VB para poder probar la RAM.

martes, 14 de febrero de 2012

Bitmaps con más color

Después de ver cómo se pueden pintar mapas de bits con 16 colores es hora de ver si se puede hacer con más. Modificando el código que pintaba 208 barras en pantalla con una paleta de 256 colores debería poder hacer lo mismo que en el post anterior.

Así que he modificado la aplicación que transforma imágenes en código para poder utilizar 256 colores y me he decidido a cargar una imagen en Arduino.


Imagen de 256 colores

Resultado en pantalla

Se aprecia una gran pérdida de color en el rasterizado horizontal. Como si el monitor no diera más de sí. Me parece raro, si el monitor es PAL, debería aguantar mucha más resolución. Y con respecto al color, lo hemos visto pintando una paleta de 256 colores. Vuelvo a hacer la prueba, esta vez con una imagen más sencilla.

Alex Kidd a 256 colores

Resultado en el monitor

Sigue habiendo pérdidas. No sé a qué puede deberse. Seguiré investigando. De momento sigo haciendo pruebas con otras paletas. Ahora toca la EGA, una paleta de 64 colores, la misma que utilizaba Sega Master System. Esta vez se trata de un color de 6 bits, así, utilizando un puerto, tenemos 2 bits libres para las dos sincronías. El esquema es el siguiente:


Esquema de conexiones EGA

Vuelvo a probar nuevamente con la imagen de Alex Kidd. Debería funcionar a la primera, se trata de la misma paleta...

Alex Kidd a 64 colores (paleta original)
 
Resultado en el monitor

¡Perfecto! ¡Ahora otro!

Psycho Fox a 64 colores (paleta original).

Resultado en el monitor
Definitivamente, mi consola tendrá la misma paleta gráfica que Sega MasterSystem.

Y como siempre, el código en la pestaña de descargas.

lunes, 13 de febrero de 2012

Bitmaps RGBI

Los mapas de bits que voy a poner de ejemplo son a toda pantalla. Como ya dije en su día voy a utilizar una resolución de 208x144, así que cada imagen ocupará:

208 * 144 = 29952 B
29952 / 1024 = 29.25 KB

Arduino Mega dispone de 8KB de SRAM, así que hay que utilizar memoria de programa. Una vez creado el array de datos podemos acceder a él y copiar los datos que vamos a utilizar a SRAM, pero tenemos el inconveniente de que esto tarda un tiempo si lo hacemos desde Arduino. Pero si lo hacemos desde ASM sólo necesitamos 3 ciclos, nos sobra 1 para pintar. Para saber más acerca de los tiempos que tarda Arduino en ejecutar algunas de las instrucciones os recomiendo que miréis este link.

Esta semana he estado buscando información sobre assembler para microcontroladores AVR ya que no lo utilizo desde las prácticas de la universidad, hace por lo menos doce años. Encontré este documento en la web de Atmel con todas las instrucciones ASM para AVR.

Buscando un poco más he encontrado la web de Masami Watanabe que ha realizado varios ejemplos para pintar bitmaps con escalas de grises en NTSC. Él utiliza las siguientes instrucciones para pintar un píxel:

 
...
"lpm r0,z+\n\t"     // 1
"nop\n\t"
"out 0x08,r0\n\t"
...
 
LPM: Carga memoria de programa en el registro e incrementa el contador de posición de memoria de programa (Z).
NOP: Delay de un ciclo.
OUT: Escribe el registro en el puerto de salida.

Total: 3 ciclos (LPM) + 1 ciclo (NOP) + 1 ciclo (OUT) = 5 ciclos.

He creado una imagen de prueba y luego la he indexado con GIMP utilizando una paleta con los 16 colores, este es el resultado:

Imagen en 16 colores RGBI

Resultado en el monitor

Para pasar la imagen a código, he hecho una pequeña aplicación en VB.Net que lee la imagen pixel a pixel y genera el código del array. Está en la página de descargas.

Y como estoy contento con el resultado, he probado de pintar alguna foto:


Proceso para generar una imagen CGA válida.

Gan Buda de Kamakura. Una foto que hice en 2008.

Resultado en el monitor.

Mi gata

Resultado en el monitor.

El código del ejemplo está en la página de descargas.





PROGMEM:
http://arduino.cc/es/Reference/PROGMEM

Ready, Set, Oscillate! The Fastest Way to Change Arduino Pins:
http://www.billporter.info/ready-set-oscillate-the-fastest-way-to-change-arduino-pins/

8-bit AVR Instruction set:
http://www.atmel.com/Images/doc0856.pdf

Masami Watanabe:
http://homepage3.nifty.com/two_legs/

GIMP:
http://www.gimp.org/

sábado, 11 de febrero de 2012

RGBI: 4Bits = 16 colores

Dejando un poco de lado el tema de los 256 colores, he querido probar la entrada RGBI del monitor. Se trata de una señal de color de 4 bits, 3 de los cuales son RGB y el otro es la intensidad. Lo interesante de este puerto de entrada es que los niveles son TTL (Transistor-Transistor Logic), lo que quiere decir que es un puerto digital, por lo tanto el pin de Arduino se conecta directo al pin del monitor. El sistema RBGI es el utilizado por las tarjetas gráficas CGA (Color Graphics Adapter) utilizadas en los 80's.

Algunos modelos de Commodore 1084 tienen un conector D-SUB 9, el conector estándar de CGA. Este no es mi caso, mi conector es un DIN de 8 pins.

1. N/C - 2. Rojo - 3. Verde - 4. Azul - 5. Intensidad - 6. Tierra - 7. Sincronía horizontal - 8. Sincronía vertical
Imagen original (Autor: Mobius)



La paleta de colores RGBI es parecida que publiqué en el artículo RGB, pero con un tono más oscuro. Al utilizar un bit más, se multiplican por 2 los colores, resultado otra vez los mismos pero con un tono más claro.

El caso del color 6 es especial, algunos monitores utilizan el color, que según el RGBI sería el correcto (amarillo oscuro) y otros (IBM y cónicos) utilizan el marrón, que se consigue reduciendo a la mitad el componente verde. Mi monitor tiene marrón, ¿Es Commodore un clónico de IBM? No lo sé.




Así que he abierto el código "RGB_3Bits.ino" y haciendo una pequeña modificación y conectándolo al DIN8 he visto los 16 colores en mi monitor.


 
...

// Video out voltage levels
//                  RGBIVH
#define _SYNC     0b000000
#define _HSYNC    0b000001
#define _VSYNC    0b000010
#define _BLACK    0b000001
#define _BLUE     0b001001
#define _GREEN    0b010001
#define _CYAN     0b011001
#define _RED      0b100001
#define _MAGENTA  0b101001
#define _BROWN    0b110001
#define _LGRAY    0b111001
#define _GRAY     0b000101
#define _LBLUE    0b001101
#define _LGREEN   0b010101
#define _LCYAN    0b011101
#define _LRED     0b100101
#define _LMAGENTA 0b101101
#define _YELLOW   0b110101
#define _WHITE    0b111101

...

//video pins
#define R_PIN    32
#define G_PIN    33
#define B_PIN    34
#define I_PIN    35
#define V_PIN    36
#define H_PIN    37

...
 


Y como siempre...

Esquema de conexiones


Resultado en el monitor


Y, aunque pueda parecer un retroceso en mi investigación de la profundidad de color para la futura consola, no lo es, ya que por ahora es la forma más cómoda de trabajar al no haber componentes entre Arduino y el monitor. De hecho las dos míticas consolas de 8 bits de los 80's, NES y Master System, sólo utilizaban 52 y 64 colores respectivamente.

Pronto publicaré la pruebas que estoy haciendo con mapas de bits, por ahora podeis bajar el código RGBI aquí.

miércoles, 8 de febrero de 2012

Arreglando la sincronía vertical

No me había dado cuenta antes de mi error, los esquemas para euroconector que había seguido de Uzebox tienen la sincronía compuesta, utilizan el mismo cable tanto para la horizontal como para la vertical. Pero la entrada RGB del monitor 1084S las tiene por separado, mejor dicho, espera recibirlas por separado. Así que toca añadir un cable al DIN6 y conectarlo a Arduino en el pin 52. He modificado el código de la siguiente manera:

Antes:
 
...
// Sync
#define _SYNC     0B00000001
...
 

Ahora:
 
...
// Sync
#define _HSYNC     0B00000001
#define _VSYNC     0B00000010
...
 

Sólo hay que activar el segundo bit menos significativo del puerto B (pin 52) para poder escribir la sincronía en él y ya está.


Cable banco: Sincronía vertical.

El código está en la sección de descargas.

sábado, 4 de febrero de 2012

Más resolución

Para dar más resolución primero hay que entender cómo funciona la señal de video en una televisión. El sistema PAL utiliza 25 fotogramas por segundo, cada uno de los cuales está formado por dos imágenes. En las líneas impares del fotograma se pinta la primera imagen (campo impar) y en las pares la segunda (campo par), generando así una frecuencia de 50 imágenes por segundo. Entonces, cada fotograma es el resultado de entrelazar dos imágenes.

 Las características del sistema PAL son las siguientes:
Aspecto: 4:3
Líneas: 625
Líneas visibles: 576
Columnas: 720
Frecuencia de cuadro: 25Hz (25 fotogramas por segundo o 40ms por fotograma)
Frecuencia de campo: 50Hz (50 imágenes por segundo o 20ms por imagen)
Periodo de línea: 64us
Periodo visible de línea: 52us
Pórtico anterior: 1.5us (Señal HIGH)
Sincronismo horizontal: 4.7us (Señal LOW)
Pórtico posterior: 5.8us (Señal HIGH)
Pulso vertical: 27.3us
Pulso igualador: 2.35us

Con esta información sabemos que cada línea invierte 64us en total y que si la parte visible invierte 52us, nos quedan 12us para generar una señal de sincronía horizontal.



Ciclos de reloj de línea visible: 52us * 16MHz = 832 ciclos


¿Qué resolución horizontal podemos obtener con 832 ciclos?

Resolución horizontal Ciclos por píxel
832 1
416 2
208 4
104 8
52 16
... ...

Yo creo que la resolución buena es la de 208px, esto quiere decir que invertiremos 4 ciclos en pintar cada uno de los píxels que forman la línea.

Si el aspecto es 4:3, entonces la resolución vertical sería:



208px / 4 * 3 = 156px.


¿Cuál es la resolución vertical disponible que más se acerca si tenemos 576 líneas visibles?

Resolución vertical Líneas por pixel
576 1
288 2
192 3
144 4
... ...

El valor más cercano a 156 es 144, así que utilizando 4 líneas para cada píxel tenemos una resolución final de 208x144.

Sincronía:

Monochrome Composite Video
Imagen original (Batsocks)

Viendo el gráfico anterior, vemos que los 12us no visibles de cada línea visible se invierten en generar una señal de sincronía horizontal de la siguiente forma:

4.7us HSync + 5.8us Back Porch + 1.5us Front Porch = 12us
4.7us HSync + 5.8us Back Porch + 52us color + 1.5us Front Porch = 64us


La sincronía vertical se obtiene de la siguiente forma:

2.5 líneas: 5 pulsos de igualación
2.5 líneas: 5 pulsos vertical
2.5 líneas: 5 pulsos de igualación
Total: 7.5 líneas de sincronía por campo


Además, entre las líneas de sincronía vertical y las líneas visibles existen otras líneas reservadas a otros temas como por ejemplo teletexto o información digital como el nombre de la cadena que estamos viendo. Estas líneas no las utilizaremos.

Pues bien, hoy voy a intentar conseguir la resolución horizontal.
Para pintar un píxel utilizando 4 ciclos he probado el siguiente código:

 
...
// Puertos
#define SYNC     PORTB
#define PORT     PORTC
...
// Dibuja un pixel
#define DrawPx(color) PORT=color; NOP; NOP; NOP;
...
 



La instrucción para escribir en un puerto (PORT=color) tarda un ciclo.
La instrucción NOP espera un ciclo sin hacer nada.
Además, esta vez he utilizado interrupciones de timer cada 40000us (1 fotograma) así dejo libre el loop para el futuro código del juego.


Resultado en el monitor


El código lo podéis descargar aquí.

miércoles, 1 de febrero de 2012

Pintando con 8 bits


Después de las pruebas de color explicaré un poco como voy a pintar con una profundidad de color de 8 bits.

8 bits = 1 byte = 256 colores

Un color de 24 bits lo representamos utilizando un byte para cada uno de los componentes de la siguiente forma: RGB(255,255,255) o HEX(FFFFFF) para el blanco y RGB(0,0,0) o HEX(000000) para el negro. Y uno de 32 bits: ARGB(255,255,255,255) o HEX(FFFFFFFF) para el blanco opaco y ARGB(0,0,0,0) o HEX(00000000) para el negro transparente. Entonces ¿Cómo se consigue un color de 8 bits? De la siguiente forma:

Rojo: 3 bits
Verde: 3 bits
Azul: 2 bits

Así que 255 sería el blanco y 0 en negro. Si lo descomponemos en bits: BIN(11111111) y BIN(00000000).

Rojo: 11100000
Verde: 00011100
Azul: 00000011

 
Para representar esto con Arduino necesitamos utilizar un puerto de 8 bits (8 pins) para escribir todo el byte a la vez. Arduino Duemilanove y Arduino UNO cuentan con 3 puertos:

PORTB: Pins 8 .. 13 (6 bits)
PORTC: Pins A0 .. A5 (6 bits)
PORTD: Pins 0 .. 7 (8 bits)
 
Podríamos utilizar el puerto D y obtener 8 bits, pero los pins 0 y 1 se utilizan en la comunicación serie y perderíamos esta funcionalidad. Otras placas como el Arduino Nano, cuentan con 2 pins analógicos más, A6 y A7, por lo tanto tenemos:

PORTC: Pins A0 .. A7 (8 bits)

Pero si utilizamos este puerto para pintar 256 colores perderemos todas las entradas analógicas, y seguro que en el futuro las necesitamos para conectar mandos analógicos como por ejemplo dos potenciómetros para jugar al Pong.

Mi elección es Arduino Mega ya que cuenta con 11 puertos, del puerto A al puerto L, excepto el puerto I (Más info). Además de 16 pins analógicos dividido en 2 puertos (F y K), cuatro puertos serie y 8K de memoria RAM.


Arduino MEGA raw CPU port names
Imagen original (Westfw - CC BY-SA 2.0)


Utilizaré de momento el puerto C que es el que he estado utilizando hasta ahora y cuenta con 8 bits, del pin 30 al 37, para generar la señal de video y el bit 0 del puerto B para generar la sincronía (pin 53).


Y ¿Cómo convertimos los 8 bits de información digital a 3 componentes de señal analógica? Pues con un DAC (Digital to analog converter), concretamente con el método de las "resistencias ponderadas". Utilizando un número de resistencias igual al número de bits que queramos convertir y conectadas en paralelo. El bit más significativo tendrá la menor resistencia, el siguiente bit tendrá una resistencia del doble de valor y así sucesivamente hasta el menos significativo.


Veamos el ejemplo de Arduino Pong:

Se utilizan 2 bits para generar los valores de sincronía, negro, gris y blanco; siendo el gris el término medio entre el blanco y el negro:

 
// Video out voltage levels
#define _SYNC 0x00
#define _BLACK 0x01
#define _GRAY 0x02
#define _WHITE 0x03
 

En el siguiente esquema vemos que el bit más significativo (pin 9) tiene una resistencia de 450Ω y el menos significativo una de 900Ω, el doble. La resistencia de 75Ω es la resistencia de impedancia, que va conectada en paralelo a tierra. La impedancia para televisiones y monitores es de 75Ω.

Arduino Pong Schematic
(Alastair Parker)

Aquí dejo un link de la web de Javier Valcarce donde viene todo clarísimamente explicado.

Es hora de aplicar el DAC al código de ejemplo que utilicé en el post anterior. Para ello voy a utilizar los mismos valores de resistencias que utiliza Uzebox: 3K, 1K5 y 750Ω, además de la resistencia de impedancia de 75Ω.

Uzebox SCART Interface
Imagen original (Belogic - CC BY-SA 3.0)
 
Los canales rojo y verde se componen de 3 bits cada uno, o sea que tienen 8 valores posibles, desde B000 hasta B111. El canal azul sólo utiliza 2 bits, así que sus valores irán desde B00 hasta B11.

Esquema de conexión
Resultado en el monitor


El código de ejemplo muestra una paleta de 256 colores a toda pantalla.
Lo podéis descargar aquí.


Para el próximo post: Más resolución…