LCD_4x27.asm


1
;LCD-Routinen für WD-C2704M - Text-Punktmatrix-LCD
2
;mit 2 Controllern HD44780. 4 Zeilen je 27 Zeichen
3
4
;Version mit frei definierbaren Steuerleitungen.
5
6
;Diese Routinensammlung baut auf Beispielen von Andreas Schwarz
7
;und T.S. auf und ist von ...HanneS... zusammengestellt worden.
8
; hannes.lux@gmx.de / www.hannes.de.md
9
10
;============================================================================
11
;Diese Sammlung enthält folgende Routinen:
12
;----------------------------------------------------------------------------
13
;  lcd_init             ;initialisiert und löscht beide Controller des LCD
14
;----------------------------------------------------------------------------
15
;  lcd_clear            ;löscht das LCD
16
;----------------------------------------------------------------------------
17
;  lcd_command          ;übergibt das Byte im Register "wl" als Kommando
18
                        ;an das LCD
19
;----------------------------------------------------------------------------
20
;  lcd_data             ;übergibt das Byte im Register "wl" zur Textausgabe
21
;----------------------------------------------------------------------------
22
;  locate      ;Macro zum Einstellen der Ausgabeposition.
23
                        ;Aufruf: locate Zeile,Spalte
24
                        ;Zeile: 0-3, Spalte: 0-26
25
;============================================================================
26
;Die Datei "LCDprint.inc" stellt einige Ausgaberoutinen zur Verfügung, die
27
;die Ausgabe von Texten und Zahlen erleichtert. Zusammen mit "locate" wird
28
;die Ausgabe von Menütexten, Zahlen usw. ähnlich einfach wie in BASIC.
29
;============================================================================
30
;Vor Schreibzugriffen wird das Busy-Flag des LCD ausgelesen und
31
;auf LCD-Bereitschaft gewartet.
32
33
;Für die Auswahl des zuständigen Controllers werden die zwei Bits 
34
;"lcdcontroller1" und "lcdcontroller2" im Register "FLAGS"
35
;des Hauptprogramms genutzt. Sie müssen dort deklariert werden.
36
;Die Übergabe der Daten erfolgt über das Register "wl", das im Hauptprogramm
37
;zu deklarieren ist. Weiterhin muss das Register "wh" deklariert sein.
38
;============================================================================
39
;Anschlussbelegung:     ;Die 4 Datenleitungen sollten in einem Nibble liegen.
40
                        ;Dies kann ein unteres oder oberes Nibble sein.
41
                        ;Die Auswahl geschieht mit "lcddatmsk"
42
                        ;Die Steuerleitungen des LCD können über alle
43
                        ;verfügbaren Ports frei gewählt werden, was diesen
44
                        ;Treiber sehr flexibel macht.
45
;----------------------------------------------------------------------------
46
.equ lcdport=portc      ;Port, an dem die 4 Datenleitungen des LCD
47
                        ;angeschlossen sind.
48
.equ lcddatmsk=$0f      ;Maske auf LCD-Datenpins.
49
                        ;$0f - LCD ist an Bit 0...3 des Ports angeschlossen.
50
                        ;$f0 - LCD ist an Bit 4...7 des Ports angeschlossen.
51
;----------------------------------------------------------------------------
52
.equ rsport=portd       ;Port, an dem RS angeschlossen ist
53
.equ rs=5               ;Pin, an dem RS angeschlossen ist
54
;----------------------------------------------------------------------------
55
.equ enable1port=portd  ;Port Anschluss Enable des ersten Controllers.
56
.equ enable1=6          ;Pin Anschluss Enable des ersten Controllers.
57
;----------------------------------------------------------------------------
58
.equ enable2port=portd  ;Port Anschluss Enable des zweiten Controllers.
59
.equ enable2=7          ;Pin Anschluss Enable des zweiten Controllers.
60
;----------------------------------------------------------------------------
61
.equ rwport=portd       ;Port, an dem R/W angeschlossen ist
62
.equ rw=0               ;Pin, an dem R/W angeschlossen ist
63
;============================================================================
64
65
66
;=============================================================================
67
;  locate Zeile,Spalte  ;Setzt die Ausgabeposition des LCD auf
68
                        ;Zeile (0...3) und Spalte (0...26).
69
                        ;Dabei wird auch der für die Ausgabe zuständige
70
                        ;Controller ausgewählt.
71
;-----------------------------------------------------------------------------
72
.macro locate ;Zeile (0...3), Spalte (0...27)
73
                        ;Positionierung der Ausgabeposition
74
                            ;Beide Controller erstmal aus:
75
 cbr flags,(1<<lcdcontroller2)|(1<<lcdcontroller1)  
76
                            ;erforderlichen Controller aktivieren:
77
 sbr flags,((@0 & 2)<<(lcdcontroller2-1))|((!(@0 & 2))<<lcdcontroller1)
78
 push wl                    ;Register sichern
79
                            ;Zeile in Bit 6, Spalte in den Rest:
80
 ldi wl,128+((@0 & 1)<<6)+(@1 & 63)
