Friday, October 17, 2014

Project: Totem Luminico

Introducción

Este proyecto consistió de crear un display de LEDs que fuera visible desde cualquier posición a 360 grados para una actividad de promoción en Interphex 2014. Para el proyecto se usaron 7680 LEDs conectados en forma de matriz de 120 columnas x 64 filas. Se usaron dos controladores. El Arduino Due se utilizó para manejar los LEDs y se utilizó un Arduino Uno para detectar las bandas de frecuencias de sonido.

El proyecto fué inspirado y basado en la librería de Adafruit_NeoMatrix.

Funcionalidad

La idea original era crear un display que se pudiera ver desde cualquier dirección en una actividad que sería al aire libre. La librería NeoMatrix de Adafruit ofrece varias funcionalidades tales como dibujar primitivos (lineas, circulos, rectangulos, triangulos, etc.) que serían utiles para crear algunas animaciones con las luces que reaccionaran a la música. El display debía proveer un refresh rate lo más cercano a 30 fps para permitir animaciones fluidas. Para que fuera efectivo debía ser cilíndrico.

LEDs

Escogimos los LEDs WS2812B ya que eran soportados en la librería de Adafruit. Los WS2812B trabajan a 5V y proveen una frecuencia de transmisión de 800KHz.

La forma en que éstos funcionan es a través de un IC incorporado dentro de cada LED. Cada LED está conectado con el siguiente a través de la linea de datos. Un LED recibe la data a través de DIN y el IC setea el color del LED y luego reproduce el resto de la data que recibe a través de DOUT. La data se envía en paquetes de 24 bits. El formato de éstos 24 bits son GGGGGGGG-RRRRRRRR-BBBBBBBB donde el primer bit que recibe es el verde (G7).

Byte verde: G7 G6 G5 G4 G3 G2 G1 G0

El primer paquete que recibe un LED lo consume para definir su color. El resto simplemente lo reenvía al próximo LED. Una vez todos los LEDs reciben un paquete se debe enviar una pausa mayor de 50uS (microsegundos). Cuando los ICs reciben esta pausa, entonces despliegan el color.

Cada bit se define en términos de una señal cuadrada, donde el duty cycle define si es un 1 o un 0 y el cycle time = 1.25uS, como se muestra en la gráfica abajo.



La ventaja más importante de estos LEDs es que se pueden controlar muchos de ellos utilizando una sola señal. Según el datasheet, son capaces de actualizar 1024 leds a un rate de 30fps teóricamente. Ésto estaba en línea con lo que queríamos hacer. Los que conseguimos venían en rollos de 4 metros. Éstos tienen una distancia entre cada LED de 16.7mm. Decidimos usar 2 metros de LEDS para la circunferencia lo que equivale a 120 LEDs.  La razón principal fue que de esta forma tendríamos 2 líneas con cada rollo de 4 metros y entonces reduciríamos la cantidad de empates y soldaduras requeridas.


Al considerar la separación de estos LEDs la altura nos dió aproximadamente 4 pies. Por esta razón escogimos 64 líneas (múltiplo de 8 más cercano). De esta forma podríamos tener grupos de 960 LEDs para mantenernos en los specs cerca de los 30fps.

Procesador: Arduino DUE

Una vez determinado de cuantas lineas por columnas iba a ser el display, nos dimos cuenta de que confrontaríamos problemas con alcanzar la velocidad de refresh rate de 30fps con la librería de Adafruit_NeoMatrix y los Arduino regulares. El primer contratiempo fue que debíamos calcular la memoria de RAM que iba a requerir poder dibujar en el display. Como cada LED requiere 24 bits, el cálculo nos dió unos 23,040 bytes de data, sólo para almacenar la información del display en cualquier momento. Esto nos hizo descartar usar el Arduino Uno y el Mega 2560 automáticamente. La única opción prometedora era el Arduino Due si queríamos mantenernos en el ecosistema de Arduino.


El Due tiene 96KB de RAM y 512KB de Flash para el programa. Además, es un procesador que corre a 84MHz lo cuál sería beneficioso para este tipo de proyecto. Su procesador es además un Atmel SAM3X8E, cuya arquitectura es un ARM Cortex-M3 y sus registros son de 32bits en lugar de 8bits en el Uno. En resumen es una bestia completamente diferente bajo el capote.

El Due nos puso en buen terreno en cuanto a la memoria. Otro problema que teníamos era que el Due trabaja a 3.3V y es sumamente intolerable a recibir cualquier voltaje por encima de 3.7. En esencia se daña el procesador si se le aplican 5V en cualquiera de los I/O. Tuvimos que utilizar los "level shifters" de Adafruit que son convertidores bi-direccionales de nivel para poder enviarle los datos a los LEDs.

Habiendo atendido esos dos contratiempos, entonces nos enfocamos en el problema del refresh rate. El protocolo de los LEDs trabaja a 800KHz, eso equivale a un cycle time de 1.25uS. Eso quiere decir que para un LED el tiempo que toma recibir un paquete es 1.25uS x 24 bits = 30uS. El display tendría en total 120x64 = 7680 LEDs. Por lo tanto, el tiempo que requería enviar los paquetes de todos los LEDs era de 7680 x 30uS = 230,400 uS. Esto nos daba entonces un refresh rate de 1 / (0.2304 S) = 4.34 fps.

