¿Patrón de programación para generar señal VGA con microcontrolador?

Quiero generar una señal VGA con un microcontrolador (como TI Tiva ARM que funciona a una velocidad de 90/120Mhz).

No estoy seguro de cómo hacer tiempos precisos con microcontrolador. ¿Qué patrón de programación necesito usar?

¿Necesito algún código ensamblador en línea? ¿Cómo usar sabiamente las interrupciones?

Sería genial si alguien mostrara algún pseudocódigo sobre cómo generar una señal VGA.

Generé con éxito la señal VGA con FPGA. Pero simplemente no puedo entender cómo hacerlo con MCU.

Hay bibliotecas VGA para AVR. Microcontrolador diferente, pero puede inspirarte. Es posible que pueda utilizar el controlador DMA para descargar la CPU principal, ya que generar una señal de video requiere bastante CPU sin este tipo de trucos.

Respuestas (2)

Esta respuesta se basa en pantallas VGA estándar, que son 640 x 480, ya que eso fue lo que mencionó en su respuesta. El tiempo para SVGA (super VGA, 800 x 600), XGA (1024 x 768), etc., será diferente. Esta página tiene una buena lista de prácticamente todas las resoluciones en uso. Pero la sincronización de píxeles para estas pantallas más grandes será tan rápida que dudo que puedas generarla usando un micro.

Temporización VGA

