lunes, 20 de junio de 2016

Arduino - Generación de señales de VGA con Arduino DUE

    Buenas!
    Un año después del último artículo (como me cuesta sentarme a escribir), en base a una propuesta de un lector del blog (único comentario hasta el día de la fecha) y continuando el artículo Arduino - Generación de señales de VGA con hardware estádar decidí modificar la librería para un Arduino mas potente: Arduino DUE con la finalidad de obtener mejor resolución / rendimiento y poder darle uso práctico.

    Las características del Arduino Due lo hacen por demás interesante para esta aplicación.
Cuenta con controlador Atmel AT91SAM3X8E que corre a 84mhz, 96k de ram y 512kb de flash entre otras.
    Los 84mhz de clock a unas 3 instrucciones por pixel permitirían teóricamente alcanzar las frecuencias requeridas para el estándar 640 x 480 (25.175mhz) aunque la memoria ram disponible no lo permite a 8bits por color (256 colores): 640 x 480 x 8bits = 307200bytes.
    Optaremos por una resolución de 320*240*256 colores (76800bytes de RAM), resolución mas que suficiente para las aplicaciones que tengo en mente del sistema.

Implementación

Hardware

    En esta oportunidad no fabriqué un impreso para el DAC conformado por resistencias. En su lugar armé el pack "en el aire", encinté y empaqueté con PVC.
    El DAC de resistencias en escalera difiere al anterior producto de que el Arduino Due utiliza 3v3 para alimentación en lugar de los 5v del Arduino estándar.
    Las señales de sincronismo, según especificación (bastante variada por cierto) deberían ser 0 - 5v. Prácticamente hablando no tuve problemas hasta ahora en los monitores que probé con señales 0 - 3v3.

ladder de resistencias empaquetado en pvc


Diagrama de conexionado

    Utilizaremos al igual que en el proyecto anterior el DAC ladder con 3bits para rojo y verde y 2bits para el azul (RGB 3-2-2).
    El microcontrolador cuenta con hasta 6 puertos de 32bits (hasta 32 pines) que están disponibles total o parcialmente dependiendo del modelo de controlador (encapsulado / cantidad de pines).
    Utilizaremos en este caso el único de los puertos (PIOD) que tiene "conexionado" al exterior desde el pin 0 del puerto (8bits menos significativos en el registro de acceso al puerto).
    El conexionado es algo "rebuscado" (los muchachos que diseñan los Arduino no suelen pensar en aquellas aplicaciones de tiempo crítico) pero no hay alternativas si se pretende tener el rendimiento buscado.

diagrama de conexionado

VGA Monocromático

    Sin implementar de momento. Veré de utilizar en lo próximo algunos de los periféricos de comunicaciones del controlador. Los primeros vistazos al USART no son muy alentadores. El baudrate máximo es FOSC / 16 (84mhz / 16 para nuestro caso = 5.25mhz), bastante por debajo de lo requerido para la resolución buscada.

Software

    La librería es la de Arduino VGA con algunas modificaciones / agregados.
    En este caso ArduinoDueVGAEngine.cpp, producto de las características del microcontrolador está íntegramente escrita en C a diferencia de la anterior que está escrita mayormente en ensamblador (generación de video) por cuestiones de rendimiento.
    Para la generación de las señales de sincronismo utilicé uno de los 9 timers de 32bits disponibles.
Las señales de sincronismo no tienen latencia (o es despreciable), producto de que se utiliza la funcionalidad "generación de señales" del timer. La señal de video se realiza dentro de la rutina de interrupción. Como en cualquier controlador existe una pequeña latencia variable entre el tiempo configurado y la ejecución de la rutina (código). Para compensar esto, en el artículo anterior realicé una rutina de demora variable. Luego de las primeras pruebas, al no poder "dar pié con bola" me interioricé con el funcionamiento del controlador y descubrí que cuenta con un Pipeline de 3 etapas; Hablando en criollo es capaz de ejecutar mas de una instrucción por ciclo de ejecución motivo por el cual es extremadamente dificultoso realizar rutinas de demora con la exactitud requerida. Esto deriva en un defecto de Jitter de video.
    Como memoria de video se utiliza un vector de 76800bytes (320 x 240 x 8bits) como buffer intermedio de las rutinas de la clase pública y las rutinas de bajo nivel de generación de señal de video. Quedan disponibles para la aplicación ~21500 bytes de RAM.

