Problema
Estoy intentando 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 trato 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 fondo
El AVR funciona a una frecuencia de 20 MHz , que es inferior a la frecuencia del reloj de píxeles VGA de 25.175 MHz . Sin embargo, creía, habiendo considerado proyectos similares en línea (2), que a través de una manipulación del número de relojes por región, podía cumplir con los tiempos estipulados en la especificación VGA. Los horarios ajustados tanto para la especificación vertical como para la horizontal se pueden encontrar a continuación:
Cronometraje vertical (cuadro)
Área visible: 15.24 ms, 480 líneas (304800 clk ciclos a 20 MHz)
Porche delantero: 0,3175 ms, 10 líneas (6350 clk ciclos a 20 MHz)
Pulso de sincronización: 0.0635 ms, 2 líneas (1270 clk ciclos a 20 MHz)
Porche trasero: 1.04775 ms, 33 líneas (20955 ciclos de 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 clk ciclos a 20 MHz)
Porche delantero: 0.65 us, 13 píxeles (13 clk ciclos a 20 MHz)
Pulso de sincronización: 3.8 us, 76 píxeles (76 ciclos de clk a 20 MHz)
Porche trasero: 1.9 us, 38 píxeles (38 clk ciclos a 20 MHz)
Línea completa: 31.75 us, 635 píxeles (635 ciclos de clk a 20 MHz)
Según estos tiempos, la frecuencia de actualización del marco es 59.9925 Hz . La región visible en la 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 comparas mis tiempos con la especificación real (3), los tiempos están muy cerca.
El software que escribí para generar estas salidas se ha escrito en el 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 emitirá indefinidamente un marco 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
; ****************************************************************************************
Resultados
Despuésdehaberprobadolaimplementacióndehardwareysoftware,encontréquefuncionabienenlainterfazVGAdemitelevisordelasaladeestar,peronofuncionaenningúnotromonitorotelevisorquehayaprobado.Descubríquefuncionaenunmonitordeamigos,mostraráunapantallarojaperoconparchesnegrosalazaryperderálaseñalperiódicamente.Losotrosmonitoresqueheprobadodetectanlaentrada,perosimplementenopuedenmostrarnadaenlapantalla.
Creoquelarazónporlaquefuncionaenalgunaspantallasynoenotras,essimplementedebidoalastoleranciasquelosfabricanteshanestipuladoensusdispositivos.
Solucionespotenciales
Heintentadonumerososajustesenelcódigo,estoimplicóprincipalmentecambiarelnúmerodeciclosderelojencadaregión,peroestonoprodujoningúnresultadopositivo.Estomellevaacreerenunadedossituacionesposibles:
1)(MÁSFÁCILMENTE)Heimplementadoelsoftwareohardwareincorrectamente,loquehacequelostiemposesténligeramentefueradetiempo.
2)Esmuydifícil/imposibleimplementarlaespecificaciónVGAconunrendimientoconstanteentodoslosdispositivosconcapacidadVGAqueutilizanelATmega328Pquefuncionaa20MHz.
Comoresultadodeesto,estoyplaneandooverclockeareldispositivoAtmegaconuncristalde25.175MHzparaasegurarquesepuedancumplirlostiemposovoyausarunmicrocontroladormáscapazconmayorcapacidaddeprocesamiento,algocomounPIC24EP128MC202.
Sialguientienealgunaideadeporquémiimplementaciónactualnofuncionaycómopodríarectificaresto,¡seríamuyapreciado!
Sihaslogradoleerhastaaquí,graciasdetodosmodos!:)
Referencias
(1)(3)SeñalVGA640x480@60HzTemporizaciónestándardelaindustria-
(2) Lucid Science VGA Video Generator (desafortunadamente el sitio ya se cerró) enlace