Entonces, la pantalla VGA estándar tiene 640 píxeles en horizontal y 480 líneas en vertical. Para generar la temporización, se utiliza un reloj de píxeles de 25,175 MHz. (Teóricamente, esto significa que el micro tendría que ser capaz de generar pulsos de exactamente 39,72 ns de largo, lo que sería bastante difícil, pero parece que puedes arreglártelas con relojes de 25 MHz y 40 ns.

El formato de temporización VGA para cada línea se remonta a los días de las pantallas de computadora CRT y antes de los televisores. Se ve así para una imagen de 640x480 (solo la horizontal que se muestra aquí):

ingrese la descripción de la imagen aquí

Hay dos pulsos de sincronización: uno por línea (sincronización horizontal) y uno por cuadro (sincronización vertical). El pulso de sincronización horizontal está precedido por un intervalo de "porche trasero" y seguido por un intervalo de "porche delantero". Entre estos dos está el video activo (640 píxeles). Sin embargo, la línea total tiene 800 píxeles de ancho.

Del mismo modo, las líneas verticales tienen un pulso de sincronización en la parte inferior, rodeadas por un porche trasero vertical y un porche delantero y tienen una altura de 524 píxeles.

ingrese la descripción de la imagen aquí

Antes, cuando el video se hacía usando CRT en lugar de pantallas LCD, había un haz de electrones que escaneaba la pantalla en busca de cada línea, que luego se movía un poco hacia abajo, escaneaba la siguiente línea, etc. En la parte inferior de la pantalla, tenía que comprimir volver a la cima.

Al final de cada línea, el rayo tiene que volver al comienzo de la siguiente línea; esto lleva un poco de tiempo. Durante este tiempo el video estuvo en blanco. Esto se conoce como el tiempo de actualización horizontal y es la suma de los tiempos del porche trasero + sincronización + porche delantero. Del mismo modo para el período de actualización vertical. Es por eso que hay un total de 800 píxeles en la pantalla y 524 líneas en vertical, aunque solo se ven 640x480.

Entonces, el tiempo se puede especificar en términos de pulsos de reloj:

ingrese la descripción de la imagen aquí

donde HPX son los 640 píxeles, HFP es el intervalo horizontal del porche trasero, HSP es el pulso de sincronización horizontal, HBP es el intervalo horizontal del porche trasero. Lo mismo para la vertical. Esto es lo que necesita usar para su sincronización en el microcontrolador (suponiendo que tenga un reloj de 40 ns).

Entonces, cada línea completa tiene 800 relojes, o 800 x 40 ns = 32 µs.

Tenga en cuenta que el tiempo crítico solo ocurre cuando se escriben píxeles en la pantalla (12 bits cada 40 ns). Mientras escribe los píxeles, no tendrá mucho tiempo para hacer nada más. Pero durante el porche delantero, el pulso de sincronización y el porche trasero tienes 635 ns, 3,81 µs y 1,9 µs respectivamente para hacer otras cosas.

Tenga en cuenta que si no tiene un procesador lo suficientemente rápido para generar píxeles de 40 ns, puede dividir todo por la mitad (esencialmente funcionando a 12,5 MHz o un reloj de 80 ns) y tendrá una resolución de 320 x 240. Por lo que cada píxel se repite dos veces en lo que al monitor se refiere. Todavía piensa que estás enviando VGA.

Salida VGA

Para generar las señales de video para cada píxel, puede crear su propio DAC (convertidor digital a analógico) usando resistencias. Si asigna 4 bits por color (RGB), necesitará 12 resistencias en total, dispuestas de la siguiente manera:

ingrese la descripción de la imagen aquí

Podría arreglárselas con menos bits, por ejemplo, 3 bits por color, o incluso 2, pero la calidad de la imagen no será tan buena.

firmware

Supongamos que tenemos un procesador de 32 bits a 100 MHz (10 ns por instrucción). Suponga también que estamos usando píxeles de 12 bits, dos píxeles almacenados en una palabra de 32 bits (por lo que desafortunadamente desperdiciamos 8 bits).

Antes de comenzar, suponga que r0 está cargado con la dirección de la palabra inicial de 32 bits que contiene píxeles para esta línea, y r1 está cargado con la dirección de un puerto de E/S mapeado en memoria, de los cuales se sacan los 12 bits inferiores D0-D11 , y r2 es un temporal que contiene dos píxeles.

Estoy usando un conjunto de instrucciones similar a RISC inventado, pero debería ser bastante obvio lo que está sucediendo. Suponga que cada instrucción toma un ciclo, o 10 ns.

ld r2,[r0]      ; load indirect through register r0, 32 bits (2 pixels)
andi r2,0xFFF   ; and immediate, get lower 12 bits
st r2,[r1]      ; store pixel to I/O port
ld r2,[r0]      ; get pixel again
rsh r2,16       ; right shift 16 bits to get upper pixel
andi r2,0xFFF   ; and immediate, get lower 12 bits
st r2,[r1]      ; store pixel to I/O port
addi r0,4       ; increment memory address (assume byte addressing)   

Si el procesador es más rápido que 100 MHz, deberá agregar nops para que toda la secuencia aún requiera 80 ns para dos píxeles.

Repite esta secuencia de ocho instrucciones 320 veces en línea. Al final, configure una interrupción para 635 ns en el futuro (final del porche trasero) y regrese del nivel de interrupción (ingresado cuando comenzó a enviarle píxeles). Mientras tanto, tiene 63 instrucciones gratis para el nivel base.

En la interrupción (final del porche trasero), genere el comienzo del pulso de sincronización horizontal (baja) y configure otra interrupción esta vez 3.81 µs en el futuro (final de la sincronización horizontal) y salga de la interrupción. Puede ejecutar alrededor de 380 instrucciones esta vez.

En la interrupción (final de la sincronización horizontal), complete la sincronización horizontal (vuelve a lo alto), establezca una interrupción de 1,9 µs en el futuro (final del porche delantero) y regrese de la interrupción. 190 o más instrucciones disponibles.

En la interrupción final (final del porche delantero), comience a generar datos de píxeles nuevamente (todo en la rutina de interrupción).

Código similar para manejar el final del cuadro (sincronización vertical, etc.).

Excepto por el tiempo de retroceso horizontal, todo esto es solo para copiar los píxeles de la RAM a la pantalla. Habría 32 µs * 44 líneas adicionales o 1,408 ms durante el retroceso vertical disponible, o 140.800 instrucciones para otras cosas. Pero esto, sumado a las instrucciones adicionales disponibles durante el retroceso horizontal (304 000), aún no sería lo suficientemente rápido como para generar un cuadro completo de video para la próxima vez con la resolución completa de 640x480. Realmente necesitaría al menos un procesador de 200 MHz para generar video y copiarlo a la salida VGA.

No es de extrañar que las PC vengan con hardware de gráficos especializado desde el principio que escribe el contenido de una RAM de video en un puerto de video (VGA o lo que sea) sin la ayuda del procesador principal.

De hecho, información muy útil. Pero puede dar más detalles sobre el lado del software. ¿Necesito un temporizador con una frecuencia de 25 Mhz? Gracias
@kesrut Sí. Vas a necesitar un procesador muy rápido. Un procesador de 200 MHz sería de 5 ns por instrucción (suponiendo un ciclo/instrucción) u 8 instrucciones por píxel de reloj. No mucho tiempo. Tenga en cuenta que el Parallax Propeller multinúcleo tiene temporizadores especiales para que pueda generar una salida VGA en un núcleo, mientras que tiene 7 núcleos más para otras cosas. Es posible que desee considerar eso como un coprocesador. Aquí tienes una demo y otra .
@kesrut En realidad, un procesador de 100 MHz (o incluso 76 MHz) servirá; ver la actualización de mi respuesta.
La gente lo ha hecho con microcontroladores atmel overclockeados: tinyvga.com/avr-vga
No hay forma de que hagas esto con un temporizador, estás en el reino de escribir un ensamblador de ciclo contado y tu procesador pasará la mayor parte de su tiempo trabajando en actualizar la pantalla. Puede hacer que su procesador vaya y haga otras cosas durante las partes de la forma de onda de la pantalla donde no hay nada en la pantalla y/o partes de la pantalla donde hay una gran sección de color sólido.
@ pjc50 Ninguno de los proyectos AVR que he visto está haciendo VGA verdadero. Como explico en mi respuesta, puede duplicar el tiempo de cada píxel durante el video activo, con la mitad de la resolución (320x240). El proyecto al que se vinculó muestra solo veinte caracteres de 8x12 en cada línea. Con el mismo tiempo de sincronización, el monitor todavía piensa que está obteniendo una imagen VGA.
@PeterGreen De acuerdo, lo explico bastante bien en mi respuesta en la sección Firmware. Actualizar el video activo requiere un código de 3 o 4 instrucciones por píxel, por lo que un total de 1920 - 2560 líneas en línea de código ensamblador dependiendo del reloj del procesador (76 o 100 MHz). Otra actividad puede tener lugar durante el porche delantero, el período de sincronización y los períodos del porche trasero, un total de 5,71 µs por línea. Si tuviera un procesador de 200 MHz (8 instrucciones por píxel), podría usar un bucle y no necesitaría dos mil líneas de código en línea. Pero no se permiten interrupciones (en realidad, esto ocurre dentro de un ISR).

Algunos proyectos utilizan un puerto i2s o spi para impulsar cada color. El tiva C tiene un puerto paralelo con dma/fifo para transmitir los datos de píxeles.

Bienvenido a EE.SE. Las mayúsculas adecuadas ayudarían a que su publicación sea más legible. Es bastante corto en detalles.
¡Usar periféricos en el chip para realizar la sincronización precisa es mucho mejor que usar el núcleo de la CPU! De todos modos, no se permite que las preguntas en un sitio de SE soliciten el código real para implementar algo; más bien, el punto es proporcionar el método basado en el cual alguien puede ir y escribir código, y esto de hecho apunta en una dirección prometedora.