Librería

    Al igual que el proyecto original, la librería está compuesta por una clase pública para el usuario (ArduinoDueVGA.cpp) y una librería de bajo nivel (ArduinoDueVGAEngine.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í.
    Para detalles sobre la utilización referirse a apartado Instalación / Utilización del artículo Arduino - Generación de señales de VGA con hardware estádar.

Resultados
  • Aceptables en relación a simplicidad del circuito / software.
  • La resolución de 320 x 240 pixeles es una de las clásicas (junto con la 320 x 200) de demoscene y juegos de los 80's / 90's es suficiente para lograr representaciones aceptables.
  • Independientemente del aumento de la potencia de cálculo, la generación de señal de video continúa siendo una tarea crítica:
    • La utilización de rutinas de interrupción "pesadas" puede generar Jitter de video.
    • El tiempo de procesador disponible ronda el 8%,
  • No le encontré la vuelta para poder generar la señal de video sin Jitter, producto de las latencias en la ejecución de rutinas de interrupción y del tiempo de ejecución variable por instrucción (Pipelines). Se observa en la imagen siguiente el defecto.


Ejemplos

        Van un par de videos de ejemplos de utilización:
                El primero un ejemplo que va junto con la librería de varios efectos de demoscene, algunos fractales y fuentes.
                El segundo un clon de Pacman en desarrollo (a medio terminar) que no forma parte de la librería. En cuanto lo termine lo publicaré y formará parte del contenido del blog.


En lo próximo
    Tal como lo había propuesto en el artículo anterior, tengo diseñado desde hace ya algunos meses un Shield (económico) para Arduino con un controlador mas potente (ATxmega) exclusivo para la generación de señales de video, memoria RAM onboard y algún que otro chiche.
    En cuanto tenga un rato voy a "cocinar" el impreso y empezaré a escribir algo de código.
    Estará disponible en cuanto lo tenga listo para publicar (al ritmo que vengo lamentablemente dentro de un año :P).


    
  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.-

Referencias

Arduino - Generación de señales de VGA con hardware estádar
  http://luispichio.blogspot.com.ar/2015/05/arduino-generacion-de-senales-de-vga.html
Arduino Due
  https://www.arduino.cc/en/Main/ArduinoBoardDue
  https://www.arduino.cc/en/uploads/Main/arduino-Due-schematic.pdf
Calculador ladder: RL-DAC
  https://code.google.com/archive/p/rl-dac/
  https://raronoff.wordpress.com/2012/10/17/rl-dac/

martes, 9 de junio de 2015

Arduino - Interfaz para Joystick de Family Game

  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:

unsigned char CheckButtons(void){
  unsigned char buttons = 0;
  unsigned char i;
  digitalWrite(_LatchPin, 1);
  delayMicroseconds(12);
  digitalWrite(_LatchPin, 0);
  for (i = 0 ; i < BUTTON_COUNT ; i++){
    delayMicroseconds(6);
    buttons <<= 1;
    buttons |= digitalRead(_DataPin) ? 0 : 1;
    digitalWrite(_ClockPin, 1);
    delayMicroseconds(6);
    digitalWrite(_ClockPin, 0);
  } 
  return buttons;
}

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).

Clase pública
class FamilyGameJoystick {
  public:
    void Init(unsigned char DataPin, unsigned char LatchPin, unsigned char ClockPin);
    void Release(void);
    unsigned char Poll(void);
    bool ButtonDown(unsigned char button);
    bool ButtonPressed(void);
    unsigned char GetButton(void);
    void onButtonDown(void (*function)(unsigned char));
    void onButtonPress(void (*function)(unsigned char));
};

Constantes utilizadas
  BUTTON_RIGHT
BUTTON_LEFT
BUTTON_DOWN
BUTTON_UP
BUTTON_START
BUTTON_SELECT
BUTTON_B
BUTTON_A

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!

