;**** 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.6 CTS und XON/XOFF ;* Last updated: 28.02.2001 ;* Target: AT90S2333 ;* ;* Author: Majunke Michael ;* Support E-mail: majunke@t-online.de ;* ;* Beschreibung: ;* mit diesem Programm kann mein 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.. ;* - ... ;* ;* GPL ;***************************************************************************** ;*** 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 PIND = 0x10 .EQU PORTD_DDR = 0x11 .EQU PORTD_D = 0x12 .EQU PINC = 0x13 .EQU PORTC_DDR = 0x14 .EQU PORTC_D = 0x15 .EQU PINB = 0x16 .EQU PORTB_DDR = 0x17 .EQU PORTB_D = 0x18 ; ***** Variablen, I/O-Register .EQU RAMEND = 0xDF .EQU SP = 0x3D .EQU EEADRESS = 0x1E ; EEPROM .EQU EECONTR = 0x1C .EQU EEDAT = 0x1D .EQU UART_UBRR_H = 0x03 .EQU UART_UBRR_L = 0x09 .EQU UART_UCSRB = 0x0A .EQU UART_UCSRA = 0x0B .EQU UART_UDR = 0x0C .EQU STATUS_FLAG = 0x3F ; Status Register .EQU TIMSK = 0x39 ; Timer/Counter Int Mask Register .EQU TIFR = 0x38 ; Timer/Counter Int Flag Register .EQU TCCR1A = 0x2F ; Timer/Counter1 Controll Register 1A 16bit .EQU TCCR1B = 0x2E ; Timer/Counter1 Controll Register 1B .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, nur Empfang .EQU UART_CR_OFF = 0b00011000 ; int off .EQU PUFFER_LAENGE_GESAMT = 22 ; Puffer auf 18 Zeichen 20 (vorerst 70 wg. Linux) .EQU PUFFER_LAENGE_RESERVE = 2 ; ReserveZeichen falls CTS zu lang braucht 2 .EQU ASC_UG = 32 ; innerhalb werden Zeichen empfangen und gepuffert .EQU ASC_OG = 126 ; von SPACE bis ~ .EQU ASC_CODE = 0x7E ; 126d '~' Steuerzeichen, wird auch gepuffert -> gehe auf CODE-EMPFANG ; die Zeichen die danach als CODE gesendet werden können .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 ; ***** DDR's festlegen um auf Display zuzugreifen .EQU DDR_READ_DISPLAY = 0b00110000 ; Datenrichtung vordef. zum Einlesen .EQU DDR_WRITE_DISPLAY = 0b00111111 ; zum Ausgeben ; ***** für init_Display_ hier kann Font,Cusour usw. geändert werden, s.Datenblatt .EQU DISPLAY_FUNK_SET = 0b00101000 .EQU DISPLAY_DISP = 0b00001100 .EQU DISPLAY_CLEAR = 0b00000001 .EQU DISPLAY_EMODE = 0b00000110 .EQU DISPLAY_DDRAM_NULL = 0b10000000 .EQU TIMER1_SET = 0b10000000 ; Timer IRQ AN .EQU TIMER1_CLOCK = 0b00000011 ; Frequenz = CK/64 .EQU TIMER_AKTIV_BYTE_DEF = 0b00000100 ; welche U-Programme in TimerIRQ sind Standartm. an ; (vorbereitung f. Modell2) ; Bit 2- LED allgemein Blinken möglich ; Bit 3-8 , welche LED soll blinken ; 0/1/8 vorerst frei ; ***** 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 TIMER_AKTIV_BYTE = r10 ; abspeichern welche U-Programme in TImerIRQ ausgef. werden sollen ; s.o. .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 X_L = r26 .DEF X_H = r27 .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 .DSEG PUFFER: .byte PUFFER_LAENGE_GESAMT ; ****************************************************************************** ; **** daten die ins EEPROM kommen .ESEG ; '11111111111111112222222222222222' ; '01234567890123456789012345678901' Intro_Meldung: .DB "..16x4 Display....Majunke V0.6..", 0 ; ****************************************************************************** ;*** 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: rjmp TIMER1_OL ; Timer 16bit, LED-Blinken 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 clr WORK out UART_UBRR_H,WORK ldi WORK,UART_CR ; Betriebsart des UART out UART_UCSRB,WORK ; Puffer Variablen setzen ldi WORK, PUFFER mov PUFFER_POS, WORK ; Adresse v. Puffer clr PUFFER_AKT_LAENG ; und akt. Größe (zeichenanzahl) clr C_MERK ; Akt. CursorPosition löschen ; Timer1 anschalten clr WORK out TCCR1A, WORK ; Reg 1A einstellen - Betriebsart ldi WORK, TIMER1_CLOCK out TCCR1B, WORK ; Reg 1B - Frequenz setzen ldi WORK, TIMER1_SET out TIMSK, WORK ; Timer1 Interupt AN ; Timer Unterprogramme AKTIVIRIEREN ldi WORK, TIMER_AKTIV_BYTE_DEF ; standartwert in Register mov TIMER_AKTIV_BYTE, WORK ; **** Main ******************************************************************** Start: rcall Init_Display ;*** Init Display rcall Intro ;*** Intro zeigen sei ;*** schalte IRQ's zu ldi WORK,17 ; sende ein XON (asc17) out UART_UDR,WORK Start_SerIn: ;*** starte tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? breq Start_SerIn rcall Read_Puffer ; dann lesen Code_Empfang: cpi DISPLAY_DAT,ASC_CODE ; ist es das Code-Zeichen ? breq Code_Locate_Cursor ; ja, dann CODE-Behandlung rjmp Main_Send_Data_MIT_LOC ; dann ausgeben ALS NORMALER TEXT ; *** 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 Code_Locate_Z: tst PUFFER_AKT_LAENG ; ist ein Zeichen im Puffer ? 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 ; sprungweite zu kurz Code_Locate_W1: swap DISPLAY_DAT mov TEMP, DISPLAY_DAT 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 ; sprungweite zu kurz Code_Locate_W2: tst DISPLAY_DAT breq Code_Locate_S2 ; Null dann direkt 2Wert 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 ; 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 rjmp End_SerIn ; *** CLS ************ Code_Clear_Display: ; sonst auswertung WELCHER BEFEHL kommt ?? cpi DISPLAY_DAT,ASC_CODE_CLEAR ; ist es das Code-Zeichen für CLEAR_DISPLAY brne Code_LED_Steuer ; nein, dann test 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 ; *** LED's *********** Code_LED_Steuer: cpi DISPLAY_DAT, ASC_CODE_LED ; ist es das Code-Zeichen ? LED ON/OFF/Blinken 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 ; 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 cpi DISPLAY_DAT, 48 ; aus ASC Zahl machen brne Code_LED_Nr_ANorBlink ; wenn nicht null dann zu test 'an oder blinken' sonst AUS in DISPLAY_DAT, PORTD_D ; Zustand ? CTS usw nicht verändern or DISPLAY_DAT, CODE_MERK ; jew. bit setzen out PORTD_D, DISPLAY_DAT com TEMP ; entprechend Bit in Timer-Register löschen and TIMER_AKTIV_BYTE, TEMP ; damit LED-Blinken abgeschaltet wird rjmp End_SerIn Code_LED_Nr_ANorBlink: cpi DISPLAY_DAT, 49 ; '1' für LED AN brne Code_LED_Nr_Blink in DISPLAY_DAT, PORTD_D ; Zustand ? CTS usw nicht verändern com CODE_MERK ; neg. and DISPLAY_DAT, CODE_MERK ; jew. bit setzen out PORTD_D,DISPLAY_DAT com TEMP ; entprechend Bit in Timer-Register löschen and TIMER_AKTIV_BYTE, TEMP ; damit LED-Blinken abgeschaltet wird rjmp End_SerIn Code_LED_Nr_Blink: cpi DISPLAY_DAT, 50 ; '2' für LED Blinken brne End_SerIn or TIMER_AKTIV_BYTE, TEMP ; Bit setzen damit U-PROGRAM in TimerIRQ ausgeführt rjmp End_SerIn ; *** Beleuchtung ***** 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 ;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 ; !!! standart wenn keine besonderen Befehle kommen !!! push DISPLAY_DAT mov DISPLAY_DAT,C_MERK rcall locateZ_S inc DISPLAY_DAT andi DISPLAY_DAT,0x3F mov C_MERK,DISPLAY_DAT pop DISPLAY_DAT ; ********************** Main_Send_Data_Ohne_LOC: ; Cursor wird über Befehle positioniert rcall Disp_Send_Data End_SerIn: clr DISPLAY_DAT rjmp Start_SerIn ; **** End-Main **************************************************************** ; ******* Unterprogrammaufrufe ************************************************* ; ****************************************************************************** ; ****************************************************************************** ; ****** Timer1 Überlauf ******************************************************* ; ***** Welche Routinen ausgeführt werden sollen, steht im Register -> *** ; ***** TIMER_AKTIV_BYTE ..nur wenn entsprechendes Bit gestezt wird -> *** ; ***** jeweiliges UnterPrgramm ausgeführt (dient als Vorbereitung f. **** ; ***** Software f. Modell2 ********************************************** TIMER1_OL: in SAVE_STATUS_FLAG,STATUS_FLAG ; Rette StatusRegister da es verändert wird !! ; *** Bit 2 in Timer_Aktiv_Byte - LED blinken mov X_L, TIMER_AKTIV_BYTE ; soll LED-Blink-Test genrell ausgeführt werden ? sbrs X_L, 2 ; also bit 1 gesetzt ? rjmp No_LED_Blink ; nein dann weiter zu nächster Aufg. bzw. Ende andi X_L,0b11111000 ; ausfiltern der 5 LED-Bits sbrs X_L, 3 ; led 0 blinken lassen ? set = aus rjmp LED_Blink_led1 ; nein dann test ob Led1 in WORK, PORTD_D ; blinken sbrs WORK, 3 rjmp l_on4 cbi PORTD_D, 3 rjmp Led_Blink_led1 l_on4: sbi PORTD_D, 3 Led_Blink_led1: ; soll LED 1 blinken ? sbrs X_L, 4 rjmp Led_Blink_led2 in WORK, PORTD_D sbrs WORK, 4 rjmp l_on5 cbi PORTD_D, 4 rjmp Led_Blink_led2 l_on5: sbi PORTD_D, 4 Led_Blink_led2: ; soll LED 2 blinken ? sbrs X_L, 5 rjmp Led_Blink_led3 in WORK, PORTD_D sbrs WORK, 5 rjmp l_on6 cbi PORTD_D, 5 rjmp No_LED_Blink l_on6: sbi PORTD_D, 5 Led_Blink_led3: ; soll LED 3 blinken ? sbrs X_L, 6 rjmp Led_Blink_led4 in WORK, PORTD_D sbrs WORK, 6 rjmp l_on7 cbi PORTD_D, 6 rjmp Led_Blink_led4 l_on7: sbi PORTD_D, 6 Led_Blink_led4: ; soll LED 4 blinken ? sbrs X_L, 7 rjmp No_LED_Blink in WORK, PORTD_D sbrs WORK, 7 rjmp l_on8 cbi PORTD_D, 7 rjmp No_LED_Blink l_on8: sbi PORTD_D, 7 No_LED_Blink: out STATUS_FLAG,SAVE_STATUS_FLAG ; und Flags in orig. zustand reti ; *** End TIMER1_OL ***************************************************** ; ****************************************************************************** ; ****** 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 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 tst DISPLAY_DAT breq Intro_End rcall Disp_Send_Data ; wo sie angezeigt werden inc WORK2 rjmp Intro_Start ; wenn alle Zeichen gesendet Ende Intro_End: 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 und CTS wieder auf AN, falls Puffer voll *** ; **** puffert nur ASC(32-127) !!! ************************************ ; ***** -- 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 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. dann noch anpassen !!!!!!!!!!!!!!!!!!!!!!!!!!!! cpi WORK_UART,ASC_CODE ; Code einleitung ? breq SAVE_C cpi WORK_UART, ASC_UG ; ab SPACE brlo End_UART_RX cpi WORK_UART, ASC_OG ; obere Grenze brsh End_UART_RX ; und weiter mit speichern in SRAM SAVE_C: mov Y_L,PUFFER_POS ; Akt. Pufferpos. lesen clr Y_H st Y+,WORK_UART ; schreibe es in Puufer an entsp. Posit. und mov PUFFER_POS,Y_L inc PUFFER_AKT_LAENG ; Länge = Lä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 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 wieder auf Aus, 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 ; 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 ; inc C_MERK 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 ; das Display-Busy abfragen ob Display bereit ldi TEMP_DISP, DDR_READ_DISPLAY ; lesen von Display einstellen out PORTB_DDR, TEMP_DISP ldi TEMP_DISP, 0b101111 ; Code lesen v. Display u. EingangsPull-up an out PORTB_D, TEMP_DISP Disp_Busy_Wait: sbi PORTC_D, 0 ; E auf High nop ; warte kurz bis Display Daten sendet nop nop in TEMP_DISP, PINB ; und einlesen cbi PORTC_D, 0 ; E auf Low nop nop rcall Send_E_Puls ; unteren 4Bits lesen (werden aber nicht benötigt) bst TEMP_DISP, 3 ; Busyflag bit nach T schieben brtc Disp_Ready ; T=0 ? dann Display bereit f. nächste Daten nop ; sonst warte noch mind. 1µS nop nop nop nop rjmp Disp_Busy_Wait Disp_Ready: ; Weiter da Display Brereit 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 ; 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 *******************************************************