;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; LCD-Routinen ;; ;; ============ ;; ;; ;; ;; ;; ;; 4bit-Interface ;; ;; DB4-DB7: PC0-PC3 ;; ;; RS: PC4 ;; ;; E: PC5 ;; ;; ;; ;; ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; es werden in dieser Routine die Variablen: temp1, temp2 verwendet ; 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 ; 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 ; warten bis wieder auf das LCD zugegriffen werden kann nop nop cbi LCD_PORT, PIN_E ; Wert wurde einmal übernommen, PIN_E wird also wieder zurückgesetzt werden ret ; wird immer ausgeführt, wenn ein Befehl übertragen werden soll lcd_command: push temp2 ; sichere die Register temp1 und temp2 push temp1 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 delay50us pop temp1 ; stelle den ursprünglichen Wert der Register temp1 und temp2 wiederher. pop temp2 ret ; wird immer ausgeführt, wenn Daten übertragen werden sollen lcd_data: push temp2 ; sichere die Register temp1 und temp2 push temp1 ; 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 sbr temp1, 1< 4Bit-Modus ein out LCD_PORT, temp1 ; Áusgabe des oberen Nibble des zu übertragenden Bytes rcall lcd_enable ; Übernahme der "Befehlsdaten" vom LCD rcall delay5ms 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, 0b00000100 ; 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 delay50us: ; 50us Pause ldi temp1, $42 delay50us_:dec temp1 brne delay50us_ ret ; wieder zurück 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 ; wird immer aufgerufen, wenn die nachfolgenden Ausgabe bei Zeile 2 beginnen sollen lcd_line2: ldi temp1, 0b11000000 ; lade temp1 mit der ersten Adresse der 2. Zeile (0x40) rcall lcd_command ; gebe die erste Adresse der 2. Zeile an das LCD ret