;**** A P P L I C A T I O N N O T E LCD-Anzeige ************************ ;* ;* Title: Ansteuerung 16*4 LCD über Ser.Schnittstelle ;* Version: 0.55 (XON/XOFF) ;* Last updated: 1.10.2000 ;* Target: AT90S2333 ;* ;* Author: Majunke Michael ;* Support E-mail: majunke@t-online.de ;* ;* Beschreibung: ;* mit diesem Programm kann ein 16*4 LCD-Display angesteuert werden.. ;* die Zeichen die an die Ser. Schnittstelle gesendet werden, gibt das Disp. ;* aus und reagiert auf Befehle.. s. Anlage.. ;* Einstellungen: ;* - 38400 Baud auf der ser. Seite.. ;* - Version mit XON/XOFF Protokoll ;***************************************************************************** ;*** Portbelegungen ********************************************************** ;* Port - Aufgabe - Pins - Richtung ;* ;* PD0 RxD Pin 2 IN ;* PD1 TxD Pin 3 OUT ;* PD2 CTS Pin 4 OUT ;* PD3 LED 1(5) Pin 5 OUT ;* PD4 LED 2(4) Pin 6 OUT ;* PD5 LED 3(3) Pin 11 OUT ;* PD6 LED 4(2) Pin 12 OUT ;* PD7 LED 5(1) Pin 13 OUT ;* ;* PC0 Display E Pin 23 OUT ;* PC1 Display Bel. Pin 24 OUT ;* ;* PB0 Display D0 Pin 14 OUT/IN ;* PB1 Display D1 Pin 15 OUT/IN ;* PB2 Display D2 Pin 16 OUT/IN ;* PB3 Display D3 Pin 17 OUT/IN ;* PB4 Display R/S Pin 18 OUT ;* PB5 Display R/W Pin 19 OUT ;* ;****************************************************************************** .DEVICE AT90S2333 ; ***** Portadressen .EQU PORTD_DDR = 0x11 .EQU PORTD_D = 0x12 .EQU PORTC_DDR = 0x14 .EQU PORTC_D = 0x15 .EQU PORTB_DDR = 0x17 .EQU PORTB_D = 0x18 ; ***** I/O-Memory .EQU RAMEND = 0xDF ; Adresse SRAM-End .EQU STATUS_FLAG = 0x3F ; Status Register .EQU SP = 0x3D ; Stackpointer .EQU EEADRESS = 0x1E ; EEPROM adressregister .EQU EEDAT = 0x1D ; Datenregister .EQU EECONTR = 0x1C ; controlregister .EQU UART_UDR = 0x0C ; UART Datenregister .EQU UART_UCSRA = 0x0B ; Contr./Statusreg.A .EQU UART_UCSRB = 0x0A ; Contr./Statusreg.B .EQU UART_UBRR_L = 0x09 ; Baudratenreg. Low .EQU UART_UBRR_H = 0x03 ; Baudratenreg. High ; ***** Variablen .EQU UART_TEILER = 12 ; Teiler nach Datenblatt einstellen, ; 12 für 38400Baud ; 51 für 9600 ; Teiler=(Mhz/Baudrate/16)-1 .EQU UART_CR = 0b10011000 ; UCSRB, IRQ erlaubt, Empfang/Senden erlaubt .EQU UART_CR_OFF = 0b00011000 ; int off .EQU PUFFER_LAENGE_GESAMT = 70 ; Puffer auf 22 Zeichen ; (vorerst 70 da unter Linux noch Probleme) .EQU PUFFER_LAENGE_RESERVE = 2 ; ReserveZeichen falls CTS zu lang braucht 2 .EQU ASC_UG = 32 ; innerhalb der ASC-Spanne werden Zeichen empfangen und gepuffert .EQU ASC_OG = 126 ; von SPACE(32) bis ~(126) .EQU ASC_CODE = 0x7E ; 126d '~' Steuerzeichen, wird auch gepuffert ; -> gehe auf CODE-EMPFANG und werte nachfolg. Zeichen aus .EQU ASC_CODE_Clear = 0x43 ; 67d 'C' - für display clear .EQU ASC_CODE_LED = 0x4C ; 76d 'L' - LED Steuern 'L' .EQU ASC_CODE_BEL = 0x42 ; 66d 'B' - Beleuchtung an/aus/ (auto-später) .EQU ASC_CODE_LOCATE = 0x50 ; 80d 'P' - Cursor Position Z_S .EQU DDR_READ_DISPLAY = 0b00110000 ; Datenrichtung vordef. zum Einlesen v Display .EQU DDR_WRITE_DISPLAY = 0b00111111 ; zum Ausgeben auf Display .EQU DISPLAY_FUNK_SET = 0b00101000 ; für init_Display_ hier kann Font, .EQU DISPLAY_DISP = 0b00001100 ; Cusour usw. geändert werden, s.Datenblatt .EQU DISPLAY_CLEAR = 0b00000001 .EQU DISPLAY_EMODE = 0b00000110 .EQU DISPLAY_DDRAM_NULL = 0b10000000 ; ***** Register .DEF SAVE_STATUS_FLAG = r0 ; wenn INT ausgeführt, hier das StatusRegister sichern .DEF PAUSE_LANG = r1 ; für Lange Pause .DEF PUFFER_POS = r2 ; Position .DEF PUFFER_AKT_LAENG = r3 ; akt. anzahl v. Zeichen im Puffer .DEF C_MERK = r4 ; Cursor Position merken .DEF WORK = r16 ; allgem. verwend. .DEF WORK2 = r17 .DEF WORK_UART = r18 .DEF TEMP = r19 .DEF TEMP_DISP = r20 ; temp f. untpr. DATEN an DISP .DEF TEMP_DISP_DS = r21 ; temp f. untpr. DATEN an DISP .DEF DISPLAY_DAT = r22 ; übergabe eines Bytes an Disp_Sendxxx ; und 2. f. LocateZ_S .DEF PAUSEWERT = r23 ; die Länge der Pause .DEF PAUSE1 = r24 ; f. Schleife in PauseRoutine .DEF CODE_MERK = r25 ; für Code-Zeichen ermitteln, zw-Speicher ; SRAM -Register .DEF Y_L = r28 ; UART_RX_C, .DEF Y_H = r29 ; .DEF Z_L = r30 ; READ_PUFFER, .DEF Z_H = r31 ; ; ****************************************************************************** ; **** daten die ins SRAM kommen (der Puffer) .DSEG PUFFER: .byte PUFFER_LAENGE_GESAMT ; ****************************************************************************** ; **** daten die ins EEPROM kommen (Meldung) .ESEG ; '11111111111111113333333333333333' Intro_Meldung: .DB "..16x4 Display..Majunke..09/2000" ; ****************************************************************************** ;*** Sprunktabelle für Interrups *** .CSEG .ORG $0 Reset: rjmp Init_Reset ; Reset behandlung, Neuinitaliesieren IRQ0: reti ; IRQ1: reti ; TIMER1_CAP: reti ; TIMER1_COMP: reti ; TIMER1_OVFL: reti ; TIMER0_OVFL: reti ; SPI_TRANSF_COMPL: reti ; UART_RX_COMPL: rjmp UART_RX_C ; ein zeichen wurde empfangen UART_DR_EMPTY: reti ; UART_TX_COMPL: reti ; ADC_CONV_COMPL: reti ; EE_RDY: reti ; ANA_COMP: reti ; ;****** Routine nach Reset bzw. Neustart Init_Reset: cli ;*** Initialize stack pointer ;* StackPointer auf höchste Adresse im SRAM legen ldi WORK, RAMEND ; SP auf RAM_END_Adresse out SP, WORK ; und setzen ;*** I/O-Pins in Schaltung auf defin. Werte einstellen ; Port D ldi WORK,0b11111100 ; LED/CTS/Rxd/TxD s. tabelle out PORTD_DDR,WORK ; AusgabeRichtung - DataDIR Port D ldi WORK,0b11111000 ; LED/CTS aus out PORTD_D,WORK ; Daten - Data Port D ; Port C ldi WORK,0b000011 ; E/Bel. ausgang out PORTC_DDR,WORK ; Ausgaberichtung - DataDIR Port C ldi WORK,0b000001 ; Bel. aus / E high out PORTC_D,WORK ; Daten - DataDIR Port C ; Port B ldi WORK,DDR_READ_DISPLAY ; Datenricht. zum lesen LESEN v. Display out PORTB_DDR,WORK ; Ausgaberichtung - DataDIR Port B ldi WORK,0b111111 ; out PORTB_D,WORK ; Daten - Data Port B ; UART - Einstellen ldi WORK,UART_TEILER ; Baudratenteiler out UART_UBRR_L,WORK ; low-teil clr WORK out UART_UBRR_H,WORK ; h-teil ldi WORK,UART_CR ; Betriebsart des UART out UART_UCSRB,WORK ; Puffer Variablen setzen ldi WORK, PUFFER mov PUFFER_POS, WORK ; Adresse v. Puffer in Reg. clr PUFFER_AKT_LAENG ; akt. Größe (zeichenanzahl) löschen clr C_MERK ; Akt. CursorPosition löschen ; **** Main ******************************************************************** ; **** ***** Start: rcall Init_Display ;*** Initalisieren des Display rcall Intro ;*** Intro anzeigen sei ; schalte IRQ's zu ldi WORK,17 ; sende ein XON (asc17) out UART_UDR,WORK Start_SerIn: ;*** starte Hauptschleife tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Start_SerIn rcall Read_Puffer ; dann lese das Zeichen aus Puffer Code_Empfang: ; und Werte es aus ... cpi DISPLAY_DAT,ASC_CODE ; ist es das Code-Einleitungs-Zeichen ? breq Code_Locate_Cursor ; ja, dann CODE-Behandlung rjmp Main_Send_Data_MIT_LOC ; sonst ausgeben ALS NORMALER TEXT ; *** Befehl Locate Z_S ****** Code_Locate_Cursor: tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? Befehlszeichen holen breq Code_Locate_Cursor ; nein ? dann warte -> sonst auswerten rcall Read_Puffer ; ***************** clr CODE_MERK cpi DISPLAY_DAT,ASC_CODE_LOCATE ; ist es das Code-Zeichen f. Locate ? brne Code_Clear_Display ; nein, -> test next ->else Code ; Eigentliche Code für Locate Z_S Code_Locate_Z: tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? Zeile breq Code_Locate_Z ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten subi DISPLAY_DAT,48 ; aus ASC Zahl machen - ZEILE cpi DISPLAY_DAT,4 ; über 3 ?? brlo Code_Locate_W1 ; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE rjmp End_SerIn ; (rjmp weil sprungweite zu kurz) Code_Locate_W1: swap DISPLAY_DAT mov TEMP, DISPLAY_DAT ; Zeile an richtige Position f. AusgabeFunktion Code_Locate_S: ; 2 Zahlen müssen eingelesen werden !! tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Code_Locate_S ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten subi DISPLAY_DAT,48 ; aus ASC Zahl machen - SPALTE*(10hoch1) cpi DISPLAY_DAT,2 ; über 1 ?? Spalte bis max ->16 Zeichen brlo Code_Locate_W2 ; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE rjmp End_SerIn ; ( rjmp weil sprungweite zu kurz) Code_Locate_W2: tst DISPLAY_DAT breq Code_Locate_S2 ; Null dann direkt 2. Wert lesen, ori CODE_MERK, 10 ; sonst 10hoch1 dazu Code_Locate_S2: tst PUFFER_AKT_LAENG ; und 10hoch0 lesen breq Code_Locate_S2 ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten subi DISPLAY_DAT,48 ; aus ASC Zahl machen - SPALTE*(10hoch0) cpi DISPLAY_DAT,10 ; über 0-15 ? F brlo Code_Locate_W3 ; außerhalb dann ABBRUCH,keine ZEICHENAUSGABE rjmp End_SerIn ; (rjmp weil sprungweite zu kurz) Code_Locate_W3: add DISPLAY_DAT, CODE_MERK or DISPLAY_DAT, TEMP ; und Zeile zu SPlate in ein Byte f. Funktion rcall locateZ_S ; LOCATE Z_S rjmp End_SerIn ; und end ; *** Befehl CLS ************ Code_Clear_Display: cpi DISPLAY_DAT,ASC_CODE_CLEAR ; ist es das Code-Zeichen für CLEAR_DISPLAY brne Code_LED_Steuer ; nein, dann auf nächsten Befehlswort testen ldi DISPLAY_DAT,DISPLAY_CLEAR ; sonst Clear Display rcall Disp_Send_Code clr DISPLAY_DAT ; und auf position eins rcall locateZ_S rjmp End_SerIn ; und ENDE ohne Zeichenausgabe ; *** Befehl um LED's zu steuern *********** Code_LED_Steuer: cpi DISPLAY_DAT,ASC_CODE_LED ; ist es das Code-Zeichen für LED ON/OFF brne Code_Beleucht_Display ; nein, dann test auf nächsten Befehl Code_LED_Nr: ; erwarte welche LED verändert werden soll tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Code_LED_Nr ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten subi DISPLAY_DAT,48 ; aus ASC Zahl machen cpi DISPLAY_DAT,5 ; welche LED ? brlo Code_LED_w ; außerhalb der LED's dann ABBRUCH,keine ZEICHENAUSGABE rjmp End_SerIn ; (rjmp weil sprunglänge zu kurz) Code_LED_w: mov CODE_MERK, DISPLAY_DAT ; Merke die Nummer und weiter zu -> teste ob LED an/AUS ldi TEMP,8 ; schiebe die LED-Nummer auf richtigen Port two_h: ; 2hochLED-Nummer tst CODE_MERK breq n_two_h lsl TEMP dec CODE_MERK rjmp two_h n_two_h: mov CODE_MERK,TEMP Code_LED_Zustand: ; und soll LED AN o. AUS sein tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Code_LED_Zustand ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten subi DISPLAY_DAT,48 ; aus ASC Zahl machen breq Code_LED_Nr_OFF ; Null dann aus, sonst an ldi DISPLAY_DAT,0b11111100 ; LED/CTS/Rxd/TxD out PORTD_DDR,DISPLAY_DAT ; AusgabeRichtung - DataDIR Port D in DISPLAY_DAT, PORTD_D ; Zustand ? CTS usw nicht verändern com CODE_MERK ; Complement (neg.) and DISPLAY_DAT,CODE_MERK ; UND jew. bit löschen out PORTD_D,DISPLAY_DAT rjmp End_SerIn ; end Code_LED_Nr_OFF: ldi DISPLAY_DAT,0b11111100 ; LED/CTS/Rxd/TxD s. tabelle out PORTD_DDR,DISPLAY_DAT ; AusgabeRichtung - DataDIR Port D in DISPLAY_DAT, PORTD_D ; Zustand ? CTS usw nicht verändern or DISPLAY_DAT,CODE_MERK ; jew. bit setzen out PORTD_D,DISPLAY_DAT rjmp End_SerIn ; *** Befehl für Beleuchtungssteuerung ***** Code_Beleucht_Display: cpi DISPLAY_DAT,ASC_CODE_BEL ; ist es das Code-Zeichen f. Beleuchtung brne Main_Send_Data_MIT_LOC ; nein, dann end Code_Beleucht_Z: ; hole den Zustand den Beleuchtung bekommen soll tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Code_Beleucht_Z ; nein ? dann warte rcall Read_Puffer ; sonst lesen und entsprechend auswerten cpi DISPLAY_DAT,48 ; ist es '0' brne Code_Beleucht_ON ; nein, dann Beleuchtung AN , sonst aus (später automatik) ;sbi PORTC_DDR,1 ; Ausgaberichtung C cbi PORTC_D,1 ; und AUS rjmp End_SerIn ; und ENDE ohne Zeichenausgabe Code_Beleucht_ON: ;sbi PORTC_DDR,1 ; Ausgaberichtung C sbi PORTC_D,1 ; und AUS rjmp End_SerIn ; und ENDE ohne Zeichenausgabe ; ********************** Main_Send_Data_MIT_LOC: ; ********************** ; ** überspringen der 'toten' Bereiche des Displays, ; ** damit alle Zeichen umlaufend angezeigt werden falls kein Positionsbefehl ; ** vom Benutzer kommt ; !!! standart wenn keine besonderen Befehle kommen !!! push DISPLAY_DAT ; rette Zeichen mov DISPLAY_DAT,C_MERK ; Positionier Cursor entsprechend akt. Cursorposition rcall locateZ_S inc DISPLAY_DAT ; erhöhe akt. Cursorposition andi DISPLAY_DAT,0x3F ; bis max. 64 Zeichen (wenn anderes Display anpassen) mov C_MERK,DISPLAY_DAT ; in C_Merk (akt. Cursorposit.) pop DISPLAY_DAT ; und Zeichen wieder nach Display_Dat ; ********************** Main_Send_Data_Ohne_LOC: ; Cursor wird über Befehle(LOCATE des Benutzers) positioniert rcall Disp_Send_Data End_SerIn: clr DISPLAY_DAT ; löschen und zurück rjmp Start_SerIn ; **** End-Main **************************************************************** ; ******* Unterprogrammaufrufe ************************************************* ; ****************************************************************************** ; ****************************************************************************** ; ****** Intro nach Reset ****************************************************** ; ***** -- Register *************************************************** ; ***** zwischenspeicher : TEMP ****************** ; ***** kommt EEPROM-Adresse rein : WORK2 *************** ; ***** übergabe des Wertes an Display : DISPLAY_DAT *********** ; ***** PAUSE länge : PAUSEWERT ************* ; ***** Display löschen : DISPLAY_DAT *********** ; ***** -- Variblen *************************************************** ; ***** Intro_Meldung-Adresse EEPROM : Intro_Meldung ********* ; ***** EEProm-Adresse : EEADRESS ************** ; ***** EEProm-Controll-Reg. : EECONTR *************** ; ***** EEProm-Daten Reg. : EEDAT ***************** ; ***** -- Routinen *************************************************** ; ***** Daten an Displ. senden : Disp_Send_Data ******** ; ***** Code an Displ. senden : Disp_Send_Code ******** ; ***** Pause : Delay_X *************** ; ********************************************************************* Intro: ; ersten 32 Zeichen aus EEPROM ausgeben ldi WORK2,Intro_Meldung ; ausgeben der IntroMeldung aus EEPROM ldi TEMP,33 ; Zähler für die 32 Zeichen Intro_Start: out EEADRESS,WORK2 ; setze EEPROM-Adresse auuf Null sbi EECONTR,0 ; setze das Daten gelsene werden sollen in DISPLAY_DAT,EEDAT ; und schiebe die Daten nach DISPLAY_DAT out UART_UDR, DISPLAY_DAT ; auch nach TxD schicken, Test der ser. Ausgabe rcall Disp_Send_Data ; wo sie angezeigt werden inc WORK2 ; erhöhe EEPROM-ADRESSE um 1 dec TEMP ; und veringer Zeichenzähler um 1 brne Intro_Start ; wenn alle Zeichen gesendet Ende ldi PAUSEWERT, 70 ; 50 mal 25ms warten = 1,75sec rcall Delay_X_long ldi DISPLAY_DAT,DISPLAY_CLEAR ;** Clear Display rcall Disp_Send_Code ldi DISPLAY_DAT,DISPLAY_DDRAM_NULL ;** und DDRAM auf NULL rcall Disp_Send_Code ret ; *** End Intro ******************************************************* ; ********************************************************************* ; **** UART *********************************************************** ; **** Interuptbehandlung nachdem ein Zeichen empfangen *************** ; **** in Puffer speichern u.CTS/XOFF entspr. setzen, w Puffer voll *** ; **** puffert nur ASC(32-126) !!! ************************************ ; ***** -- Register *************************************************** ; ***** Allg. : WORK_UART *( ab r16)********* ; ***** schreiben in RAM : r28,r29 Y_L,Y_H ******* ; ***** Pufferposisition und Größe : 2x unter r16 **************** ; ***** zwischenspeicher : TEMP *(ab r16)********* ; ***** in Stack gesichert !****** ; ***** zwischenspeicher : WORK *(ab r16)********* ; ***** in Stack gesichert !****** ; ***** -- Routinen *************************************************** ; ***** DD-RAM ADresse an Display : Disp_Send_Code ******** ; ********************************************************************* UART_RX_C: in SAVE_STATUS_FLAG,STATUS_FLAG ; Rette StatusRegister da es verändert wird !! ; teste ob es ein Fehler war der Empfangen wurde in WORK_UART,UART_UCSRA ; Status einlesen sbrs WORK_UART,4 ; wenn bit=1 dann ein FramError u. 'jmp Read' rjmp SAVE_CHAR ; wird überspr. , wenn kein Fehler ab zu Zeichen lesen in WORK_UART,UART_UDR ; nur wenn FrameError, UDR muß gelesen werden damit IRQ frei wird reti ; EXIT SAVE_CHAR: ; Zeichen einlesen u. in Puffer speichern in WORK_UART,UART_UDR ; lese UDR - Zeichen, gib int frei ; Code Zeichen ~ wird auch gepuffert, sonst -> ; was war es für Zeichen ? nur Zeichen 32-127 wird gepuffert !!!! ; wenn ä/ö/ü usw. benötigt, dann noch anpassen !!!!!!!!!!!!!!!!!! cpi WORK_UART,ASC_CODE ; Code einleitungszeichen ? breq SAVE_C cpi WORK_UART, ASC_UG ; Zeichen ab SPACE speichern in Puffer brlo End_UART_RX cpi WORK_UART, ASC_OG ; obere Grenze für Zeichen die gesp. werden brsh End_UART_RX ; und weiter mit speichern in SRAM (Puffer) SAVE_C: mov Y_L,PUFFER_POS ; Akt. Pufferpos. lesen clr Y_H st Y+,WORK_UART ; schreibe Zeichen in Puffer(SRAM) an entsp. Posit. mov PUFFER_POS,Y_L inc PUFFER_AKT_LAENG ; akt. PufferLänge = PufferLänge + 1 ldi Y_H, PUFFER_LAENGE_GESAMT-PUFFER_LAENGE_RESERVE ; Schutz vor Überlauf cp PUFFER_AKT_LAENG,Y_H ; PUFFER_LAENGE auf Puffer Voll ? brlt End_UART_RX ; dann CTS / od. XOFF aktiv machen sonst P_überlauf ;sbi PORTD_DDR,2 ; Port_D-CTS auf Ausgabe ;sbi PORTD_D,2 ; und CTS aktiv damit keine Daten mehr kommen ldi WORK_UART,19 out UART_UDR, WORK_UART ; XOFF senden ldi Y_H, PUFFER_LAENGE_GESAMT+1 ; BUFFERÜBERLAUF doch passiert ? cp PUFFER_AKT_LAENG,Y_H brlo End_UART_RX ; dann RESET rcall Init_Reset End_UART_RX: out STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand reti ; *** End UART_RX_C *************************************************** ; ********************************************************************* ; **** zeichen aus Puffer lesen und Puffer-Zeiger enstpr. Posit. ****** ; **** und CTS/XOFF entspr. setzen, falls an war ********************** ; ***** -- Register *************************************************** ; ***** Ausgabe des gelesenen Wertes : DISPLAY_DAT *( ab r16)* ; ***** Lesen aus RAM : r30,r31 Z_L,Z_H ******* ; ***** Pufferposisition und Größe : 2x unter r16 **************** ; ***** zwischenspeicher : TEMP *(ab r16)********* ; ***** in Stack gesichert !****** ; ***** zwischenspeicher : WORK *(ab r16)********* ; ***** in Stack gesichert !****** ; ***** -- Routinen *************************************************** ; ***** DD-RAM ADresse an Display : Disp_Send_Code ******** ; ********************************************************************* READ_PUFFER: push TEMP ; zum test push WORK ldi Z_L,UART_CR_OFF ; UART - Einstellen das kein Int out UART_UCSRB,Z_L ; beim auslesen ausgelöst wird tst PUFFER_AKT_LAENG ; Puffer leer ? also lange null breq End_leer ; dann ende ldi Z_L,PUFFER ; 1.Zeichen aus Puffer lesen clr Z_H ; aus SRAM ld DISPLAY_DAT, Z ; und nach DISPLAY_DAT (vorerst!-evt. extraReg.) mov WORK, PUFFER_AKT_LAENG ; sichern dec WORK dec PUFFER_AKT_LAENG ; Pufferlänge minus 1 breq End_jetzt_leer ; ist länge jetzt 0 ? (also puffer leer) READ_Loop: ; Puffer schieben (später evt. andere Art) ldd TEMP, Z+1 ; lese nächstes Zeichen st Z+, TEMP dec WORK brne READ_Loop End_jetzt_leer: clr TEMP st Z, TEMP ; 1.Zeichen löschen mov PUFFER_POS,Z_L End_leer: mov WORK, PUFFER_AKT_LAENG ; mache Puffer erstmal etwas leer bevor CTS frei wird cpi WORK, 2 ; brge End_E ; ; muß ERST dafor angeschalten werden, sont evt überlappungen I/O ldi Z_L,UART_CR ; Betriebsart des UART auf AN out UART_UCSRB,Z_L ;sbi PORTD_DDR,2 ; ansonsten cts aus ;cbi PORTD_D,2 ; damit neue Zeichnen kommen können ldi WORK, 17 ; XON out UART_UDR,WORK End_E: ldi Z_L,UART_CR ; Betriebsart des UART auf AN out UART_UCSRB,Z_L pop WORK pop TEMP ret ; *** End Read_Puffer************************************************** ; ********************************************************************* ; ***** setzt DD-RAM ADRESSE auf angegeb. Position ******************** ; ***** locate Zeile, Spalte als HEX_ZAHL ***************************** ; ***** bsp. locate mit übergabe 0x23 -> 2Zeile, 3Spalte ************** ; ***** Zeile 0-3, Spalte 0-F !! *** KEINE FEHLERAUSWERTUNG !!!!! ***** ; ***** ; ***** -- Register *************************************************** ; ***** übergabe des Wertes : DISPLAY_DAT *( ab r16)* ; ***** zwischenspeicher : TEMP *(ab r16)********* ; ***** in Stack gesichert !****** ; ***** -- Routinen *************************************************** ; ***** DD-RAM ADresse an Display : Disp_Send_Code ******** ; ********************************************************************* LocateZ_S: push DISPLAY_DAT ; rette mov C_MERK,DISPLAY_DAT ; setze Merkposition tst DISPLAY_DAT ; ZP -Zeile,Spalte hex breq loc ; Zeile,Spalte ist Null dann direkt zum Ende push TEMP ; retten damit nur ein REG belegt mov TEMP,DISPLAY_DAT ; rette Wert swap DISPLAY_DAT ; Zeile in untere Bits.. andi DISPLAY_DAT,0x0f ; Zeile filtern (Sp. löschen) breq loc_S ; Z=0 ? Zeile = 0 dec DISPLAY_DAT ; -1 Z1 breq loc_Z1 ; ist es Null ? dann war Zeile = 1 dec DISPLAY_DAT ; -1 Z2 breq loc_Z2 ; ist es Null ? dann war Zeile = 2 ldi DISPLAY_DAT,0x50 ; Zeilenstart , hier Zeile = 3 rjmp loc_S loc_Z2: ldi DISPLAY_DAT,0x10 ; Zeilenstart rjmp loc_S loc_Z1: ldi DISPLAY_DAT,0x40 ; Zeilenstart rjmp loc_S loc_S: ; und SPalte addieren andi TEMP,0x0f ; Spalte filtern (Zeile. löschen) or DISPLAY_DAT, TEMP ; dann addieren pop TEMP ; Wiederherstellen loc: ori DISPLAY_DAT,DISPLAY_DDRAM_NULL ; und Basis dazu rcall Disp_Send_Code ; ergibt DDRAM-ADresse pop DISPLAY_DAT ret ; *** end LocateZ_S *************************************************** ; ********************************************************************* ; ***** Sende Commando oder Daten an das Display ********************** ; ***** -- Register *************************************************** ; ***** übergabe des Wertes : DISPLAY_DAT *********** ; ***** zwischenspeicher : TEMP_DISP ************* ; ***** Verknüpfung ent Dat/Code : TEMP_DISP_DS ********** ; ***** PAUSE länge : PAUSEWERT ************* ; ***** zu and. arbeiten : WORK ****************** ; ***** -- Variblen *************************************************** ; ***** Display auf Empfang setzen : DDR_WRITE_DISPLAY ***** ; ***** -- Ports ****************************************************** ; ***** PortB -DDR : PORTB_DDR ************* ; ***** PortB -Daten : PORTB_D *************** ; ***** -- Routinen *************************************************** ; ***** den E-Puls senden : Send_E_Puls *********** ; ***** Pause : Delay_X *************** ; ********************************************************************* ; *** für Datensenden muß R/S H Disp_Send_Data: ldi TEMP_DISP_DS,16 ; R/S auf H für Daten (f. spät. or) rjmp Disp_Send ; *** für Codesenden muß R/S L Disp_Send_Code: clr TEMP_DISP_DS ; R/S auf L für Code ; *** Sende Daten Disp_Send: ldi WORK,UART_CR_OFF ; UART - Einstellen das kein Int out UART_UCSRB,WORK ; beim auslesen ausgelöst wird ldi WORK,DDR_WRITE_DISPLAY ; Portrichtung zum schreiben auf display out PORTB_DDR,WORK cbi PORTB_D,5 ; R/W auf L damit Displ. auf Empfang geht ; denn 8bit-WErt in 2x4bit Wert umwandeln um senden mov TEMP_DISP, DISPLAY_DAT ; sichere übergeb. Wert swap DISPLAY_DAT ; tausche Nibbles, damit obere hälfte zuerst gesendet andi DISPLAY_DAT,0x0f ; lösche obere Hälfte or DISPLAY_DAT,TEMP_DISP_DS ; und setze wieder R/S out PORTB_D,DISPLAY_DAT ; ausgeben an Ports rcall Send_E_Puls ; und Displ. übernimmt Daten durch E-Impuls andi TEMP_DISP,0x0f ; jetzt noch den LOW-Teil der ja im gesicht. TEMP steht or TEMP_DISP,TEMP_DISP_DS ; R/S wieder setzen out PORTB_D,TEMP_DISP ; ... rcall Send_E_Puls ; ... ldi WORK,UART_CR ; UART - wieder an out UART_UCSRB,WORK ; ldi PAUSEWERT,10 ; und die unbed. nötige Pause 10x100us nach jedem rcall Delay_X ; 8bit Wert.. ; später das Display nach Busy abfragen !!!!!!!!!!! ret ;*** End Daten/Code senden ******************************************* ; ******************************************************************** ; ***** Display initalisieren **************************************** ; ***** s. Datenblatt zum Display ************************************ ; ***** -- Register ************************************************** ; ***** zu allg. arbeiten : WORK ( ab r16) ******* ; ***** PAUSE länge : PAUSEWERT ( ab r16) ** ; ***** übergabe eine Wertes an Display : DATEN_DAT ( ab r16) ** ; ***** -- Variblen ************************************************** ; ***** Display auf Empfang setzen : DDR_WRITE_DISPLAY **** ; ***** setzen der Modi s. Datenblatt, : DISPLAY_FUNK_SET ***** ; ***** werte vordef. am Anfang des : DISPLAY_DISP ********* ; ***** programmes : DISPLAY_CLEAR ******** ; ***** : DISPLAY_EMODE ******** ; ***** -- Ports ***************************************************** ; ***** PortB -DDR : PORTB_DDR ************ ; ***** PortB -Daten : PORTB_D ************** ; ***** -- Routinen ************************************************** ; ***** den E-Puls senden : Send_E_Puls ********** ; ***** Pause : Delay_X ************** ; ******************************************************************** Init_Display: ldi WORK,DDR_WRITE_DISPLAY ; Portrichtung zum schreiben auf display out PORTB_DDR,WORK ;*** ab hier siehe datenblatt ldi PAUSEWERT, 170 ; 170 x ca. 100us = ca. 17ms rcall Delay_X ; Pause mind. 15ms ;* ERSTE (4bit) sequ. laut datenblatt ldi WORK, 0b000011 out PORTB_D, WORK rcall Send_E_Puls ; mit E_PULS datenübernahme ldi PAUSEWERT, 50 ; 5ms rcall Delay_X ; und eine Pause mind. 4ms ;* ZWEITE ; da Ports schon gesetzt kann auf nochmal. setzen verzichtet werden ; und einfach mit den Pausen und E_pulsen fortgefahren werden rcall Send_E_Puls ; E_PULS datenübernahme ldi PAUSEWERT, 20 ; 2ms rcall Delay_X ; und eine Pause ;* DRITTE sequ. laut datenblatt rcall Send_E_Puls ; E_PULS datenübernahme ldi PAUSEWERT, 20 ; zwar nicht angeb. aber nötig ! rcall Delay_X ; und eine Pause ;* VIERTE sequ. laut datenblatt cbi PORTB_D,0 ; einfach bit 0 löschen rcall Send_E_Puls ; E_PULS datenübernahme ldi PAUSEWERT, 20 ; w.o. rcall Delay_X ; und eine Pause ;** ab hier ist auf 4bit umgeschalten !!! und es muß dem Display mitgeteilt ;* werden das Code kommt !!! ;** funktions-set ldi DISPLAY_DAT, DISPLAY_FUNK_SET rcall Disp_Send_Code ; routine zum CODE an Displ. zu senden ;** display an ldi DISPLAY_DAT,DISPLAY_DISP rcall Disp_Send_Code ;** Clear Display ldi DISPLAY_DAT,DISPLAY_CLEAR rcall Disp_Send_Code ;** Entry-Mode-Set ldi DISPLAY_DAT,DISPLAY_EMODE rcall Disp_Send_Code ; DD-RAM auf NULL ldi DISPLAY_DAT,DISPLAY_DDRAM_NULL rcall Disp_Send_Code ret ; *** End Display initalisieren ************************************** ; ******************************************************************** ; ***** sende E-Puls damit Displ. Daten übernimmt ******************** ; ***** -- Ports ***************************************************** ; ***** PortC -DDR : PORTC_DDR ************ ; ***** PortC -Daten : PORTC_D ************** ; ******************************************************************** Send_E_Puls: sbi PORTC_D,0 ; E auf HIGH nop ; ... nop ; kurz E halten nop ; ... cbi PORTC_D,0 ; und E wieder LOW ret ; *** End E-Puls senden ********************************************** ; ******************************************************************** ; ***** Pause von ca. 100us bis 6.4sec / bei 8Mhz ******************** ; ***** ; ******** innere Pause Delay_X : (bei 8Mhz u. Fakt=1 ca. 100us)*** ; ******** -- Register *********************************************** ; ***** Übergebener Wert, die Länge d.P.: PAUSEWERT ************ ; ***** Faktor : 1-255 ergibt 100us-25ms , 0 maximum ***************** ; ***** äußerer Schleifen Zähler : PAUSE1 (>=r16) ******* ; ***** ; ******** Große Pause Delay_X_long :(bei 8Mhz u. Fakt=1 ca. 25ms )*** ; ******** -- Register *********************************************** ; ***** Übergebener Wert, die Länge d.P.: PAUSEWERT ************ ; ***** Faktor : 1-255 ergibt 25ms-6,4sec , 0 maximum **************** ; ***** zus. zu PAUSE1 noch das Register PAUSE_LANG, welches ********* ; ***** nicht direkt beschrieben werden kann (unter r16) ************* ; ******************************************************************** Delay_X_long: ; Schleife Pause Lang mov PAUSE_LANG,PAUSEWERT ; (indirk.Reg) Wert sichern clr PAUSEWERT ; clr damit Delay_X auf max läuft rjmp Dlong ; und beginn Schleife Delay_X: ; Schleife Pause Klein ldi PAUSE1,1 ; wenn kl.Pause muß PAUSE_LANG (r0) mov PAUSE_LANG,PAUSE1 ; so sein, das Gr.Schleife u. nicht durchlaufen wird !! Dlong: ; die äußere Schleife clr PAUSE1 ; 256 durchläufe Loop1: ; die innere Schleife dec PAUSE1 brne Loop1 ; ** dec PAUSEWERT brne Dlong ; ** dec PAUSE_LANG brne Dlong ; ** ret ; *** End Pause ****************************************************** ; *** End Unterprogramme *******************************************************