;**************************************************************************** ;* * ;* LCD Controller für Dualscan 640x480 LCD * ;* * ;* Paralleler Datenbus * ;* * ;* Version 0.2 * ;* * ;**************************************************************************** ;* * ;* Einfacher LCD Controller für ein SW 640x480 LCD, wie es in vielen alten * ;* Notebooks verbaut ist. * ;* Außer einem mega8515, einem 74HC32, dem 74x573 Adresslatch im Datenbus, * ;* und einem 64kB SRAM (mit 55ns oder schneller) werden keine weiteren * ;* Bauteile benötig. Neue Daten werden über ein paralleles Interface mit * ;* bis zu 100kByte (mit einem einfachen QBasic Programm schaffe ich rund * ;* 50kByte/s) in den Speicher geladen. * ;* Aufgrund der hohen Datenrate des Parallelports und der LCD Ausgang, * ;* läuft der AVR mit 18,432MHz und steuert das LCD mit rund 60Hz an. * ;* Betreibt man den AVR mit 22,1184MHz kann man auf 70-75Hz erreichen. * ;* * ;* Der Datentransfer über den Parallelport behindert die Datenausgabe auf * ;* dem LCD nicht, man sieht also kein Flackern oder ähnliches. * ;* * ;* Um den Datentransfer zwischen LCD-Speicer und Ansteuerung-Speicher zu * ;* steuern gbt es 3 Möglichkeiten: * ;* a) Ansteuerung hat Priorität (z.B. bi SED1330). Sobals CS\ Low wird, * ;* wird der Datentransfer zum LCD unterbrochen und die Pixel dunkel * ;* geschaltet. Dies hat den Nachteil, dass das LCD bei vielen Daten * ;* deutlich sichtbar flackert, aber den Vorteil, dass man ohne ein Busy * ;* zu überprüfen, Daten senden kann. * ;* b) Ansteuerung des LCD hat Priorität (z.B. T6963). Hier muss man vor * ;* jedem Datentransfer das Busy Flag überprüfen, was die Übertragung * ;* ausbremst, aber eine saubere Anzeige liefert. * ;* c) Einen ausreichend schneller Speicher verwenden, und den LCD * ;* Controller mit dem Bustakt synchronisieren (z.B. S1D13704/5). * ;* Bei meiner Ansteuerung habe ich einen Mittelweg gewählt. An sich hat * ;* die Ansteuerung Priorität gegenüber der LCD Ausgabe, die quasi im * ;* Hintergrund läuft, und immer wieder durch die Ansteuerung unterbrochen * ;* wird. Allerdings wird ab und zu der Busy Pin auch während der * ;* Datenübertragung des Controllers zum LCD gesetzt. Sollte man trotzdem * ;* Daten senden, ist es nicht weiter schlimm, solange der Controller noch * ;* genügend Zeit übrig hat, um die Daten ans LCD auszugeben. * ;* Sendet man dagegen dicht hintereinander zwei Datenwerte, noch ehe das * ;* das Busy vom ersten Wert gelöscht ist, geht der zweite warscheinlich * ;* verloren. * ;* Das ganze klingt im ersten Moment etwas merkwürdig, hat aber seine * ;* Gründe: Überprüft man das Busy Bit, stellt fest es ist gelöscht und * ;* wird, ehe man das Byte senden kann von einem Interrupt unterbrochen, * ;* dann kann es durchaus passieren, dass der Controller inziwschen Busy * ;* gesetzt hat, weil er Daten ans LCD ausgibt. Daher ist es nicht schlimm, * ;* wenn trotzdem Daten gesendet werden. * ;* * ;* Die Datenübertragung läuft über 3 Leitungen und den 8bit Datenbus: * ;* Write, Command/Data und Busy dienen zur Steuerung des Datenbusses * ;* Da das Interface Softwaremäßig gelöst wurde, muss man ein paar Details * ;* beachten: * ;* Bei der fallenden Flanke von Write werden nach etwa 0,3-1,5us die Daten * ;* gespeichert. Command/Data und die Daten müssen also solange stabil * ;* bleiben, bis diese Zeit vorüber ist, oder Busy auf High Pegel geht. * ;* Der Writeimpuls selber wird Hardwaremäßig abgefragt und muss mindestens * ;* 100ns lang sein. Alle Write Impulse die während der Ausführung eines * ;* Befehls ankommen, werden ignoriert (es ginge auch anderst, aber dies * ;* dient zur "Entprellung", da beim Betrieb am LPT sonst Störungen * ;* auftreten. Mit einen 1,5m Kabel habe ich keine Störungen, bei 2m ist es * ;* nahezu unbrauchbar. Auf jedenfall benötigen Write und Command/Data * ;* PullUps gegen +5V. Diese sollten etwa 400-1000 Ohm haben. * ;* * ;* Befehle: * ;* * ;* 001 Set Cursor. Gefolgt von zwei Parametern, nämlich der Zeilen * ;* und Spaltenadresse. Die Zeilenadresse liegt im Bereich 0-239, * ;* und die Spaltenadresse im Bereich 0-79 (plus 128, falls die * ;* Position in der unteren Displayhälfte liegt.) * ;* * ;* 002 Set PWM. Stellt das Tastverhältnis des PWM Ausgangs OC1B ein. * ;* Das Tastverhältnis folgt auf diesen Befehl als Parameter. * ;* * ;* 010 Clear Line. Dieser Befehl benötigt keinen Parameter und löscht * ;* die aktuelle Zeile. Danach wird der Cursor auf den Beginn der * ;* nächsten Zeile gesetzt. So kann man 480 mal diesen Befehl * ;* senden, um das ganze LCD zu löschen. Allerdings ist dieser * ;* Befehl sehr komplex, und benötigt daher einiges an Zeit. Sendet * ;* man also diesen Befehl öfters ohne Pause, dann kommt es zu * ;* einem kurzen Flackern des LCDs. * ;* * ;* 011 Clear LCD. Dieser Befehl löscht das LCD in extrem kurzer Zeit. * ;* Aber wie auch schon beim Clear Line Befehl, benötigt er einiges * ;* an Zeit um den kompletten Speicher zu löschen. Das LCD Timing * ;* wird zwar nicht ganz eingehalten, aber man merkt nichts davon, * ;* da ab und zu dummy Daten ans LCD gesendet werden. * ;* * ;* 192 Display Character. Mit diesem Befehl wird ein ASCII Zeichen * ;* mittels des interenen Character Generator dargestellt. Das * ;* gewünschte Zeichen wird als Parameter übergeben. Die * ;* Darstellung erfolgt als 8x8 Zeichen. Die Schriftart entspricht * ;* der auf einem PC im 640x480 Grafikmodus. * ;* * ;* Daten: Nach dem Senden eines Datenwertes wird dieser in den Speicher * ;* geschrieben, und der Adresszähler anschließend erhöht. * ;* Jedes Byte enthält 8 nebeneinanderliegende Pixel. Im SRAM * ;* werden dagegen 4 nebeneinander liegende Pixel aus der oberen, * ;* und 4 nebeneinander liegende Pixel aus der unteren Hälfte * ;* gespeichert. Das mach das Laden von Daten etwas komplizierter, * ;* vereinfacht und beschleunigt die Ausgabe aber extrem. * ;* * ;* IO Pins: * ;* * ;* PA0-7 Adr/Dat Daten/Low Adresse für den SRAM * ;* PB0-7 Daten 8 Bit Befehle/Daten für den Controller * ;* PC0-7 Adresse High Adresse für den SRAM * ;* PD0 C/D Command/Data: Auswahl Befehl (high) und Daten (low) * ;* PD1 Busy Status des Display Controllers: Busy=high, Ready=low * ;* PD2 WR\ Write Eingang. Ein Low Impuls läd die Daten * ;* PD3 EXSCL Aktivierung für XSCL. Dieser Pin aktiviert über ein * ;* 74HC02 den Schiebetakt für die Daten * ;* PD4 LP Latchpuls. Der Impuls läd die Daten aus dem Schiebe- * ;* register ins Display. Dient als HSync. * ;* PD5 FLM VSync für das LCD. Das LCD springt in die erste Zeile. * ;* PD6 VWR\ Write Enable für den SRAM. * ;* PD7 VRD\ Output Enable für den SRAM und XSCL über den 74HC32 * ;* für das LCD. Bei jedem Lesevorgang werden die Daten ins * ;* LCD geladen * ;* PE0 EnLCD Schaltet über einen PNP und einen NPN Transistor die * ;* LCD Spannung, damit im Falle eines Fehlers (z.B. Ausfall* ;* der +5V) das LCD nicht zerstört wird. * ;* PE1 ALE Adress Latch für Low Adressen des SRAMs * ;* PE2 PWM PWM Ausgang, z.B. für Displaykontrast (geht noch nicht) * ;* * ;**************************************************************************** .include "m8515def.inc" ;@18,432MHz .def null = r0 .def eins = r1 .def voll = r2 .def regsave = r3 .def tempm = r4 .def L_COL = r21 .def H_COL = r22 .def COL = r23 .def TmpCount = r24 .def temp = r16 .def temp2 = r17 .def temp3 = r18 .def NextOp = r19 ;Zähler für Parameter eines Befehls .def Flags = r20 .def XAdresseLow=r26 ;Writepointer Spalte .def ZeileL =r27 ;Writepointer Zeile LSB .def ReadAddressL=r28 ;Readpointer Spalte .def ReadAddressH=r29 ;Readpointer Zeile ;PortB ; 8bit IO Data ;PortD .equ CD =0 ;Command/Data .equ Busy =1 ;Controller Busy .equ WR =2 ;Write Data: min. 1us Low .equ XSCL =3 ;Enable XSCL .equ LP =4 ;HSync .equ FLM =5 ;VSync ;PortE .equ EnLCD =0 ;LCD Spannung ein/ausschalten ;Flags .equ Frame =0 ;Warten bis 1/(60fps * 242 Zeilen) Sekunden vorbei ist, ehe die nächste Zeile ausgegeben wird. .equ ZeileH =7 ;Writepointer Zeile MSB, entscheidet zwischen obere und untere Displayhälte, also zwischen High und Low Nibble eines RAM Bytes .equ ZStart =4 ;High Adresses des Bildanfangs des externen SRAMs (um internen SRAM auszublenden) ;NextOPs .equ SetCurL =1 .equ SetCurH =2 .equ SetPWMV =3 .equ CGChar =4 .equ YPos =100 ;Position des Logos .equ XPos =100 ;Position des Logos .org 0000 rjmp reset .org INT0addr ;Interruptvektor für Daten-Empfang in temp, pinb ;Daten von PORTB lesen sbi portd, Busy ;Busy Pin setzen in temp2, pind ;C/D von PORTD lesen rjmp Write .org OVF0addr ;Interruptvektor für Beginn von neuer Zeile (Display Timing) sbrc Flags, Frame ;Wurde letze Zeile überhaupt ausgegeben, oder hat Datentransfer alles blockiert ? sbi portd, Busy ;Wenn ja, dann Busy Pin setzen (ist zwar nicht unbedingt notwndig, verbesser aber das Display Timing=weniger Flimmern) out Tcnt0, tempm ;Timerwert laden sbr Flags, (1< Max ? brne normal clr XAdresseLow ;Spalten Adresse = 0 inc ZeileL ;Zeilen Adresse ++ cpi ZeileL, 120+ZStart ;Zeilen Adresse > Max ? brne normal ldi ZeileL, ZStart ;Zeile = Beginn XRAM sbr Flags, (1< Max ? brne normal clr XAdresseLow ;Spalten Adresse = 0 inc ZeileL cpi ZeileL, 120+ZStart ;Zeilen Adresse > Max ? brne normal ldi ZeileL, ZStart ;Zeile = Beginn XRAM cbr Flags, (1< Max ? brne normal2 clr XAdresseLow ;Spalten Adresse = 0 subi ZeileL, -8 ;Zeilen Adresse ++ cpi ZeileL, 120+ZStart ;Zeilen Adresse > Max ? brlo normal2 ldi ZeileL, ZStart ;Zeile = Beginn XRAM ldi temp, (1<Bit3 ? Dann im nächsten Byte lesen inc XAdresseLow andi temp2, 3 ;zu löschendes Bit (Bit 0-39 clr temp3 clc inc temp2 ser temp3 pow: rol temp3 dec temp2 brne pow ;temp3 enthält nun (1<