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...
|