Forum: Mikrocontroller und Digitale Elektronik ATMega2560 und VGA-Verbindung


von Ano N. (anonym1)


Lesenswert?

Guten Tag,

wir müssen als Semesterprojekt mit einem ATMega2560 eine VGA-Verbindung 
aufbauen und Bilder oder Text erstmal übertragen lassen. Es handelt sich 
um einen Informatik Studiengang und Elektronik ist eher am Rande 
abgedeckt. Deshalb entschuldigt bitte meine rudimentären Kenntnisse. Wir 
sitzen bereits seit vielen vielen Stunden an dem Projekt. Momentan sind 
wir noch an dem Punkt, dass wir das Horizontale und Vertikale Signal per 
C-Code nachstellen und mit dem Oszillator nachprüfen. Beim Einstellen 
des Timers haperts vermutlich. Wir haben ein paar wenige Ansätze im 
Internet gefunden, doch konnten diese bisher nicht korrekt abändern um 
das richtige Signal zu produzieren.

Beim vertikalen Signal brauchen wir ca. 64µs eine 0 und dann 16.67ms 
eine 1. Und dann wieder von vorne.

http://prntscr.com/f6j7ju

Das bekommen wir schon nicht hin. Eventuell liegts an der Timer 
Konfiguration. Aus einem ähnlichen Projekt mit einem Arduino entnehmen 
wir, dass wir den Timer Mode 15 brauchen. Aber damit klappt es auch 
nicht so richtig.

Habt ihr Vorschläge zur Timer Konfiguration?
Über jegliche Hinweise wäre ich sehr dankbar.

von Ralf J. (cosmicos)


Lesenswert?


von Ano N. (anonym1)


Lesenswert?

Ja genau, daher stammt auch mein Screenshot. Das haben wir schon 
reichlich gelesen.

von Ralf J. (cosmicos)


Lesenswert?

Ein konkreteres Beispiel findet ihr sogar hier im Forum. Inkl. Code und 
Schaltplan...

Beitrag "AVR VGA Terminal"

Habt ihr das auch schon angeschaut?

von Ralf J. (cosmicos)


Lesenswert?

Und hier noch ein Beispiel für Arduino:

https://forum.arduino.cc/index.php?topic=102181.0

Vielleicht ein bisschen mehr Mühe geben beim googeln ;-)

von Ano N. (anonym1)


Lesenswert?

Ralf J. schrieb:
> Ein konkreteres Beispiel findet ihr sogar hier im Forum. Inkl. Code und
> Schaltplan...
>
> Beitrag "AVR VGA Terminal"
>
> Habt ihr das auch schon angeschaut?

Ja, das haben wir uns schon angeschaut. War leider auch nur bedingt 
hilfreich. Ich schreib gleich nochmal einen Nachtrag, bei dem das 
Problem hoffentlich klarer wird.

von Ano N. (anonym1)


Lesenswert?

Die Generierung des vertikalen Signales ist mir bereits gelungen.
Dafür habe ich bei einem MCU Takt von 16Mhz den Prescaler 1024 
verwendet, sodass jeder Takt 64µs andauert. Die Erste Flanke hat somit 
eine Länge von einem Takt und die zweite Flanke eine Länge von 260 
Takten (16,64ms).
Wir gehen nach diesen Informationen: 
https://www.uni-koblenz.de/~physik/informatik/ECC/vga.pdf

Vertikal Synch:
http://prntscr.com/f6j7ju


Nun versuche ich mit einem Prescaler von 8 dasselbe Ergebnis für das 
horizontale Signal zu erreichen.
Rechnung:
1 / 16 000 000 = 62,5ns
62,5ns * 8 = 0,5µs

Siehe hier: http://prntscr.com/f6ls1d


Leider werden mir die 0,5µs/Takt nicht am Oszilloskop angezeigt. Ich 
bekomme nun bei einem Timer im CTC Mode eine Flankenlänge von 2,2µs 
angezeigt.
Ich habe versucht den Timer Mode zu wechseln, bislang leider ohne 
Erfolg. Keiner bringt den errechnetet Wert heraus.

0    32,8ms
1    256µs
2    512µs
3    1024ms
4    2,2µs
5    128µs
6    256µs
7    512µs
8    2,2µs
9    2,2µs
10    2,2µs
11    2,2µs
12    2,2µs
13    2,2µs
14    2,2µs
15    2,2µs


