Ich versuche, über die VGA-Spezifikation den geeigneten Signalausgang für die Verbindung eines AVR ATmega328P-Mikrocontrollers mit einem LCD-Monitor zu erzeugen. Die VGA-Spezifikation, die ich zu erfüllen versuche, ist der Industriestandard 640*480 mit einer Bildwiederholfrequenz von 60 Hz (1).
Der AVR arbeitet mit einer Frequenz von 20 MHz , was kurz vor der VGA-Pixeltaktfrequenz von 25,175 MHz liegt . Nachdem ich ähnliche Projekte online betrachtet hatte (2), glaubte ich jedoch, dass ich durch eine gewisse Manipulation der Anzahl der Uhren pro Region die in der VGA-Spezifikation festgelegten Timings einhalten könnte. Meine angepassten Timings für die vertikale und horizontale Spezifikation finden Sie unten:
Vertikales Timing (Frame)
Sichtbarer Bereich: 15,24 ms, 480 Zeilen (304800 Taktzyklen bei 20 MHz)
Vordere Schwarzschulter: 0,3175 ms, 10 Zeilen (6350 Taktzyklen bei 20 MHz)
Synchronimpuls: 0,0635 ms, 2 Zeilen (1270 Taktzyklen bei 20 MHz)
Hintere Schwarzschulter : 1,04775 ms, 33 Zeilen (20955 Taktzyklen bei 20 MHz)
Vollbild: 16,66875 ms, 525 Zeilen (333375 Zyklen bei 20 MHz)
Horizontales Timing (Linie)
Sichtbarer Bereich: 25,45 us, 508 Pixel (508 Taktzyklen bei 20 MHz)
Vordere Schwarzschulter: 0,65 us, 13 Pixel (13 Taktzyklen bei 20 MHz)
Synchronimpuls: 3,8 us, 76 Pixel (76 Taktzyklen bei 20 MHz)
Hintere Schwarzschulter : 1,9 us, 38 Pixel (38 Taktzyklen bei 20 MHz)
Ganze Zeile: 31,75 us, 635 Pixel (635 Taktzyklen bei 20 MHz)
Basierend auf diesen Timings beträgt die Frame-Aktualisierungsrate 59,9925 Hz . Der sichtbare Bereich auf dem Bildschirm hat eine Auflösung von 508*480 im Gegensatz zu den 640*480 der Spezifikation.
Mir ist bewusst, dass bei einer Taktfrequenz von 20 MHz die Timings nicht exakt eingehalten werden können, aber wenn man meine Timings mit der tatsächlichen Spezifikation (3) vergleicht, liegen die Timings sehr nahe beieinander.
Die Software, die ich geschrieben habe, um diese Ausgaben zu erzeugen, wurde in AVR-Assembler geschrieben, dies ermöglicht mir, die Anzahl der Taktzyklen für jede Region zu zählen, und kann direkt unter diesem Absatz gefunden werden. Die Software gibt auf unbestimmte Zeit einen roten Rahmen auf dem Bildschirm aus.
Ein Bild des Schaltplans für die Hardwareimplementierung finden Sie unter dem Code.
;
; 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
; ****************************************************************************************
Nachdem ich die Hardware- und Softwareimplementierung getestet habe, habe ich festgestellt, dass sie auf der VGA-Schnittstelle meines Fernsehers im Wohnzimmer gut funktioniert, aber auf keinem anderen Monitor oder Fernseher, den ich getestet habe. Ich habe festgestellt, dass es auf einem Monitor eines Freundes irgendwie funktioniert, es gibt einen roten Bildschirm aus, aber mit zufälligen schwarzen Flecken und verliert regelmäßig das Signal. Die anderen Monitore, auf denen ich getestet habe, erkennen zwar die Eingabe, können aber einfach nichts auf dem Bildschirm ausgeben.
Ich glaube, der Grund, warum es auf einigen Displays funktioniert und auf anderen nicht, liegt einfach an den Toleranzen, die die Hersteller in ihren Geräten festgelegt haben.
Ich habe zahlreiche Optimierungen im Code ausprobiert, dabei ging es hauptsächlich darum, die Anzahl der Taktzyklen in jeder Region zu ändern, aber dies führte zu keinem positiven Ergebnis. Dies führt mich zu einer von zwei möglichen Situationen:
1) (HÖCHSTWAHRSCHEINLICH) Ich habe die Software oder Hardware falsch implementiert, was dazu geführt hat, dass die Timings leicht verschoben sind.
2) Es ist sehr schwierig/unmöglich, die VGA-Spezifikation mit konsistenter Leistung auf allen VGA-fähigen Geräten zu implementieren, die einen ATmega328P verwenden, der mit 20 MHz arbeitet.
Aus diesem Grund plane ich, das Atmega-Gerät entweder mit einem 25,175-MHz-Quarz zu übertakten, um sicherzustellen, dass die Timings eingehalten werden können, oder ich werde einen leistungsfähigeren Mikrocontroller mit größerer Rechenleistung verwenden, etwa einen PIC24EP128MC202.
Wenn jemand eine Idee hat, warum meine aktuelle Implementierung nicht funktioniert und wie ich dies beheben könnte, wäre ich sehr dankbar!
Wenn Sie es geschafft haben, bis hierher zu lesen, danke trotzdem! :)
(1)(3) VGA-Signal 640 x 480 bei 60 Hz Industriestandard-Timing – http://tinyvga.com/vga-timing/640x480@60Hz
(2) Lucid Science VGA Video Generator (leider wurde die Seite jetzt geschlossen) https://web.archive.org/web/20141102012544/http://www.lucidscience.com:80/pro-vga%20video%20generator -6.aspx
Erstens sollte es keine Probleme geben, VGA mit einem ATMega328 auszugeben. Sie können VGA ausgeben, das mit allem funktioniert, von alten CRTs, kleinen mysteriösen LCD-Modulen von aliexpress oder einem modernen LCD. Ich habe noch nie wirklich von Kompatibilitätsproblemen mit VGA gehört - insbesondere nicht von Timing-bezogenen.
Es gibt Dutzende von Projekten, die erfolgreich mindestens monochromatische 640x480-VGA (parallele RGB-Zeilen) mit viel weniger als einem ATMega ausgeben, und Sie benötigen nicht einmal einen 20-MHz-Takt - mehrere Projekte kommen mit einem 16-MHz-Takt aus. Und wenn Sie mit weniger als 640 x 480 einverstanden sind, gibt dieses Projekt gültiges VGA mit einem ATTiny15 aus, das mit 1,6 MHz läuft.
Nun, ich habe Ihre Baugruppe nicht doppelt überprüft, daher könnte ich falsch liegen, aber es scheint unwahrscheinlich, dass das Timing auf einem Display gut funktioniert, auf anderen jedoch nicht. Nein, ich denke, das ist einfach ein Problem der Signaldämpfung aufgrund von Impedanzfehlanpassungen.
Die 3 Farblinien erwarten 0 bis 0,7 V. Und wie ich sehe, haben Sie Ihre Widerstände so dimensioniert, dass sie bei einer Impedanz von 75 Ω entweder 0,696 V oder 0,348 V ergeben.
Ich weiß, das scheint wahrscheinlich eine sehr schöne, elegante Lösung zu sein, um Schwarz + zwei Farbtöne zu erhalten, aber ich fürchte, es wird nicht sehr gut funktionieren. Ihre Widerstände sind viel zu groß und dies verursacht einen schlimmen Fall von Impedanzfehlanpassung. Und Sie würden wahrscheinlich genau die Probleme haben, die Sie beschreiben - einige Displays (eine kleine Minderheit, die ich bei so großen Widerständen erwarten würde) könnten das Signal verwenden, die meisten jedoch nicht oder würden Pixel nur zeitweise pro Frame korrekt lesen.
Wenn Sie eine Impedanz von 75 Ω richtig treiben und gleichzeitig eine höhere Spannung auf den richtigen Spannungsbereich reduzieren möchten, müssen Sie ein Impedanzanpassungsnetzwerk wie einen L-Pad-Teiler verwenden.
Die Diskussion der Theorie hinter L-Pad-Teilern würde den Rahmen dieser Frage sprengen, aber Sie würden gut daran tun, Transistoren zu verwenden, um die Spannungspegel richtig einzustellen, ohne die hohe Impedanz eines Teilers zu haben.
Hier, nur um eine Plausibilitätsprüfung durchzuführen und zu sehen, ob meine Vermutung richtig ist, lassen Sie die zweite IO-Leitung an den 3 Farbeingängen fallen. Sie können einen VGA-Eingang mit den 75 Ohm sowieso nicht als Teilnehmer an einem Spannungsteiler korrekt ansteuern, daher müssen Sie diese Idee leider vollständig verwerfen. Sie können mit der Verwendung einer richtigen Leiter im DAC-Stil davonkommen, aber selbst dann sind alle Widerstände eine Größenordnung kleiner als 1K und 2K.
Verwenden Sie einfach einen IO-Pin, und ich habe gerade festgestellt, dass dies wahrscheinlich den Einzelpin-Strom übersteigt ... 50 mA, denke ich? Aber das ideale L-Pad für 75 Ω, das 5 V auf 0,7 V dämpft, wäre ein 64 Ω- und 12 Ω-Widerstand in Reihe mit Masse, wobei das Signal zum Display der Abgriff zwischen den beiden ist.
Die Magie hier ist, dass, wenn Sie meine Rundungsfehler entfernen, dies zu einem Widerstand von 75 Ω führt. Das bedeutet angepasste Impedanz. Dass jedes Ende das Äquivalent von 75 Ω vom Signalstift zur Masse hat.
Grundsätzlich sind die Pins Ihres Atmega nicht stark genug, um eine Impedanz von 75 Ω bei voller Leistung bei Betrieb mit 5 V zu treiben, da dies mehr als 50 mA verbrauchen würde. Verwenden Sie stattdessen einfach eine Leiter mit einem ähnlichen Verhältnis, aber nicht so groß. Versuchen Sie es mit 220 Ω und 56 Ω, und das sollte Ihre Signalprobleme lösen und die Signalspannung auf 0,7 V reduzieren, OHNE eine so große unangepasste Impedanz zu haben, dass die meisten Displays nicht einmal richtig funktionieren.
Werfen Sie einen Blick auf die LTDC-VGA-Timing-Spezifikationen. Bei LCD-TFT-Monitoren ist das Timing sehr ähnlich wie bei Standard-VGA-Monitoren, aber die Signalreihenfolgen sind unterschiedlich.
Filo
Tom Fink
Nazar
Tom Fink
winzig
Tony Stewart EE75
Chris Stratton
Chris Stratton
Tom Fink
Chris Stratton