-
Notifications
You must be signed in to change notification settings - Fork 13
Pantallas de video. Fundamentos. Matriz de 4x4 LEDs
🚧 TODO 🚧
- Icestudio: Todos los ejemplos se han probado con Icestudio-0.12. Usa esta versión o superior
- Ejemplos: Todos los ejemplos de este cuaderno técnico están accesibles en su repositorio en github
- Introducción
- Encendiendo un LED
- Fila de 2 LEDs
-
Matriz de 2x2 LEDs
- Ejemplo 5: Encendido del LED de la columna 1 y fila 0 (1,0)
- Ejemplo 6: Ráster horizontal lento en fila 0
- Ejemplo 7: Ráster horizontal lento. Ráster Y manual
- Ejemplo 8: Recorrido lento de la matriz 2x2
- Ejemplo 9: Lamp-test
- Señal de vídeo y sincronización
- Componente Refresh 2x2
- Ejemplo 10: Probando el componente Refresh
- Memoria de vídeo
En el Cuaderno Técnico CT20: Pantallas de vídeo. Fundamentos. Display de 1x4 LEDs aprendimos los conceptos básicos sobre los sistemas de vídeo, aplicados a un display muy sencillo de 1x4 LEDs: Una única fila de 4 LEDs
Sin embargo, las pantallas presentan la información en dos dimensiones y por ello tienen sus píxeles organizados en una matriz de MxN. Cada fila y cada columna de píxeles se identifican mediante un número. Y cada pixel queda unívocamente identificado por dos números: sus coordenadas, que indican en qué fila y en qué columna se encuentra
Así, el pixel de la esquina superior izquierda es el (0,0)
, el de la esquina superior derecha el (M-1, 0)
, el de la esquina inferior izquierda (0, N-1)
y el de la esquina inferior derecha (M-1, N-1)
En este cuaderno técnico aprenderemos a controlar una matriz de 4x4 LEDs, que tiene 16 LEDs en total. Así la representamos cuando todos los LEDs están apagados:
Encendiendo los LEDs que decidamos creamos la imagen a mostrar. En este ejemplo se muestra la imagen de la letra A
, en resolución de 4x4 píxeles
Aunque en la pantalla real vemos una A
, la realidad es que sólo hay un pixel activo cada vez, que se mueve una velocidad muy alta, como vimos en el cuaderno técnico 20. Si se recorre la pantalla a una velocidad mayor o igual a 50Hz, entonces se verá la 'A' perfectamente. Esto es debido a la ilusión óptica
El pixel activo está movido por el ráster, y ahora recorre la matriz de LEDs línea a línea, de izquierda a derecha y de arriba a abajo. En esta animación se muestra el ráster recorriendo todos los LEDs, a baja velocidad:
Si la frecuencia de refresco llega a los 50Hz, entonces veremos todos los píxeles de la pantalla encendidos
Para entender bien el funcionamiento del controlador de esta pantalla empezaremos por lo más básico: Encendiendo un LED. Luego progresivamente iremos haciendo los controladores para las pantallas de 2x2, 3x3 y finalmente 4x4 LEDs
El circuito típico para encender un LED ya lo conocemos. En el Cuaderno Técnico 16: Conexión de LEDs en la Alhambra II. Placa AP‐LED8‐THT vimos todo los detalles
El esquema para su conexionado es este:
La pata positiva del LED se conecta a un pin de la FPGA, como por ejemplo el Pin D0 de la Alhambra II. La otra pata se conecta directamente a GND. Sólo necesitamos 1 pin para encender/apagar el LED
Para conectar los LEDs formando una matrix, necesitamos controlar el LED utilizando 2 pines. El esquema de conexión es el mostrado en esta figura:
La pata positiva la conectamos a un pin que llamamos Selección de fila y la pata negativa al pin de Selección de columna
Para encender el LED hay que poner un 1
en la selección de fila, y un 0
en la selección de columna. El resto de valores NO hacen que se encienda el LED. Los mostramos en esta tabla de verdad
Selección de columna (D1) | Selección de fila (D0) + | LED |
---|---|---|
0 | 0 | Apagado |
0 | 1 | Encendido |
1 | 0 | Apagado |
1 | 1 | Apagado |
En este ejemplo encedemos el LED usando dos pines. El pin de selección de fila lo ponemos a 1
. En estas condiciones, si ponemos el pin de selección de columna a 0
, se enciende el LED, y si lo ponemos a 1
se apaga. Por ello, el LED se comporta con Lógica negativa
Este es el escenario. Conectamos el LED directamente a los pines hembra D0
y D1
de la Alhambra II. El pin positivo del LED (la pata más larga) está conectada al pin D0
En este ejemplo conectamos el pin de selección de columna (D1) al pulsador SW1
En este vídeo de Youtube lo vemos en funcionamiento:
Para encender los LEDs es más cómo utilizar la lógica positiva. La selección de la fila ya está con lógica positiva: al aplicar 1
se activa el LED, y en función de la otra pata se enciende o se apaga. Queremos que la columna también se seleccione con un 1
, por lo que añadimos una puerta NOT
Así es como queda la tabla de verdad:
Selección de columna (D1) | Selección de fila (D0) + | LED |
---|---|---|
0 | 0 | Apagado |
0 | 1 | Apagado |
1 | 0 | Apagado |
1 | 1 | Encendido |
Sólo si la fila está a 1
y la columna también está a 1
se enciende el LED
Este es el nuevo circuito:
Ahora al apretar el pulsador SW1
se enciende el LED, y al soltarlo se apaga. Efectivamente funciona con lógica positiva
En este vídeo de Youtube lo vemos en funcionamiento:
Vamos a construir un display 2x1, formado por 2 LEDs en la misma fila. Que estén en la misma fila significa que sus patas positivas (de selección de fila) están unidas, por lo que activamos ambos LEDs a la vez (decimos que activamos la fila). Luego, en función de cómo estén los pines de las columnas, los LEDs estarán encendidos o apagados
Este es el esquema:
Hemos añadido las puertas NOT para que la activación de las columnas sea en Lógica positiva
Necesitamos 3 pines en la FPGA para controlar este display. El pin de fila0 sirve para activar la fila de LEDs. Como sólo hay una fila, la denotamos como la fila 0. Si fila0=0
todos los LEDs de la fila está deshabilitados y no se pueden encender con independencia de cómo estén los pines de las columnas (col0 y col1). Si fila=1
la fila está activa y el valor de sus LEDs ahora sí que está determinado por los pines de activación de las columnas: col0
y col1
Esta es la tabla de verdad:
Fila0 | Col0 | Col1 | LED0 | LED1 | Descripción |
---|---|---|---|---|---|
0 | ❌ | ❌ | OFF | OFF | Fila desactivada |
1 | 0 | 0 | OFF | OFF | Fila activada |
1 | 0 | 1 | OFF | ON | Fila activada |
1 | 1 | 0 | ON | OFF | Fila activada |
1 | 1 | 1 | ON | ON | Fila activada |
Para probar los ejemplos hemos construido una mini-placa con 2 LEDs, que tiene los 3 pines para conectarlo a la Alhambra-II
Comprobamos que la placa funciona encendiendo manualmente los LEDs, con los pulsadores SW1
y SW2
. La fila se deja activada enviando un 1
constante por fila0
En este vídeo de Youtube lo vemos en funcionamiento:
En este ejemplo usamos un contador unario one-hot de 2 bits para mostrarlo en los LEDs. El efecto que se consigue es que los LEDs se encienden alternativamente, a la frecuencia de 2 Hz. Con el pulsador SW1
se selecciona/deselecciona la fila 0. Cuando está seleccionada se muestra la animación en los LEDs. Cuando se desactiva ambos LEDs quedan apagados
También aprovechamos para ir separando las partes. El acceso a los LEDs del display se hace mediante las etiquetas col0, col1 y fila0. Por otro lado tenemos el ráster x, que recorre ambos LEDs a baja frecuencia. Y como circuito separado tenemos el que hace que se active/desactive la fila 0, mediante un biestable T
En este vídeo de Youtube lo vemos en funcionamiento:
Vamos a construir una matriz de 2x2 LEDs para luego diseñar un controlador que nos permita mostrar información y animaciones. Este es el esquema de conexionado:
Hay dos cables horizontales y dos verticales, que se cruzan pero sin estar conectados. Entre ellos se conectan los 4 LEDs. Las patas positivas se conecta a los cables de fila, y la negativas a los de columna. En las columnas añadimos las puertas NOT para trabajar con lógica positiva
Para encender, por ejemplo, el LED de la parte superior derecha, hay que seleccionar la fila 0 y la columna 1, poniendo un 1
en ambos cables. El LED es el que está en la posición (1,0)
Este es un prototipo de la matriz de 2x2 LEDs que se conecta directamente a la placa Alhambra-II
En total usamos 4 pines para la conexión. Dos se corresponden con las filas (fila 0 y fila 1) y otros dos para las columnas (columna 0 y columna 1). El número de pines es igual al que necesitamos si conectásemos 4 LEDs de la manera tradicional (como en el cuaderno técnico 20). Pero para matrices mayores a 2x2 ya empieza el ahorro de pines cuando se usan las matrices
Este es el mapa del display 2x2, que se deja como referencia
Comenzamos a probar el display, encendiendo el LED (1,0), que se encuentre en la columna 1 y la fila 0 (Esquina superior derecha). En este ejemplo lo encendemos directamente poniendo su valor con constantes. Este es el circuito en icestudio:
Este es el resultado al probarlo
Para recorrer toda la matriz de LEDs ahora hay 2 Rásters, uno para las columnas y otro para las filas. En este ejemplo se mueve el ráster X a baja frecuencia (2Hz), por lo que se están activando alternativamente las columnas 0 y 1. La fila 0 está todo el rato seleccionada, mediante un ráster Y constante
Hemos aprovechado para agrupar las columnas y filas en un array dos arrays de 2 pines. El Ráster X está formado por un registro de desplazamiento a la derecha de 2 bits. Su salida la conectamos directamente a los pines de las columnas. El Ráster Y está formado por una constante de dos bits, con un valor fijo (en unario) para dejar seleccionada la fila 0 todo el rato
Este es el circuito en Icestudio
En este vídeo de Youtube lo vemos en funcionamiento:
Ahora hacemos que el ráster Y sea también variable, pero activado el pulsador SW1
. Cada vez que se aprieta el pulsador se selecciona la fila siguiente (Se conmuta entre las fila 0 y 1). El Ráster X se mueve autónomamente, a la frecuencia de 2 Hz
En este vídeo de Youtube lo vemos en funcionamiento:
El raster recorre ahora la matriz completa, a velocidad lenta (2Hz). Empieza barriendo de izquierda a derecha la fila 0, luego pasa a la fila 1 y vuelve a comenzar. El ráster Y se incrementa cuando el ráster X vuelve al comienzo, lo que ocurre cuando se activa la señal endx
y llega el siguiente tick del corazón de 2 Hz
En esta animación se muestra el funcionamiento
Este es el circuito en Icestudio
En este vídeo de Youtube lo vemos en funcionamiento:
En este ejemplo construimos el primer controlador, que tiene un raster matricial que recorre la pantalla línea a línea a la máxima velocidad. Se utiliza una señal de vídeo constante para encender todos los LEDs y comprobar que el refresco funciona. La señal de vídeo se demultiplexa y se envía a los LEDs
Este es el circuito en icestudio
Las señales de sincronización se miden con el analizador lógico. las veremos en el próximo apartado
Este es el resultado: se ven todos los LEDs encendidos, aunque con menor intensidad
La señal que se muestra en los LEDs de la pantalla 2x2 provienen de una señal de vídeo, igual que la del display 1x4 del cuaderno técnico 20. El pixel clock utilizado es de 12Mhz. Esta señal tiene 4 ciclos por cada frame, ya que en cada frame hay 4 píxeles. Los dos primeros ciclos se corresponden con los LEDs de las columnas 0 y 1 de la fila 0, y los dos siguientes los de la fila 1
Hay dos señales de sincronización:
- HSync: Sincronización horizontal. Indica cuándo se termina de recorrer una fila, y en el próximo ciclo se comienza la siguiente
- VSync: Sincronización vertical: Indica el final del frame actual. En el próximo ciclo comienza un frame nuevo
En esta figura se muestran una señal de vídeo genérica y las señales de sincronización para el display 2x2
Este es el resultado de la medición del ejemplo 9 anterior: el Lamp-test. La señal de vídeo está siempre a 1
El componente Refresh 2x2 recibe la señal de vídeo por la entrada y refresca la matriz de 2x2 LEDs. Este componente, además, saca las señales de sincronización, VSync, HSync, así como el ráster x y el ráster y que serán necesarios para los circuitos de generación del vídeo
Esta es la implementación:
Probamos el componente Refresh 2x2 haciendo un lamp-test, igual que en el ejemplo 9. Ahora queda todo mucho más simplificado, y listo para añadir circuitos más complejos
El resultado es el mismo que el del ejemplo 9
La información a mostrar en las pantallas se almacena en una memoria de vídeo. De ahí se saca serializada para generar la señal de vídeo que llega al controlador de refresco
La memoria de vídeo se puede implementar de diferentes maneras. Lo veremos en los siguientes ejemplos
Como estamos trabajando con un display pequeño, de sólo 2 filas de 2 píxles cada una, implementamos la memoria de vídeo usando registros de 2 bits. Utilizamos un registro por fila. Inicialmente estos registros están a 0, por lo que la pantalla está apagada al arrancar. Al apretar el pulsador SW1
se cargan los registros-fila con los valores correspondientes y se muestra la información en la pantalla (refrescándose continuamente)
Para generar la señal de vídeo se construye un serializador a partir de 3 multiplexores 2-1 unarios. Los dos primeros seleccionan de cada registro los bits de la columna actual, que está dada por el raster x. Luego, se usa otro multiplexor para seleccionar la fila, a partir del ráster y
Al cargar el circuito y pulsar el botón SW1
esto es lo que se muestra en la pantalla:
La señal de vídeo generada tiene 4 bits por frame, porque hay 4 píxeles en total. Como es un Frame fijo, siempre se muestra la misma imagen por lo que todos los frames son iguales. Tienen el valor: 1001
En este vídeo de Youtube lo vemos en funcionamiento:
Una forma muy común de mostrar información en los displays es utilizando una memoria ROM donde están almacenados los bits que indican el estado de cada pixel. Se pueden utilizar ROMs con diferentes organizaciones. En este ejemplo se usa una ROM 4x1, que tiene 4 posiciones cada una de 1 bit. La ventaja de usar memorias de 1 bit es que a su salida obtenemos directmente la señal de vídeo, sin necesidad de añadir un circuito serializador
🚧 TODO 🚧 (DEBUG)
- Utilizamos una memoria rom unaria (por hacer)...
- Circuito en Icestudio
- Foto con el resultado
🚧 TODO 🚧 (DEBUG)
- Ejemplo 13: ROM 2x2. Un sprite
- Ejemplo 14: ROM 2x2x2.Dos sprites, con conmutación manual (Con el pulsador se cambia de un frame a otro)
- Ejemplo 15: ROM 4x2x2. Cuatro sprites. Crear animacion de 4 fotogramas, desde memoria
- Generación directo de vídeo?
- Linea Horizontal
- Linea vertical
- Linea diagonal
- Cursor
- Normal
- Parpadeante
- Movimiento del cursor con teclas
🚧 TODO 🚧
- Circuito de animacion 2x2 en matriz 3x3
- Animaciones 3x3
- Un led rotando
- Una barra rotando
- Pensar otras!
- Sprite moviendose pixel a pixel fuera de pantalla
- Videojuego! (Tal vez sea de demasiado alto nivel)
- Pixel saltando cosas que llegan
- Laberinto!!
🚧 TODO 🚧
- Aplicaciones!
- Unidad de PWM de frecuencia aproximada
- VGA Retro: Puesta en marcha. MonsterLED
- Pines de Entrada/Salida
- Control de LEDs
- SPI esclavo
- SPI Maestro
- Display SPI de 4 dígitos de 7 segmentos
- Entrada y Salida de Bits con Componentes Virtuales
- Memorias
- Entradas y pulsadores
- Señales del sistema. Medición con el LEDOscopio
- Controlador LCD 16x2
- Señales periódicas y temporización
- Buses: Medio compartido
- Memoria Flash SPI
- Conexión de LEDs en la Alhambra II. Placa AP‐LED8‐THT
- Periféricos PMOD
- Fundamentos. Sistema Unario
- Autómatas
- Pantallas de vídeo. Fundamentos. Display de 1x4 LEDs