1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
volatile int v_sync_timer = 0;
6
volatile int h_sync_timer = 0;
7
8
int main( void )
9
{
10
  
11
  DDRL=0xff;
12
  DDRA=0xff;
13
  
14
  PORTL = 0xFF;
15
  PORTA = 0xFF;
16
  // initialize Timer1
17
  cli();          // disable global interrupts
18
  TCCR1A = 0;     // set entire TCCR1A register to 0
19
  TCCR1B = 0;     // same for TCCR1B
20
  
21
  // set compare match register to desired timer count:
22
  OCR1A = 0;
23
  // turn on CTC mode:
24
  TCCR1B |= (1 << WGM12);
25
  // Set CS10 and CS12 bits for 1024 prescaler:
26
  TCCR1B |= (1 << CS10);
27
  TCCR1B |= (1 << CS12);
28
  //TCCR1B |= (1 << CS11);
29
  // enable timer compare interrupt:
30
  TIMSK1 |= (1 << OCIE1A);
31
  
32
  
33
  
34
  TCCR3A = 0;     // set entire TCCR1A register to 0
35
  TCCR3B = 0;     // same for TCCR1B
36
    
37
  // set compare match register to desired timer count:
38
  OCR3A = 0;
39
  // turn on CTC mode:
40
  TCCR3B |= (1 << WGM32);
41
  
42
  // Set Prescaler to 8
43
  TCCR3B |= (1 << CS31);
44
  //TCCR3B |= (1 << CS32);
45
  
46
  // enable timer compare interrupt:
47
  TIMSK3 |= (1 << OCIE3A);
48
  
49
  
50
  sei();          // enable global interrupts
51
  
52
  
53
  while( 1 );
54
}
55
56
57
ISR(TIMER1_COMPA_vect)
58
{
59
  //PORTL ^= 0xFF;
60
61
  if (v_sync_timer == 0){
62
    PORTL ^= 0xFF;
63
    v_sync_timer = v_sync_timer + 1;
64
  }else if (v_sync_timer == 259) {
65
    PORTL ^= 0xFF;
66
    v_sync_timer = 0;
67
  } else {
68
    v_sync_timer = v_sync_timer + 1;
69
  }
70
}
71
72
ISR(TIMER3_COMPA_vect)
73
{
74
  PORTA ^= 0xFF;
75
/*
76
  if (h_sync_timer == 0){
77
    PORTA ^= 0xFF;
78
    h_sync_timer = h_sync_timer + 1;
79
    }else if (h_sync_timer == 63) {
80
    PORTA ^= 0xFF;
81
    h_sync_timer = 0;
82
    } else {
83
    h_sync_timer = h_sync_timer + 1;
84
  }
85
  */
86
}

von Ano N. (anonym1)


Lesenswert?

Ralf J. schrieb:
> Und hier noch ein Beispiel für Arduino:
>
> https://forum.arduino.cc/index.php?topic=102181.0
>
> Vielleicht ein bisschen mehr Mühe geben beim googeln ;-)

Das haben wir auch versucht nachzubauen :) Siehe Nachtrag, da wird das 
Problem hoffentlich klarer.

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?


von Stefan F. (Gast)


Lesenswert?

Hast du bedacht, dass Timer Interrupts niemals "sofort" aufgerufen 
werden, sondern mal mehr und mal weniger Takte verzögert werden?

Ich bin ziemlich sicher, dass Interruptroutinen nicht geeignet sind, so 
kurze Zeiten hinreichend genau zu verarbeiten.

Wo hast du eigentlich gemessen?
Und wie hast du Dir denn gedacht, dass die Timer funktionieren sollen?

Beschreibe mal, wie dein programm funktionieren SOLL, dann vergleichen 
wir das mit dienem Code und zeigen Dir, was du ändern musst. Momentan 
hast du uns nämlich lediglich ein Ratespiel präsentiert. Das Programm 
funktioniert nicht und ich kann auch nicht wirklich erkennen, was du den 
vorhattest, zu tun.

Vergiss nicht, zu beschreiben, wie die die I/O Pins belegt hast.

von c-hater (Gast)


Lesenswert?

Stefan U. schrieb:

> Hast du bedacht, dass Timer Interrupts niemals "sofort" aufgerufen
> werden, sondern mal mehr und mal weniger Takte verzögert werden?

Hat er garantiert nicht. An dem Code ist doch rein garnichts selbst 
erdacht, es handelt sich um C&P-Code, an dem dann völlig ohne Sinn und 
Verstand ein wenig rumgestümpert wurde.

> Ich bin ziemlich sicher, dass Interruptroutinen nicht geeignet sind, so
> kurze Zeiten hinreichend genau zu verarbeiten.

Sagen wir mal so: zumindest der Typ kriegt das ziemlich sicher nicht 
gebacken, aber möglich ist es natürlich durchaus.

Es ist sogar ziemlich sinnvoll, denn es eröffnet die Möglichkeit, sehr 
leicht nutzbare zusätzliche Rechenzeit für den Teil des Gesamtsystems 
bereitzustellen, der mit der Imagegenerierung nichts zu tun hat. Und das 
besonders Schicke an dieser zusätzlichen Rechenzeit ist, dass sie 
relativ häufig verfügbar ist, nämlich einmal pro Scanline (wenn auch 
natürlich nur in entsprechend kleinen Stücken). Es reicht aber immerhin, 
um in main() noch relativ zeitnah auf äußere Ereignisse reagieren zu 
können und erspart so manchen konkurrierenden Interrupt, der sehr viel 
störendere Auswirkungen auf die Imagegenerierung hätte...

