;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;; Modified LCD- Routines - Can be used with LCD Displays with ;;;;;;;; ;;;;;; 2 LCD-Controllers --- 4*27 Chars ---- HD44780 compatible ;;;;;;;; ;;;;;; ;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; .equ LCD_PORT = PORTD .equ LCD_DDR = DDRD .equ PINE1 = 5 .equ PINE2 = 6 .equ PINRS = 4 .equ XTAL = 8000000 ;sendet ein Datenbyte an das LCD - obere Displayhälfte lcd_data: push temp1 push temp2 mov temp2, temp1 ; "Sicherungskopie" für ; die Übertragung des 2.Nibbles swap temp1 ; Vertauschen andi temp1, 0b00001111 ; oberes Nibble auf Null setzen sbr temp1, 1<= 8 MHz) kann es notwendig sein, ; vor dem Enable High 1-2 Wartetakte (nop) einzufügen. ; Siehe dazu http://www.mikrocontroller.net/topic/81974#685882 lcd_E1: nop sbi LCD_PORT, PINE1 ; E1 --> high nop ; warte einen Zyklus nop ; warte einen Zyklus nop ; warte einen Zyklus cbi LCD_PORT, PINE1 ; E1 --> low ret ; Und zurück lcd_E2: nop sbi LCD_PORT, PINE2 ; E2 --> high nop ; warte einen Zyklus nop ; warte eienn Zyklus nop ; warte einen Zyklus cbi LCD_PORT, PINE2 ; E2 --> low ret ; Und zurück ; Pause nach jeder Übertragung delay50us: ; 50us Pause ldi temp1, ( XTAL * 50 / 3 ) / 1000000 delay50us_:dec temp1 brne delay50us_ ret ; wieder zurück ; Längere Pause für manche Befehle delay5ms: ; 5ms Pause ldi temp1, ( XTAL * 5 / 607 ) / 1000 WGLOOP0: ldi temp2, $C9 WGLOOP1: dec temp2 brne WGLOOP1 dec temp1 brne WGLOOP0 ret ; wieder zurück delay10ms: rcall delay5ms rcall delay5ms delay50ms: rcall delay10ms rcall delay10ms rcall delay10ms rcall delay10ms rcall delay10ms ; Initialisierung LCD 1: muss ganz am Anfang des Programms aufgerufen werden lcd_init: push temp1 ldi temp3,50 powerupwait: rcall delay5ms dec temp3 brne powerupwait ldi temp1, 0b00000011 ; muss 3mal hintereinander gesendet out LCD_PORT, temp1 ; werden zur Initialisierung rcall lcd_E1 ; 1 rcall delay5ms rcall lcd_E1 ; 2 rcall delay5ms rcall lcd_E1 ; und 3! rcall delay5ms ldi temp1, 0b00000010 ; 4bit-Modus einstellen out LCD_PORT, temp1 rcall lcd_E1 rcall delay5ms ldi temp1, 0b00101000 ; 4Bit / 2 Zeilen / 5x8 rcall lcd_command ldi temp1, 0b00001100 ; Display ein / Cursor aus / kein Blinken rcall lcd_command ldi temp1, 0b00000100 ; inkrement / kein Scrollen rcall lcd_command pop temp1 ret ; Initialisierung LCD2: muss ganz am Anfang des Programms aufgerufen werden lcd_init1: push temp1 ldi temp3,50 powerupwait1: rcall delay5ms dec temp3 brne powerupwait1 ldi temp1, 0b00000011 ; muss 3mal hintereinander gesendet out LCD_PORT, temp1 ; werden zur Initialisierung rcall lcd_E2 ; 1 rcall delay5ms rcall lcd_E2 ; 2 rcall delay5ms rcall lcd_E2 ; und 3! rcall delay5ms ldi temp1, 0b00000010 ; 4bit-Modus einstellen out LCD_PORT, temp1 rcall lcd_E2 rcall delay5ms ldi temp1, 0b00101000 ; 4Bit / 2 Zeilen / 5x8 rcall lcd_command1 ldi temp1, 0b00001100 ; Display ein / Cursor aus / kein Blinken rcall lcd_command1 ldi temp1, 0b00000100 ; inkrement / kein Scrollen rcall lcd_command1 pop temp1 ret ; Sendet den Befehl zur Löschung des Displays lcd_clear: ldi temp1, 0b00000001 ; Display löschen E1 rcall lcd_command rcall delay5ms ret ; Sendet den Befehl: Cursor Home lcd_home: ldi temp1, 0b00000010 ; Cursor Home E1 rcall lcd_command rcall delay5ms ret ; Sendet den Befehl zur Löschung des Displays lcd_clear1: ldi temp1, 0b00000001 ; Display löschen E2 rcall lcd_command1 rcall delay5ms ret ; Sendet den Befehl: Cursor Home lcd_home1: ldi temp1, 0b00000010 ; Cursor Home E2 rcall lcd_command1 rcall delay5ms ret ; Einen konstanten Text aus dem Flash Speicher ; ausgeben. Der Text wird mit einer 0 beendet lcd_flash_string_o: push temp1 push ZH push ZL lcd_flash_string_1_o: lpm temp1, Z+ cpi temp1, 0 breq lcd_flash_string_2_o rcall lcd_data rjmp lcd_flash_string_1_o lcd_flash_string_2_o: pop ZL pop ZH pop temp1 ret lcd_flash_string_u: push temp1 push ZH push ZL lcd_flash_string_1_u: lpm temp1, Z+ cpi temp1, 0 breq lcd_flash_string_2_u rcall lcd_data1 rjmp lcd_flash_string_1_u lcd_flash_string_2_u: pop ZL pop ZH pop temp1 ret ;LCD Line Select line1: ldi temp1, 0b10000000 rcall lcd_command rcall delay5ms ret line2: ldi temp1, 0b10111000 rcall lcd_command rcall delay5ms ret line3: ldi temp1, 0b10000000 rcall lcd_command1 rcall delay5ms ret line3a: ldi temp1, 'X' rcall lcd_data1 ret line4: ldi temp1, 0b10111000 rcall lcd_command1 rcall delay5ms ret ;********************************************************************** ; ; Gibt einen Text aus dem RAM aus ; Der Text wird mit einer 0 beendet ; Anfang des Textes wird mit Z adressiert ; ; Übergabe: Z - Zeiger auf den Null-Terminierten String ; veränderte Register: keine ; lcd_ram_string: push zl push zh push temp1 lcd_ram_string_1: ld temp1, Z+ cpi temp1, 0 breq lcd_ram_string_2 rcall lcd_data rjmp lcd_ram_string_1 lcd_ram_string_2: pop temp1 pop zh pop zl ret ;********************************************************************** ; ; Eine Zahl aus dem Register temp1 hexadezimal ausgeben ; ; Übergabe: - ; veränderte Register: keine ; lcd_number_hex: push temp1 swap temp1 andi temp1, $0F rcall lcd_number_hex_digit pop temp1 push temp1 andi temp1, $0F rcall lcd_number_hex_digit pop temp1 ret lcd_number_hex_digit: cpi temp1, 10 brlt lcd_number_hex_digit_1 subi temp1, -( 'A' - '9' - 1 ) lcd_number_hex_digit_1: subi temp1, -'0' rcall lcd_data ret ;********************************************************************** ; ; Lädt User Zeichen in den GC-Ram des LCD bis Tabellenende (0xFF) ; gelesen wird. (max. 8 Zeichen können geladen werden) ; ; Übergabe: - ; veränderte Register: temp1, temp2, temp3, zh, zl ; Bemerkung: ist einmalig nach lcd_init aufzurufen ; lcd_load_user_chars: ldi zl, LOW (ldc_user_char * 2) ; Adresse der Zeichentabelle ldi zh, HIGH(ldc_user_char * 2) ; in den Z-Pointer laden clr temp3 ; aktuelles Zeichen = 0 lcd_load_user_chars_2: clr temp2 ; Linienzähler = 0 lcd_load_user_chars_1: ldi temp1, 0b01000000 ; Kommando: 0b01aaalll add temp1, temp3 ; + akt. Zeichen (aaa) add temp1, temp2 ; + akt. Linie (lll) rcall lcd_command ; Kommando schreiben lpm temp1, Z+ ; Zeichenline laden rcall lcd_data ; ... und ausgeben ldi temp1, 0b01001000 ; Kommando: 0b01aa1lll add temp1, temp3 ; + akt. Zeichen (aaa) add temp1, temp2 ; + akt. Linie (lll) rcall lcd_command lpm temp1, Z+ ; Zeichenline laden rcall lcd_data ; ... und ausgeben inc temp2 ; Linienzähler + 1 cpi temp2, 8 ; 8 Linien fertig? brne lcd_load_user_chars_1 ; nein, dann nächste Linie subi temp3, -0x10 ; zwei Zeichen weiter (addi 0x10) lpm temp1, Z ; nächste Linie laden cpi temp1, 0xFF ; Tabellenende erreicht? brne lcd_load_user_chars_2 ; nein, dann die nächsten ; zwei Zeichen ret ;********************************************************************** ; ; Eine 8 Bit Zahl ohne Vorzeichen ausgeben ; ; Übergabe: Zahl im Register temp1 ; veränderte Register: keine ; lcd_number: push temp1 ; die Funktion verändert temp1 und temp2, push temp2 ; also sichern wir den Inhalt, um ihn am Ende ; wieder herstellen zu können mov temp2, temp1 ; das Register temp1 frei machen ; abzählen wieviele Hunderter ; in der Zahl enthalten sind ;** Hunderter ** ldi temp1, '0'-1 ; temp1 mit ASCII '0'-1 vorladen lcd_number_1: inc temp1 ; ASCII erhöhen (somit ist nach dem ersten ; Durchlauf eine '0' in temp1) subi temp2, 100 ; 100 abziehen brcc lcd_number_1 ; ist dadurch kein Unterlauf entstanden? ; nein, dann zurück zu lcd_number_1 subi temp2, -100 ; 100 wieder dazuzählen, da die ; vorherhgehende Schleife 100 zuviel ; abgezogen hat rcall lcd_data ; die Hunderterstelle ausgeben ;** Zehner ** ldi temp1, '0'-1 ; temp1 mit ASCII '0'-1 vorladen lcd_number_2: inc temp1 ; ASCII erhöhen (somit ist nach dem ersten ; Durchlauf eine '0' in temp1) subi temp2, 10 ; 10 abziehen brcc lcd_number_2 ; ist dadurch kein Unterlauf enstanden? ; nein, dann zurück zu lcd_number_2 subi temp2, -10 ; 10 wieder dazuzählen, da die ; vorherhgehende Schleife 10 zuviel ; abgezogen hat rcall lcd_data ; die Zehnerstelle ausgeben ;** Einer ** ldi temp1, '0' ; die Zahl in temp2 ist jetzt im Bereich add temp1, temp2 ; 0 bis 9. Einfach nur den ASCII Code für rcall lcd_data ; '0' dazu addieren und wir erhalten dierekt ; den ASCII Code für die Ziffer pop temp2 ; den gesicherten Inhalt von temp2 und temp1 pop temp1 ; wieder herstellen ret ; und zurück ;********************************************************************** ; ; Eine 16 Bit Zahl ohne Vorzeichen ausgeben ; ; Übergabe: Zahl im Register temp2 (low Byte) / temp3 (high Byte) ; veränderte Register: keine ; lcd_number16: push temp1 push temp2 push temp3 ; ** Zehntausender ** ldi temp1, '0'-1 lcd_number1: inc temp1 subi temp2, low(10000) sbci temp3, high(10000) brcc lcd_number1 subi temp2, low(-10000) sbci temp3, high(-10000) rcall lcd_data ; ** Tausender ** ldi temp1, '0'-1 lcd_number2: inc temp1 subi temp2, low(1000) sbci temp3, high(1000) brcc lcd_number2 subi temp2, low(-1000) sbci temp3, high(-1000) rcall lcd_data ; ** Hunderter ** ldi temp1, '0'-1 lcd_number3: inc temp1 subi temp2, low(100) sbci temp3, high(100) brcc lcd_number3 subi temp2, -100 ; + 100 High-Byte nicht mehr erforderlich rcall lcd_data ; ** Zehner ** ldi temp1, '0'-1 lcd_number4: inc temp1 subi temp2, 10 brcc lcd_number4 subi temp2, -10 rcall lcd_data ; ** Einer ** ldi temp1, '0' add temp1, temp2 rcall lcd_data ; ** Stack aufräumen ** pop temp3 pop temp2 pop temp1 ret ;********************************************************************** ; ; Eine BCD Zahl ausgeben ; ; Übergabe: BCD Zahl in temp1 ; ; veränderte Register: keine ; lcd_bcd: push temp2 mov temp2, temp1 ; temp1 sichern swap temp1 ; oberes mit unterem Nibble tauschen andi temp1, 0b00001111 ; und "oberes" ausmaskieren subi temp1, -0x30 ; in ASCII umrechnen rcall lcd_data ; und ausgeben mov temp1, temp2 ; ... danach unteres andi temp1, 0b00001111 subi temp1, -0x30 rcall lcd_data mov temp1, temp2 ; temp1 rekonstruieren pop temp2 ret ;********************************************************************** ; ; Eine 8 Bit Zahl ohne Vorzeichen binär ausgeben ; ; Übergabe: Zahl im Register temp1 ; veränderte Register: keine ; lcd_number_bit: push temp1 ; temp1 gesichert push temp2 push temp3 mov temp2, temp1; ldi temp3, 8; ; 8 Bits werden ausgelesen lcd_number_loop: dec temp3; rol temp2; ; Datenbits ins Carry geschoben ... brcc lcd_number_bit_carryset_0; brcs lcd_number_bit_carryset_1; rjmp lcd_number_loop; lcd_number_bit_carryset_0: ldi temp1, '0' ; Bit low ausgeben rcall lcd_data tst temp3; breq lcd_number_ende; rjmp lcd_number_loop; lcd_number_bit_carryset_1: ldi temp1, '1' ; Bit high ausgeben rcall lcd_data tst temp3; breq lcd_number_ende; rjmp lcd_number_loop; lcd_number_ende: pop temp3 pop temp2 pop temp1 ret ldc_user_char: ; Zeichen ; 0 1 .db 0b10001, 0b00100 ; @ @ , @ .db 0b10001, 0b01010 ; @ @ , @ @ .db 0b11111, 0b10001 ; @@@@@ , @ @ .db 0b10001, 0b10001 ; @ @ , @ @ .db 0b10001, 0b10001 ; @ @ , @ @ .db 0b10001, 0b10001 ; @ @ , @ @ .db 0b01110, 0b10001 ; @@@ , @ @ .db 0b00000, 0b00000 ; , ; Zeichen ; 2 3 .db 0b10001, 0b00000 ; @ @ , .db 0b01001, 0b00000 ; @ @ , .db 0b00101, 0b00000 ; @ @ , .db 0b11111, 0b11111 ; @@@@@ , @@@@@ .db 0b10001, 0b00000 ; @ @ , .db 0b10001, 0b00000 ; @ @ , .db 0b01111, 0b00000 ; @@@@ , .db 0b00000, 0b00000 ; , ; Zeichen ; 4 5 .db 0b10001, 0b11111 ; @ @ , @@@@@ .db 0b10001, 0b00001 ; @ @ , @ .db 0b10001, 0b00001 ; @ @ , @ .db 0b10001, 0b01111 ; @ @ , @@@@ .db 0b10101, 0b00001 ; @ @ @ , @ .db 0b11011, 0b00001 ; @@ @@ , @ .db 0b10001, 0b11111 ; @ @ , @@@@@ .db 0b00000, 0b00000 ; , ; Zeichen ; 6 7 .db 0b11110, 0b11111 ; @@@@ , @@@@@ .db 0b10001, 0b01010 ; @ @ , @ @ .db 0b10001, 0b00100 ; @ @ , @ .db 0b11101, 0b01110 ; @@@ @ , @@@ .db 0b00001, 0b00100 ; @ , @ .db 0b10001, 0b01010 ; @ @ , @ @ .db 0b01110, 0b11111 ; @@@ , @@@@@ .db 0b00000, 0b00000 ; , ; End of Tab .db 0xFF, 0xFF