jueves, 28 de mayo de 2015

Arduino - Generación de señales de VGA con hardware estádar

  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)
LD R0,Y+  //2 ciclos de reloj
//pone en el puerto A el contenido del registro
OUT PORTA, R0  // 1 ciclo de reloj
LD R0,Y+
OUT PORTA, R0
...
LD R0,Y+
OUT PORTA, 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)
LD R0, Y+
//transmite por USART el contenido del registro
STS UDR1, R0
//carga contenido del siguiente pixel
LD R0, 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
STS UDR1, R0
...
LD R0, Y+
NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP NOP
STS UDR1, 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.-

sábado, 28 de febrero de 2015

FBBTelnet - Consola telnet vía web para FBB BBS

  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).

    Utilización en Android
      En Android tanto Chrome como el navegador nativo carecen de soporte para flash. En su lugar se pueden utilizar alternativos como Firefox o Dolphin Browser:
        Firefoxhttps://play.google.com/store/apps/details?id=org.mozilla.firefox
        Dolphin Browserhttps://play.google.com/store/apps/details?id=mobi.mgeek.TunnyBrowser
      El reproductor de flash se debe descargar e instalar de forma manual:
        Proceso de instalación: https://helpx.adobe.com/flash-player/kb/installing-flash-player-android-devices.html
        Aplicación: https://helpx.adobe.com/flash-player/kb/archived-flash-player-versions.html

  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.
  73's de LW6DIO.

Referencias
  FBB BBS
     http://www.f6fbb.org/
  FlashDevelop - IDE de código abierto para AS2 / AS3
    http://www.flashdevelop.org/
  myLib - Componentes visuales para AS3.
    http://mylib.samystudio.net/
  Socket Policy File Server
    http://socketpolicyfile.codeplex.com/
  Políticas de seguridad de Adobe Flash Player
    http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html
    http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html


sábado, 13 de diciembre de 2014

Demoscene - Fuego (Parte 2)

Buenas!
Comenzando una serie de artículos sobre el tema vamos con el efecto fuego.
Se trata probablemente del efecto mas simple, no por ello, menos llamativo.
No vamos a realizar una simulación, ya que una tarea compleja de dinámica de fluídos; Nos limitaremos a implementar un algoritmo simple, conocido y utilizado desde la vieja escuela.


La implementación será realizada en Java (NetBeans 8.0.02 + JDK 1.7) por su portabilidad, no así por su rendimiento.
El efecto es de tipo "generación de textura en tiempo real". Se articularán las herramientas para poder reutilizar el presente ejemplo en futuros artículos (efectos de deformación, renderizado, etc).

A los bifes

Para la generación del efecto haremos uso de un buffer de efecto (cálculo) y otro de imagen (representación)
A su vez, el buffer de efecto tendrá un doble buffer (copia), ya que algunos de los procesos son destructivos (suavizado, enfriamiento, convección).


Cada elemento del buffer de efecto es una representación numérica de la intensidad del fuego en un punto del posterior volcado en pantalla.
A mayor valor numérico, fuego mas intenso (tendiendo a amarillo o blanco a nivel gráfico).
A la inversa, a menor valor numérico, menos intenso (tendiendo a rojo o negro a nivel gráfico).
A cada valor de intensidad se le asignará posteriormente un color (Paleta de colores).

El buffer de imagen tendrá las dimensiones del efecto que se quiera representar.
En el ejemplo de 256 x 128 pixeles (32768 pixels).
El buffer de efecto será ligeramente mas grande: Un par de pixeles en altura (parte inferior) en donde se generará la ignición. Este sector no se transferirá el buffer de imagen (no será visible).

Paleta de colores
Probablemente el punto crítico del efecto.
Con la paleta adecuada, el efecto es casi siempre agradable a la vista.
La paleta incorrecta, no solo da aspecto de irreal (colores); También afecta la convección y altura del efecto.


Para el ejemplo utilizaremos una paleta de 112 colores

  16 colores [0..15] -> Negro a Rojo (menor intensidad)
  32 colores [16..47] -> Rojo a Amarillo (pasando por naranja)
  32 colores [48..80] -> Amarillo a Blanco
  32 colores [80..112] -> Blancos (mayor intensidad)

