![]() |
AVR-Anwendungen LCD-Ansteuerung mit AVR in Assembler Include-Datei "lcd.inc" |
;
; ***********************************
; * LCD Include Routinen *
; * Version 3, Maerz 2019 *
; * (C)2018/19 avr-asm-tutorial.net *
; ***********************************
;
; ***********************************************
; * L C D I N T E R F A C E R O U T I N E N *
; ***********************************************
;
; +-------+----------------+--------------+
; |Routine|Funktion |Parameter |
; +-------+----------------+--------------+
; |LcdInit|Initiiert die |Ports, Pins |
; | |LCD im einge- | |
; | |stellten Modus | |
; +-------+----------------+--------------+
; |LcdText|Gibt den Text im|Z=2*Tabellen- |
; | |Flash ab Zeile 1| adresse |
; | |aus |0x0D: Naechste|
; | | | Zeile |
; | | |0xFF: Ignorie-|
; | | | re |
; | | |0xFE: Textende|
; +-------+----------------+--------------+
; |LcdSram|Gibt den Text im|Z=SRAM-Adresse|
; | |SRAM aus |R16: Anzahl |
; +-------+----------------+--------------+
; |LcdChar|Gibt ein Zeichen|R16: Zeichen |
; | |aus | |
; +-------+----------------+--------------+
; |LcdCtrl|Gibt Kontrollbe-|R16: Befehl |
; | |fehl aus | |
; +-------+----------------+--------------+
; |LcdPos |Setzt Ausgabe- |ZH: Zeile 0123|
; | |position |ZL: Spalte 0..|
; +-------+----------------+--------------+
; |LcdSpec|Erzeugt Spezial-|Z: 2*Tabellen-|
; | |zeichen | adresse |
; +-------+----------------+--------------+
; | S C H A L T E R L C D D E C I M A L |
; +-------+----------------+--------------+
; |LcdDec2|Gibt zwei Dezi- |R16: Binaer- |
; | |malstellen aus | zahl |
; +-------+----------------+--------------+
; |LcdDec3|Gibt drei Dezi- |R16: Binaer- |
; | |malstellen aus | zahl |
; +-------+----------------+--------------+
; |LcdDec5|Gibt fuenf Dezi-|Z: Binaerzahl |
; | |malstellen aus | 16 Bit |
; +-------+----------------+--------------+
; | S C H A L T E R L C D H E X |
; +-------+----------------+--------------+
; |LcdHex2|Gibt zwei Hexa- |R16: Binaer- |
; | |dezimalen aus | zahl |
; +-------+----------------+--------------+
; |LcdHex4|Gibt vier Hexa- |Z: Binaerzahl |
; | |dezimalen aus | 16 Bit |
; +-------+----------------+--------------+
;
; ***********************************
; P A R A M E T E R - V O R L A G E
; ***********************************
;
; Standard-Parameter-Satz der Einstellungen
;.equ clock = 1000000 ; Taktfrequenz Prozessor in Hz
; LCD-Groesse:
;.equ LcdLines = 1 ; Anzahl Zeilen (1, 2, 4)
;.equ LcdCols = 8 ; Anzahl Zeichen pro Zeile (8..24)
; LCD-Ansteuerung
;.equ LcdBits = 4 ; Busbreite (4 oder 8)
; Wenn 4-Bit-Ansteuerung:
;.equ Lcd4High = 1 ; Busnibble (1=Oberes, 0=Unteres)
;.equ LcdWait = 0 ; Ansteuerung (0 mit Busy, 1 mit Warteschleifen)
; LCD-Datenports
;.equ pLcdDO = PORTA ; Daten-Ausgabe-Port
;.equ pLcdDD = DDRA ; Daten-Richtungs-Port
; LCD-Kontrollports und -pins
;.equ pLcdCEO = PORTB ; Control E Ausgabe-Port
;.equ bLcdCEO = PORTB0 ; Controll E Ausgabe-Portpin
;.equ pLcdCED = DDRB ; Control E Richtungs-Port
;.equ bLcdCED = DDB0 ; Control E Richtungs-Portpin
;.equ pLcdCRSO = PORTB ; Control RS Ausgabe-Port
;.equ bLcdCRSO = PORTB1 ; Controll RS Ausgabe-Portpin
;.equ pLcdCRSD = DDRB ; Control RS Richtungs-Port
;.equ bLcdCRSD = DDB1 ; Control RS Richtungs-Portpin
; Wenn LcdWait = 0:
;.equ pLcdDI = PINA ; Daten-Input-Port
;.equ pLcdCRWO = PORTB ; Control RW Ausgabe-Port
;.equ bLcdCRWO = PORTB2 ; Control RW Ausgabe-Portpin
;.equ pLcdCRWD = DDRB ; Control RW Richtungs-Port
;.equ bLcdCRWD = DDB2 ; Control RW Richtungs-Portpin
; Wenn Dezimalausgabe benoetigt wird:
;.equ LcdDecimal = 1 ; Wenn definiert: einbinden
; Wenn Hexadezimalausgabe benoetigt wird:
;.equ LcdHex = 1 ; Wenn definiert: einbinden
; Wenn nur Simulation im SRAM:
;.equ avr_sim = 1 ; 1=Simulieren, 0=Nicht simulieren
;
; *****************************************
; T E X T A U S G A B E - V O R L A G E N
; *****************************************
;
; Tabellen zum Kopieren fuer verschiedene Formate
;
; --------------------------
; Einzeilige LCD
; 8 Zeichen pro Zeile
; Text_1_8:
; .db " ",0xFE,0xFF
; 01234567
;
; 16 Zeichen pro Zeile
; Text_1_16:
; .db " ",0xFE,0xFF
; 0123456789012345
;
; 20 Zeichen pro Zeile
; Text_1_20:
; .db " ",0xFE,0xFF
; 01234567890123456789
;
; 24 Zeichen pro Zeile
; Text_1_24:
; .db " ",0xFE,0xFF
; 012345678901234567890123
;
; --------------------------
; Zweizeilige LCD
; 16 Zeichen pro Zeile
; Text_2_16:
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 0123456789012345
;
; 20 Zeichen pro Zeile
; Text_2_20:
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 01234567890123456789
;
; 24 Zeichen pro Zeile
; Text_2_24:
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 012345678901234567890123
;
; --------------------------
; Vierzeilige LCD
; 16 Zeichen pro Zeile
; Text_4_16:
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 0123456789012345
;
; 20 Zeichen pro Zeile
; Text_4_20:
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 01234567890123456789
;
; 24 Zeichen pro Zeile
; Text_4_24:
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0x0D,0xFF
; .db " ",0xFE,0xFF
; 012345678901234567890123
;
; *********************************
; P A R A M E T E R C H E C K
; *********************************
;
; Sind alle Parameter korrekt angegeben?
;
; Groesse definiert?
.ifndef LcdLines
.error "LCD line size (LcdLines) undefined!"
.else
.if (LcdLines!=1)&&(LcdLines!=2)&&(LcdLines!=4)
.error "LCD illegal line size (LcdLines)!"
.endif
.endif
.ifndef LcdCols
.error "LCD column size (LcdCols) undefined!"
.else
.if (LcdCols<8)||(LcdCols>24)
.error "LCD illegal column size (LcdCols)!"
.endif
.endif
;
; clock definiert?
.ifndef clock
.error "Clock frequency (clock) undefined!"
.endif
;
; 4- oder 8-Bit-Interface gewaehlt?
.ifndef LcdBits
.error "LCD data bus bits (LcdBits) undefined!"
.else
.if (LcdBits != 4) && (LcdBits != 8)
.error "LCD data bus bits (LcdBits) not 4 or 8!"
.endif
.if LcdBits == 4
.ifndef Lcd4High
.error "LCD 4 bit data bus nibble (Lcd4High) undefined!"
.else
.if (Lcd4High != 0) && (Lcd4High != 1)
.error "LCD 4 bit data bus nibble (Lcd4High) not 0 or 1!"
.endif
.endif
.endif
.endif
;
; LCD data ports
.ifndef pLcdDO
.error "LCD data output port (pLcdDO) undefined!"
.endif
.ifndef pLcdDD
.error "LCD data direction port (pLcdDD) undefined!"
.endif
.if LcdWait == 0
.ifndef pLcdDI
.error "LCD data input port (pLcdDI) undefined!"
.endif
.endif
;
; LCD Control Ports und Pins
.ifndef pLcdCEO
.error "LCD control E output port (pLcdCEO) undefined!"
.endif
.ifndef pLcdCED
.error "LCD control E direction port (pLcdCED) undefined!"
.endif
.ifndef bLcdCEO
.error "LCD control E output pin (bLcdCEO) undefined!"
.endif
.ifndef bLcdCED
.error "LCD control E direction pin (bLcdCED) undefined!"
.endif
.ifndef pLcdCRSO
.error "LCD control RS output port (pLcdCRSO) undefined!"
.endif
.ifndef pLcdCRSD
.error "LCD control RS direction port (pLcdCRSD) undefined!"
.endif
.ifndef bLcdCRSO
.error "LCD control RS output pin (bLcdCRSO) undefined!"
.endif
.ifndef bLcdCRSD
.error "LCD control RS direction pin (bLcdCRSD) undefined!"
.endif
.ifndef LcdWait
.error "LCD operating property (LcdWait) undefined!"
.else
.if LcdWait == 0
.ifndef pLcdCRWO
.error "LCD control RW output port (pLcdCRWO) undefined!"
.endif
.ifndef bLcdCRWO
.error "LCD control RW output pin (bLcdCRWO) undefined!"
.endif
.ifndef pLcdCRWD
.error "LCD control RW direction port (pLcdCRWD) undefined!"
.endif
.ifndef bLcdCRWD
.error "LCD control RW direction pin (bLcdCRWD) undefined!"
.endif
.endif
.endif
;
; *************************************
; S I M U L A T I O N A V R _ S I M
; *************************************
;
; Simulation ermitteln
.ifdef avr_sim
.equ simulation = avr_sim
.else
.equ simulation = 0
.endif
.if simulation == 1
.dseg
SimStart:
SimDisplayPos:
.byte 1
SimCtrlClear:
.byte 1
SimCtrlReset:
.byte 1
SimCtrlInputmode:
.byte 1
SimCtrlDisplay:
.byte 1
SimCtrlCursorShift:
.byte 1
SimCtrlFunctionset:
.byte 1
SimCtrlCharGenRamAdr:
.byte 1
SimCtrlDisplayRamAdr:
.byte 1
SimDataDisplay:
.byte LcdLines*LcdCols
SimEnd:
.cseg
.endif
;
; *********************************
; D A T E N - S E T U P - Z E I T
; *********************************
;
.Macro PulseDelay ; Verzoegerung fuer laengere Pulsdauer
.if clock>1000000 ; Weitere NOPs fuer hoeheren Takt
nop
.endif
.if clock>2000000
nop
.endif
.if clock>3000000
nop
.endif
.if clock>3000000
nop
.endif
.if clock>4000000
nop
.endif
.if clock>5000000
nop
.endif
.if clock>6000000
nop
.endif
.if clock>7000000
nop
.endif
.if clock>8000000
nop
.endif
.if clock>9000000
nop
.endif
.if clock>10000000
nop
.endif
.if clock>11000000
nop
.endif
.if clock>12000000
nop
.endif
.if clock>13000000
nop
.endif
.if clock>14000000
nop
.endif
.if clock>15000000
nop
.endif
.if clock>16000000
nop
.endif
.if clock>17000000
nop
.endif
.if clock>18000000
nop
.endif
.if clock>19000000
nop
.endif
.endmacro
.Macro DataSetup ; Verzoegerung beim Umschalten von RS und RW
.if clock>=4000000
nop
.endif
.if clock>=8000000
nop
.endif
.if clock>=12000000
nop
.endif
.if clock>=16000000
nop
.endif
.if clock>=20000000
nop
.endif
.endmacro
;
; *********************************
; L C D - R O U T I N E N
; *********************************
;
; LcdInit: Ports und Pins initiieren
; Einschalt-Wartezyklus
; Funktionseinstellungen
; LCD loeschen
LcdInit:
; Init der LCD Kontroll-Bits
cbi pLcdCEO,bLcdCEO ; E-Pin low
sbi pLcdCED,bLcdCED ; E-Pin output
cbi pLcdCRSO,bLcdCRSO ; RS-Pin low
sbi pLcdCRSD,bLcdCRSD ; RS-Pin output
.ifdef pLcdCRWO
.ifdef bLcdCRWO
cbi pLcdCRWO,bLcdCRWO ; RW-Pin low
.endif
.endif
.ifdef pLcdCRWD
.ifdef bLcdCRWD
sbi pLcdCRWD,bLcdCRWD ; RW-Pin output
.endif
.endif
; Init der LCD Datenbus-Ports
.if LcdBits == 8
clr R16 ; Datenbus auf Null
.else
in R16,pLcdDO ; Lese Outputbits Datenbus
.if Lcd4High == 1
andi R16,0x0F ; Oberes Nibble loeschen
.else
andi R16,0xF0 ; Unteres Nibble loeschen
.endif
.endif
out pLcdDO,R16 ; Datenbus Output auf Null
DataSetup ; Warte bei hoeheren Taktraten
.if LcdBits == 8
ldi R16,0xFF ; Setze alle Richtungsbits
.else
in R16,pLcdDD ; Lese Richtungsbits Datenbus
.if Lcd4High == 1
ori R16,0xF0 ; Oberes Nibble high
.else
ori R16,0x0F ; Unteres Nibble high
.endif
.endif
out pLcdDD,R16 ; Richtungsbits Datenbus setzen
DataSetup ; Warte bei hoeheren Taktraten
; LCD-Startphase
.if simulation == 0
rcall LcdWait50ms
.endif
; LCD auf 8-Bit-Datenbusbreite einstellen
ldi R16,0x30
rcall Lcd8Ctrl ; im 8-Bit-Modus an LCD-Kontroll
.if simulation == 0
rcall LcdWait5ms ; 5 ms lang warten
.endif
ldi R16,0x30
rcall Lcd8Ctrl ; im 8-Bit-Modus an LCD-Kontroll
.if simulation == 0
rcall LcdWait5ms ; 5 ms lang warten
.endif
ldi R16,0x30
rcall Lcd8Ctrl ; im 8-Bit-Modus an LCD-Kontroll
.if simulation == 0
rcall LcdWait5ms ; 5 ms lang warten
.endif
ldi R16,0x30
rcall Lcd8Ctrl ; im 8-Bit-Modus an LCD-Kontroll
.if simulation == 0
rcall LcdWait5ms ; 5 ms lang warten
.endif
; Wenn 4-Bit-Interface: auf 4-Bit-Datenbus umschalten
.if LcdBits == 4
ldi R16,0x20 ; 4-Bit-Interface
rcall Lcd8Ctrl
.if simulation == 0
rcall LcdWait5ms
.endif
.endif
; Function set
.if LcdBits == 8
ldi R16,0x30 ; 8-Bit-Datenbus
.else
ldi R16,0x20 ; 4-Bit-Datenbus
.endif
.if LcdLines > 1
ori R16,0x08 ; Mehrzeiliges Display
.endif
rcall LcdCtrl
; Display mode
ldi R16,0x0C ; Display an, Unterstrich aus, Cursorblink aus
rcall LcdCtrl
; LCD Entry Mode setzen
ldi R16,0x06 ; Cursor nach rechts, Cursor shift
rcall LcdCtrl
; LCD loeschen
ldi R16,0x01 ; Display loeschen
rjmp LcdCtrl
;
; LcdText
; Gibt den Text im Flash auf der LCD aus
; Z zeigt auf Texttabelle
; 0x0D: Zeilenvorschub und Wagenruecklauf
; 0xFF: Ignoriere (Fuellzeichen)
; 0xFE: Ende des Texts
LcdText:
push R0 ; R0 sichern
ldi R16,LcdLines ; Max Zeilenanzahl
mov R0,R16 ; in R0
LcdText1:
lpm R16,Z+ ; Lese naechstes Zeichen
cpi R16,0xFE ; Endezeichen?
breq LcdText3
brcc LcdText1 ; Ueberlese Fuellzeichen
cpi R16,0x0D ; Zeilenvorschub+Wagenruecklauf
brne LcdText2
dec R0 ; Zeilenzaehler vermindern
breq LcdText1
.if simulation == 1
push ZH
push ZL
push R16
ldi ZH,High(SimDataDisplay) ; Wagenruecklauf und Zeilenvorschub
ldi ZL,Low(SimDataDisplay)
ldi R16,LcdLines
LcdText1a:
adiw ZL,LcdCols
dec R16
cp R16,R0
brne LcdText1a
ldi R16,Low(SimDataDisplay)
sub ZL,R16
ldi R16,High(SimDataDisplay)
sbc ZH,R16
sts SimDisplayPos,ZL
pop R16
pop ZL
pop ZH
.endif
push ZH ; Z sichern
push ZL
ldi ZH,LcdLines ; Zeile berechnen
sub ZH,R0
clr ZL ; Zeilenanfang
rcall LcdPos
pop ZL ; Z wieder herstellen
pop ZH
rjmp LcdText1 ; nachstes Zeichen
LcdText2:
rcall LcdChar ; Zeichen in R16 an LCD ausgeben
rjmp LcdText1 ; Naechstes Zeichen
LcdText3:
pop R0 ; R0 wieder herstellen
ret
;
; LcdSRam gibt Text im SRAM an der aktuellen LCD-Position aus
; Z zeigt auf SRAM-Adresse
; R16: Anzahl Zeichen
LcdSRam:
push R16 ; Rette R16
ld R16,Z+ ; Lese Zeichen
rcall LcdChar ; Zeichen in R16 an LCD ausgeben
pop R16 ; R16 wieder herstellen
dec R16 ; Anzahl Zeichen abwaerts
brne LcdSRam ; weiter Zeichen ausgeben
ret
;
; LcdChar gib Zeichen in R16 an der aktuellen Position aus
; R16: Zeichen
LcdChar:
.if simulation == 1
push ZH
push ZL
push R16
ldi ZH,High(SimDataDisplay)
ldi ZL,Low(SimDataDisplay)
lds R16,SimDisplayPos
inc R16
sts SimDisplayPos,R16
dec R16
add ZL,R16
ldi R16,0
adc ZH,R16
pop R16
st Z,R16 ; Ausgabe in SRAM schreiben
pop ZL
pop ZH
.endif
rjmp LcdData
;
; LcdByte gibt Kontrollbefehl in R16 an die LCD aus
LcdCtrl:
.if simulation == 1
cpi R16,0x80 ; Display RAM-Adresse?
brcs LcdCtrlSim1
sts SimCtrlDisplayRamAdr,R16
push ZH
push ZL
mov ZH,R16
andi R16,0x40
brne LcdCtrlSim0a
clr ZL ; Zeile 1 oder 3
rjmp LcdCtrlSim0b
LcdCtrlSim0a:
ldi ZL,LcdCols ; Zeile 2 oder 4
LcdCtrlSim0b:
mov R16,ZH
andi R16,0x3F
subi R16,LcdCols
brcc LcdCtrlSim0c ; Zeilen 3 oder 4
subi R16,-LcdCols
rjmp LcdCtrlSim0d
LcdCtrlSim0c:
sbrc ZH,6 ; Zeile 4?
subi R16,-LcdCols ; Spaltenlaenge addieren
LcdCtrlSim0d:
add ZL,R16
sts SimDisplayPos,ZL
pop ZL
pop ZH
rjmp LcdCtrlSim9
LcdCtrlSim1:
cpi R16,0x40 ; Character Generator RAM-Adresse?
brcs LcdCtrlSim2
sts SimCtrlCharGenRamAdr, R16
rjmp LcdCtrlSim9
LcdCtrlSim2:
cpi R16,0x20 ; Functionset?
brcs LcdCtrlSim3
sts SimCtrlFunctionSet, R16
rjmp LcdCtrlSim9
LcdCtrlSim3:
cpi R16,0x10 ; Cursor shift
brcs LcdCtrlSim4
sts SimCtrlCursorShift, R16
rjmp LcdCtrlSim9
LcdCtrlSim4:
cpi R16,0x08 ; Display control?
brcs LcdCtrlSim5
sts SimCtrlDisplay, R16
rjmp LcdCtrlSim9
LcdCtrlSim5:
cpi R16,0x04 ; Display control?
brcs LcdCtrlSim6
sts SimCtrlInputmode, R16
rjmp LcdCtrlSim9
LcdCtrlSim6:
cpi R16,0x02 ; Reset?
brcs LcdCtrlSim7
sts SimCtrlReset,R16
rjmp LcdCtrlSim9
LcdCtrlSim7:
cpi R16,0x01 ; Clear?
brcs LcdCtrlSim8
sts SimCtrlClear,R16
; LCD clear display
push ZH
push ZL
ldi ZH,High(SimEnd)
ldi ZL,Low(SimEnd)
clr R16
LcdCtrl7a:
st -Z,R16 ; Rueckwaerts mit Nullen fuellen
cpi ZL,Low(SimStart)
brne LcdCtrl7a
cpi ZH,High(SimStart)
brne LcdCtrl7a
ldi R16,0x01
pop ZL
pop ZH
rjmp LcdCtrlSim9
LcdCtrlSim8: ; 00-Befehl
LcdCtrlSim9:
.endif
.if LcdWait == 0
rcall LcdBusy
.endif
cbi pLcdCRSO,bLcdCRSO ; RS-Bit low
.if LcdBits == 4
push ZL
push R16
in ZL,pLcdDO ; Daten Output Port lesen
.if Lcd4High == 1
andi ZL,0x0F ; Oberes Nibble loeschen
andi R16,0xF0 ; Oberes Nibble Eingabezahl loeschen
.else
andi ZL,0xF0 ; Unteres Nibble Output loeschen
swap R16 ; Oberes und unteres Nibble vertauschen
andi R16,0x0F ; Oberes Nibble loeschen
.endif
or R16,ZL ; Kombinieren
out pLcdDO,R16
rcall LcdPulseE ; E aktivieren
pop R16 ; R16 wieder herstellen
push R16 ; und sichern
in ZL,pLcdDO ; Daten Output Port lesen
.if Lcd4High == 1
andi ZL,0x0F ; Unteres Nibble erhalten
swap R16 ; Unteres und oberes Nibble vertauschen
andi R16,0xF0
.else
andi ZL,0xF0 ; Oberes Nibble erhalten
andi R16,0x0F ; Unteres Nibble erhalten
.endif
or R16,ZL ; Kombinieren
out pLcdDO,R16 ; Auf Datenbus
DataSetup ; Warten bei hoeheren Takten
rcall LcdPulseE ; E aktivieren
pop R16 ; Eingabezahl wieder herstellen
pop ZL ; ZL wieder herstellen
.else
out pLcdDO,R16 ; Eingabezahl auf Datenbus
DataSetup ; Warten bei hoeheren Takten
rcall LcdPulseE ; E aktivieren
.endif
.if LcdWait == 1
andi R16,0xFC ; Obere Bits Kontrollbefehl
brne LcdCtrl1 ; Kurze Verzoegerung
rjmp LcdWait1640us ; 1,64 ms warten
LcdCtrl1:
rjmp LcdWait40us ; 40 us warten
.else
ret
.endif
;
; LcdPos setzt den LCD Cursor an die Position in Z
; ZH: Zeile (0 bis Anzahl Zeilen - 1)
; ZL: Spalte (0 bis Anzahl Spalten - 1)
LcdPos:
cpi ZH,1 ; Zeile = 1?
ldi R16,0x80 ; Zeile 0
.if LcdLines < 2 ; LCD hat nur eine Zeile
rjmp LcdPos1
.endif
brcs LcdPos1
ldi R16,0xC0 ; Zeile 2 bei zweizeiliger LCD
.if LcdLines == 2
rjmp LcdPos1
.endif
breq LcdPos1
ldi R16,0x80+LcdCols ; Zeile 3
cpi ZH,2 ; Zeile = 3
breq LcdPos1
ldi R16,0xC0+LcdCols ; Zeile 4
LcdPos1:
add R16,ZL ; Spaltenadresse addieren
rjmp LcdCtrl
;
; LcdSpec erzeugt Spezialzeichen auf der LCD
; Z zeigt auf 2*Tabellenadresse
; Tabellenformat:
; 1.Byte: Adresse des Zeichens 0b01zzz000,
; 0: Ende der Tabelle
; 2.Byte: Dummy
; 3. bis 10.Byte: Daten der Zeilen 1 bis 8
LcdSpec:
push R0 ; R0 ist Zaehler
LcdSpec1:
lpm R16,Z+ ; Lese Adresse des Zeichens
tst R16 ; Ende der Tabelle?
breq LcdSpec3 ; Ende Tabelle
rcall LcdCtrl ; Adresse schreiben
adiw ZL,1 ; Ueberlese Dummy
ldi R16,8 ; 8 Byte pro Zeichen
mov R0,R16 ; R1 ist Zaehler
LcdSpec2:
lpm R16,Z+ ; Datenbyte lesen
rcall LcdData ; Datenbyte ausgeben
dec R0 ; Abwaerts zaehlen
brne LcdSpec2 ; Weiter Daten ausgeben
rjmp LcdSpec1 ; Naechstes Zeichen
LcdSpec3:
pop R0 ; R0 wieder herstellen
ldi ZH,0 ; Cursor Home
ldi ZL,0
rjmp LcdPos
;
; **********************************
; D E Z I M A L A U S G E B E N
; **********************************
; Routinen zur Ausgabe von Dezimalzahlen
;
.ifdef LcdDecimal
;
; LcdDec2 gibt Binaerzahl in R16 dezimal mit zwei Stellen aus
; ohne Unterdrueckung fuehrender Nullen
LcdDec2:
mov ZL,R16 ; Zahl kopieren
ldi R16,'0'-1 ; Zehnerzaehler
cpi ZL,100 ; Zweistellig?
brcs LcdDec2a
ldi ZL,99 ; dreistellig, auf Maximalwert setzen
LcdDec2a:
inc R16
subi ZL,10
brcc LcdDec2a
rcall LcdData ; Zehner ausgeben
ldi R16,10+'0'
add R16,ZL
rcall LcdData
ret
;
; LcdDec3 gibt Binaerzahl in R16 dezimal mit drei Stellen aus
; mit Unterdrueckung fuehrender Nullen
LcdDec3:
ldi ZH,1 ; Fuehrende Nullen unterdruecken
LcdDec3null: ; behalte fuehrende Nullen-Unterdrueckung bei
push R0
mov ZL,R16 ; Zahl in ZL
ldi R16,100 ; Hunderter
rcall LcdDec3a
rcall LcdData
ldi R16,10 ; Zehner
rcall LcdDec3a
rcall LcdData
ldi R16,'0'
add R16,ZL
rcall LcdData
pop R0
ret
;
LcdDec3a: ; Dezimalziffer ermitteln
clr R0 ; Zaehler
dec R0 ; auf - 1
LcdDec3b:
inc R0 ; Erhoehe Zaehler
sub ZL,R16 ; Subtrahiere 100 oder 10
brcc LcdDec3b ; Kein Ueberlauf: weiter
add ZL,R16 ; Letzte Subtraktion rueckgaengig
tst R0 ; Ziffer Null?
breq LcdDec3c ; Ja, pruefe Nullunterdrueckung
clr ZH ; Keine Nullenunterdrueckung mehr
ldi R16,'0' ; Ziffer in ASCII
add R16,R0
ret
LcdDec3c:
tst ZH ; Nullenunterdrueckung?
breq LcdDec3d
ldi R16,' '
ret
LcdDec3d:
ldi R16,'0'
ret
;
; LcdDec5 gibt Zahl in Z mit fuenf Dezimalstellen aus
; mit Unterdrueckung fuehrender Nullen
LcdDec5:
push R2
push R1
push R0
clr R2 ; Fuehrende Nullen
inc R2 ; unterdruecken
mov R1,ZH ; Kopiere Zahl nach R1:R0
mov R0,ZL
ldi ZH,High(10000) ; 1. Dezimalstelle
ldi ZL,Low(10000)
rcall LcdDec5a ; Konvertiere 1. Dezimalstelle
ldi ZH,High(1000) ; 2.Dezimalstelle
ldi ZL,Low(1000)
rcall LcdDec5a ; Konvertiere 2. Dezimalstelle
ldi ZH,High(100) ; 2.Dezimalstelle
ldi ZL,Low(100)
rcall LcdDec5a ; Konvertiere 3. Dezimalstelle
ldi ZH,High(10) ; 2.Dezimalstelle
ldi ZL,Low(10)
rcall LcdDec5a ; Konvertiere 4. Dezimalstelle
ldi R16,'0'
add R16,R0
rcall LcdChar ; Zeige 5. Dezimalstelle an
pop R0
pop R1
pop R2
ret ; Gib R16 als Dezimal mit drei Stellen aus
;
LcdDec5a: ; Ermittle Dezimalstelle
ldi R16,'0'-1
LcdDec5b:
inc R16 ; Erhoehe Zaehler
sub R0,ZL ; Subtrahiere Dezimalstelle LSB
sbc R1,ZH ; und MSB mit Carry
brcc LcdDec5b
add R0,ZL ; Letzte Subtraction rueckgaengig
adc R1,ZH
cpi R16,'0' ; Nullunterdrueckung?
breq LcdDec5c ; Ja, pruefe
clr R2 ; Nullunterdrueckung ausschalten
rjmp LcdChar
LcdDec5c:
tst R2 ; Nullunterdrueckung aus?
breq LcdDec5d ; Ja
ldi R16,' ' ; Null unterdruecken
LcdDec5d:
rjmp LcdChar
;
.endif
;
; ****************************************
; H E X A D E Z I M A L A U S G E B E N
; ****************************************
; Routinen zur Ausgabe von Dezimalzahlen
.ifdef LcdHex
;
; LcdHex2 gibt Zahl in R16 in Hexadezimalformat
; auf der LCD aus
LcdHex2:
push R16 ; Wird noch gebraucht
swap R16 ; Oberes Nibble in unteres Nibble
rcall LcdNibble
pop R16
LcdNibble:
andi R16,0x0F ; Unteres Nibble erhalten
subi R16,-'0' ; ASCII-Null addieren
cpi R16,'9'+1 ; A bis F?
brcs LcdNibble1 ; Nein
subi R16,-7 ; 7 addieren
LcdNibble1:
rjmp LcdChar ; Zeichen in R16 ausgeben
;
; LcdHex4
LcdHex4:
mov R16,ZH ; Oberes Byte in R16
rcall LcdHex2
mov R16,ZL ; Unteres Byte in R16
rjmp LcdHex2
;
.endif
;
; *********************************
; L C D - A N S T E U E R U N G
; *********************************
;
; Warte bis LCD Busy-Flagge geloescht
; wird nur benoetigt wenn Warten aus
.if LcdWait == 0
LcdBusy:
push R16 ; Inhalt von R16 sichern
.if LcdBits == 8
clr R16 ; Datenbus-Richtung auf Input
.else
in R16,pLcdDD ; Lese Richtungs-Bits
.if Lcd4High == 1
andi R16,0x0F ; Loesche oberes Nibble
.else
andi R16,0xF0 ; Loesche unteres Nibble
.endif
.endif
out pLcdDD,R16 ; Richtungsregister setzen
.if LcdBits == 8
clr R16 ; Alle Ausgabepins low
.else
in R16,pLcdDO ; Lese Ausgabeport
.if Lcd4High == 1
andi R16,0x0F ; Loesche oberes Nibble
.else
andi R16,0xF0 ; Loesche unteres Nibble
.endif
.endif
out pLcdDO,R16 ; Loesche Pull-Ups
cbi pLcdCRSO,bLcdCRSO ; RS-Pin auf Low
DataSetup ; Warten bei hoeheren Takten
sbi pLcdCRWO,bLcdCRWO ; RW-Pin auf High
DataSetup ; Warten bei hoeheren Takten
LcdBusyWarte:
rcall LcdIn ; Aktiviere E, lese Datenport, deaktiviere E
.if LcdBits == 4
rcall LcdPulseE ; Dummy fuer lower nibble
.endif
lsl R16 ; Busy-Flag in Carry schieben
brcs LcdBusyWarte ; Flagge 1, weiter warten
cbi pLcdCRWO,bLcdCRWO ; RW-Pin auf Low
.if LcdBits == 8
ldi R16,0xFF ; Datenbus wieder auf Ausgang
.else
in R16,pLcdDD ; Lese Richtung Datenport
.if Lcd4High == 1
ori R16,0xF0 ; Oberes Nibble High
.else
ori R16,0x0F ; Unteres Nibble High
.endif
.endif
out pLcdDD,R16 ; Richtungsport setzen
pop R16 ; R16 wieder herstellen
ret ; Fertig
; Lese Busy-Flagge in R16
; wird nur benoetigt, wenn Warten ausgeschaltet
LcdIn:
cbi pLcdCRSO,bLcdCRSO ; LCD-RS-Pin auf Low
DataSetup ; Warten bei hoeheren Takten
sbi pLcdCEO,bLcdCEO ; Setze Bit bLcdCEO im LCD-Kontrollport
nop ; Warte einen Takt
PulseDelay ; Warte weitere Zeit bei hoeheren Takten
in R16,pLcdDI ; Lese Datenbus
cbi pLcdCEO,bLcdCEO ; Loesche Bit bLcdCEO
ret
.endif
;
; 1 us Impuls am LCD-E-Pin ausgeben
LcdPulseE:
sbi pLcdCEO,bLcdCEO ; Setze Bit bLcdCEO im LCD-Kontrollport
PulseDelay ; Warte weitere Zeit bei hoeheren Takten
cbi pLcdCEO,bLcdCEO ; Loesche Bit bLcdCEO
ret
;
; R16 im 8-Bit-Modus an LCD-Kontrolle ausgeben
Lcd8Ctrl:
.if simulation == 1
push R16
clr R16
sts SimDisplayPos,R16
pop R16
.endif
.if LcdBits == 4
push ZL ; ZL sichern
in ZL,pLcdDO ; Daten Output Port lesen
.if Lcd4High == 1
andi ZL,0x0F ; Oberes Nibble Output loeschen
andi R16,0xF0 ; Unteres Nibble Eingabezahl loeschen
.else
andi ZL,0xF0 ; Unteres Nibble Output loeschen
swap R16 ; Oberes und unteres Nibble vertauschen
andi R16,0x0F ; Unteres Nibble Daten isolieren
.endif
or R16,ZL ; Kombinieren
pop ZL
.endif
out pLcdDO,R16 ; Daten auf LCD Output Port
cbi pLcdCRSO,bLcdCRSO ; RS-Bit low
DataSetup ; Warten bei hoeheren Takten
rjmp LcdPulseE ; E-Puls ausloesen
;
; LcdData: R16 im eingestellten Modus Datenausgabe an LCD
; R16: Zeichen
LcdData:
.if LcdWait == 0
rcall LcdBusy ; Warte auf Busy-Flag der LCD
.endif
sbi pLcdCRSO,bLcdCRSO ; Setze RS-Bit
DataSetup ; Warten bei hoeheren Takten
.if LcdBits == 4
push ZL
push R16
in ZL,pLcdDO ; Daten Output Port lesen
.if Lcd4High == 1
andi ZL,0x0F ; Oberes Nibble loeschen
andi R16,0xF0 ; Unteres Nibble Eingabezahl loeschen
.else
andi ZL,0xF0 ; Unteres Nibble Output loeschen
swap R16 ; Oberes und unteres Nibble vertauschen
andi R16,0x0F ; Oberes Nibble loeschen
.endif
or R16,ZL ; Kombinieren
out pLcdDO,R16
DataSetup ; Warten bei hoeheren Takten
rcall LcdPulseE ; E aktivieren
pop R16 ; R16 wieder herstellen
push R16 ; und wieder sichern
in ZL,pLcdDO ; Daten Output Port lesen
.if Lcd4High == 1
andi ZL,0x0F ; Unteres Nibble Output erhalten
swap R16 ; Unteres zum oberen Nibble machen
andi R16,0xF0 ; Oberes Nibble Eingabezahl erhalten
.else
andi ZL,0xF0 ; Oberes Nibble Output erhalten
andi R16,0x0F ; Unteres Nibble Eingabezahl erhalten
.endif
or R16,ZL ; Kombinieren
out pLcdDO,R16 ; Auf Datenbus
DataSetup ; Warten bei hoeheren Takten
rcall LcdPulseE ; E aktivieren
pop R16 ; Eingabezahl wieder herstellen
pop ZL ; ZL wieder herstellen
.else
out pLcdDO,R16 ; Eingabezahl auf Datenbus
DataSetup ; Warten bei hoeheren Takten
rcall LcdPulseE ; E aktivieren
.endif
.if LcdWait == 1
rjmp LcdWait40us
.endif
ret
;
; ********************************
; W A R T E R O U T I N E N
; ********************************
;
; 50 ms lang warten
; Da bei Taktfrequenzen oberhalb von 3,1 MHz die
; maximale Dauer der Z-Schleife ueberschritten
; wuerde, wird 10 mal die 5ms-Routine aufgerufen
LcdWait50ms:
push R16 ; 10 mal 5 ms aufrufen
ldi R16,10
LcdWait50ms1:
rcall LcdWait5ms
dec R16
brne LcdWait50ms1
pop R16
ret
;
LcdWait5ms:
; 5 ms warten, RCALL 3 Takte
.equ cLcdZ5ms = (5*clock/1000 - 18 + 2) / 4
push ZH ; Rette ZH, + 2 Takte
push ZL ; Rette ZL, + 2 Takte
ldi ZH,High(cLcdZ5ms) ; + 1 Takt
ldi ZL,Low(cLcdZ5ms) ; + 1 Takt
rjmp LcdWaitZ ; + 2 Takte
; Insgesamt: 11 Takte
;
.if LcdWait == 1 ; mit Wait-Zyklen
; 1,64 ms warten
.equ cLcdZ1640us = (164*(clock/1000)/100 - 18 + 2) / 4
LcdWait1640us:
push ZH ; Rette ZH
push ZL ; Rette ZL
ldi ZH,High(cLcdZ1640us)
ldi ZL,Low(cLcdZ1640us)
rjmp LcdWaitZ
;
; 40 us warten
.equ cLcdZ40us = (40*clock/1000000 - 18 + 2) / 4
LcdWait40us:
push ZH ; Rette ZH
push ZL ; Rette ZL
ldi ZH,High(cLcdZ40us)
ldi ZL,Low(cLcdZ40us)
rjmp LcdWaitZ
.endif
;
; Warteroutine mit Z Zyklen
LcdWaitZ: ; 11 Takte
sbiw ZL,1 ; Abwaerts zaehlen, 2 Takte
brne LcdWaitZ ; Nicht Null, weiter, 2 Takte bei Sprung, 1 Takt am Ende
pop ZL ; ZL wieder herstellen, 2 Takte
pop ZH ; ZH wieder herstellen, 2 Takte
ret ; Zurueck, 4 Takte
; Insgesamt: 11 + 4 * (Z - 1) + 3 + 8
;
; Anzahl Takte = 11 + 4 * (Z - 1) + 3 + 8
; 11: RCALL, PUSH, LDI, LDI, RJMP
; 4: 4 Takte pro Z-Zyklus (minus 1)
; 3: 3 Takte fuer letzten Zyklus
; 8: POP, RET
; = 4 * Z + 11 - 4 + 3 + 8
; = 4 * Z + 18
; Z = (Anzahl Takte - 18 + 2) / 4
; +2: Aufrunden vor Teilen durch 4
;
; End of include file
;