¿Por qué no funciona mi implementación de VGA usando un microcontrolador AVR?

Problema

Estoy tratando de generar la salida de señal adecuada para conectar un microcontrolador AVR ATmega328P con un monitor LCD, a través de la especificación VGA. La especificación VGA que estoy tratando de cumplir es el estándar de la industria 640*480 con una frecuencia de actualización de cuadros de 60 Hz (1).

Información de contexto

El AVR está funcionando a una frecuencia de 20 MHz , que es inferior a la frecuencia de reloj de píxeles VGA de 25,175 MHz . Sin embargo, creí, después de haber considerado proyectos similares en línea (2), que a través de alguna manipulación de la cantidad de relojes por región, podría cumplir con los tiempos estipulados en la especificación VGA. Mis tiempos ajustados para la especificación vertical y horizontal se pueden encontrar a continuación:

Temporización vertical (fotograma)

Área visible: 15,24 ms, 480 líneas (304800 ciclos de reloj @ 20 MHz)
Porche delantero: 0,3175 ms, 10 líneas (6350 ciclos de reloj @ 20 MHz)
Pulso de sincronización: 0,0635 ms, 2 líneas (1270 ciclos de reloj @ 20 MHz)
Porche trasero : 1,04775 ms, 33 líneas (20955 ciclos clk a 20 MHz)
Cuadro completo: 16,66875 ms, 525 líneas (333375 ciclos a 20 MHz)

Temporización horizontal (línea)

Área visible: 25,45 us, 508 píxeles (508 ciclos clk a 20 MHz)
Porche delantero: 0,65 us, 13 píxeles (13 ciclos clk a 20 MHz)
Pulso de sincronización: 3,8 us, 76 píxeles (76 ciclos clk a 20 MHz)
Porche trasero : 1,9 us, 38 píxeles (38 ciclos clk a 20 MHz)
Toda la línea: 31,75 us, 635 píxeles (635 ciclos clk a 20 MHz)

Según estos tiempos, la frecuencia de actualización de fotogramas es de 59,9925 Hz . La región visible en pantalla tiene una resolución de 508*480 en contraste con los 640*480 de la especificación.

Soy consciente de que a una frecuencia de reloj de 20 MHz, los tiempos no se pueden cumplir con precisión, pero si compara mis tiempos con la especificación real (3), los tiempos son muy parecidos.
El software que escribí para generar estas salidas ha sido escrito en ensamblador AVR, esto me permite contar el número de ciclos de reloj para cada región, y se puede encontrar justo debajo de este párrafo. El software mostrará indefinidamente un cuadro rojo en la pantalla.
Una imagen del esquema para la implementación del hardware se puede encontrar debajo del código.

;
; VGA_INTERFACE.asm
;
; Outputs the required signals with the correct timings for VGA output to a monitor.
;
;
;
; Created: 13/02/2018 
; Author : Tom
;









; COMPILER SETTINGS
.INCLUDE "M328pDEF.INC"


; INTERRUPT VECTORS
.org 0      ; defines absolute address for interrupt vector




; ****************************************************************************************
; **** IO PORT D SETUP
; ****************************************************************************************

    ; ddrd pin I/O direction configured

    sbi ddrd,0  ; RED BIT 0             
    sbi ddrd,1  ; RED BIT 1             
    sbi ddrd,2  ; GRN BIT 0
    sbi ddrd,3  ; GRN BIT 1
    sbi ddrd,4  ; BLU BIT 0
    sbi ddrd,5  ; BLU BIT 1
    sbi ddrd,6  ; HORIZONTAL SYNC       
    sbi ddrd,7  ; VERTICAL SYNC          



    ldi r20, 0xC0
    out portd, r20      ; clears the RGB bits and sets the sync pulses high





; ****************************************************************************************
; **** STARTUP SEQUENCE
; ****************************************************************************************

; INITIALIZE STACK POINTER

    ldi r16,low(ramend)     ; loads the lower byte of top stack address into register 16 
    out spl,r16             ; stack pointer lower byte is set to lower byte of the top 
                            ; stack address stored in register 16

    ldi r16,high(ramend)    ; loads the upper byte of top stack address into register 16
    out sph,r16             ; stack pointer upper byte is set to upper byte of the top 
                            ; stack address stored in register 16






; main program loop
main:



V_LOOP:

; ****************************************************************************************
; **** VERTICAL LOOP - BEGIN
; ****************************************************************************************


; **** V-SYNC DRIVE LOW (2 lines, 1,270 cycles)




    cbi portd,7     ;2 drives v-sync active low 


; ========================================================================================
    ; Delay 1268 cycles
    ldi  r18, 2
    ldi  r19, 165
L1: dec  r19
    brne L1
    dec  r18
    brne L1
; ========================================================================================


    sbi portd,7     ;2 drives v-sync high 









; **** VERTICAL BACK PORCH (33 lines, 20955 cycles)

    ; **NOTE: Only 20951 cycles required to be wasted as 4 cycles are used by Horizontal 
    ; loop. 2 are used when setting max loop value in r16 and r17. A further 2 are used 
    ; setting horizontal sync active low.


; ========================================================================================
    ; Delay 20951 cycles
    ldi  r18, 28
    ldi  r19, 52
L2: dec  r19
    brne L2
    dec  r18
    brne L2
    rjmp PC+1
; ========================================================================================









; ****************************************************************************************
; **** HORIZONTAL LOOP - BEGIN (LOOPS 480 times)
; ****************************************************************************************


    ldi r16,low(480)        ;1 holds LSB of loop value
    ldi r17,high(480)       ;1 hold MSB of loop value




H_LOOP:


; **** H-SYNC DRIVE LOW (76 cycles)


    cbi portd,6     ;2 drives h-sync active low 

; ========================================================================================
    ; Delay 74 cycles
    ldi  r18, 24
L3: dec  r18
    brne L3
    rjmp PC+1
; ========================================================================================


    sbi portd,6     ;2 drives h-sync high









; **** HORIZONTAL BACK PORCH (38 cycles)


    ; **NOTE: Only 36 cycles required to be wasted as 2 cycles are used by RGB for setting
    ; the red bit 0 high.



; ========================================================================================
    ; Delay 36 cycles
    ldi  r18, 12
L4: dec  r18
    brne L4
; ========================================================================================









; **** RGB (508 cycles)

    ldi r20, 0xC1       ;1
    out portd, r20      ;1 sets red bit 0 high, all other RGB low, sync pulses high 


; ========================================================================================  
    ; Delay 506 cycles
    ldi  r18, 168
L5: dec  r18
    brne L5
    rjmp PC+1
; ========================================================================================


    ldi r20, 0xC0       ;1
    out portd, r20      ;1 sets the RGB outputs low, sync pulses high 









; **** HORIZONTAL FRONT PORCH (13 cycles)


    ; **NOTE: Only 5 cycles required to be wasted as 8 cycles are used up already. 4 are    
    ; are used for subtracting one from the loop counter. A further 4 are used for 
    ; jumping to start of horizontal loop and setting the Horizontal sync active low.


; ========================================================================================
    ; Delay 5 cycles
    lpm
    rjmp PC+1
; ========================================================================================


    ldi r18, low(1)     ;1
    ldi r19, high(1)    ;1


    sub r16,r18         ;1
    sbc r17,r19         ;1  


    brne H_LOOP     ; 2 cycles if true, 1 if false  









; ****************************************************************************************
; **** HORIZONTAL LOOP - END
; ****************************************************************************************











; **** VERTICAL FRONT PORCH (10 lines, 6350 cycles)

    ; **NOTE: Only 10 cycles have been used up for the Horizontal front porch, as a result a  
    ; further 3 must be added to the vertical front porch. 
    ; However 4 cycles are already being used, 2 to jump to start of vertical loop and a 
    ; further 2 to drive horizontal sync active low.
    ; As a result taking these two factors into account, the delay needs to be 6350+3-4 =
    ; 6349  cycles long.


; ========================================================================================
    ; Delay 6349 cycles
    ldi  r18, 9
    ldi  r19, 62
L6: dec  r19
    brne L6
    dec  r18
    brne L6
; ========================================================================================


    rjmp V_LOOP     ;2  relative jump to start of vertical loop









; ****************************************************************************************
; **** VERTICAL LOOP - END
; ****************************************************************************************

Implementación de hardware ATmega328P

Resultados

