$nomod51 $nopaging $title (SHT11 via 8051 auf 4-Zeilen-Display) $include (80C515.mcu) DSEG AT 20h ;Für die Festkommazahlausgabe Fix_Flags: ds 1 ;Steuerbits Fix_Feldb: ds 1 ;Feldbreite in Zeichen Fix_Dezim: ds 1 ;Dezimalstellen DSEG AT 30h CurDisplay: ds 1 ;Für folgende Ausgabe aktive Display-Hälfte(n) Num_Messung: ds 1 ;mein 8-bit-Zähler Produkt: ds 4 ;das 16x16-Produkt TMinMax: ds 4 ;minimale (LOW) und maximale (HIGH) Temperatur FMinMax: ds 4 ;minimale (LOW) und maximale (HIGH) Feuchte CSEG AT RESET ;****************************************************************************** ;** Wenn man beim Löten etwas Mist baut (hier: die Steuer- und Datenpins ** ;** unglücklich verteilt), kann man's auch durch Software wieder hinbiegen, ** ;** ist aber NICHT nachahmenswert! ** ;** Verbleibenden Portpins werden rigoros auf den _gelesenen_ Pegel gesetzt, ** ;** Vorsicht bei Eingängen! ** ;****************************************************************************** DISP_DATA set P4 ; Port mit den 4 Datenleitungen (Nibble-Modus) DATA0_BIT set 0 DATA1_BIT set 2 DATA2_BIT set 4 DATA3_BIT set 6 DATA0 set 1 shl DATA0_BIT DATA1 set 1 shl DATA1_BIT DATA2 set 1 shl DATA2_BIT DATA3 set 1 shl DATA3_BIT DISP_CONTROL set P5 ; Port mit den 4 Steuerleitungen E_unten set 1 shl 7 ; 1=Enable untere 2 Zeilen E_oben set 1 shl 6 ; 1=Enable obere 2 Zeilen RW set 1 shl 5 ; 0=Schreiben, 1=lesen RS set 1 shl 4 ; 0=Steuerbefehl, 1=Zeichen Eu_BIT set DISP_CONTROL.7 Eo_BIT set DISP_CONTROL.6 RW_BIT set DISP_CONTROL.5 RS_BIT set DISP_CONTROL.4 SCK set P3.4 ; für SHT11 DAT set P3.5 ; für SHT11 ljmp init ;Reset-Vektor (keine Interrupts) ;****************************** ;** Eine dumme Warteschleife ** ;****************************** wait1: ;Wartet 1 Millisekunde ;PE: - ;PA: - ;VR: R5=0 MOV R5,#250 wait4xR5: NOP ;1 Wartet R5*4µs NOP ;1 DJNZ R5,wait4xR5 ;2 ;4µs*256=1ms ret ;wait1 ENDP wait: ;Wartet Millisekunden call wait1 djnz r4,wait RET ;******************************* ;** Anzeige: LowLevel-Zugriff ** ;******************************* ;PUBLIC disp_addr_in, disp_data_in, disp_ctrl_out, disp_char_out, disp_init_ll disp_nib_in: ;1 Nibble vom Display einlesen ;PE: [CurDisplay]=E_unten _oder_ E_oben, RW_BIT=1 und RS_BIT schon gesetzt ;PA: a=gelesenes Nibble, High-Teil=0 ;VR: a mov a,DISP_DATA orl a,#(DATA3 or DATA2 or DATA1 or DATA0) mov DISP_DATA,a ; alle Bits high = hochohmig mov a,DISP_CONTROL orl a,CurDisplay mov DISP_CONTROL,a nop ;Zeit bis zum Erscheinen der Daten t(DDR)=360ns clr a mov c,DISP_DATA.DATA3_BIT rlc a mov c,DISP_DATA.DATA2_BIT rlc a mov c,DISP_DATA.DATA1_BIT rlc a mov c,DISP_DATA.DATA0_BIT rlc a disp_disable: clr Eu_BIT clr Eo_BIT ret disp_data_in: ; Befehl "Read Data" ;PE: r2=E_oben _oder_ E_unten ;PA: a=gelesene Daten ;VR: a,b, RW_BIT=1, RS_BIT=1 setb RS_BIT sjmp disp_byt_in disp_addr_in: ; Befehl "Read Busy+Addr" ;PE: r2=E_oben _oder_ E_unten ;PA: a=gelesene Daten ;VR: a,b, RW_BIT=1, RS_BIT=0 clr RS_BIT disp_byt_in: ;1 Byte vom Display einlesen ;PE: [CurDisplay] = E_oben _oder_ E_unten sowie RW=1 und RS wie gewünscht ;PA: a=gelesene Daten ;VR: a,r2 setb RW_BIT call disp_nib_in ;High-Nibble zuerst lesen swap a mov r2,a call disp_nib_in ;Low-Nibble danach lesen orl a,r2 ret disp_wait_ready: ;Wartet bis Display bereit zum Datenempfang ;VR: RW_BIT=1, RS_BIT=0, kann komplett blockieren! push ACC disp_warte: call disp_addr_in jb ACC.7,disp_warte pop ACC ret disp_char_out: ;1 Zeichen (mit RS=1) auf Display-Hälfte(n) ausgeben, wartet auf Bereitschaft ;PE: a=Zeichen (oder Zeile des Zeichenbildes) [CurDisplay]=Display-Hälften-Bit(s) ;PA: - ;VR: r2 call disp_wait_ready setb RS_BIT sjmp disp_byt_out disp_ctrl_out: ;1 Steuer-Byte (mit RS=0) auf Display-Hälfte(n) ausgeben, wartet auf Bereitschaft ;PE: a=Steuerbyte, [CurDisplay]=Display-Hälften-Bit(s) ;PA: - ;VR: r2 call disp_wait_ready ;löscht RS bereits(!) disp_byt_out: ;1 Byte auf Display ausgeben OHNE zu warten ;PE: a=Zeichen- oder Steuerbyte, RS_BIT nach Wunsch, [CurDisplay]=Bits ;PA: - ;VR: r2 clr RW_BIT ;immer "schreiben" acall disp_nib_out disp_nib_out: ;Ausgabe eines Nibbles aufs Display(s) ohne auf Not-Busy warten ;PE: a=Zeichen- oder Steuer-Nibble IM HIGH-TEIL, r2="Steuerleitungen", also ; E_oben = oberen Controller ansprechen, E_unten = entsprechend, auch beide gleichzeitig möglich ; RS_BIT = Zeichenausgabe (sonst Steuerbefehl) ;PA: a: Low-Nibble zum High-Nibble geschafft, Low-Nibble undefiniert ;VR: a,r2 rlc a ;Bit ausschieben mov DISP_DATA.DATA3_BIT,c ;Bit3 hier einsetzen rlc a ;Bit ausschieben mov DISP_DATA.DATA2_BIT,c ;Bit2 dort einsetzen rlc a ;Bit ausschieben mov DISP_DATA.DATA1_BIT,c ;dito rlc a ;Bit ausschieben mov DISP_DATA.DATA0_BIT,c mov r2,a mov a,DISP_CONTROL orl a,CurDisplay mov DISP_CONTROL,a ;das setzt die gewünschen Bits mov a,r2 jmp disp_disable ;fallende Flanke(n) erzeugen disp_init_ll: ;LowLevel-Initialisierung des Displays und seines Interfaces ;PE: - ;PA: [CurDisplay] adressiert noch beide Display-Hälften ;VR: a,r2,r4,r5 mov CurDisplay,#E_oben or E_unten ;beide Hälften clr RS_BIT ;immer "Steuerbefehl" clr RW_BIT ;immer "schreiben" mov a,#30h call disp_nib_out mov r4,#4 call wait mov a,#30h call disp_nib_out call wait1 mov a,#30h call disp_nib_out call wait1 mov a,#20h ; auf Nibble-Betrieb stellen call disp_nib_out call wait1 mov a,#28h ; "System Set": 2/4 Zeilen jmp disp_ctrl_out ;******************************** ;** Anzeige: HighLevel-Zugriff ** ;******************************** ;PUBLIC put_cg, gotoxy, put_xy, Put_Follow, Put_XY_Follow, disp_init put_cg: ;Zeichen-Bitmap setzen ;PE: a=CG-Adresse (40h=1. Zeichen, 48h=2. Zeichen usw.) ; r3=Anzahl Bytes ; DPTR=Bitmap-Zeiger (ins Code-Segment) 8 Bytes ;PA: DPTR zeigt hinter die 8 Bytes ;VR: a,b,DPTR,r2,r3,[CurDisplay] (danach gotoxy aufrufen!) mov CurDisplay,#E_oben or E_unten ;beide Chips call disp_ctrl_out ; "CG-Adresse setzen" pu_cg_next: clr a movc a,@a+dptr inc dptr call disp_char_out djnz r3,pu_cg_next ret gotoxy: ;PE: R3=Spalte (Bits 0..5) und Zeile (Bits 6 und 7) ;PA: [CurDisplay]=Zeichen-Ausgabe-"Adresse" für folgende byt_out-Aufrufe ;VR: a,r2 mov CurDisplay,#E_oben mov a,r3 ;r3 legt fest E_oben oder E_unten mit Bit 7 anl a,#80h jz pu_1 ;E_oben oder E_unten? mov CurDisplay,#E_unten pu_1: mov a,r3 orl a,#80h ; Adress-Befehl zusammensetzen jmp disp_ctrl_out ; "CC-Adresse setzen" ;gotoxy ENDP put_xy: ;String-Ausgabe aus Codesegment ;PE: r3=Ausgabe-Adresse (0=1. Zeile, 40h=2. Zeile, 80h=3. Zeile, C0h=4. Zeile) ; DPTR=String-Zeiger (im Code-Segment) ;PA: DPTR zeigt hinter die Null ;VR: a=0,DPTR,r2 call gotoxy sjmp put_string put_string_loop: call disp_char_out put_string: clr a movc a,@a+dptr inc dptr jnz put_string_loop ;(noch ein) Zeichen ausgeben ret Put_Follow: ;Ausgabe konstanter ASCIIZ-String, der nach dem Unterprogramm-Aufruf ;notiert wird ;PE: - (CurDisplay muss durch einen Aufruf von gotoxy stehen) ;PA: - ;VR: a=0,dptr,r2 pop DPH pop DPL call put_string jmp @a+dptr ;zurück zum Aufrufer (wirklich!) Put_XY_Follow: ;wie "gotoxy" und "Put_Follow" zusammen ;PE: R3 = Position ;PA: - ;VR: a=0,dptr,r2 pop DPH pop DPL call put_xy jmp @a+dptr ;zurück zum Aufrufer (wirklich!) disp_init: ;HighLevel-Initialisierung: Modus setzen u.ä. mov a,#08h call disp_ctrl_out mov a,#01h call disp_ctrl_out mov a,#06h call disp_ctrl_out mov a,#00001100b ;Cursors blinken usw. jmp disp_ctrl_out ;******************************************************** ;** Anzeige: Anwendungs-orientierte Routinen und Daten ** ;******************************************************** ;Hallo2: db 1,9," Tr",0E1h,"nen",0F5h,"berstr",0EFh,"mt ",0e4h,"F k",8,0 ;Hallo3: db "µ@#+*'€^^ ",0 ;ein paar "hübsche" (fehlende) Zeichen fürs Display ZG0: db 01110b ;SYMBOL großes Ohm db 10001b ; (das kleine sieht ja hässlich aus!) db 10001b db 10001b db 01010b db 01010b db 11011b db 00000b ;Cursorzeile ZG1: db 10001b ;LATIN1 großes Ä db 00100b db 01010b db 10001b db 11111b db 10001b db 10001b db 00000b ;Cursorzeile ZG2: db 10001b ;LATIN1 großes Ö db 01110b db 10001b db 10001b db 10001b db 10001b db 01110b db 00000b ;Cursorzeile ZG3: db 10001b ;LATIN1 großes Ü db 00000b ;(auch das kleine ü sieht blöd aus) db 10001b db 10001b db 10001b db 10001b db 01110b db 00000b ;Cursorzeile ZG4: db 00111b ;WIN1250 Euro-Symbol (hä?) db 01000b db 11111b db 10000b db 11110b db 10000b db 01110b db 00000b ;Cursorzeile ZG5: db 00000b ;ASCII Backslash \ db 10000b db 01000b db 00100b db 00010b db 00001b db 00000b db 00000b ;Cursorzeile ZG6: db 00000b ;ASCII Schlange/Tilde ~ db 00000b db 10110b db 01101b db 00000b db 00000b db 00000b db 00000b ;Cursorzeile Prepare_Display: mov r3,#0*64+0 ;oben links call Put_XY_Follow db "Temperatur + Feuchtemessung",0 ;Spalte mit 3x "°C" ausgeben mov r3,#1*64+13 ;Zeile 1 = 2. Zeile ph1: call Put_XY_Follow db 0DFh,"C",0 mov a,r3 add a,#1*64 ;1 Zeile runter mov r3,a jnc ph1 ;Spalte mit 3x "%rF" ausgeben mov r3,#1*64+24 ;ganz hinten ph2: call Put_XY_Follow db "%rF",0 mov a,r3 add a,#1*64 ;1 Zeile runter mov r3,a jnc ph2 ;"Min" und "Max" ausgeben mov r3,#2*64+0 call Put_XY_Follow db "Min:",0 mov r3,#3*64+0 call Put_XY_Follow db "Max:",0 ret itoa: ;PE: r4=auszugebende 8-bit-Zahl mov r3,#0 mov a,r4 itoa_1: mov B,#10 div ab push B inc r3 jnz itoa_1 itoa_2: pop ACC add a,#'0' call disp_char_out djnz r3,itoa_2 ret FixOut16: ;PROC ;Ausgabe einer 16-bit-Festkommazahl ;PE: r7:r6 = 16-bit-Zahl vzb. ; Fix_Feldb = Feldbreite ; Fix_Dezim = Anzahl Dezimalstellen ; Fix_Flags = weitere Steuerbits... (???) ;PA: - ;VR: r3=0,r4,r5=0,r6=0,r7=0 mov a,r7 rlc a mov Fix_Flags.7,c ;Vorzeichen-Merker jnc do_6 clr c clr a subb a,r6 ;Zweierkomplement mov r6,a clr a subb a,r7 mov r7,a do_6: mov r3,#0 do_3: mov r5,#16 mov r4,#0 do_2: ;Dividend R4:R7:R6 linksschieben mov a,r6 rlc a mov r6,a mov a,r7 rlc a mov r7,a mov a,r4 rlc a mov r4,a ;Bedingte Subtraktion von 10 von R4 add a,#-10 jnc do_1 mov r4,a do_1: ;cpl c mov a,r6 ;CY ins Bit 0 setzen rr a rlc a mov r6,a djnz r5,do_2 ;nach der Division: R4 = Rest, R7:R6=Quotient do_8: push AR4 inc r3 mov a,r3 cjne a,Fix_Dezim,do_7 mov r4,#'.'-'0' push AR4 ;Punkt einpushen inc r3 do_7: mov a,r6 orl a,r7 jnz do_3 mov r4,#0 mov a,r3 ;bspw. =3 bei 2 Dezimalstellen ist (noch) nicht OK dec a setb c subb a,Fix_Dezim jc do_8 jnb Fix_Flags.7,do_9 mov r4,#'-'-'0' push AR4 inc r3 ;ab hier wird gepoppt und ausgegeben do_9: setb c mov a,Fix_Feldb subb a,r3 jc do_4 inc a mov r4,a do_5: mov a,#' ' call disp_char_out ;führende Leerzeichen djnz r4,do_5 do_4: pop ACC add a,#'0' call disp_char_out djnz r3,do_4 ret ;DezOut16 ENDP SaveFixOut16XY: call gotoxy SaveFixOut16: push AR3 push AR6 push AR7 call FixOut16 pop AR7 pop AR6 pop AR3 ret HexOut: ;PROC ;PE: A=auszugebende Hex-Zahl, R2 siehe nib_out ;PA: - ;VR: A push ACC swap a acall NibOut ;vorwärts pop ACC NibOut: ;gibt Low-Nibble von A hexadezimal aus anl a,#0Fh add a,#90h da a addc a,#40h da a ;auf wundersame Weise wird so aus 0..F ein '0'..'F' jmp disp_char_out ;HexOut ENDP Mul16x16: ;PROC ;PE: R5:R4 = Faktor 1 (vzl.) ; R7:R6 = Faktor 2 (vzl.) ;PA: [Produkt] = Produkt ;VR: a,b ;1. mov a,r4 mov B,r6 mul ab mov Produkt,a mov Produkt+1,B ;2. mov a,r5 mov B,r7 mul ab mov Produkt+2,a mov Produkt+3,B ;3. mov a,r4 mov B,r7 acall mu2 ;vorwärts: ASM51 macht nur 1 Pass mov a,r5 mov B,r6 mu2: mul ab add a,Produkt+1 mov Produkt+1,a mov a,B addc a,Produkt+2 mov Produkt+2,a jnc mu1 inc Produkt+3 mu1: ret ;Mul16x16 ENDP ;******************* ;** SHT11-Zugriff ** ;******************* SHT11_ConnReset: ;PROC ;Reset- und Startsequenz für SHT11 ausgeben --> muss das sein?? ;PE: - (DATA=H, SCL=L) ;PA: - (DATA=H, SCL=L) ;VR: r5=0 ;ALLE__ Zeiten sind evtl viel zu lang! mov r5,#10 setb DAT abcd: setb SCK setb DAT nop clr SCK setb DAT nop djnz r5,abcd SHT11_Start: setb SCK nop nop clr DAT nop nop clr SCK nop nop setb SCK nop nop setb DAT nop nop clr SCK ;Ende SHT_Start ret ;SHT11_ConnReset ENDP SHT11_ByteOut: ;PROC ;(Kommando-) Byte ausgeben und Acknowledge abholen ;PE: A=Kommando-Byte (DATA=H, SCL=L) ;PA: CY=1 wenn Fehler, CY=0 wenn Ack-Bit OK (DATA=H, SCL=L) ;VR: R5=0,a ;Zähler mit 8 Laden mov r5,#8 SHT11_ByteOut_Loop: rlc a ;Bits ausschieben mov DAT,c ;Datenbit setzen oder rücksetzen setb SCK ;Takt=H nop clr SCK ;Takt=L setb DAT djnz r5,SHT11_ByteOut_Loop ;SHT11ack: setb SCK nop mov C,DAT ;!!!!!!!!Wenn SHT11_DATA = 1 wird, alles ok! clr SCK setb DAT ret ;SHT11_ByteOut ENDP SHT11_ByteIn: ;PROC ;PE: a=0: weiteres Byte anfordern (Bit 0) ; a=1: kein weiteres Byte anfordern ;PA: a=gelesenes Byte mov r5,#8 SHT11_ByteIn_Loop: setb SCK setb DAT nop nop mov c,DAT rlc a clr SCK setb DAT djnz r5,SHT11_ByteIn_Loop ;Quittungsbit mov DAT,c ;aus A gekommen setb SCK nop nop clr SCK setb DAT ret ;SHT11_ByteIn ENDP SHT11_Get_Messwert: ;proc ;PE: r6=5 für %rF, =3 für Temperatur ;PA: r7:r6=Messwert vom SHT11 ; CY=1 wenn kein Messwert lieferbar ;VR: r7,r6,r4,r5=0,a call SHT11_Start mov a,r6 call SHT11_ByteOut jc SHT11_raus mov r4,#0 ;256 ms warten call wait setb C jb DAT,SHT11_raus ;wenn SHT11 immer noch nicht fertig clr a ;weitere Daten anfordern call SHT11_ByteIn mov r7,a ;High-Byte %rF mov a,#1 ;keine weiteren Daten (Prüfsumme) anfordern call SHT11_ByteIn mov r6,a ;Low-Byte %rF clr C SHT11_raus: ret ;SHT11_Get_Messwert endp Calc_Feuchte: ;PROC ;PE: R7:R6=Wert vom Sensor ("SO(RH)") ;PA: R7:R6=Feuchte in Hundertstel Prozent (Festkomma) ;VR: R4,R5 push AR2 push AR3 ;linearer Summand mov r4,#LOW(1037) ;LOW(4.05*256) mov r5,#HIGH(1037) call Mul16x16 mov r2,Produkt+1 ;"mittlerer" Teil, entspr. /256 mov r3,Produkt+2 ;quadratischer Summand mov r4,6 mov r5,7 call Mul16x16 ;Quadrat mov r4,#LOW(4698) ;0.07168*64K mov r5,#HIGH(4698) mov r6,Produkt+1 ;"mittlerer" Teil, /256 mov r7,Produkt+2 call Mul16x16 ;subtrahieren clr c mov a,r2 subb a,Produkt+2 ;"oberer" Teil, /64K mov r2,a mov a,r3 subb a,Produkt+3 mov r3,a ;konstanter Summand mov a,r2 add a,#LOW(-400) mov r6,a mov a,r3 addc a,#HIGH(-400) mov r7,a pop AR3 pop AR2 ret ;Calc_Feuchte ENDP InitAllMinMax: ;initialisiert TMinMax und FMinMax (aufeinanderfolgend!) ;PE: - PA: - VR: R0 mov r0,#TMinMax call InitMinMax InitMinMax: ;initialisiert zwei aufeinanderfolgende WORDs mit MAXINT und -MAXINT mov @r0,#0FFh inc r0 mov @r0,#07Fh inc r0 mov @r0,#000h inc r0 mov @r0,#080h inc r0 ret MakeMinMax: ;Berechnet neue Minima und Maxima und gibt diese bei Veränderung aus ;PE: R3 = Cursor-Spalte (Bit 5:0), Zeile=1 ; R7:R6 = aktueller Messwert ; R0 = Zeiger auf Minimum-WORD (LOW) und Maximum-WORD (HIGH), d.h. ; [R0]=LOW Minimum, [R0+1]=HIGH Minimum, [R0+2]=LOW Maximum usw. ;PA: - ;VR: R0,R2,R3 clr c mov a,r6 subb a,@r0 inc r0 mov a,r7 subb a,@r0 jnb OV,mmm1 ;=OV cpl a mmm1: jnb ACC.7,NoNewMin mov a,r7 mov @r0,a dec r0 mov a,r6 mov @r0,a inc r0 mov a,r3 anl a,#03Fh ;Spalte stehen lassen orl a,#2*64 ;Zeile 2 mov r3,a call SaveFixOut16XY NoNewMin: inc r0 ;jetzt zeigt R0 aufs Maximum clr c mov a,@r0 subb a,r6 inc r0 mov a,@r0 subb a,r7 swap a ;sprungfreie Version, genauso lang rr a xrl a,PSW jnb ACC.2,NoNewMax mov a,r7 mov @r0,a dec r0 mov a,r6 mov @r0,a mov a,r3 anl a,#03Fh ;Spalte stehen lassen orl a,#3*64 ;Zeile 3 mov r3,a call SaveFixOut16XY NoNewMax: ret NoFixOutXY: ;Ausgabe KEINER Zahl bei Interface-Problem mit dem SHT11 call Put_XY_Follow db " ---.--",0 ret init: mov SP,#07Fh call disp_init_ll ;LowLevel-Initialisierung call disp_init ;HighLevel-Init. mov DPTR,#ZG0 ;großes Ohm, Ä, Ö, Ü, Euro, \, ~ mov a,#40h ;Adress-Befehl: Adresse 0 mov r3,#8*7 ;7 aufeinanderfolgende Zeichen call put_cg call Prepare_Display mov Fix_Feldb,#7 mov Fix_Dezim,#2 mov Num_Messung,#0 call InitAllMinMax MainLoop: mov r3,#1*64+1 call gotoxy inc Num_Messung mov a,Num_Messung call HexOut ;Temperatur einlesen, umrechnen und ausgeben mov r6,#00000011b ;Kommando: Temperatur call SHT11_Get_Messwert mov r3,#1*64+5 jc NoTemp mov a,r6 add a,#LOW(-4000) mov r6,a mov a,r7 addc a,#HIGH(-4000) mov r7,a call SaveFixOut16XY mov r0,#TMinMax call MakeMinMax ;übernimmt auch R3 sjmp TempOK NoTemp: call NoFixOutXY TempOK: ;Feuchte einlesen, umrechnen und ausgeben mov r6,#00000101b ;Kommando: %rF call SHT11_Get_Messwert mov r3,#1*64+16 jc NoHumi call Calc_Feuchte ;"umrechnen" in Prozent call SaveFixOut16XY mov r0,#FMinMax call MakeMinMax ;übernimmt auch R3 sjmp HumiOK NoHumi: call NoFixOutXY HumiOK: mov r4,#0 call wait ;256 ms (Maximum) verbraten mov P1,#128 mov a,P6 ;P6 ist leider nicht bitadressierbar jb ACC.0,MainLoop call InitAllMinMax ;mit der Taste an P6.0 Minima/Maxima rücksetzen sjmp MainLoop END