;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; LCD-Routinen ;; ;; ============ ;; ;; ;; ;; ;; ;; 4bit-Interface ;; ;; DB4-DB7: PC0-PC3 ;; ;; RS: PC4 ;; ;; E: PC5 ;; ;; R/W: PD0 ;; ;; ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; es werden in dieser Routine die Variablen: temp1, temp2 ; sie müssen im eigentlichen Programm noch Registern zugeordnet werden. .equ LCD_PORT = PORTC .equ LCD_DDR = DDRC .equ PIN_E = 5 .equ PIN_RS = 4 .equ PIN_RW = 0 .equ LCD_PORT_RW = PORTD .equ LCD_DDR_RW = DDRD .equ LCD_PIN = PIND ; wird immmer nach dem Senden eines Befehls oder Datenbytes an das LCD aufgerufen ; ist das Busy-Flag bereit kann die nächste Operation beginnen lcd_busy: sbi LCD_DDR, PIN_RW ; setzt PC3 auf Eingang, so dass das Busy Flag gelesen werden kann. sbi LCD_PORT_RW, PIN_RW ; setze das LCD auf "lesen" cbi LCD_PORT, PIN_RS ; setze das LCD auf "Befehl" lcd_busy_2: nop sbi LCD_PORT, PIN_E nop in temp1, LCD_PIN ; lade den aktuellen Zustand des LCD_Port in das Register temp1 nop cbi LCD_PORT, PIN_E nop sbi LCD_PORT, PIN_E nop in temp2, LCD_PIN nop cbi LCD_PORT, PIN_E swap temp1 andi temp1, 0b10000000 breq lcd_busy_2 ; wenn der Vergleich zutrifft, also das Busy-Flag gesetzt ist, werden die LCD_Port-Daten erneut gelesen etc. cbi LCD_DDR, PIN_RW ; setzt PC3 auf Ausgang, so dass der Pin wieder zur Ausgabe verwendet werden kann. ret ; wird immer ausgeführt, wenn Daten oder Befehle zur Ausgabe an das LCD bereitstehen. lcd_enable: sbi LCD_PORT, PIN_E ; setze das Enable-Bit des LCD_PORT, so dass der aktuelle Wert (als Befehl oder Daten) ausgegeben wird nop nop nop cbi LCD_PORT, PIN_E ; Wert wurde einmal übernommen, PIN_E wird also wieder zurückgesetzt werden ; siehe vorheriger Aufruf "lcd_busy" ret ; wird immer ausgeführt, wenn ein Befehl übertragen werden soll lcd_command: cbi LCD_PORT, PIN_RS ; setze PIN_RS auf 0, so dass das LCD bereit ist als nächstes einen Befehl zu empfangen. mov temp2, temp1 ; in Register temp1 steht das zu sendende Befehlsbyte, es wird nun ebenfalls in das Register temp2 bewegt swap temp1 ; es müssen zuerst die oberen HalbBytes in das LCD-Modul geschrieben werden, damit im 4 Bit Modus diese Bits automatisch ; beim Schreiben des unteren HalbBytes in den oberen Bereich der LCD-Arbeitsregister geschoben werden andi temp1, 0b00001111 ; diese Maskierung des unteren Halbbytes von temp1 bewirkt, dass das obere Halbbyte seinen derzeitigen Zustand beibehält out LCD_PORT, temp1 ; hier erfolgt konkret die Ausgabe des oberen Nibble rcall lcd_enable ; sorgt dafür, dass die Werte vom LCD auch tatsächlich übernommen werden andi temp2, 0b00001111 ; es soll das untere Nibble vom ursprünglichen zu sendenden Byte übertragen werden, das obere Nibble soll dabei unbeeinflusst bleiben out LCD_PORT, temp2 ; hier erfolgt konkret die Ausgabe des unteren Nibble rcall lcd_enable ; sorgt dafür, dass die Werte vom LCD auch tatsächlich übernommen werden rcall lcd_busy ret ; wird immer ausgeführt, wenn Daten übertragen werden sollen lcd_data: sbi LCD_PORT, PIN_RS ; setze PIN_RS auf 0, so dass das LCD bereit ist als nächstes Daten zu empfangen. mov temp2, temp1 ; in Register temp1 steht das zu sendende Befehlsbyte, es wird nun ebenfalls in das Register temp2 bewegt swap temp1 ; es müssen zuerst die oberen HalbBytes in das LCD-Modul geschrieben werden, damit im 4 Bit Modus diese Bits automatisch ; beim Schreiben des unteren HalbBytes in den oberen Bereich der LCD-Arbeitsregister geschoben werden andi temp1, 0b00001111 ; diese Maskierung des unteren Halbbytes von temp1 bewirkt, dass das obere Halbbyte seinen derzeitigen Zustand beibehält out LCD_PORT, temp1 ; hier erfolgt konkret die Ausgabe des oberen Nibble rcall lcd_enable ; sorgt dafür, dass die Werte vom LCD auch tatsächlich übernommen werden andi temp2, 0b00001111 ; es soll das untere Nibble vom ursprünglichen zu sendenden Byte übertragen werden, das obere Nibble soll dabei unbeeinflusst bleiben out LCD_PORT, temp2 ; hier erfolgt konkret die Ausgabe des unteren Nibble rcall lcd_enable ; sorgt dafür, dass die Werte vom LCD auch tatsächlich übernommen werden rcall lcd_busy ret ; wird zum Löschen des LCD-Inhaltes aufgerufen lcd_clear: ldi temp1, 0b00000001 ; löscht den Bildschirminhalt rcall lcd_command ; gibt das Kommando an das LCD ret ; wird aufegrufen, wenn der Displayinhalt nach rechts verschoben werden soll lcd_shift_right: ldi temp1, 0b00011100 ; Displayinhalt nach rechts schieben: S/C (PC3) = 1; R/L (PC2) = 1 rcall lcd_command ; "Befehlsdaten" an das LCD senden ret ; wird aufgerufen, wenn der Displayinhalt nach links verschoben werden soll lcd_shift_left: ldi temp1, 0b00011000 ; Displayinhalt nach links schieben: S/C (PC3) = 1; R/L (PC2) = 0 rcall lcd_command ; "Befehlsdaten" an das LCD senden ret ; setze den Cursor auf die Startposition lcd_cursor_home: ldi temp1, 0b00000010 ; setze Cursor auf die Startposition PC1 = 1 rcall lcd_command ; übernehme Daten am LCD ret lcd_init: ldi temp1, 0xFF ; setze alle Pins des LCD_PORT auf Ausgang out LCD_DDR, temp1 ldi temp3,6 powerupwait: rcall delay5ms dec temp3 brne powerupwait ldi temp1, 0b00000011 ; muss 3mal hintereinander gesendet out LCD_PORT, temp1 ; werden zur Initialisierung rcall lcd_enable ; 1. rcall lcd_busy rcall lcd_enable ; 2. rcall lcd_busy rcall lcd_enable ; 3. Mal rcall lcd_busy ldi temp1, 0b00000010 ; oberes Nibble des zu übertragenden Bytes -> 4Bit-Modus ein out LCD_PORT, temp1 ; Áusgabe des oberen Nibble des zu übertragenden Bytes rcall lcd_enable ; Übernahme der "Befehlsdaten" vom LCD rcall lcd_busy ldi temp1, 0b00101000 ; Zeigt das gesamte zu übertragende Byte, wobei der obere Teil bereits gesendet worden ist -> 4Bit-Modus; 2Zeiliges Display rcall lcd_command ; Übernahme der "Befehlsdaten" vom LCD ldi temp1, 0b00001100 ; Display on, Cursor off: Cursor blinkt nicht rcall lcd_command ; Übernahme der "Befehlsdaten" vom LCD ldi temp1, 0b00000111 ; Modus des LCD festlegen --> Displayinhalt weiterschieben, Cursorposition inkrementieren rcall lcd_command ; Übernahme der "Befehlsdaten" vom LCD ret ; 5ms Pause delay5ms: ldi temp1, $21 WGLOOP0: ldi temp2, $C9 WGLOOP1: dec temp2 brne WGLOOP1 dec temp1 brne WGLOOP0 ret lcd_flash_string: push temp1 ; sichere den Inhalt von temp1 auf dem Stack push ZH ; sichere den Inhalt der ersten Position, auf die der Z-Ponter zeigt ? push ZL ; siehe oben lcd_flash_string_1: lpm temp1, Z+ ; laden den Inhalt der Speicheradresse, auf die der Z-Pointer gerade zeigt in temp1 und inkrementiere den Z-Pointer, so dass er auf die nächste Adresse zeigt cpi temp1, 0 ; Verlgeiche den Inhalt von temp1 mit 0. 0 gibt hier das Ende der Zeichenkette an. breq lcd_flash_string_2 ; wenn das Ende des Strings erreicht wurde wird das Beenden der Subroutine eingeleitet rcall lcd_data ; gebe das aktuelle Zeichen am LCD aus rjmp lcd_flash_string_1 ; direkter Sprung auf eine Markierung erwartet im Gegensatz zu"rcall" keinen weiteren "ret"-Befehl lcd_flash_string_2: pop ZL ; stelle den Inhalt vorherigen Zustand des Z-Pointer wieder her. pop ZH ; siehe oben pop temp1 ; schreibe den alten Wert von temp1 zurück ret lcd_number: push temp2 ; sichere den Inhalt von Register temp2 ; wird für Zwischenergebnisse gebraucht ldi temp2, '0' ; lädt den ASCII-Wert von 0 in temp2, da die Zahlen später als ASCII-Wert ausgegeben werden lcd_number_10: subi temp1, 10 ; abzählen wieviele Zehner in brcs lcd_number_1 ; der Zahl enthalten sind inc temp2 ; temp2 wird so lange die Subraktion nicht negativ wird erhöht --> temp2 beinhaltet also den Wert der 10er Stelle rjmp lcd_number_10 lcd_number_1: push temp1 ; sichere temp1 auf dem Stack, da temp1 für die Ausgabe über die Subtroutine "lcd_data" benötigt wird mov temp1,temp2 ; schreibe den Wert von temp2 in temp1 rcall lcd_data ; die Zehnerstelle ausgeben pop temp1 ; den Rest des Wertes also die 1er Stelle ausgeben subi temp1, -10 ; 10 wieder dazuzählen, da zuvor 10 zu viel abgezogen wurden ; das Subtrahieren von -10 ; = Addition von +10 ist ein Trick ; da kein addi Befehl existiert ldi temp2, '0' ; Wieder den ASCII-Wert von 0 in temp2 laden add temp1, temp2 ; den ASCII-Wert von 0 zu dem Wert der 1er Stelle addieren rcall lcd_data ; Die 1er Stelle als ASCII-Wert ausgeben pop temp2 ; Register temp2 wieder herstellen ret