Este resultado nos dice que independientemente de la velocidad y el RAM que tengamos disponible en el procesador, lo máximo que podríamos aspirar con estos LEDs serían 4 fps. Eso sin contar con el tiempo adicional que el procesador se tardaría en hacer todo el procesamiento relacionado al programa de del display. Ante esta situación, teníamos dos alternativas básicamente. La primera era utilizar el Due por su capacidad de memoria como el "master controller" y tener 8 controladores independientes que manejaran 960 LEDs cada uno, dándonos un refresh rate teórico de 35 fps.

Esta alternativa nos ponía una presión adicional de tener que superar aún otra curva de aprendizaje.  Tendríamos que implementar un protocolo de comunicación, posiblemente basado en SPI (que todos los Arduino apoyan) para mover la data entre el master y los slaves y luego para poder sincronizar los segmentos habría que programar los slaves para que no despleguen hasta tanto no reciban una señal por interrupt externo. El master tendría que cargar a cada slave su porción de la imagen en formato de round-robin y cuando hubiera enviado la última entonces activar 8 lineas simultaneamente que serían interpretadas por los slaves como el trigger para comenzar a dibujar. SPI podría ser configurado para correr al máximo recomendado por Arduino que son 4MHz.  La librería de Arduino solo apoya utilizar el Arduino como Master. Habría que escribir nuestra propia librería para los slaves o utilizar una de varias creadas por miembros de la comunidad de Arduino. En fin, eran muchas variables nuevas adicionales que presentaban riesgos que no podíamos manejar así que la descartamos.

La segunda alternativa era tratar de generar las señales en paralelo en el Due y así controlar 8 segmentos simultaneamente. El primer obstaculo fue entender el codigo de Adafruit que manipula los NeoPixels para el Due. La librería de Adafruit está muy bien documentada en el mismo source code con la desafortunada excepción de la porción del código para procesadores ARM. La primera tarea fue disectar el código hasta llegar a tener aquello que era específico del Due. Increíblemente el código se redujo a unas 20 a 30 líneas. El mecanismo utilizado difería considerablemente del acercamiento para el resto de los Arduino. En este caso no se utilizaban instrucciones de "assembly" sino que se instanciaban unos objetos particulares de ARM Cortex. En fin, luego de tratar infructuosamente de obtener dirección de parte de Phil Burgess y Tony Dicolla autores de la librería de Adafruit, tuvimos que buscar literatura de ARM, Cortex-M3 y Atmel SAM3X8E. Este esfuerzo no fué trivial, ni fácil. El feature que vino a salvarnos el proyecto fue la capacidad de los Cortex-M3 de escribir en una sola instrucción a un puerto entero de I/O. El Due tiene 4 puertos A,B,C y D. Cada uno tiene 32 bits. Nuestra solución consistió entonces en desarrollar un código de bajo nivel con algunas llamadas en Assembly de ARM (Thumb-2) para lograr implementar paralelismo de hasta 8 salidas simultáneamente.

Esta solución requiere que cada uno de los bits para cada una de las salidas se puedan acomodar en una memoria contigua. La data de la imagen que se genera para ser mostrada en los LEDs se acomoda en un "array" de forma consecutiva. Ésto porque los WS2812B esperan la data en esta forma. El primer bit en salir por una salida es el primero de los verdes del primer LED. Por está razón hay que crear una transposición que ubique cada bit n en un byte n. Este byte n tiene el bit n de cada salida. Al final se requieren dos arreglos o "arrays" de bytes. El primero (video RAM) de 960 LEDs  * 4 bytes = 3,840 bytes por salida * 8 salidas = 30,720 bytes. El segundo (Output Data) de 32 bytes por LED * 960 LEDs  = 30,720 bytes. En este cálculo se están usando 32 bits, aunque los LEDs sólo reciben 24. Eso se hizo necesario para agilizar los cómputos ya que el procesador trabaja con registros de 32 bits. Con este acercamiento,  tenemos entonces 60KB que consumen esos dos arreglos del total provisto por el Due de 96KB. Los restantes 36KB  son los disponibles para la aplicación.

Procesador: Uno (Spectrum Analyzer)

Una de las funciones principales del tótem es la de reaccionar a la música. Para lograr ésto utilizamos un segundo Arduino cuya función principal es detectar el comportamiento de las frecuencias bajas y altas.  Este procesador utiliza un shield (tarjeta de expansión) Spectrum Analyzer de Sparkfun, que detecta los niveles de sonido en 7 bandas de frecuencias.

Arquitectura Sistema de Control


Power

To be continued....

Fabricación

To be continued....

Thursday, October 16, 2014

Nuestro Blog

Por fin!!!!

Podemos usar este blog para compartir detalles de nuestros proyectos
y artículos para nuestra comunidad. Las contribuciones serán para
fomentar el desarrollo de la comunidad. Espero que sea un lugar de
colaboración y compartir nuestras hazañas.

Ansioso por ver como se desarrolla.

:)