Buscando un control para un par de proyectos con Arduino que estoy desarrollando encontré una par de Joystick's de un viejo Clon Family (Dynacom en rigor de verdad) y me parecieron adecuados para la aplicación.
Tratando de encontrar en internet la forma de conexionado y una librería para el manejo me encontré con que no coincidía la información existente con el PinOut del controlador que tenía en mi poder.
Después de desarmar, analizar un poco el circuito (mas detalles abajo para los interesados) y de hacer un "tanteo" logré dar con el PinOut y el protocolo para la lectura de teclas.
Desarrollé una librería (si bien hay alguna que se podría llegar a adaptar me gusta perder tiempo ;)) simple para el manejo.
A los bifes
PinOut
Los muchachos que clonaron al NES no se gastaron mucho con respecto; Si bien el PinOut y el conector es distinto, comparten señales con los Joystick clásicos de NES / SNES (supongo empezaron haciendo Joystick's genéricos para las NES :P).
El conector utilizado es un clásico DB9 utilizando solo cinco pines de los nueve; dos para alimentación y tres para señal.
La alimentación (VCC + GND) es de 5V (probé con 3V3 y funcionó perfectamente).
Con respecto a los pines de señal:
Latch: Entrada digial que toma el estado de cada botón del controlador (pueden haber varios apretados a la vez) y genera un registro de tantos bits como botones (8).
Clock: Entrada digital de reloj que produce el desplazamiento del registro de 8bits con el estado de cada botón (1 bit por botón).
Data: Salida digital. Bit a bit (desplazamiento) del registro de estado de cada botón.
Conexionado
Para el conexionado se pueden utilizar cualquiera de las E/S del Arduino (inclusive las analógicas).
Para el ejemplo utilicé las E/S 49 para Data, 51 para Latch y 53 para el Clock (los pines utilizados se pasan como parámetros de la rutina de inicialización de la librería).
Protocolo
El protocolo utilizado es el de los Joystick's NES:
60 veces por segundo, el CPU del NES genera un pulso de 12uS sobre el pin Latch. Esta señal toma el estado de cada botón y el mismo se almacena a razón de 1 bit por botón en un registro de 8 bits.
6 uS después el CPU envía un tren de 8 pulsos de 12uS de período al 50% de duty (6uS de estado bajo + 6uS de estado alto) por el pin Clock.
Sincronizado con el flanco descendiente del pin clock se realiza un desplazamiento del registro de 8 bits por el pin Data. El CPU del NES captura el estado de cada botón leyendo el estado del pin Data en cada flanco ascendente del pin Clock.
Cada botón tiene su "bit" específico.
Lo anteriormente mencionado puede apreciarse en la siguiente figura:
Notas
Es posible presionar múltiples teclas al mismo tiempo sin ningún inconveniente (salida con tantos bits 1 como botones apretados).
Si bien el Joystick tiene 10 botones, 2 de los mismos son "virtuales". A y B turbo equivalen a la pulsación repetida de los botones A y B respectivamente.
La rutina de "lectura" de teclas es de implementación extremadamente sencilla:
Librería
La librería de control es bien simple; Se trata de una única clase que verifica las teclas pulsadas / presionadas y genera los eventos asociados.
Puede descargarse del repositorio de blog desde aquí
Características generales:
No utiliza interrupciones (usa Polling).
Cuenta con Buffer circular de los últimos botones presionados (últimos 16).
Se pueden asociar Callback's a los eventos de teclas presionadas y pulsadas.
Permite la utilización de múltiples instancias (más de 1 Joystick).
Descripción de los métodos void Init(unsigned char DataPin, unsigned char ClockPin, unsigned char LatchPin); Rutina de inicialización de la librería. Se pasan como parámetros los pines del Arduino conectados a los pines Data (pin 4 del DB9), Clock (pin 2 del DB9) y Latch (pin 3 del DB9) del Joystick. void Release(void); Libera los recursos asociados a la librería (puertos). unsigned char Poll(void); Rutina de Polling. Interroga el Joystick en busca de teclas pulsadas y genera los eventos asociados. Debe ser llamada periódicamente desde el bucle principal de la aplicación para el correcto funcionamiento del resto de las rutinas. Puede ser llamada desde una interrupción. Retorna máscara de 8 bits con botones actualmente pulsados utilizando las constantes [BUTTON_RIGHT .. BUTTON_A]. bool ButtonDown(unsigned char button); Retorna [true] si el / los botones pasados en el parámetro [button] están pulsados. Se entiende "pulsado" como el botón apretado que se mantiene en ese estado mientras se realiza el chequeo. El parámetro [button] es una máscara de 8 bits utilizando las constantes [BUTTON_RIGHT .. BUTTON_A]. bool ButtonPressed(void); Retorna [true] si hay al menos un botón en el buffer de botones presionados Se entiende como "presionado" a la acción de pulsar y liberar cualquiera de los botones. unsigned char GetButton(void); Retorna el primer botón del buffer de botones presionados. El valor retornado se encuentra en el rango [BUTTON_RIGHT .. BUTTON_A]. void onButtonDown(void (*function)(unsigned char)); Registra "callback" de tecla pulsada. La rutina [function] pasada como parámetro se "ejecuta" mientras se mantenga pulsada una o varias teclas. El parámetro pasado a la función se encuentra en el rango [BUTTON_RIGHT .. BUTTON_A]. void onButtonPress(void (*function)(unsigned char)); Registra "callback" de tecla presionada. La rutina [function] pasada como parámetro se "ejecuta" cada vez que se presiona (pulsa y libera) una tecla.
El parámetro pasado a la función se encuentra en el rango [BUTTON_RIGHT .. BUTTON_A].
Instalación / Utilización
La instalación es la "clásica" del IDE Arduino:
Menú Programa -> Include Library -> Add .ZIP Library
Para más información consultar la documentación oficial: Installing Additional Arduino Libraries
Una vez instalada la librería en el IDE, en un Sketch nuevo o existente se debe proceder con la inclusión de la librería: Programa -> Include Library -> FamilyGameJoystick.
Sketch de ejemplo
A continuación un ejemplo simple sin la utilización de eventos.
Este y otros ejemplos se adjuntan con la librería y pueden ser abiertos como Sketch's de ejemplo de la librería (Archivo -> Ejemplos -> FamilyGameJoystick).
#include <FamilyGameJoystick.h>
//variable global de acceso al Joystick
FamilyGameJoystick Joystick;
void setup() {
// inicialización del terminal serial
Serial.begin(9600);
// rutina de inicialización del Joystick
// E/S 49 de arduino a pin Data del Joystick
// E/S 51 de arduino a pin Latch del Joystick
// E/S 53 de arduino a pin Clock del Joystick
Joystick.Init(49, 51, 53);
}
//bucle principal de la aplicación
void loop() {
// se llama a la rutina de Polling
// la misma establece comunicaciones con el joystick, detecta pulsación de botones,
// genera los eventos asociados, etc.
Joystick.Poll();
//si se presionó un botón (buffer de hasta 16 botones)
if (Joystick.ButtonPressed()){
Serial.print("Botón presionado: ");
// escribimos máscara del botón presionado
Serial.println(Joystick.GetButton());
}
//si el botón "izquierda" está siendo pulsado
if (Joystick.ButtonDown(BUTTON_LEFT)){
//decrementar la posición X del personaje
Serial.println("Izquierda");
}
//si el botón "derecha" está siendo pulsado
if (Joystick.ButtonDown(BUTTON_RIGHT)){
//incrementar la posición X del personaje
Serial.println("Derecha");
}
}
En lo próximo
Generaré otros ejemplos de aplicación (tengo un PacMan a punto de salir del horno).
Implementaré sistema "anti-rebotes" (estimé estaría implementado en el controlador del Joystick / a nivel eléctrico pero me equivoqué).
Corregiré pequeños detalles, documentaré el código fuente, puliré la documentación, etc.
Trataré de conseguir Joystick's de otras marcas / modelos (temo que el PinOut difiera).
Espero el contenido haya sido de utilidad.
Consultas / comentarios son siempre bienvenidos.
Nos vemos en la próxima entrada, Saludos!
Buenas!
Navegando en viejos almacenes de demoscene, dí con un proyecto que me llamó poderosamente la atención: Craft de Linus Åkesson; Una obra de arte en miniatura basada en un ATmega88 que genera señales de VGA, los efectos y hasta sonido!
El hallazgo coincidió con el recién adquirido clon chino de Arduino Mega 2560 y con mis ganas de generar contenido para el blog así que... ¿Porqué no hacer una librería para generar señales de VGA con hardware estándar?
Manos a la obra
Empezaré con una introducción teórica, para luego discutir la implementación, el hardware y por último el software asociado (librería).
Introducción Teórica
Se hace referencia a continuación a los tubos de rayos catódicos (monitor de vidrio), cuando los mismos están prácticamente en desuso, reemplazados por pantallas LCD / LED. De todas formas, a nivel eléctrico el estándar se ha mantenido y lo que hacen los monitores modernos es "emular" el comportamiento de un crt para mantener la compatibilidad.
Funcionamiento de un monitor VGA
A groso modo un monitor o tubo de rayos catódicos (crt) es un tubo al vacío de vidrio con una película de fósforo en la parte frontal (pantalla) sobre la que se hace incidir un haz de rayos catódicos.
El haz (o los múltiples haces), que "recorre" (barre) constantemente la pantalla de izquierda a derecha y de arriba a abajo (normalmente) al incidir sobre la película de fósforo genera un punto iluminado (pixel) que conforma la imagen mostrada en la pantalla.
El movimiento de barrido del haz se realiza por medio de los llamados deflectores.
El control de deflexión y cañón es manejado, en el caso de un monitor VGA por 5 señales eléctricas (5 pines + tierra del DB15 del monitor): Sincronización Horizontal (H-SYNC): Señal digital (pulso 0 - 5V) asociada al deflector vertical del crt (o a su equivalente en un led / lcd) que marca el inicio de una línea, determina la resolución horizontal y asegura que el monitor muestre los pixeles de la misma entre los margenes izquierdo y derecho del área visible de la pantalla. Sincronización Vertical (V-SYNC): Señal digital (pulso 0 - 5V) asociada al deflector vertical del crt (o a su equivalente en un led / lcd) que marca el inicio de un "frame", determina la resolución vertical y asegura que las líneas horizontales estén entre el margen superior y inferior del área visible de la pantalla. R, G, B: Señales analógicas en el rango 0V (oscuro) a 0.7V (máxima intensidad) asociadas al cañón de electrones del crt (o a su equivalente en un led / lcd) que controlan la intensidad de cada componente de color que combinados conforman el color pixel a pixel, linea a linea de la pantalla.
El estándar es muy estricto en cuanto a los tiempos de las formas de onda. Cualquier variación genera deformaciones, parpadeos, problemas de alineación, etc.
Cada modo de los muy variados tiene tu respectivo diagramas de tiempo; Para mas información consultar VGA Timings.
A continuación diagramas / tablas de formas de onda y tiempos del modo VGA 640 x 480 x 60hz.
Diagrama de tiempos simplificado para el modo VGA 640x480 60hz
Tabla de tiempos
Representación gráfica alternativa
Análisis de Implementación
¿Porqué 640x480? Primero es el más estándar de los modos (introducido por IBM para las PS/2 por el año 1987). Segundo es el de menor resolución y por ende el mas "alcanzable". 320x200 y 320 x240 son variantes del 640x480 y 640x400 (cuatro pixeles por pixel) y no hay beneficios para la implementación.
El primer escollo de la implementación tienen que ver con la frecuencia de reloj del los pixeles: 25.175mhz.
¿Que es este valor? La frecuencia (velocidad) a la que deben variar las señales analógicas de los 3 componentes de color (R, G y B) para conseguir la resolución horizontal de 640 pixeles.
Demás está decir que esa frecuencia de reloj es inalcanzable con hardware estándar (Arduino basado en microcontrolador de familia ATmega).
Para nuestro caso en particular, utilizando DAC's de escaleras de resistencias (se explicará posteriormente), la frecuencia máxima alcanzable será de 16mhz / 3 (ciclos de instrucción por pixel) ~5,3mhz. La máxima resolución alcanzable con esta configuración será de ~135pixeles en ancho por 480 líneas de alto (sin límites).
Se plantea una alternativa para el modo monocromático utilizando el puerto serial de controlador para generar las señales. En este caso la frecuencia máxima alcanzable será de 16mhz / 2 (máxima frecuencia del usart) = 8mhz. La máxima resolución alcanzable con esta configuración será de ~203pixeles en ancho por 480 líneas de alto.
Con reloj de 20mhz (en algunos casos viene / se puede reemplazar) mejorará levemente el rendimiento y se podrá subir ligeramente la resolución.
La utilización con reloj de 8mhz queda directamente descartada.
Implementación
Circuito
Se plantean dos circuitos diferentes para la generación de color en los modos color y monocromático.
VGA Color
Como mencionaba anteriormente, para los niveles de RGB es necesario generar señales analógicas en el rango [0V - 0.7V].
Como primera medida, la familia de microcontroladores AVR ATmega de Atmel carecen de DAC's; pero aún si los tuvieran, se requiere de una frecuencia según especificación de 25.175 mhz (en criollo, el DAC tiene que ser capaz de "cambiar de valor" 25 millones de veces por segundo), lo que directamente escapa a controladores de gama baja / media, independientemente de la marca.
Utilizaré entonces los clásicos y conocidos DAC's conformados por resistencias en escalera (http://es.wikipedia.org/wiki/Red_R-2R).
Este tipo de topología es probablemente la forma mas simple y económica de construir un DAC para generar una señal analógica (Vout) a partir de los datos digitales del puerto digital al que las resistencias estén conectadas (a(0) .. a(n-1)),
Para nuestro caso usaremos 2 redes de 3 bits para los componentes rojo / verde y una de 2 bits para los componentes azules.
Pueden generarse entonces 8 niveles de rojo / verde y 4 de azul siendo la máxima cantidad de colores representables 8 * 8 * 4 = 256 (RGB 3-3-2).
En el esquema se utilizan las E/S digitales [22 - 29] (corresponden al PORTA del microcontrolador) para los DAC en escalera de los componentes de color y PWM [12, 13] para las señales de sincronismo. El puerto utilizado puede modificarse desde el include de configuración de la librería (ArduinoVGAConfig.h).
R4, R9 y R16: Están contempladas para el caso de que algún monitor no tenga los 75ohms según especificación, en cuyo caso, se necesitarían para "compensar" ya que el cálculo del DAC ladder es para esa impedancia de carga.
VGA Monocromático
El circuito para el modo monocromático (blanco y negro) es bastante mas sencillo. A diferencia del circuito para generación de VGA color se necesitan solo dos niveles de color (dos estados), blanco y negro. El negro se obtiene a partir de la aplicación de 0V en cada uno de los canales de color.
El blanco a partir de la aplicación de 0.7V (intensidad máxima) en cada uno de los canales.
Requiere de muy pocos componentes y puede ser construido en protoboard o directamente dentro de las "tapitas" del conector DB15.
Cuenta con solo 5 resistencias de las cuales son 3 las estrictamente necesarias (R1, R2 y R3).
R1, R2 y R3 están calculadas para generar una caída de tensión de ~0.7V en la resistencia interna del monitor (75ohms).
R4 y R5 son limitadoras de corriente con la finalidad de proteger el puerto del controlador del Arduino ante cualquier eventualidad.
En el esquema se utiliza la E/S digital 18 (TX del USART1 del microcontrolador) para los componentes de color, PWM [12, 13] para las señales de sincronismo y tierra (se puede usar cualquiera). Los puertos utilizados puede modificarse desde el include de configuración de la librería (ArduinoVGAConfig.h).
Software
A nivel software se deben generar por una lado las señales de sincronismo, las de video y proveer al usuario de una interfaz (clase pública) con rutinas que permitan modificar el contenido de la pantalla.
Señales de sincronismo
Las señales de sincronismo deben ser generadas con la mayor exactitud / precisión posible.
Utilizaré para ello uno de los Timers de 16bits de la familia de microcontroladores ATmega.
El timer es un periférico de hardware accesible desde el código vía registros capaz de ejecutar rutinas cada intervalos específicos (interrupción), medir tiempos y generar formas de onda.
A partir del Timer se generará:
Interrupción cada 31,8uS (horizontal)
Pulso de sincronismo horizontal de 3.8uS generado por módulo Ouput Compare del Timer
Señal de video
Pulso de sincronismo vertical generado desde la interrupción
Memoria de video
Espacio de memoria RAM asignado como buffer intermedio entre la interfaz de usuario (clase pública) y la librería de generación de señales de bajo nivel.
Las funciones gráficas de la librería de control vuelcan contenido sobre la memoria de video.
La sección de bajo nivel de la librería, a la tasa de refresco correspondiente con el modo (60hz), vuelca el contenido en la pantalla crt (genera las formas de onda acorde al contenido).
Para el modo VGA color se utilizará un byte por pixel.
Para una resolución de 640 x 480 pixeles se requerirían 640*480 = 307200bytes (El Arduino mega2560 cuenta con 8192 bytes de ram); Demás está decir que una de las limitantes del modo color con hardware estándar será entonces la memoria RAM disponible.
En los ejemplos utilizaré una resolución de 64 x 64 = 4096bytes. Estos parámetros podrán modificarse en el include de configuración de la librería (ArduinoVGAConfig.h).
Para el modo VGA monocromático se utilizará un bit por pixel (8 pixeles por byte).
Utilizaré una resolución de 160 x 120 pixeles = 2400bytes. Estos parámetros se podrán modificar también desde el #include de configuración de la librería para permitir su utilización a costas de reducir la resolución en Arduinos de baja gama.
Generación de señales de color
Propongo dos conjuntos de rutinas para la generación de señales para los dos modos, ambas dentro de la misma librería y configurables por condicionales de compilación (ArduinoVGAConfig.h).
VGA Color
Para la generación de señales a color utilizaré a nivel software uno de los puertos estándar disponibles en el microcontrolador del Arduino.
En la figura se observa uno de los DAC escalera (componente rojo) conectado a 3 pines de un puerto del microcontrolador.
Los 3 bits en estado bajo corresponderán a una tensión a la salida de la red de 0V (menor intensidad de rojo = negro).
Alternando el estado de los pines del controlador se pueden obtener diferentes niveles de tensión (escalera) hasta alcanzar el máximo de 0.7V (mayor intensidad de rojo).
El mismo esquema se repite para los 3 componentes de color (rojo, verde, azul).
La resistencia de carga de 75ohms corresponde a la impedancia interna del monitor en las líneas de color (R, G, B).
Los componentes de color para el relleno de una línea horizontal se obtienen a partir de alternar tantos bytes por el puerto como pixeles haya de resolución; En nuestro caso, unos 128bytes.
La salida de datos por el puerto por cuestiones de tiempo debe necesariamente de escribirse en ensamblador:
//carga en registro el contenido del puntero [Y] (apunta a la memoria de video)
//incrementa Y en 1 (siguiente pixel)
LDR0,Y+ //2 ciclos de reloj
//pone en el puerto A el contenido del registro
OUTPORTA, R0 // 1 ciclo de reloj
LDR0,Y+
OUTPORTA, R0
...
LDR0,Y+
OUTPORTA, R0 VGA Monocromático
Como mencionaba en párrafos anteriores, utilizaré un puerto de comunicaciones USART para generar las formas de onda.
El puerto USART de los controladores ATmega cuentan con las siguientes características interesantes para nuestra aplicación:
(1) Soportan el modo SPI con lo que se eliminan los elementos de sincronismo (start bit, stop bit) de la línea de datos.
(2) Tienen doble buffer de transmisión, que permite "ir cargando" el contenido del segundo byte a transmitir cuando todavía se está transmitiendo el primero (el puerto SPI no puede usarse para esta aplicación por este motivo).
(3) Máximo baudrate soportado: FOSC / 2. Supera ampliamente en frecuencia a la operación sobre puerto estándar.
En la figura se observa el pin de la transmisión del USART conectado a los 3 pines de los componentes de color del monitor VGA.
Las resistencias de 75ohms corresponden a las impedancias internas de cada una de las entradas de color del monitor.
El divisor resistivo formado por cada resistencia de 470ohms y la impedancia interna de 75ohms esta calculado para una caída de tensión de ~0.7V cuando el pin TX del controlador esté en estado alto (1) y de 0V cuando el pin esté en estado bajo (0).
Los componentes de color para el relleno de una línea horizontal se obtienen a partir de enviar tantos bytes por el puerto serial como pixeles / 8 haya de resolución; En nuestro caso, para 160 bytes corresponden 20 bytes enviados por el USART del controlador.
A 8 pixeles por byte, el color de cada pixel será negro si el bit correspondiente es 0 y blanco (0.7v para Rojo, Verde y Azul) si el bit es 1.
La salida de datos por el puerto por cuestiones de tiempo debe necesariamente de escribirse en ensamblador:
//carga en registro el contenido del puntero [Y] (apunta a la memoria de video)
//incrementa Y en 1 (siguiente pixel)
LDR0, Y+
//transmite por USART el contenido del registro
STSUDR1, R0
//carga contenido del siguiente pixel
LDR0, Y+
//demora hasta que esté libre el doble buffer para el siguietne byte a transmitir
NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP
//transmite por USART el contenido del registro
STSUDR1, R0
...
LDR0, Y+
NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP
STSUDR1, R0 Librería
La librería está compuesta por una clase pública para el usuario (ArduinoVGA.cpp) y una librería de bajo nivel (ArduinoVGAEngine.cpp) con las rutinas de generación de señal.
La misma es de código abierto (licencia a definir) y puede ser descargada desde la web de SourceForge desde aquí.
Importante (1) Por cuestiones de rendimiento es necesario desactivar el Timer0, utilizado para las rutinas de demora: millis(), micros(), etc. Se provee de una rutina de generación de demoras como reemplazo (VGA.Delay()). No es posible utilizar estas rutinas como así tampoco otros periféricos con la generación de interrupciones activa. Es una limitación importante de la librería y uno de los costos por no utilizar hardware externo. (2) El ~92% del tiempo el microcontrolador estará dedicado a la generación de señales de VGA. Solo en el ~8% restante (denominado "tiempo de blanqueo") el controlador estará "relativamente liberado" para ejecutar el código de usuario (1,5mS de 16,7ms) así que habrá que "ingeniárselas".
Clase pública
class ArduinoVGA {
public:
Init(void);
void PutPixel(unsigned short x, unsigned short y, unsigned char color);
void PutPixel(unsigned short x, unsigned short y, unsigned char r, unsigned char g, unsigned char b);
unsigned char RGB2Color(unsigned char r, unsigned char g, unsigned char b);
void ClrScr(unsigned char color);
void ClrScr(unsigned char r, unsigned char g, unsigned char b);
void ClrScr(void);
void Delay(unsigned short ms);
void Line(short x0, short y0, short x1, short y1, unsigned char color);
void Rectangle(short x0, short y0, short x1, short y1, unsigned char color);
void FillRectangle(short x0, short y0, short x1, short y1, unsigned char color);
void Triangle(short x0, short y0, short x1, short y1, short x2, short y2, unsigned char color);
void Polygon(short x[], short y[], unsigned char vertexCount, unsigned char color);
void Circle(short x0, short y0, short radius, unsigned char color);
void FontLoad(const unsigned char *font);
void FontColor(unsigned char color, unsigned char backgroundColor);
void GotoXY(unsigned char x, unsigned char y);
void Write(char *s);
void Write(unsigned char x, unsigned char y, char *s);
};
Descripción de los métodos
Init();
Inicializa la librería.
PutPixel(x, y, color);
Pone un pixel en las coordenadas en pixeles [x, y] de color RGB empaquetado [0..255].
PutPixel(x, y, r, g, b);
Pone un pixel en las coordenadas en pixeles [x, y] de componentes de color r [0..7], g [0..7] y b [0..3].
RGB2Color(r, g, b);
Convierete color de tres componentes [r], [g], [b] a empaquetado de 8 bits [0..255].
El formato del empaquetado (byte) será de bit mas significativo a menos significativo [BBGGGRRR].
ClrScr(color);
Borra la pantalla pintando con color RGB empaquetado [0..255].
ClrScr(r, g, b);
Borra la pantalla pintando con color de componentes r [0..7], g [0..7] y b [0..3].
ClrScr();
Borra la pantalla (negro).
Delay(ms);
Genera demora en ms.
Como mencionábamos anteriormente, varias de las rutinas estándar de Arduino son desactivadas durante la inicialización de esta librería por cuestiones de rendimiento.
Line(x0, y0, x1, y1, color);
Dibuja línea desde coordenadas en pixeles [x0, y0] a coordenadas [x1, y1] de color RGB empaquetado [0..255].
Rectangle(x0, y0, x1, y1, color);
Dibuja un rectángulo con coordenadas de vértice superior izquierdo [x0, y0], [x1, y1] de vértice inferior derecho y de color RGB empaquetado [0..255].
FillRectangle(x0, y0, x1, y1, color);
Dibuja un rectángulo relleno con coordenadas de vértice superior izquierdo [x0, y0], [x1, y1] de vértice inferior derecho y de color RGB empaquetado [0..255].
Triangle(x0, y0, x1, y1, x2, y2, color);
Dibuja un triángulo con vértices de coordenadas [x0, y0], [x1, y1] y [x2, y2] de color RGB empaquetado [0..255].
Polygon(x[], y[], vertexCount, color);
Dibuja un polígono de [vertexCount] vértices de coordenadas {[x[0], y[0]] .. [x[vertexCount - 1], y[vertexCount - 1]]} de color RGB empaquetado [0..255].
Circle(x0, y0, radius, color);
Dibuja circunsferencia con centro en coordenadas en pixeles [x0, y0], radio en pixeles [radius] y color RGB empaquetado [0..255].
FontLoad(*font);
Selecciona fuente [font] almacenada en memoria flash.
Se utilizan las fuentes del proyecto arduino-tvout ya incluidas dentro de la librería.
FontColor(color, backgroundColor);
Configura color de fuente y color de fondo. Ambos parámetros color RGB empaquetado [0..255].
GotoXY(x, y);
Posiciona puntero de escritura en coordenadas en pixeles [x, y] Write(char *s);
Escribe cadena de caracteres en posición de puntero de escritura.
Modifica la posición del puntero (permite escribir múltiples cadenas una a continuación de la otra).
Write(x, y, *s);
Escribe una cadena [s] en las coordenadas en pixeles [x, y].
Modifica la posición del puntero (permite escribir múltiples cadenas una a continuación de la otra).
Instalación / Utilización
La instalación es la "clásica" del IDE Arduino:
Menú Programa -> Include Library -> Add .ZIP Library
Para más información consultar la documentación oficial: Installing Additional Arduino Libraries
Una vez instalada la librería en el IDE, en un Sketch nuevo o existente se debe proceder con la inclusión de la librería: Programa -> Include Library -> Arduino VGA.
Los métodos de la clase publica están disponibles vía la instancia global VGA. (VGA.PutPixel, VGA.ClrScr, VGA.Line, VGA.Circle, etc).
Sketch de ejemplo
#include <ArduinoVGA.h>
#include <ArduinoVGAConfig.h>
#include <ArduinoVGAEngine.h>
#include <font6x8.h>
// codificado dentro del método setup()
// la librería desactiva varias funcionalidades de Arduino por cuestiones
//de rendimiento.
void setup() {
// inicialización de la librería
VGA.Init();
// carga fuente a utilizar por las rutinas de escritura
VGA.FontLoad(font6x8);
// bucle infinito
while (1){
// dibuja circulo de radio, posición y aleatoria de la pantalla
VGA.Circle(random(RX), random(RY), random(10) + 1, random(2));
// selecciona color de fuente aleatorio y color de fondo negro
VGA.FontColor(random(2), 0);
// escribe cadena "Hola mundo!" en posición aleatoria de la pantalla
VGA.Write(random(RX), random(RY), "Hola mundo!");
}
}
// la rutina no se llama nunca en este ejemplo
// jamás se retorna de la rutina setup()
void loop() {
}
Otros ejemplos de uso
A continuación un demo para cada uno de los modos.
Ambos forman parte de la librería y pueden ser abiertos como Sketch's de ejemplo de la librería (Archivo -> Ejemplos -> ArduinoVGA).
VGA Color
Para el modo color algunos fractales y un par de efectos de demoscene en resolución 64 x 64 (no puedo mas por cuestiones de RAM).
Efectos según orden de aparición:
Fuego (básico por cuestiones de RAM)
Fractales Sierpisky Mandelbrot
Prueba de fuentes
Shadebos
VGA Monocromático
Para el modo monocromático programé una librería de gráficos de tortuga para nada pulida para hacer algunos fractales recursivos.
La resolución elegida en este caso es de 160 x 120.
Efectos según orden de aparición:
Fractales
Arbol Dragón Sierpisky
Koch
Prueba de fuentes
Bolas sobre curvas de Lissajous
Figuras geométricas
Conclusiones
El resultado es bastante bueno considerando las importantes limitaciones producto de la memoria RAM y la frecuencia de reloj.
En modo monocromático cuenta con resolución aceptable y es relativamente utilizable para aplicaciones varias.
En modo color es bastante limitado (resolución), es con fines de experimentación y no se le puede pedir mucho mas.
Tiene unos cuantos detalles que estimo iré puliendo con el tiempo.
En lo próximo
Con la misma idea voy a diseñar un shield con un microcontrolador mas potente (tentativamente algún de la familia ATxmega), memoria RAM onboard y algún que otro chiche, comandado vía SPI por un Arduino y su correspondiente librería de control.
Agregaré alguna que otra función a la librería de interfaz (ArduinoVGA).
Crearé repositorio con el contenido aquí presentado (Sourceforge probablemente).
Trabajaré la librería de bajo nivel (generación de señales) con el fin de exprimir al máximo el hardware y poder mejorar levemente la resolución en los diferentes modos.
Corregiré pequeños detalles de la implementación.
Documentaré el código fuente.
Doy por terminado este artículo, espero el contenido haya sido de utilidad.
Cualquier duda no duden en consultar, nos vemos en la siguiente entrada.
Saludos, Luis.-
Coincidiendo con la "vuelta a la actividad" del Radio Club 25 de Mayo (increíble tarea llevada a cabo por el amigo y colega Sebastión LW9ERO), que motivó también mi retorno, desarrollé una aplicación para acceso a FBB BBS como reemplazo de cliente telnet.
La plataforma seleccionada es Flash (AS3) que permite la utilización de cualquier explorador web con soporte Flash, evitando al usuario la instalación de software adicional no específico (cliente de telnet).
La aplicación, aún en desarrollo, tiene como fin la experimentación con las diferentes tecnologías (aprendizaje), el acercamiento a colegas sin el equipamiento adecuado, la colaboración y lo más importante: La práctica del hobby.
Características
Utilización simple (al menos para el cliente).
Multiplataforma (aplicación web).
Interfaz gráfica escalable adaptable a diferentes formatos / resoluciones.
Historial de comandos.
Aceleradores: Mensajes nuevos, propios, lectura, estado del bbs, etc.
Texto enriquecido. Reconocimiento de palabras clave (url's, correos electrónicos, números de mensaje, licencias, etc).
Descarga / Visualización directa de YAPP / 7+ (a implementar).
Requerimientos Cliente
(a) Navegador con soporte Flash 9+ (cualquier navegador moderno).
(b) Acceso a autorizado (contraseña) al BBS vía telnet (gestionado por el Sysop del BBS).
Servidor
(a) WinFBB / LinFBB con puerto telnet configurado (tema que no trataremos en este artículo).
(b) Socket Policy File Server: Por políticas de seguridad de flash es necesaria la instalación de un servidor de seguridad que habilite a la aplicación externa (cliente) a realizar una conexión al puerto 23 (Telnet). Concretamente debe de haber un servicio respondiendo a una solicitud que hace el reproductor de flash que habilita o no la conexión del software en ejecución.
Pueden encontrarse diferentes implementaciones en la red, pongo a disposición una genérica para windows y una propia multiplataforma: http://socketpolicyfile.codeplex.com/
...
Acceso a la aplicación
La aplicación es accesible desde el siguiente hipervínculo: http://qsl.net/lw6dio/fbbtelnet/
El usuario puede "incrustar" la aplicación flash en una web propia sin ningún tipo de inconvenientes. Junto con el código fuente se encuentra el binarios (swf) y un html de ejemplo.
Código fuente / Binarios
El código fuente y el binario de la aplicación es de dominio publico y se encuentra disponible en https://sourceforge.net/projects/fbbtelnet/.
Se utilizó para el desarrollo la herramienta de código abierto FlashDevelop (http://www.flashdevelop.org/), Adobe Flex 4.6 SDK (http://www.adobe.com/devnet/flex/flex-sdk-download.html) y la librería de componentes visuales de código abierto myLib (http://mylib.samystudio.net/). A futuro
En lo próximo se implementará la descarga / visualización de binarios YAPP y 7+ (si consigo alguna especificación técnica del formato).
Aceleradores de lectura / escritura de mensajes están siendo considerados.
Comentarios, sugerencias y críticas son siempre bienvenidas.