Generación del efecto

Para simplificar la explicación, vamos a dividir la generación del efecto en las siguientes etapas:
  (1) Ignición
  (2) Propagación de calor
  (3) Enfriamiento
  (4) Convección

(1) Ignición (generación de llama)
La generación de llama consiste en agregar puntos de calor (valores altos) al buffer de efecto de forma aleatoria y normalmente en la parte baja (fuego desde abajo hacia arriba).
En cada ciclo de animación refrescaremos un par de líneas en la parte inferior del buffer de efecto, que, por lo anteriormente comentado, no formará parte del área visible del efecto.

Pseudocódigo de ignición
para cada x
  buffer(x, 0) = numero_al_azar()

(2) Propagación de calor
La "magia" del efecto, es tan simple como un "suavizado" (smooth) del buffer de efectos.
Hay muchos tipos de "suavizado", para el ejemplo utilizaremos el mas simple y eficiente: Media aritmética.
En cada ciclo de la animación, a cada punto lo reemplazaremos por la media aritmética de él y sus vecinos:


Pseudocódigo de propagación del calor
para cada x, y
  buffer(x, y) = (
    buffer(x, y) +
    buffer(x + 1, y) +
    buffer(x - 1, y) +
    buffer(x, y + 1) +
    buffer(x + 1, y - 1)
  ) / 5

(3) Enfriamiento
La técnica consiste restar un valor aleatorio a cada elemento del buffer de efectos.
Lo de "valor aleatorio" es relativo; Utilizaremos una máscara (otro buffer) generada a partir de valores aleatorios, lo que contribuirá de manera significativa al rendimiento por no tener que generar valores en tiempo real.
Con una paleta estándar, al "restar" estamos oscureciendo o quitando intensidad al fuego en ese sector.
La finalidad es darle mas dinámica al efecto (movimiento), favorecer la convección, darle aspecto aleatorio a el efecto, etc.

Pseudocódigo de enfriamiento
para cada x, y
  buffer(x, y) = buffer(x, y) - buffer_enfriamiento(x, y)
    si buffer(x, y) < 0
      buffer(x, y) = 0

Utilizaremos una máscara de enfriamiento (buffer circular simétrico) generada a partir de un relleno al azar suavizado, lo que le conferirá homogeneidad.


Ciclo a ciclo de la animación haremos un scroll ascendente a la máscara, lo que favorecerá la convección.

Pseudocódigo de generación de mapa de enfriamiento
//relleno al azar
para cada x, y
  buffer_enfriamiento(x, y) = numero_al_azar()

//suavizado
repetir 20
  para cada x, y
    buffer_enfriamiento(x, y) = (
      buffer_enfriamiento(x, y) +
      buffer_enfriamiento(x + 1, y) +
      buffer_enfriamiento(x - 1, y) +
      buffer_enfriamiento(x, y + 1) +
      buffer_enfriamiento(x, y - 1)
    ) / 5

(4) Convección
Tiene la finalidad de darle "aspecto ascendente" a  nuestras llamas.
En cada ciclo de animación, haremos un scroll ascendente de 1 o 2 líneas sobre el buffer de efecto.

Código fuente / binarios compilados


Observaciones
  Por cuestiones de rendimiento y compatibilidad no se utilizarán vectores de dos dimensiones, en su lugar usaremos vectores unidimensionales.
  Se hará uso de un único tipo de entero (entero de 32bits con signo) por no detectarse variaciones significativas en el rendimiento y complicarse la implementación y legibilidad del código (casteos, control de desbordes, etc).

Variaciones del efecto
  Controlando la ignición es posible lograr variaciones vistosas del efecto.
  Propongo al lector en lugar de generar ignición en la parte baja del efecto, utilizar máscaras con texto, formas geométricas, gráfica, máscaras animadas (bolas de fuego), etc.

Doy por terminado este primer artículo, espero el contenido haya sido de utilidad.
Cualquier duda no duden en consultar, nos vemos en la siguiente entrada.
Saludos, Luis.-