81
 rcall lcd_command          ;an LCD als Befehl ausgeben
82
 pop wl                     ;Register wiederherstellen
83
.endmacro
84
85
86
;============================================================================
87
; Aufrufbare Routine, initialisiert und löscht das LCD
88
;----------------------------------------------------------------------------
89
lcd_init:               ;LCD-Initialisierung
90
91
 sbr flags,(1<<lcdcontroller1)|(1<<lcdcontroller2)
92
                            ;erstmal beide LCD-Controller aktivieren
93
94
 in wl,lcdport-1            ;Datenrichtung LCD-Datenport holen
95
 sbr wl,lcddatmsk           ;benutzte Datenpins als Ausgang
96
 out lcdport-1,wl           ;definieren
97
 sbi rsport-1,rs            ;RS-Leitung als Ausgang
98
 sbi enable1port-1,enable1  ;Enable1-Leitung als Ausgang
99
 sbi enable2port-1,enable2  ;Enable2-Leitung als Ausgang
100
 sbi rwport-1,rw            ;Read/Write-Leitung als Ausgang
101
102
 push xl                    ;Inhalt sichern
103
 ldi xl,50                  ;>15ms warten, nachdem Vcc > 4,5V ist
104
powerupwait:                ;hier 250ms warten, dafür keine Spannungsprüfung
105
 rcall delay5ms
106
 dec xl
107
 brne powerupwait
108
 pop xl                     ;alten Inhalt zurück holen
109
110
 cbi rwport,rw              ;Steuerleitungen auf
111
 cbi rsport,rs              ;L-Pegel legen
112
;Startsequenz
113
 in wl,lcdport              ;ganzen Port einlesen
114
 cbr wl,lcddatmsk           ;Platz für Datenbits löschen
115
 ldi wh,0b00110011          ;benötigte Datenbits in beide Nibbles
116
 andi wh,lcddatmsk          ;nur das genutzte Nibble
117
 or wl,wh                   ;übernehmen
118
 out lcdport,wl             ;und wieder ausgeben
119
;dreimal ausgeben
120
 rcall lcd_impuls           ;1. Ausgabe (von 3)
121
 rcall delay5ms             ;>4,1ms warten
122
 rcall lcd_impuls           ;2
123
 rcall delay5ms             ;>0,1ms warten
124
 rcall lcd_impuls           ;und 3!
125
 rcall delay5ms             ;Ab hier BUSY prüfen
126
;4-Bit-Modus aktivieren
127
 in wl,lcdport              ;ganzen Port einlesen
128
 cbr wl,lcddatmsk           ;Platz für Datenbits löschen
129
 ldi wh,0b00100010          ;benötigte Datenbits in beide Nibbles
130
 andi wh,lcddatmsk          ;nur das genutzte Nibble
131
 or wl,wh                   ;übernehmen
132
 out lcdport,wl             ;und wieder ausgeben
133
 rcall lcd_impuls
134
 rcall delay5ms
135
;ab jetzt 4-Bit-Zugriff mit Busy-Abfrage
136
 ldi wl,0b00101000          ;4bit-Modus einstellen
137
 rcall lcd_command
138
139
 ldi wl,0b00001100          ;Display ein, Cursor aus
140
 rcall lcd_command
141
142
 ldi wl,0b00000100          ;Entry-Mode
143
 rcall lcd_command
144
                            ;(Code geht weiter...)
145
146
147
;============================================================================
148
; Aufrufbare Routine, löscht das LCD
149
;----------------------------------------------------------------------------
150
lcd_clear:  ;Sendet den Befehl zur Löschung des Displays
151
 sbr flags,(1<<lcdcontroller1)|(1<<lcdcontroller2) ;beide Controller
152
 ldi wl,0b00000001          ;Display löschen
153
 rcall lcd_command
154
 cbr flags,1<<lcdcontroller2;nur ersten Controller aktiviert lassen
155
 rcall delay5ms
156
 ret
157
158
159
;============================================================================
160
; Aufrufbare Routine, sendet den Befehl in wl an das LCD
161
;----------------------------------------------------------------------------
162
lcd_command:            ;sendet den Befehl in wl an das LCD
163
 rcall lcd_busy             ;LCD auf Bereitschaft prüfen
164
 push wh                    ;für Nibble-Wahl
165
 ldi wh,lcddatmsk           ;Referenz für Nibblewahl
166
 push wl                    ;Commandobyte sichern
167
 sbrc wh,0                  ;wenn LCD am unteren Nibble angeschlossen ist
168
 swap wl                    ;dann oberes Nibble nach unten
169
 cbi rsport,rs              ;RS löschen
170
 rcall lcd_nibble           ;erstes Nibble ausgeben...
171
 pop wl                     ;Kopie vom Commandobyte holen
172
 sbrc wh,4                  ;wenn LCD am oberen Nibble angeschlossen ist
173
 swap wl                    ;dann unteres Nibble nach oben
174
 pop wh                     ;wieder frei
175
 cbi rsport,rs              ;RS löschen
176
 rjmp lcd_nibble            ;zweites Nibble ausgeben und zurück...
