Pfad: Home => AVR-DE => Anwendungen => LCD-Ansteuerung mit AVR => Include
LCD klein AVR-Anwendungen

LCD-Ansteuerung mit AVR in Assembler

Include-Datei "lcd.inc"
Logo

Include-Datei für eine LCD an einem AVR

Der Assembler-Quellcode, wie er hier angezeigt wird, ist hier downloadbar.

;
; *********************************
; * LCD include routines          *
; * (C)2018 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 Dezimalausgaberoutinen erwuenscht sind:
  ;.equ LcdDecimal = 1
; Wenn Hexadezimalausgabe erwuenscht ist:
  ;.equ LcdHex = 1
; 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
;
; *********************************
;      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 pLcdCRWD
      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
  .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
  ; 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
    out pLcdDO,R16 ; Auf Datenbus
    rcall LcdPulseE ; E aktivieren
    pop R16 ; Eingabezahl wieder herstellen
    pop ZL ; ZL wieder herstellen
    .else
    out pLcdDO,R16 ; Eingabezahl auf Datenbus
    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
; *********************************
;
.if LcdWait == 0
  ; Warte bis LCD Busy-Flagge geloescht
  ;   wird nur benoetigt wenn Warten ausgeschaltet ist
  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
    sbi pLcdCRWO,bLcdCRWO ; RW-Pin auf High
  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 ist
  LcdIn:
    cbi pLcdCRSO,bLcdCRSO ; LCD-RS-Pin auf Low
    sbi pLcdCEO,bLcdCEO ; Setze Bit bLcdCEO im LCD-Kontrollport
    nop ; Warte einen Takt
    .if clock>1000000
      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
    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
  .if clock>1000000
    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
  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
  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
  .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 Eingabezahl loeschen
      .endif
    or R16,ZL ; Kombinieren
    out pLcdDO,R16
    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
    rcall LcdPulseE ; E aktivieren
    pop R16 ; Eingabezahl wieder herstellen
    pop ZL ; ZL wieder herstellen
    .else
    out pLcdDO,R16 ; Eingabezahl auf Datenbus
    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
;



Lob, Tadel, Fehlermeldungen, Genöle und Geschimpfe oder Spam bitte über das Kommentarformular an mich.

Zum Anfang dieser Seite

©2018 by http://www.avr-asm-tutorial.net