Hi, ich habe fuer ein Uni-Projekt diesen Code geschrieben. Er erzeugt ein VGA-Signal mit 640x400 Pixeln bei 75Hz Bildwiederholrate. Das Bild wird aus einem 80x50 Zeichen Framebuffer erzeugt, das Ganze macht ausschlisslich schwarz/weiss Text. Verwendet wird ein ATmega644P (auf 25MHz uebertaktet) und ein 74HC165 Schieberegister (AVR gibt 8 Pixel parallel aus, werden im Schieberegister serialisiert um die 25 MHz Pixeltakt zu erreichen). Ein Paar weiter Infos gibt's hier: http://www.thebackend.de/microcontroller:dmm_vga Das ganze ist unter Zeitdruck entstanden, erwartet also keinen aufgeraeumten Code und keine hilfreichen Kommentare... aber vielleicht hilft's ja auch so dem ein oder anderen. Sebastian
Hier noch der Schaltplan. Und ein Video von der Demo die ich mit draufgebaut habe: http://www.youtube.com/watch?v=DA1N-lE49q8
Hier noch eine aktualisierte Version. Habe den Code ein /klein/wenig aufgeraeumt und vor allem die Schrift durch eine mit CP437 (DOS) Codierung ersetzt. Damit hat man nun auch all die huebschen Liniensegmente zur Verfuegung. Sebastian
Schön, hättest Du den IC2A durch einen Multiplexer ersetzt, so hättest Du ganz einfach Farbe machen können. Trotzdem nicht schlecht.
Christian Berger schrieb: > Schön, hättest Du den IC2A durch einen Multiplexer ersetzt, so hättest > Du ganz einfach Farbe machen können. Trotzdem nicht schlecht. Mir war wichtig eine Moeglichst hohe Aufloesung zu machen damit das ganze als Terminal-Ersatz taugt. So habe ich nur 8 Prozessortakte um 8 Pixel auszugeben und der Speicher ist auch zu >99% voll. Wo ich da noch Farbe machen soll sehe ich nicht so recht. Oder erklaer deine Idee mal genauer...
Sebastian B. schrieb: > So habe ich nur 8 Prozessortakte um 8 > Pixel auszugeben und der Speicher ist auch zu >99% voll. Wo ich da noch > Farbe machen soll sehe ich nicht so recht. Oder erklaer deine Idee mal > genauer... Naja, so ähnlich wie das auch in kommerziellen Lösungen gemacht wird. Du speicherst nicht die ganze Zeile mit Zeichen und Farbe, sondern nimmst Sonderzeichen her, welche beispielsweise "5 Leerzeichen" oder "schalte auf grün" bedeuten. Damit kannst Du zwar nicht jedes Zeichen in jeder Farbe darstellen, aber zumindest Textteile hervorheben.
Christian Berger schrieb: > Naja, so ähnlich wie das auch in kommerziellen Lösungen gemacht wird. Du > speicherst nicht die ganze Zeile mit Zeichen und Farbe, sondern nimmst > Sonderzeichen her, welche beispielsweise "5 Leerzeichen" oder "schalte > auf grün" bedeuten. Damit kannst Du zwar nicht jedes Zeichen in jeder > Farbe darstellen, aber zumindest Textteile hervorheben. Verstehe, das wuerde aber in diesem Projekt nicht funktionieren. Ich braeuchte entweder entscheidend mehr Zeit um im Framebuffer noch irgendwelche Escape-Sequenzen auszuwerten (also eine viel niedrigere Aufloesung) oder mehr Speicher um aus den Escape-Sequencen meinen normalen Framebuffer quasi zu rendern :-) Im Moment koennte ich die Farben hoechstens Zeilenweise umschalten... das ist glaub ich nicht sehr sinnvoll... Sebastian
Christian Berger schrieb: > Naja, Du musst ja kein Zeichen ausgeben, wenn Du ein Escape Zeichen > hast. Naja, es muss aber ja auch das Timing und die Anzahl der Zeichen je Zeile stimmen, man will ja trotz Steuersequenzen die volle Zeile nutzen... also ganz so einfach wird das hier kaum klappen. Wenn du mal in den Code schaust wirst du sehen das waehrend der Ausgabe einer Videozeile keine Zeit mehr ist fuer die Auswertung von Escape-Sequenzen. Da muss die Verarbeitung jedes Bytes gleich lang sein, sonst gibt's schwarze Flecken auf dem Bildschirm. Hast du sowas schonmal gebaut? Interessieren wuerde mich ein Quellcode dazu ja schon. Sebastian
also escape-sequenzen sind an der stelle denke ich gar nicht machbar. dadurch das man 8 takte zeit hat sich das nächste byte (welches ja aus dem textframebuffer mit der entsprechenden verküpfung aus dem char ram geholt wird) was wirklich schon echt sehr knapp ist, bleibt keine zeit den bytestream zu interpretieren. das einzige was ginge, wäre über einen multiplexer die vor und hintergrundfarbe global zu setzen (z.b. ein separates hc165 um die jeweils 3 pins für vor und hintergrundfarbe an jedem frame anfang oder ende zu setzen), oder die 3 letzten verbleibenenden pinne pb0-pb2 für die text farbe zu nehmen (also schwarz/rot oder schwarz/blau oder schwarz/irgendwas aus den 5 möglichkeiten) die dann ebenfalls durch einen multiplexer geschaltet werden. mehr denke ich dürfte da nicht machbar sein. 25 mhz müssen eben sein für ein std vga signal. wobei die framerate bei 640x400 bei 25Mhz dann bei ca 60Hz liegen dürfte, und nicht 72Hz, da läge man bei ca 30MHz.
Moin, neue Version: inline Assembler Teil ueberarbeitet. Ist jetzt etwas weniger haesslich aber nicht unbedingt leichter zu verstehen ;-) Die 75 Hz Bildwiederholrate stimmen schon so grob. 800 Takte/Zeile, 421 Zeilen macht:
Nagut, also 74Hz. Zeigt der Monitor auch so an. Sebastian
Nicht schlecht. Schau Dir doch mal die Schaltung des ZX80 von Sinclair an. Deutlich langsamer getaktet und das Bild wird in einer NMI-Routine des Z80-Prozessors ausgegeben. In 1K-RAM haben BASIC-Programm und Bildschirmspeicher platz. Die ROM-Routinen zur Erzeugung des Videosignals sind sehr aufschlussreicht. Schaltplan und kommentiertes ROM-Listing gibt es auf www.worldofspectrum.org.
25MHz Pixeltakt ist schon eine ordentliche Leistung. Theoretisch sollten etwa 40MHz möglich sein (40MHz für das Schieberegister, 20MHz für den AVR), allerdings ist das ganze dann ziemlich timingkritisch und benötigt 64kByte, schnelles, externes RAM, denn nur so schafft es ein AVR ein Byte innerhalb von 4 Takten mit sinnvollen Daten zu füllen. Dafür erhält man aber auch 800x600 Pixel Auflösung die man mit beliebigen Daten füllen kann.
Naja, es geht schon noch mehr als 2xTaktfrequenz. Man könnte beispielsweise einen FIFO und einen Timer extern betreiben. Dann schiebt man einfach das Bild als RLE raus. Damit geht zwar wohl kein Text, aber für Graphiken reicht das.
Christian Berger schrieb:
> Dann schiebt man einfach das Bild als RLE raus.
Geht das so einfach ohne viel Aufwand? Ich hatte mit RLE bisher wenig zu
tun, aber ich stelle mir das Umwandeln in die Pixeldaten mit
Standardbauteilen relativ aufwendig vor.
Christian Berger schrieb: > Naja, es geht schon noch mehr als 2xTaktfrequenz. Man könnte > beispielsweise einen FIFO und einen Timer extern betreiben. Dann schiebt > man einfach das Bild als RLE raus. Damit geht zwar wohl kein Text, aber > für Graphiken reicht das. Wenn das alles so einfach ist bin ich auf deine Loesung gespannt... Eigentlich sollte ich noch die Terminal-Emulation im aktuellen Projekt in etwas funktionierendes verwandeln... aber ich glaube da hab ich erstmal keine Lust mehr drauf und fuer die Uni brauch ichs nicht unbedingt :-) Ich habe hier noch ein 640x200 LCD rumliegen (irgendwo... mal sehen ob ichs wiederfinde). Mit dem werd ich mich demnaecht auch noch beschaeftigen, sollte ja recht aehnlich gehen. Da gibts hier im Forum und bei elm-chan schon einige schoene Ideen. Sebastian
Benedikt K. schrieb: > Christian Berger schrieb: >> Dann schiebt man einfach das Bild als RLE raus. > > Geht das so einfach ohne viel Aufwand? Ich hatte mit RLE bisher wenig zu > tun, aber ich stelle mir das Umwandeln in die Pixeldaten mit > Standardbauteilen relativ aufwendig vor. Nunja, man bräuchte beispielsweise einen einfachen Timerbaustein, welcher rückwärts zählt, und dann bei 0 einen neuen Wert aus dem FIFO einließt. Das ist nicht ganz trivial, hat aber eine Netzliste in der Größenordnung der externen RAM Lösung. Es ist definitiv einfacher als selbst gebauter RAM. :)
ich denke wenn man soviel aufwand treiben muß, lohnt sich dann schon ein cpld zur unterstützung ...
ausbuddel Nur mal so als Frage, wieso haste denn ein externes Schieberegister verwendet? Der AVR hat doch eins integriert, nennt sich SPI.
Ja, das wäre wirklich mal ein geiles Feature. Danach käme noch die Möglichkeit das Stoppbit los zu werden und schon Daten rein schreiben zu können, während ein Byte gesendet wird. Idealerweise hätte man noch mehrere SPIs die man gleichzeitig betreiben könnte. :)
Hallo zusammen, kann die Soft auf ein BAS-Signal angepasst werden? Ich brauche da was einfaches das ein Videosignal, H-und V-Sync mit einem Gitter oder Balkensignal zum Fernseher (Videoteil) testen erzeugt. Hat da schon mal einer was gemacht? Mit dem Analogteil komme ich klar aber der MC.... die gabs noch nicht als ich Fernsehtechniker gelernt habe.
Hallo allerseits, da doch alle Paar Monate mal wieder Fragen zu dem Projekt auftauchen, habe ich Quellcode und Schaltplan nochmal überarbeitet. Angehängt ist der aktualisierte Sourcecode. An den Features hat sich seit 2009 nichts geändert, es wurden nur diverse Compiler-Warnungen behoben. Außerdem ein Schaltplan und zweilagiges PCB Design in Eagle 6. Einige weitere Notzizen finden sich hier: http://www.ags.tu-bs.de/?id=e.lab:projekte:avrvga Sebastian
:
Bearbeitet durch User
Hallo allerseits, auch ich hatte mich im letzten Monat mit dem Projekt beschäftigt, und dank Sebastians Unterstützung funktioniert das Terminal wie beschrieben. Um mit einer einseitig entflochtenen Platine auszukommen, habe ich die Schaltung bei gleicher Funktion noch etwas vereinfacht: Entfallen sind der Programmierstecker und der Bustreiber 74HC244, der für die eigentliche Funktion keine Rolle spielt. Das Ergebnis befindet sich im Anhang. Die Firmware läuft hierauf ohne Änderung. Thomas
Kurze Frage zu den benötigten Fuses. Ist Low=0xd0 und High=0x99 korrekt für Atmega644 mit externen 25MHz Quarzoszillator? (Ist schon soo lange her, das ich mal mit AVR's direkt gearbeitet habe) Und ins eeprom muss nichts geflasht werden für dieses Projekt? Nur die dmm_vga.hex ins flash. Peter
:
Bearbeitet durch User
wichtig sind vor Allem die CKSEL Bits zur Wahl der Taktquelle. Beiliegender Screenshot zeigt mit welchen Einstellungen mein Exemplar des Terminals arbeitet. Thomas
Danke für Rückmeldung! VGA-Terminal / Demo Bild habe ich. Aber drücken der Leertaste scheint nichts zu bewirken..? Da muss ich wohl nochmal die Verbindungen zw. PS/2 und AVR prüfen.. EDIT: Geprüft - aber sind ok: PS/2 - AVR 1 - 16 2 - 3 - GND 4 - +5V 5 - 18 6 - ?? Weiss auch noch nicht wofür der Options Header ist? Platine ist selbst gestrickt . aber im Prinzip nur Rx+Tx auf VG64 Leiste gelegt (MFA). Peter
:
Bearbeitet durch User
Die Option Pins hatte ich in meinem Layout für mögliche Erweiterungen vorgesehen (haben hier keine Funktion). Thomas
Ah ok. Danke. Habe jetzt auch im Source gefunden wo+wie die G_MODE's umgeschaltet werden. Zuerst LOGO - wartet auf SPACE Dann PRESENTATION: t = Terminal-Mode; l=LOGO Nur die PS2 / der AVR will/wollen keinen Leertaste generieren/sehen..? Komme aus dem LOGO Mode mit Leertaste nicht raus.. muss ich wohl mal Logikanalysator dran fummeln um zu sehen, ob die PS/2 Tastatur etwas / ein SPACE sendet. Peter
Hmm. Es will nicht bei mir!? Alle Verbindungen geprüft und ok. Auch mal ohne Pullups versucht - keine Änderung. Alle 3 LED leuchten kurz auch nach Einschalten an der Tastatur. 2 verschiedene Tastaturen getestet - keine will. PS/2 Auduino Adapter mal ersatzweise dran gehabt - keine Änderung. In keyboard.c steht: /* PC KEYBOARD Interface for AVR *********************** DATA on PORTD BIT 5 SCK on INTERRUPT 2 *********************** */ INT2 ist aber doch PIN 3?? Und lt. Schaltplan Pin 16 = INT0? Und DATA liegt lt. Schaltplan an Pin 18 = PD4 (Meint das 5 bit von Port D?) Habe den 644 gebraucht gekauft.. kann mir nur noch erklären, das der das Problem sein könnte. Fuses sind so wie bei dir nun. Peter
Peter S. schrieb: > PD4 (Meint das 5 bit von Port D?) Nein, das meint Bit 4. Die Bits werden von 0 bis 7 gezählt.
Muss Rx und Tx auch schon im ersten Screen gebrückt sein?? D.h das/ein Leerzeichen muss zu einem angeschlossenen System gehen und dieses muss es zurück senden?? EDIT: Habe ich als Test auch mal gemacht - nichts gebracht. EDIT2: Ich habe einen Atmega644 ohne P, A, PA etc. oder braucht es einen 644P? Ich meine im Source gesehen zu haben, das 2 UARTs genutzt werden. UART0 = serielle Schnittstelle und UART1 = Tastatur? Aus keyboard.c: UCSR1A = 0; UCSR1B = (1<<RXEN1); // Enable receiver UCSR1C = (1<<UMSEL10)|(1<<UCSZ11)|(1<<UCSZ10); // sync. mode, no parity, 1 stop bit, sample on falling clock edge Peter
:
Bearbeitet durch User
Hi Peter. Peter S. schrieb: > EDIT2: Ich habe einen Atmega644 ohne P, A, PA etc. oder braucht es einen > 644P? Der ATmega644 hat nur einen UART - damit kann die Tastatur nicht gehen. Sebastian
Nochmal zum Anschluss der Tastatur: Der Kommentar in der Keyboard.c ist falsch. Ich habe den Code zum Dekodieren dar Tastendrücke irgendwoher kopiert aber die Ansteuerung des UART geändert... alles Jugendsünden :-) Für die Tastatur wird der UART1 (nur bei 644P* verfügbar) im synchronen Modus verwendet - also mit getrennter Daten- und Clock Leitung. Tastatur-Datenleitung muss an PD2 (RXD1). Tastatur-Taktleitung muss an PD4 (XCK1). Sebastian
:
Bearbeitet durch User
644P ist da. Geflasht und alles funktioniert - hardware technich :-( Jetzt muss ich wohl doch noch avr_gcc aufsetzen um den Code zu ändern.. Ich denke, das MFA braucht ein CR als Return und kein NL. Außerdem muss ich das lokale Echo unterbinden. vt100.c: case VS_CHARACTER: if(c == 27) vt_state = VS_ESCAPE; else if(c == '\n') cursor_newline(); else if(c == '\r') nop(); else vt100_putchar(c); break; keyboard.c: if(c == '\n') cursor_newline(); else { fb_putchar(c); <-- auskommentieren = lokales Echo weg? cursor_advance(); } Und dann muss der Zeichensatz ggf. wieder auf ASCII angepasst werden.. Aber erst einmal ***Danke*** für dieses schöne Projekt! Peter
Du solltest in "keyboard.c" auch cursor_newling und cursor_advance nicht aufrufen. Bei einem Terminal sind Tastatur und Bildschirmausgaben strikt getrennt, die Tastatureingaben werden nur über die serielle Schnittstelle zum Host übertragen. Allerdings: Wenn das Terminal einen Konfigurationsmodus hat, bei dem tastaturgesteuert irgendwelche Parameter eingestellt werden können, dann muss die Tastatur natürlich ausgewertet werden - d.h. mit putchar etc.
Diese Auskommentierung:
1 | void put_kbbuff(unsigned char c) |
2 | {
|
3 | /*
|
4 | if (buffcnt<KB_BUFF_SIZE) // If buffer not full
|
5 | {
|
6 | *inpt=c; // Put character into buffer
|
7 | inpt++; // Increment pointer
|
8 | buffcnt++;
|
9 | if (inpt >= (kb_buffer + KB_BUFF_SIZE)) inpt = kb_buffer;
|
10 | }
|
11 | */
|
12 | /*PS
|
13 | if(c == '\n')
|
14 | cursor_newline();
|
15 | else
|
16 | {
|
17 | fb_putchar(c);
|
18 | cursor_advance();
|
19 | }
|
20 | */
|
21 | uart_putchar(c); |
22 | }
|
Hat (glaube ich) zur Folge, das alle Zeichen der Tastatur 1:1 zum Uart gegeben werden und KEIN lokales Echo erfolgt. --- Aber anstatt CR (Return/Enter) wird anscheinend etwas anderes ans System gesendet..? --- Es werden ja die Zeichen anhand des Scancodes aus einer Lookup Tabelle gelesen: // Unshifted characters scan_code unshifted[] PROGMEM = { { 0x0d,9}, { 0x0e,'^'}, { 0x15,'q'}, { 0x16,'1'}, ... Wo ist dabei aber CR..? EDIT: Argh. Wer richtig schauen kann ist klar im Vorteil ;-) In scancodes.h:
1 | { 0x5a,'\r'}, /*PS was \n */ |
und in vt100.c noch:
1 | case VS_CHARACTER: |
2 | if(c == 27) vt_state = VS_ESCAPE; |
3 | else if(c == '\n') nop(); /*PS cursor_newline();*/ |
4 | else if(c == '\r') cursor_newline(); /*PS nop();*/ |
5 | else vt100_putchar(c); |
So ist es nun nutzbar für MFA. Aber Feinheiten stehen sicher noch aus.. Peter
:
Bearbeitet durch User
Inzwischen habe ich ein simples Backspace (0x08) ohne line wrap eingebaut: in framebuffer.c/h:
1 | void cursor_backspace() |
2 | {
|
3 | if(cursor_x > 0) cursor_x--; |
4 | }
|
und in vt100.c:
1 | case VS_CHARACTER: |
2 | if(c == 27) vt_state = VS_ESCAPE; |
3 | else if(c == '\n') nop(); /*PS cursor_newline();*/ |
4 | else if(c == '\r') cursor_newline(); /*PS nop();*/ |
5 | else if(c == '\b') cursor_backspace(); /*PS simple BS*/ |
6 | else if(c == '\t') nop(); /*PS ignore TAB*/ |
7 | else vt100_putchar(c); |
8 | break; |
Dann möchte ich ein echtes scrolling einbauen. Idee dazu:
1 | // 1st: move framebuf array content from line 2 until last line to start of array (line 1)
|
2 | // dest address=framebuf=start of array
|
3 | // src address=framebuf+H_CHARS=framebuf+1 line chars (here: H_CHARS=80)
|
4 | // len = (H_CHARS*V_CHARS)-H_CHARS = 80*50-80
|
5 | void memmove(framebuf,framebuf+H_CHARS,(H_CHARS*V_CHARS)-H_CHARS); |
6 | // 2nd: clear last line (fill with blanks)
|
7 | // dest address=framebuf+((H_CHARS*V_CHARS)-H_CHARS) = framebuf+((80*50)-80)
|
8 | // len = H_CHARS = 80
|
9 | // (80*50)-80 = 3920
|
10 | void memset(framebuf+((H_CHARS*V_CHARS)-H_CHARS),' ',H_CHARS); |
Ggf. sollte anstatt ' ' (eines Blanks) auch eine 0 zum löschen der untersten Zeile rein? Siehe fb_clear():
1 | void fb_clear() |
2 | {
|
3 | uint16_t i; |
4 | for(i=0 ; i<(V_CHARS*H_CHARS) ; i++) |
5 | {
|
6 | framebuf[i] = 0; |
7 | }
|
8 | }
|
Evtl. kann ich auch einfach memcpy nehmen anstatt memmove, da dest address kleiner als src address ist? Woher nimmt memmove den temp. buffer, wenn die 4k Ram bereits belegt sind im AVR? Geht das so? Hat jemand schon mal mit dem Verschieben eines Array Inhalts gearbeitet? Bessere/andere Ideen? Peter
:
Bearbeitet durch User
Habe jetzt erst einmal ein 'half page scrolling' eingebaut. Also untere Hälfte des Bildschirm wird in die obere Hälfte kopiert, untere Hälfte gelöscht und Cursor in die Mitte gesetzt:
1 | void scroll() |
2 | {
|
3 | if(cursor_y == V_CHARS) |
4 | {
|
5 | /*PS simple fake scroll - just clear screen + cursor home
|
6 | fb_clear();
|
7 | cursor_y = 0;
|
8 | */
|
9 | // half page scrolling
|
10 | for(int i=0; i<((V_CHARS*H_CHARS)/2); i++) |
11 | {
|
12 | framebuf[i] = framebuf[i+((V_CHARS*H_CHARS)/2)]; |
13 | framebuf[i+((V_CHARS*H_CHARS)/2)]=0; |
14 | }
|
15 | cursor_y = (V_CHARS/2); |
16 | }
|
17 | }
|
18 | |
19 | void cursor_advance() |
20 | {
|
21 | cursor_x++; |
22 | if(cursor_x == H_CHARS) |
23 | {
|
24 | cursor_y++; |
25 | cursor_x = 0; |
26 | }
|
27 | scroll(); |
28 | }
|
29 | |
30 | void cursor_newline() |
31 | {
|
32 | fb_putchar(0); |
33 | |
34 | cursor_y++; |
35 | cursor_x = 0; |
36 | scroll(); |
37 | }
|
Wenn ich 'richtig' scrolle, also immer die unterste Zeile um 1 nach oben, muss sonst nach jeder neuen Zeile immer gescrollt werden. Keine Ahnung, ob das dann zu Bildschirmflackern etc. führt. Kann ich ja ggf. dann auch noch mal ausprobieren. Peter
:
Bearbeitet durch User
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.