Wenn es also nicht nur darum geht, VGA-kompatible Synchronsignale zu 
generieren, sondern am Ende in die Frames auch selber Bildinhalte zu 
packen, ist es wohl i.d.R. die beste Variante, dies mit einem 
Scanline-Interupt zu machen. Durch Vergleich mit dem Wert des Timers, 
der ihn anstößt, kann man die variable Interruptlatenz kompensieren, 
dann Bildinhalte raus, dann HSYNC raus, Verwaltung des Vertikalkrams und 
Ende. Der Rest der Zeilenzeit, also ein großer Teil des HBLANK steht 
main() als Rechenzeit zur Verfügung.

Und, wie immer bei zeitkritischen Anwendungen, ist das alles in 
Assembler sehr viel einfacher umzusetzen, weil man die 
"Taktverbrauchsanzeige" quasi live im Quelltext hat und zumindest ein 
Teil (Kompensation der variablen Latenz) auch bei einer C-Lösung sowieso 
zwingend in Assembler zu programmieren wären...

von Ano N. (anonym1)


Lesenswert?

Stefan U. schrieb:
> Hast du bedacht, dass Timer Interrupts niemals "sofort" aufgerufen
> werden, sondern mal mehr und mal weniger Takte verzögert werden?

Macht sich das erst bei einer höheren Frequenz bemerkbar?
Das vertikale Signal wird mir ziemlich sauber angezeigt.


> Ich bin ziemlich sicher, dass Interruptroutinen nicht geeignet sind, so
> kurze Zeiten hinreichend genau zu verarbeiten.
>
> Wo hast du eigentlich gemessen?
> Und wie hast du Dir denn gedacht, dass die Timer funktionieren sollen?
>
> Beschreibe mal, wie dein programm funktionieren SOLL, dann vergleichen
> wir das mit dienem Code und zeigen Dir, was du ändern musst. Momentan
> hast du uns nämlich lediglich ein Ratespiel präsentiert. Das Programm
> funktioniert nicht und ich kann auch nicht wirklich erkennen, was du den
> vorhattest, zu tun.
>
> Vergiss nicht, zu beschreiben, wie die die I/O Pins belegt hast.


ISR(TIMER1_COMPA_vect) soll durch OCR1A = 0; jeden Takt aufgerufen 
werden.

Im ersten Takt dreht das Programm PORTL von 0xFF auf 0x00 um.

0x00 bleibt nun solang auf 0x00, bis v_sync_timer die Zahl 259 erreicht 
hat.

Da jeder Takt 64µs andauert, haben wir nun eine Zeit von 16,576ms, wo 
der PORTL auf 0x00 steht.

Danach wird PORTL wieder umgedreht.  0x00 wird zu 0xFF.

v_sync_timer wird wieder auf 0 gesetzt. Das heißt der Zustand 0xFF hält 
ganz genau einen Takt, also 64µs an, bis PORTL wieder umgedreht wird.

Dadurch entsteht ein horizontales Signal, das 64µs auf 0xFF und 16ms auf 
0x00 steht.

von fpga (Gast)


Lesenswert?

>ISR(TIMER1_COMPA_vect) soll durch OCR1A = 0; jeden Takt aufgerufen
>werden.

Du Simpel, die ISR muss auch von der CPU abgearbeitet werden. Alleine um 
einen Interrupt nur zu "initialisieren" bedarfs es schon ein paar 
CPU-Takte. Beispiel aus der Luft gegriffen und vereinfacht:
Sagen wir du hast eine 1MHz CPU, pro Takt ein Befehl, eine ISR braucht 2 
Takte fürs Betretten und 2 Takte fürs Verlasen.
Die ISR braucht also 4 Takte für sich selbst, ohne das dein USer-Code 
mitzählt. Sagen wir dein USer-Code braucht 10 Takte, sind also 14 Takte 
ins. für die ISR. Wenn du nun rechnest: 1.000.000 / 14 = 71429.
Du kannst diese ISR also maximal 71429 mal in der Sekunde aufrufen. Ohne 
das dabei Rechenzeit in der Hauptschleife übrig bliebe, also ein 
theoretisches Beispiel.

Du willst die ISR 16.000.000 mal in nur 16.0000.000 Takten 
aufrufen...merkst du das Problem?

Also aufhören mit copy&paste und selber mal nachdenken...im Hinblick auf 
deine spätere Arbeit bestimmt keine verkehrte Methodik.

Gruß J

von spess53 (Gast)