Después de probar la implementación del hardware y el software, descubrí que funciona bien en la interfaz VGA del televisor de mi sala de estar, pero no funciona en ningún otro monitor o televisor que haya probado. Descubrí que funciona en el monitor de un amigo, mostrará una pantalla roja pero con parches negros aleatorios y perderá señal periódicamente. Los otros monitores en los que he probado detectan la entrada, pero simplemente no pueden mostrar nada en la pantalla.

Creo que la razón por la que funciona en algunas pantallas y no en otras se debe simplemente a las tolerancias que los fabricantes han estipulado en sus dispositivos.

Posibles soluciones

Probé numerosos ajustes en el código, esto implicó principalmente cambiar la cantidad de ciclos de reloj en cada región, pero esto no arrojó ningún resultado positivo. Esto me lleva a creer una de dos situaciones posibles:

1) (MÁS PROBABLE) He implementado el software o el hardware incorrectamente, lo que ha provocado que los tiempos estén ligeramente fuera de lugar.

2) Es muy complicado/imposible implementar la especificación VGA con un rendimiento constante en todos los dispositivos compatibles con VGA que utilicen un ATmega328P que funcione a 20 MHz.

Como resultado de esto, planeo overclockear el dispositivo Atmega con un cristal de 25,175 MHz para garantizar que se cumplan los tiempos o voy a usar un microcontrolador más capaz con mayor potencia de procesamiento, algo así como un PIC24EP128MC202.

Si alguien tiene alguna idea de por qué mi implementación actual no funciona y cómo podría rectificar esto, ¡se lo agradecería mucho!

Si has logrado leer hasta aquí, ¡gracias de todos modos! :)

Referencias

(1)(3) Señal VGA 640 x 480 a 60 Hz Temporización estándar de la industria - http://tinyvga.com/vga-timing/640x480@60Hz

(2) Lucid Science VGA Video Generator (lamentablemente, el sitio se cerró ahora) https://web.archive.org/web/20141102012544/http://www.lucidscience.com:80/pro-vga%20video%20generator -6.aspx

¿Tienes un osciloscopio?
He probado usando un analizador lógico, sin embargo, tengo la intención de probar con un osciloscopio.
Recuerdo que la parte del receptor de la lógica del monitor verifica si las señales de tiempo están dentro de las desviaciones aceptables. ¿Puede verificar que sus señales se ajusten a las especificaciones del monitor?
Después de mirar las hojas de especificaciones de los monitores, no puedo encontrar detalles específicos sobre los tiempos aceptados por el monitor. Simplemente dicen 640*480 a 60 Hz (59,94 Hz).
Esto me viene a la mente: linusakesson.net/scene/craft
¿Puedes ir a 50Hz en su lugar? Eso funciona para una pantalla LCD. Luego, su reloj de píxeles se puede reducir sin comprometer el tamaño del porche.
La premisa fundamental de usar un reloj de punto más bajo siempre que mantenga los tiempos horizontales y verticales es válida para un estándar de video analógico. Un analizador lógico debería ser suficiente para verificar el tiempo, aunque también debe considerar si los voltajes de la señal están dentro del rango.
Considere que las entradas VGA a menudo tienen una impedancia en el rango de 75 ohmios. Si estos tienen una terminación resistiva, sus resistencias de la serie 1K y 2K producirán niveles muy bajos.
Los voltajes están bien según mis cálculos, teniendo en cuenta la resistencia de 75 ohmios, el voltaje más bajo será de aproximadamente 0,3 V, que está dentro del rango de voltaje de 0 y 0,7 V y, como digo, la salida está bien en uno de los monitores. probado en
El punto es que los monitores varían en qué nivel aceptarán. Es probable que sus señales de sincronización en particular no se balanceen lo suficiente.

Respuestas (2)

En primer lugar, no debería haber ningún problema con la salida de VGA usando un ATMega328. Puede generar VGA que funcione con todo, desde CRT antiguos, pequeños módulos LCD misteriosos de aliexpress o un LCD moderno. Realmente nunca he oído hablar de problemas de compatibilidad con VGA, especialmente nada relacionado con el tiempo.

Hay docenas de proyectos que generan con éxito al menos un VGA monocromático de 640x480 (en paralelo con las líneas RGB) usando mucho menos que un ATMega, y ni siquiera necesita un reloj de 20 MHz; varios proyectos se manejan con un reloj de 16 MHz. Y si está de acuerdo con menos de 640x480, este proyecto genera una salida VGA válida utilizando un ATTiny15 que se ejecuta a 1,6 MHz.

