;****************************************************************************
;* *
;* LCD Controller für Dualscan 640x480 LCD *
;* *
;* Serieller Datenbus *
;* *
;* Version 0.21 *
;* *
;* © by Benedikt *
;* *
;* Email: benedikt83 ät gmx.net *
;* *
;****************************************************************************
;* *
;* Die Software darf frei kopiert und verändert werden, solange sie nicht *
;* ohne meine Erlaubnis für kommerzielle Zwecke eingesetzt wird. *
;* *
;****************************************************************************
;* *
;* Einfacher LCD Controller für ein SW 640x480 LCD, wie es in vielen alten *
;* Notebooks verbaut ist. *
;* Außer einem mega8515, einem 74x02, 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 150kByte in den Speicher geladen. *
;* Aufgrund der hohen Datenrate des Parallelports und der LCD Ausgang, *
;* läuft der AVR mit 16,000MHz und steuert das LCD mit rund 60Hz an. *
;* *
;* 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. *
;* *
;* 003 Set X Start. Stellt den Startwert für den Zeilenbeginn ein. *
;* Damit lässt sich ein Ausschnitt aus dem virtuellen 1000x480 *
;* Bild im Speicher auf dem LCD anzeigen. *
;* *
;* 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 *
;* PC0-7 Adresse High Adresse für den SRAM *
;* PD0 RXD UART: RXD *
;* PD1 Busy UART: CTS, Busy=high, Ready=low *
;* 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 *
;* *
;****************************************************************************
.include "m8515def.inc" ;@16,000MHz
.def null =r0
.def eins =r1
.def voll =r2
.def regsave=r3
.def tempm =r4
.def XStart =r5 ;Beginn einer Spalte ab Nibble
.def XScan =r6 ;Endadresse bei der Bildausgabe in X Richtung
.equ XSize =160 ;Bildgröße in X Richtung in Nibbles
.equ YSize =200;240 ;Bildgröße in Y Richtung pro Halbbild
.equ XOScan =0 ;Anzahl der zusätzlich auszugebenden Nibbles in X Richtung
.equ YOScan =2 ;Anzahl der zusätzlich auszugebenden Zielen in Y Richtung (muss >=2 sein, sonst geht z.B. das Pollin LCD kaputt !)
.equ YScan =YMin+YSize+YOScan ;Endadresse bei der Bildausgabe in Y Richtung
.equ XMax =250 ;Maximale Speicheradresse in XRichtung
.equ YMin =4 ;Minimale Speicheradresse in Y Richtung
.equ YMax =YMin+YSize ;Maximale Speicheradresse in Y Richtung
.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 AdresseLow =r28 ;Readpointer Spalte
.def AdresseHigh=r29 ;Readpointer Zeile
;PortB
; 8bit IO Data
.equ LCDon =0 ;LCD ein/ausschalten
;PortD
.equ RXD =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
;NextOPs
.equ SetCurL =1
.equ SetCurH =2
.equ SetPWMV =3
.equ CGChar =4
.equ SetScroll =5
.equ Escape =14
.equ CD =7
.equ YPos =190 ;Position des Logos
.equ XPos =200 ;Position des Logos
.equ FrameFreq =60 ;Bildwiederholfrequenz
.equ TReload = 256-(2000000/FrameFreq/(YSize+YOScan)) ;Reloadwert für Timer
.org 0000
rjmp reset
.org OVF0addr ;Interruptvektor für Beginn von neuer Zeile (Display Timing)
rjmp HSync
.org URXCaddr ;Interruptvektor für Daten-Empfang
in temp, udr ;Daten von PORTB lesen
rjmp Write
.org 32
reti
HSync:
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< NextOp.Escape setzen.
out sreg, regsave
reti
;*** Bearbeitung der Daten ***
Daten:
cpi NextOp, 0 ;Paramter oder Daten ?
brne Next
sbi portd, XSCL ;Datenverbindung zum LCD unterbrechen
sbrc Flags, ZeileH ;Pixel in der oberen oder unteren Displayhälfte ?
rjmp untereHalfte
obereHalfte:
ld temp2, X ;Lader Wert aus SRAM
andi temp2, 15 ;obere Datenhälfte der SRAM Daten behalten
mov temp3, temp
andi temp3, 240 ;erste Hälfte der Daten behalten
or temp2, temp3 ;Beide Hälften zusammenfügen
st X+,temp2
ld temp2, X
andi temp2, 15 ;obere Datenhälfte der SRAM Daten behalten
swap temp
andi temp, 240 ;zweite Hälfte der Daten behalten
or temp2, temp
st X+,temp2
cpi XAdresseLow, XMax ;Spalten Adresse > Max ?
brlo normal
clr XAdresseLow ;Spalten Adresse = 0
inc ZeileL ;Zeilen Adresse ++
cpi ZeileL, YMax ;Zeilen Adresse > Max ?
brlo normal
ldi ZeileL, YMin ;Zeile = Beginn XRAM
sbr Flags, (1< Max ?
brlo normal
clr XAdresseLow ;Spalten Adresse = 0
inc ZeileL
cpi ZeileL, YMax ;Zeilen Adresse > Max ?
brlo normal
ldi ZeileL, YMin ;Zeile = Beginn XRAM
cbr Flags, (1< Max ?
brne normal2
clr XAdresseLow ;Spalten Adresse = 0
subi ZeileL, -8 ;Zeilen Adresse ++
cpi ZeileL, YMax ;Zeilen Adresse > Max ?
brlo normal2
ldi ZeileL, YMin ;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<