Lesenswert?

Hi

>Du Simpel, die ISR muss auch von der CPU abgearbeitet werden. Alleine um
>einen Interrupt nur zu "initialisieren" bedarfs es schon ein paar
>CPU-Takte.

Er meint Timer-Takte, also fcpu/1024.

Allerdings verstehe ich nicht warum da ein Interrupt gebraucht wird. Da 
langt fast PWM im Timermode 14 oder 15. Top mit OCR1 oder OCR1A auf die 
16,..ms gestellt und OCR1A oder OCR1B auf die 64µs

von spess53 (Gast)


Lesenswert?

Hi

Mist aus Versehen auf Absenden geraden.

.... auf die 64µs gestellt. Das Ganze noch mit den COM-Bits auf 
invertierende PWM gestellt und fertig. Mit einem Prescaler von 8 an 
Stelle von 1024 bekommt man das auch noch genauer hin.

MfG Spess

von Ano N. (anonym1)


Lesenswert?

Vielen Dank an euch alle für die Hinweise und Tipps.
Ich habe zwischenzeitlich deutliche Fortschritte dank euch gemacht. Ich 
habe es jetzt geschafft ein sauberes VGA-Signal mit Farben auszugeben. 
Jedoch immer nur eine Farbe auf einmal und dann den ganzen Bildschirm 
oder einen Teil des Bildschirms, aber keine einzelnen Pixel.
Wie im oberen Beitrage beschrieben, werden die Signale durch den 
Fast-PWM am Timer 0 & Timer 1 erstellt.

Nun will ich noch auf die ISR-Funktionen der Timer zugreifen. Scheinbar 
werden diese aber nicht mehr getriggert, da PWM ja nicht primär mit 
diesen arbeitet.
Erst wenn ich sei() in den Quellcode mit einfüge, funktionieren die 
ISR-Funktionen, aber die PWM-Signale werden dadurch nicht mehr 
ausgegeben.

Ich wollte nun fragen, ob es möglich ist, *dass man einen Fast-PWM 
und einen Timer-Overflow-Interrupt* (und dann mit der entsprechenden 
ISR-Funktion arbeiten) auf dem gleichen Timer konfigurieren kann.

Nur zur Verdeutlichung, damit ihr wisst, wohin das Ganze führen 
soll:
Das Endziel sollte irgendwann sein, dass ich farbige einfach Grafiken 
darstellen kann. Wie z.B. eine Sonne oder die Uhrzeit oder Ähnliches. 
Wenn die Grafiken dann nur 120x60 Pixel haben wäre das auch in Ordnung.

Unser aktueller Code:
1
#define F_CPU 16000000UL
2
#include <avr/io.h>
3
#include <avr/interrupt.h>
4
5
6
int main( void )
7
{
8
  
9
  DDRH=0xff;
10
  DDRB=0xff;
11
  DDRL=0xFF;
12
  PORTL = 0x00;    
13
  DDRE = 0xff;
14
   
15
  TCCR2A = 0;
16
  TCCR2B = 0;
17
  
18
  // Timer Mode 7 
19
  TCCR2A |= (1 << WGM20) | (1 << WGM21);
20
  TCCR2B |= (1 << WGM22);
21
  
22
  //Clear B on Compare Inverted
23
  TCCR2A |= (1<< COM2B1|(1<< COM2B0));
24
  
25
  //Clock Prescaler 8
26
  TCCR2B |= 2;
27
  
28
  OCR2A = 63;
29
  OCR2B = 7;
30
  
31
  TIFR2 =  (1 << TOV2);
32
  TIMSK2 =  (1 << TOIE2);
33
  
34
  //Configuration Timer 1
35
  TCCR1A = 0;
36
  TCCR1B = 0;
37
  
38
  //Timer Mode 15
39
  TCCR1A |=  (1<<WGM10) | (1<<WGM11);
40
  TCCR1B |=  (1<<WGM12) | (1<<WGM13);
41
  
42
  //Clear B on Compare Inverted
43
  TCCR1A |= (1<< COM1B1|(1<< COM1B0));
44
  
45
  // Clock Prescaler 1024
46
  TCCR1B |= 5;
47
  
48
  OCR1A = 259;
49
  OCR1B = 0;
50
  
51
  TIFR1 = (1 << TOV1);
52
  TIMSK1 = (1 << TOIE1);
53
      
54
  //sei();
55
  
56
  while( 1 );
57
}
58
59
ISR (TIMER1_OVF_vect){
60
  
61
  PORTL = 0xFF; 
62
  
63
}

von Ano N. (anonym1)


Lesenswert?

Da die letzte Anschlussfrage das ursprüngliche Thema etwas sprengt, habe 
ich einen neuen Beitrag veröffentlicht.

Beitrag "Fast-PWM und ISR Funktionen gleichzeitig nutzen"

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.