Ahora, no he verificado dos veces su ensamblaje, por lo que podría estar equivocado, pero parece poco probable que la sincronización funcione bien en una pantalla pero no en otras. No, creo que esto es simplemente un problema de atenuación de la señal debido a la falta de coincidencia de impedancia.

Las 3 líneas de color esperan de 0 a 0.7V. Y veo que ha dimensionado sus resistencias de tal manera que, con una impedancia de 75 Ω, dará como resultado 0,696 V o 0,348 V.

Sé que probablemente parezca una solución muy agradable y elegante para obtener negro + dos tonos, pero me temo que no funcionará muy bien. Sus resistencias son demasiado grandes y esto está causando un mal caso de desajuste de impedancia. Y es probable que experimente los problemas exactos que describe: algunas pantallas (una pequeña minoría que esperaría con resistencias tan grandes) podrían usar la señal, pero la mayoría no lo haría, o solo leería correctamente los píxeles de forma intermitente por cada cuadro.

Si desea conducir correctamente una impedancia de 75 Ω mientras reduce un voltaje más alto al rango de voltaje correcto, debe usar una red de adaptación de impedancia, como un divisor L-pad.

Discutir la teoría detrás de los divisores L-pad está más allá del alcance de esta pregunta, pero haría bien en usar transistores para establecer correctamente los niveles de voltaje sin tener la alta impedancia de un divisor.

Aquí, solo para hacer una verificación de cordura y ver si mi suposición es correcta, deshazte de la segunda línea IO en las entradas de 3 colores. De todos modos, no puede manejar correctamente una entrada VGA usando los 75 ohmios como participante en un divisor de voltaje, por lo que me temo que tendrá que deshacerse de esa idea por completo. Puede salirse con la suya usando una escalera de estilo DAC adecuada, pero incluso entonces, todas las resistencias serán un orden de magnitud más pequeñas que 1K y 2K.

Solo use un pin IO, y me acabo de dar cuenta de que esto probablemente excede la corriente de un solo pin ... ¿50 mA, creo? Pero el l-pad ideal para 75 Ω que atenúa de 5 V a 0,7 V sería una resistencia de 64 Ω y 12 Ω en serie a tierra, con la señal que sale a la pantalla como la derivación entre los dos.

La magia aquí es que, si elimina mis errores de redondeo, esto da como resultado una resistencia de 75 Ω. Eso es lo que significa la impedancia adaptada. Que cada extremo tiene el equivalente a 75Ω desde el pin de señal a tierra.

Básicamente, los pines de su atmega no son lo suficientemente fuertes como para generar una impedancia de 75 Ω a plena potencia cuando se ejecuta desde 5 V, ya que consumiría más de 50 mA. En su lugar, use una escalera con una proporción similar, pero no tan grande. Pruebe con 220 Ω y 56 Ω, y eso debería resolver sus problemas de señal y reducir el voltaje de la señal a 0,7 V SIN tener una impedancia tan inigualable que la mayoría de las pantallas ni siquiera funcionen correctamente.

No precisamente. Los reflejos de la señal por desajuste de impedancia no son el problema. Los niveles pueden ser inadecuados, pero los reflejos en las líneas de color solo causarán imágenes fantasma, que no es lo que se observa. Mire los circuitos que se usan para esto, y verá que el problema aquí no es la falta de un atenuador de almohadilla, sino simplemente tener una resistencia de fuente en serie demasiado alta. Lo más probable es que el problema real sea con las señales de sincronización, no con los colores.
Hola, si tienes razón Cris. Fue debido a las señales de sincronización. Las impedancias estaban bien, no hubo problemas con los tamaños de resistencia. Todo se redujo al hecho de que cada nueva línea de escaneo requiere un pulso de sincronización horizontal, incluso en las regiones del porche. Tal vez solo soy yo, pero según la documentación que leí, esto no estaba claro en la especificación VGA. Habiendo hecho este cambio, funciona perfectamente en todos los monitores que he probado. ¡Simplemente sucedió que a un televisor en el que probé no parecía importarle si el H-Sync no estaba en todas las líneas!

Eche un vistazo a las especificaciones de tiempo LTDC VGA. En los monitores LCD TFT, la temporización es muy similar a la de los monitores VGA estándar, pero el orden de las señales es diferente.