177
178
179
;============================================================================
180
; Aufrufbare Routine, sendet das Zeichen in wl an das LCD
181
;----------------------------------------------------------------------------
182
lcd_data:               ;sendet das Zeichen in wl an das LCD
183
 rcall lcd_busy             ;LCD auf Bereitschaft prüfen
184
 
185
 push wh                    ;für Nibble-Wahl
186
 ldi wh,lcddatmsk           ;Referenz für Nibblewahl
187
 push wl                    ;Datenbyte sichern
188
 sbrc wh,0                  ;wenn LCD am unteren Nibble angeschlossen ist
189
 swap wl                    ;dann oberes Nibble nach unten
190
 sbi rsport,rs              ;RS setzen
191
 rcall lcd_nibble           ;erstes Nibble ausgeben...
192
 pop wl                     ;Kopie vom Datenbyte holen
193
 sbrc wh,4                  ;wenn LCD am oberen Nibble angeschlossen ist
194
 swap wl                    ;dann unteres Nibble nach oben
195
 pop wh                     ;wieder frei
196
 sbi rsport,rs              ;RS setzen
197
lcd_nibble:
198
 andi wl,lcddatmsk          ;Daten auf vom LCD genutztes Nibble maskieren
199
 push wh                    ;benutztes Register sichern
200
 in wh,lcdport              ;ganzen Port einlesen
201
 cbr wh,lcddatmsk           ;nur LCD-Datenbits löschen
202
 or wh,wl                   ;neue Datenbits einfügen
203
 out lcdport,wh             ;ausgeben
204
 pop wh                     ;alten Wert in Register wiederherstellen
205
lcd_impuls:                 ;erzeugt den Enable-Puls
206
 sbrc flags,lcdcontroller1  ;Controller 1?
207
 sbi enable1port,enable1    ;ja, Enable high
208
 sbrc flags,lcdcontroller2  ;Controller 2?
209
 sbi enable2port,enable2    ;ja, Enable2 high
210
 rcall lcd_impulsR          ;7 Takte warten
211
 in wl,lcdport-2            ;im Readmode LCD auslesen
212
 cbi enable1port,enable1    ;Enable wieder low
213
 cbi enable2port,enable2    ;Enable wieder low
214
lcd_impulsR: 
215
 ret                        ;und wieder zurück...
216
217
218
;============================================================================
219
; Interne Routine, wartet bis LCD bereit ist
220
;----------------------------------------------------------------------------
221
lcd_busy:               ;wartet bis LCD bereit ist
222
 push wl                    ;benutzte Register
223
 push wh                    ;sichern
224
 ldi wh,0                   ;maximale Wartezeit für Not-Abbruch
225
lcd_busy1:
226
 dec wh                     ;Wartezeit runter
227
 breq lcd_busy2             ;abgelaufen? Not-Abbruch...
228
 in wl,lcdport-1            ;nein, Datenrichtung LCD-Datenport holen 
229
 cbr wl,lcddatmsk           ;LCD-Datenport auf Lesezugriff
230
 out lcdport-1,wl           ;schalten
231
 sbi rwport,rw              ;R/W am LCD setzen
232
 cbi rsport,rs              ;RS am LCD löschen um Busy abzufragen
233
 rcall lcd_impuls           ;Enable-Impuls und LCD H-Nibble lesen
234
 swap wl                    ;Lesenibble (Bit 4...7 des LCD) nach oben
235
 push wl                    ;und sichern
236
 rcall lcd_impuls           ;Enable-Impuls und LCD L-Nibble lesen (Dummy)
237
 pop wl                     ;oberes Nibble wiederholen, unteres verwerfen
238
 rol wl                     ;Busy-Flag ins Carry
239
 brcs lcd_busy1             ;noch gesetzt? ja, nochmal auslesen...
240
lcd_busy2: 
241
 cbi rwport,rw              ;nein, R/W am LCD wieder löschen
242
 in wl,lcdport-1            ;Datenrichtung LCD-Datenport holen 
243
 sbr wl,lcddatmsk           ;LCD-Datenport auf Schreibzugriff
244
 out lcdport-1,wl           ;schalten
245
 pop wh                     ;Register 
246
 pop wl                     ;wiederherstellen
247
 ret
248
249
250
;============================================================================
251
; Interne Routine, erzeugt (vernichtet??) Wartezeit
252
;----------------------------------------------------------------------------
253
delay5ms:               ;5ms Pause für manche Befehle
254
 push zl                    ;alten Inhalt
255
 push zh                    ;sichern
256
 ldi zh,high(clock/11000*50);Verzögerungswert für 50ms
257
 ldi zl,low(clock/11000*50) ;setzen
258
delay5ms1:
259
 rcall delay5ms2            ;7 Takte vertrödeln
260
 sbiw zh:zl,1               ;-1 (2 weitere Takte)
261
 brne delay5ms1             ;0? nein, nochmal (2 Takte)
262
 pop zh                     ;alten Inhalt
263
 pop zl                     ;zurück holen
264
delay5ms2: 
265
 ret                        ;zurück...