$PROCESSOR(80515)
$WF(D:)
$BA
$NOPI

;**********************************************************************
;*                                                                    *
;*     PID-REGLER-PROGRAMM für den Microcontrollerbaustein SAB 80535  *
;*   																  *
;*                                                                    *
;*                                                                    *
;*     programmiert von A.Leischner                                   *
;*                                                                    *
;*     (C) MAI 1993 by A.Leischner                                    *
;*                                                                    *
;**********************************************************************

;*********************
;
;Variablendefinitionen
;
;*********************

SOLLWERT1       EQU     71              ;Startwert Heizung Lowbyte
SOLLWERT2       EQU     208             ;Startwert Heizung Highbyte
KP_WERT         EQU     3               ;Voreinstellung KP-Wert
KI_WERT         EQU     28              ;Voreinstellung KI-Wert
KD_WERT         EQU     3               ;Voreinstellung KD-Wert
TIMER_CLICK_HI	EQU	216		;Initialisierung Takt Timer 0
TIMER_CLICK_LO  EQU     239             ;
INIT_HEIZ_EIN	EQU	1 		;Initialisierung Heizung EIN
INIT_HEIZ_AUS	EQU	199 		;Initialisierung Heizung AUS
INIT_RS		EQU	11011010B	;Initialisierung RS232
INIT_PWR	EQU	07FH		;Initialisierung fr PCON
TAST_INC	EQU	'+'		;Taste +
TAST_DEC	EQU	'-'		;Taste -
TAST_0		EQU	'0'		;Taste 0
TAST_1		EQU	'1'		;Taste 1
START_BEREICH   EQU     41H             ;Initialisierung Startwert Messbereich AD-Wandler
AD_WERT_HI	EQU	10H		;Zwischenspeicher Wert aus AD-Wandler 1.Messung in BIN
AD_WERT_LO	EQU	11H		;Zwischenspeicher Wert aus AD-Wandler 2.Messung in BIN
AD_ZW_HI        EQU     12H             ;Zwischenspeicher AD-Wert Rechnen
AD_ZW_LO        EQU     13H             ;Zwischenspeicher AD-Wert Rechnen
AD_BEREICH	EQU	14H		;Zwischenspeicher Messbereich in BIN
HEIZ_EIN	EQU	15H		;Speicherstelle fr Heizungs EIN Wert
HEIZ_AUS	EQU	16H		;Speicherstelle fr Heizungs AUS Wert
HEIZ_COUNT	EQU	17H		;Z„hler Heizung EIN AUS
KP              EQU     18H             ;Zeiger Speicherstelle KP-Wert
KI              EQU     19H             ;Zeiger Speicherstelle KI-Wert
KD              EQU     1AH             ;Zeiger Speicherstelle KD-Wert
SOLL1           EQU     1BH             ;Speicher Skalenwert Sollwert 1.Wandlung
SOLL2		EQU	1CH		;Speicher Skalenwert Sollwert 2.Wandlung
SOLLMB		EQU	1DH		;Speicher Me?bereich Sollwert
SIGN		EQU	1FH		;Zeichen aus serieller Schnittstelle
TIME            EQU     35H             ;Speicherstelle Timer
BCD_BUF         EQU     36H             ;Speicherstelle serielle Schnittstelle
ZWMEM     	EQU	3CH		;Zwischenergebnis aus Rechnen
KIMEM		EQU	50H		;Speicher Integrierwert
KDMEM		EQU	54H		;Speicher Differenzierwert
STACKPOINTER    EQU     58H             ;Startwert Stackpointer

;************************************************
;
;Definition der Bitadressierbaren Speicherstellen
;
;************************************************


ZNEU	BIT	0 		;ZNEU=1: UART HAT NEUES ZEICHEN EMPFANGEN
HZ	BIT	1		;Heizungspulsflag
HPR0	BIT	2		;Heizung 0 Prozent Heizleistung wenn gesetzt
HZA	BIT	3		;Heizung gesperrt wenn 1
HAND    BIT	4		;Hand-Automatik-Flag,wenn 1 Handbetrieb
ETU	BIT	5		;šbertemperaturflag, 1 wenn šbertemperaturschalter ausgel”st
ADOV	BIT	6		;Wandleroverflowflag, 1 wenn Wandlung im šberlauf
HZ1	BIT     7		;Warteflag fr Ablauf Heizperiode
				;um neuen Wert schreiben zu k”nnen
FULL    BIT     8               ;Wenn Bit gesetzt, volle Heizleistung

;********************************
;
;Definition der Interruptadressen
;
;********************************

                ORG     0                       ;an Adresse 0 = RESET wird die 
                JMP     100H                    ;Programmstartadresse abgelegt 

                ORG     SINT                    ;an Adresse 24H =SINT wird
                JMP     IN_CHAR                 ;die Adresse der seriellen Schnittstellen Interrupt Routine abgelegt

                ORG     TIMER0                  ;an Adresse 10H=TIMER0 wird 
                JMP     TNINT                   ;die Adresse der TIMER0 Interruptbehandlungsroutine abgelegt


;*********************************************
;
;Programmstart Initialisierung des Prozessors
;
;********************************************


		ORG	100H
START1:		MOV	SP,#STACKPOINTER	;Stackpointer initialisieren
                SETB    ADCON.7                 ;Initialisierung der 
                ANL     PCON,#INIT_PWR          ;seriellen Schnittstelle fr Baudrate 4800Bps
		SETB	TR1			;START  TIMER 1
		MOV	SCON,#INIT_RS		;UART MODE 3, EMPFAENGER FREIGEGEBEN
 						;SENDER-INTERRUPT-FLAG GESETZT
                SETB    ES                      ;SERIELLER-PORT-INTERRUPT FREIGEBEN
                ORL     TMOD,#1                 ;TIMER 0 als 16-Bit Timer initialisieren
                SETB    ET0                     ;und TIMER0 Interrupt festlegen
                MOV     TH0,#TIMER_CLICK_HI     ;Startwert TIMER0
                MOV     TL0,#TIMER_CLICK_LO     ;laden Interrupt alle 10ms(halbe Periode 50Hz)
                MOV     HEIZ_EIN,#INIT_HEIZ_EIN ;Startwert der Heizleistung EIN-Zeit in Prozent laden
                MOV     HEIZ_AUS,#INIT_HEIZ_AUS ;Startwert der Heizleistung AUS-Zeit in Prozent laden
                MOV     HEIZ_COUNT,HEIZ_AUS     ;fr Programmstart Heizung auf AUS initialisieren
                MOV     KP,#KP_WERT             ;\
                MOV     KI,#KI_WERT             ; > Startwerte fr PID-Regelung laden
                MOV     KD,#KD_WERT             ;/
                MOV     TIME,#0                 ;Sekundenz„hler auf 0 setzen
                MOV     SOLL1,#SOLLWERT1        ;\
                MOV     SOLL2,#SOLLWERT2        ;/Startsollwert laden
                MOV     SOLLMB,#START_BEREICH+11H ;Anfangsme?bereich AD-Wandlung setzen
                MOV     20H,#0                  ;Bitadressierbare Speicherstelle auf Startwert setzen
                SETB    TR0                     ;TIMER0 Interrupt freigeben
                SETB    HPR0                    ;Heizungsinterrupt freigeben
		SETB	EAL			;FREIGABE ALLER INTERRUPTS
		CLR	ZNEU			;NOCH KEIN ZEICHEN EMPFANGEN
                CLR     P4.7                    ;Steuerleitung Heizung l”schen
                CLR     HZ                      ;Heizungsflag l”schen
                CLR     HAND                    ;Handschaltung l”schen

;******************************************
;
;Eingabe- und Warteschleife (Hauptprogramm)
;
;******************************************

WARTE:          MOV     A,SIGN                  ;Zeichen aus Zwischenspeicher serieller Port einlesen
                MOV     SIGN,#0                 ;und Zwischenspeicher fr n„chstes Zeichen l”schen
                CJNE    A,#TAST_1,NEXT1         ;Wenn Taste 1 am PC gedrckt                    
                SETB    HZA                     ;Heizungsflag setzen, Einschalten erfolgt bei n„chstem TIMER0 Interrupt
NEXT1:          CJNE    A,#TAST_0,NEXT2         ;Wenn Taste 0 am PC gedrckt                    
                CLR     P4.7                    ;Steuerleitung Heizung ausschalten
                CLR     HZA                     ;Heizungsflag l”schen
NEXT2:		CJNE	A,#TAST_DEC,NEXT3	;Wenn -Taste gedrckt Heizleistung zurckfahren
                INC     HEIZ_AUS                ;Z„hler Heizleistung AUS erh”hen
                DJNZ    HEIZ_EIN,NEXT3          ;Z„hler Heizleistung EIN erniedrigen
                MOV     HEIZ_EIN,#INIT_HEIZ_EIN ;Wenn Z„hler Heizleistung EIN = 0
                MOV     HEIZ_AUS,#INIT_HEIZ_AUS ;Heizleistung auf Startwert zurcksetzen 
NEXT3:		CJNE	A,#TAST_INC,NEXT4	;Wenn +Taste gedrckt Heizleistung hochfahren
                INC     HEIZ_EIN                ;Z„hler Heizleistung EIN erh”hen
                DJNZ    HEIZ_AUS,NEXT4          ;Z„hler Heizleistung AUS erniedrigen
                MOV     HEIZ_AUS,#INIT_HEIZ_EIN ;Wenn Z„hler Heizleistung AUS = 0
                MOV     HEIZ_EIN,#INIT_HEIZ_AUS ;Heizleistung auf volle Leistung schalten
NEXT4:          CJNE    A,#'S',NEXT41           ;Wenn 'S' empfangen wird
                SETB    HPR0                    ;Heizungsinterrupt freigeben
NEXT41:         CJNE    A,#'R',NEXT42           ;Wenn 'R' empfangen wird 
                CLR     HPR0                    ;Heizungsinterrupt sperren
NEXT42:         CJNE    A,#'2',NEXT5            ;Bei Empfang von '2'
                INC     KP                      ;KP-Wert +1
NEXT5:          CJNE    A,#'3',NEXT6            ;Bei Empfang von '3'
                DEC     KP                      ;KP-Wert -1
NEXT6:          CJNE    A,#'4',NEXT7            ;Bei Empfang von '4'
                INC     KI                      ;KI-Wert +1
NEXT7:          CJNE    A,#'5',NEXT8            ;Bei Empfang von '5'
                DEC     KI                      ;KI-Wert -1
NEXT8:          CJNE    A,#'6',NEXT9            ;Bei Empfang von '6'
                INC     KD                      ;KD-Wert +1
NEXT9:          CJNE    A,#'7',NEXT10           ;Bei Empfang von '7'
                DEC     KD                      ;KD-Wert -1
NEXT10:         CJNE    A,#'P',NEXT11           ;Bei Empfang von 'P'
                INC     SOLL2                   ;Sollwert 2.Messung +1
NEXT11:         CJNE    A,#'M',NEXT12           ;Bei Empfang von 'M'
                DEC     SOLL2                   ;Sollwert 2.Messung -1
NEXT12:         CJNE    A,#'W',NEXT13           ;Bei Empfang von 'W'
                INC     SOLL1                   ;Sollwert 1.Messung +1
NEXT13:         CJNE    A,#'X',NEXT14           ;Bei Empfang von 'X'
                DEC     SOLL1                   ;Sollwert 1.Messung -1
NEXT14:         CJNE    A,#'I',NEXT15           ;Bei Empfang von 'I'
                MOV     A,SOLLMB                ;Sollmessbereich laden
                ADD     A,#11H                  ;1 Stufe raufschalten
                MOV     SOLLMB,A                ;abspeichern
                CJNE    A,#0DH,NEXT15           ;Wenn kein šberlauf weiter
                MOV     SOLLMB,#0FCH            ;sonst auf h”chsten  Messbereich umschalten
NEXT15:         CJNE    A,#'D',NEXT16           ;Bei Empfang von 'D'
                MOV     A,SOLLMB                ;Sollmessbereich laden
                CLR     C                       ;
                SUBB    A,#11H                  ;1 Stufe runterschalten
                MOV     SOLLMB,A                ;abspeichern
                CJNE    A,#30H,NEXT16           ;Wenn kein unterschreiten des Messbereichs weiter
                MOV     SOLLMB,#41H             ;sonst auf niedrigsten Messbereich umschalten
NEXT16:         CJNE    A,#'H',NEXT17           ;Bei Empfang von 'H'
                CPL     HAND                    ;Handschaltflag toggeln
NEXT17:         JB      P4.6,UT                 ;Wenn šbertemperatur anspricht, šbertemperatur-Behandlung aufrufen
                CLR     ETU                     ;sonst šbertemperaturflag l”schen
NEXT18:		CALL    ADCV			;AUFRUF DER AD-WANDLUNG
                CALL    OUT_STATUS              ;Statusmeldungen ausgeben
   		JMP	WARTE			;WARTEN AUF INTERRUPT DER SERIELLEN SCHNITSTELLE

;*********************************
;
;šbertemperatur-Behandlungsroutine
;
;*********************************

UT:             CLR     P4.7                    ;Steuerleitung Heizung ausschalten
                SETB    ETU                     ;šbertemperaturflag setzen
                JMP     NEXT18                  ;Warten bis šbertemperatur gel”scht

;******************************
;
;Interruptserviceroutine TIMER0
;
;******************************

TNINT:		MOV 	TH0,#TIMER_CLICK_HI	;TIMER 0 neu laden
		MOV	TL0,#TIMER_CLICK_LO	;  "   "  "    "
                PUSH    ACC                     ;Inhalt Accu auf STACK retten
		DJNZ	HEIZ_COUNT,WEITER	;Perioden warten
		JBC 	P4.7,H_AUS     		;wenn Leitung P4.7 High war, dann zurck setzen
;               JB      HPR0,H_AUS              ;Wenn Heizungsflag gel”scht, Heizung ausschalten  
                CLR     HZ1                     ;Messroutine sperren
                JNB     HZA,H_AUS               ;Wenn Heizungs EIN-Flag gel”scht Heizung ausschalten
                JB      FULL,LOAD_HEIZ          ;Wenn FULL-Flag gesetzt Heizung auf volle Leistung schalten
                MOV     A,#50                   ;Grenze MaxHeizleiszung 
                SUBB    A,HEIZ_EIN              ;Wenn Heizleistung kleiner als Grenze
                JNC     LOAD_HEIZ               ;Heizung EIN-Z„hler mit HEIZ_EIN laden
                MOV     HEIZ_EIN,#30            ;sonst Heizleistung auf 30Prozent schalten
LOAD_HEIZ	MOV	HEIZ_COUNT,HEIZ_EIN	;Z„hler mit Heizungseinzeit laden
                SETB    P4.7                    ;Steuerleitung Heizung einschalten
                SETB    HZ                      ;Heizungsflag setzen
                JMP     WEITER                  ;Fortsetzung der Interruptbehandlung
H_AUS:		MOV	HEIZ_COUNT,HEIZ_AUS	;Z„hler mit Heizungsauszeit laden
                CLR     P4.7                    ;Steuerleitung Heizung ausschalten
                CLR     HZ                      ;Heizungsflag l”schen
                CLR     HZ1                     ;Messroutine freigeben
WEITER:         INC     TIME                    ;Taktz„hler +1
                CLR     C                       ;
                MOV     A,#100                  ;Wenn Taktz„hler 100
		SUBB	A,TIME
                JC      TIMEINC                 ;dann Uhr inkrementieren
TIMELOAD:       POP     ACC                     ;Accu wiederherstellen
                RETI                            ;Interruptbehandlung beenden
;***********
;
;interne Uhr
;
;***********

TIMEINC:        INC     TIME-1                  ;Sekundenz„hler
		CLR	C
		MOV	A,#59
		SUBB	A,TIME-1
		JC	TIMEINCM
		MOV	TIME,#0
		JMP	TIMELOAD
TIMEINCM:       INC     TIME-2                  ;Minutenz„hler
		MOV	TIME-1,#0
		MOV	TIME,#0
		CLR	C
		MOV	A,#59
		SUBB	A,TIME-2
		JC	TIMEINCH
		JMP 	TIMELOAD
TIMEINCH:       INC     TIME-3                  ;Stundenz„hler
		MOV	TIME-2,#0
		JMP	TIMELOAD

;***********************
;
;Analog-Digital Wandlung
;
;***********************

ADCV:           CLR     ADOV            ;Wandleroverflowflag l”schen
		ORL	ADCON,#0	;AD-Wandler Kanal 0 setzen
                MOV     R1,#0           ;Messbereich Grobmessung setzen
ADCVW:          CALL    MESSAD          ;und messen
                MOV     AD_WERT_HI,A    ;Wandlerwert fr Weiterverarbeitung zwischenspeichern
                MOV     AD_ZW_HI,A      ;     "       "          "                 "
ADH:    	MOV 	DPTR,#ADTAB	;Zeiger mit Tabelle fr Wandlerumschaltung laden
		MOV	R1,#START_BEREICH;Register 1 mit Versatz fr Wandlerbereichsumschaltung laden
		CLR	C		;Carry-Flag fr Berechnung Wandlerbereich l”schen
		MOV	A,AD_WERT_HI	;prfen ob
		ADD	A,#24		;šberlauf Wandler
                JNC     ADOVW           ;wenn nein weiter                                           
                SETB    ADOV            ;Meldung und Wandlerwert 1.Messung ausgeben
		RET
ADOVW:          MOV     A,AD_WERT_HI    ;Wandlerbereich
		SUBB	A,#15		;ermitteln
		JNC	MESS3		;wenn Wandlerbereich klein, sofort messen
MESS5:          RET                     ;sonst ENDE der Messung 
MESS3:          MOV     A,AD_WERT_HI    ;Wandlerwert 1.Messung zurckholen
		MOV	R0,A		;Accu zwischenspeichern
MESS1:		CLR	A		;und l”schen
		MOVC	A,@A+DPTR	;1.Tabellenwert laden
                SUBB    A,R0            ;und auf Gr”?e 1.Messung vergleichen
		JNC	MESS2		;wenn Gr”?e zu klein n„chsten Bereich w„hlen
		MOV	A,R1		;Wandlerbereich
		ADD	A,#11H		;hochschalten
                MOV     R1,A            ;und zwischenspeichern
                INC     DPTR            ;n„chsten Eintrag aus Tabelle laden
                JMP     MESS1           ;und erneut prfen
MESS2:		MOV	AD_BEREICH,R1 	;Me?bereich speichern
                CALL    MESSAD          ;und messen
		MOV	AD_WERT_LO,A	;Wandlerwert zwischenspeichern
                MOV     AD_ZW_LO,A      ;    "              "
;               JMP     ADL             ;Fr Schnellmessung diesen Befehl entfernen

;***********************************************
;
;Schnellmessung zur Überprüung der Regelgrenzen
;
;***********************************************

		MOV 	DAPR,R1
		JB	ADCON.4,$
		PUSH	ACC
		CLR	C
		ADD 	A,#10
		SUBB	A,AD_ZW_LO
		POP	ACC
		JC	ADL0
		CLR 	C
		SUBB	A,#0
		XCH	A,AD_ZW_LO
		SUBB	A,AD_ZW_LO
		JC	ADL0

;****************************
;
; Überprüfung der Regelgrenzen
;
;****************************


ADL:            JB      ETU,MESS5       ;Wenn šbertemperatur Messung beenden                    
                JNB     HAND,ADL1       ;Wenn keine Handschaltung weiter
ADL0:           JMP     MESS4           ;ENDE-Messung
ADL1:           MOV     A,AD_BEREICH    ;Zur Zeit gltigen Messbereich laden
		CLR	C
                SUBB    A,SOLLMB        ;und mit Sollmessbereich vergleichen
                JZ      ADL2            ;wenn IST-Messbereich gleich
                JC      ADL2            ;oder kleiner Sollmessbereich weiter
                JMP     NOHEAT          ;sonst Heizung ausschalten
ADL2:           MOV     A,SOLL1         ;Sollwert 1.Messung laden
                SUBB    A,#5            ;Wenn Istwert
                SUBB    A,AD_WERT_HI    ;kleiner Sollwert -5
                JC      LZWI            ;dann Regelparameter ermitteln
                SETB    FULL            ;sonst volle Heizleisung schalten
ADL3:           MOV     HEIZ_EIN,#INIT_HEIZ_AUS ;und
                MOV     HEIZ_AUS,#INIT_HEIZ_EIN ;laden
                CLR     HPR0            ;Messroutine sperren
                JMP     MESS4           ;Ende Regelung
;*************************************
;*                                   *  
;*   Berechnung der Regelparameter   *
;*                                   *
;*************************************

;************************
;
;Zwischenspeicher löschen
;
;************************


LZWI:           CLR     FULL            ;Volle Heizleistung sperren
                MOV     R1,#ZWMEM       ;Zwischenspeicher
                MOV     R0,#3           ;fr Regelparameter 
LZWM:           MOV     @R1,#0          ;l”schen
		INC	R1
		DJNZ	R0,LZWM

;***************************************
;
;Regelparamaeter Proportianalverstellung
;
;***************************************

                MOV     A,SOLL2         ;Sollwert 2.Messung laden
		CLR	C
                SUBB    A,AD_WERT_LO    ;mit Istwert 2.Messung vergleichen
                MOV     KDMEM+1,A       ;und Differenz speichern 
                JC      NOHEAT          ;wenn Istwert g”sser Sollwert, Heizung ausschalten
                MOV     B,KP            ;sonst Regeldifferenz
                MUL     AB              ;mit KP-Wert multiplizieren
                MOV     R7,A            ;und fr weitere Bearbeitung zwischenspeichern

;***********************************
;
;Reglerparameter Integralverstellung
;
;***********************************

                MOV     A,KDMEM+1       ;Regeldifferenz laden
                ADDC    A,KIMEM         ;zum letzten Wert des I-Anteils addieren
                MOV     KIMEM,A         ;und abspeichern
                JNC     MESS2I          ;Wenn kein šberlauf fortfahren
                INC     KIMEM+1         ;sonst Speicher NSB KIMEM +1
                CLR     C               ;Carry-Flag
                CLR     A               ;und Accu l”schen
                SUBB    A,KIMEM+1       ;NSB KIMEM auf šberlauf prfen
                JNC     MESS2I          ;wenn kein šberlauf fortfahren
                INC     KIMEM+2         ;sonst MSB KIMEM erh”hen
MESS2I:         MOV     A,KIMEM         ;neu errechneten Wert des I-Anteils laden
                MOV     B,KI            ;mit KI-Wert
                MUL     AB              ;multiplizieren
                MOV     ZWMEM,A         ;und Rest zwischenspeichern
                MOV     R0,B            ;Ergebnis der Multiplikation zwischenspeichern
                MOV     A,KIMEM+1       ;NSB KIMEM laden
                MOV     B,KI            ;mit KI-Wert
                MUL     AB              ;multiplizieren
                ADDC    A,R0            ;Ergebnis aus MUL LSB KIMEM addieren
                MOV     ZWMEM+1,A       ;und abspeichern
                JNC     MESS2I1         ;wenn kein šberlauf aufgetreten ist fortfahren
                INC     B               ;sonst B+1
MESS2I1:        MOV     R0,B            ;Ergebnis zwischenspeichern
                MOV     A,KIMEM+2       ;MSB KIMEM laden
                MOV     B,KI            ;mit KI-Wert
                MUL     AB              ;multiplizieren
                ADDC    A,R0            ;Ergebnis aus MUL NSB KIMEM addieren
                MOV     ZWMEM+2,A       ;und abspeichern
                JNC     MESS2I2         ;wenn kein šberaluf aufgetreten ist fortfahren
                INC     B               ;sonst B+1
MESS2I2:        MOV     ZWMEM+3,B       ;šberlauf in OV-KIMEM abspeichern
                MOV     A,R7            ;Ergebnis der Proportionalverknpfung laden
                ADD     A,ZWMEM+3       ;šberlauf aus OV KIMEM addieren
                MOV     R7,A            ;und Ergebnis zwischenspeichern

;**************************************
;
;Regelparameter Differentialverstellung
;
;**************************************

                MOV     A,KDMEM+1       ;zur Zeit gltige Regeldifferenz laden
                CLR     C               ;Carry Flag l”schen
                SUBB    A,KDMEM         ;Differenz aus alter und neuer Regeldifferenz bilden
                MOV     B,KD            ;mit KD-Wert
                MUL     AB              ;multiplizieren
                XCH     A,B             ;Ergebnis
                ADD     A,R7            ;zum ermittelten P- + I-Anteil hinzuaddieren
                MOV     R7,A            ;und speichern
                MOV     KDMEM,KDMEM+1   ;fr n„chst Berechnung neue Regeldifferenz gleich alte Regeldifferenz setzen
                MOV     A,#INIT_HEIZ_AUS+1 ;Alte Heizleistung holen
                CLR     C               ;Carry-Flag l”schen
                SUBB    A,R7            ;und ermittelten Regelparameter subtrahieren
                JC      LOAD_H0         ;wenn neuer Regelparameter gr”sser als alter dann Heizung auf 1Prozent
                MOV     8,A             ;Ergebnis fr Heizung AUS Zeit zwischenspeichern
                CLR     HPR0            ;Heizungsflag 1Prozent l”schen
;               JB      HZ,MESS4        ;Wenn Heizungspulsflag
                JB      HZ1,MESS4       ;oder Heizungsflag gesetzt warten bis letzter Regelvorgang beendet
                SETB    HZ1             ;sonst Heizungsflag setzen

MESS38:         CJNE    R7,#0,MESS39    ;wenn Regelparameter nicht null dann weiter
                INC     R7              ;sonst auf 1 setzen (kleinste Heizleistung)
MESS39:         MOV     HEIZ_EIN,R7     ;Heizungs EIN Zeit mit ermitteltem PID-Wert laden
                MOV     HEIZ_AUS,8      ;Heizungs AUS Zeit mit Komplement von EIN-Zeit laden
MESS4:          RET                     ;Ende Regelung

NOHEAT:         MOV     HEIZ_EIN,#INIT_HEIZ_EIN   ;Heizung EIN Zeit auf Null stellen
                MOV     HEIZ_AUS,#INIT_HEIZ_AUS+1 ;Heizung AUS Zeit auf Max  stellen 
                MOV     A,KDMEM+1       ;zur Zeit gltige Regeldifferenz laden
                CPL     A               ;Ergebnis
                XCH     A,KIMEM         ;aus Berechnung
                SUBB    A,KIMEM         ;subtrahieren
                XCH     A,KIMEM         ;und in LSB KIMEM abspeichern
                JNC     N1              ;wenn kein šberlauf aufgetreten weiter
                MOV     A,KIMEM+1       ;NSB KIMEM laden
                SUBB    A,#0            ;šbertrag subtrahieren
                XCH     A,KIMEM+1       ;und abspeichern
                JNC     N1              ;wenn kein weiterer šbertrag weiter
                MOV     A,KIMEM+2       ;sonst MSB KIMEM laden 
                SUBB    A,#0            ;šbertrag subtrahieren
                JC      N2              ;wenn šbertrag KIMEM komplett 0 setzen
                XCH     A,KIMEM+2       ;sonst MSB KIMEM abspeichern
N1:             CLR     P4.7            ;Steuerleitung Heizung abschalten
;               SETB    HPR0            ;Heizung 0Prozent Flag setzen
                RET                     ;Ende Regelung
LOAD_H0:        MOV     HEIZ_AUS,#1     ;Heizungleistung auf Max-1Prozent setzen
                CLR     HPR0            ;Heizung 0Prozent Flag l”schen
                RET                     ;Ende Regelung

;*************
;
;KIMEM l”schen
;
;*************

N2:		MOV	KIMEM,#0
		MOV	KIMEM+1,#0
		MOV	KIMEM+2,#0
		JMP	N1

;*********************************
;
;Messroutine AD-Wandlung 2.Messung
;
;*********************************


MESSAD:         MOV     R6,#40H         ;Messz„hler auf 64 Messungen einstellen
                MOV     A,#0            ;Register
                MOV     B,#0            ;l”schen
MESSAD1:        MOV     DAPR,R1         ;Messung mit Messbereichsvorgabe starten
                JB      ADCON.4,$       ;warten bis Messung beendet
                ADDC    A,ADDAT         ;Messwert aufaddieren
                JNC     MESSAD2         ;wenn kein šberaluf weiter
                INC     B               ;sonst HighByte Messwert +1
MESSAD2:        CLR     C               ;Carry Flag l”schen
                DJNZ    R6,MESSAD1      ;Messung wiederholen bis Messz„hler = 0

;***********************************************
;
;Mittelwertbildung der vorangegangenen Messungen
;die Routine erzeugt eine Division durch 64
;
;***********************************************

                MOV     R2,#0           ;Register fr 
                MOV     R6,#6           ;Mittelwertbildung des Messwertes initialisieren
                XCH     A,B             ;Inhalt A mit Inhalt B vertauschen
MESSAD3:        RRC     A               ;rechtes Bit von A ins Carry schieben 
                XCH     A,B             ;A+B vertauschen
                RRC     A               ;Carry in oberes Bit von A,rechtes Bit von A ins Carry 
                XCH     A,B             ;A+B vertauschen
                XCH     A,R2            ;A+R2 vertauschen
                RRC     A               ;Carry in oberes Bit von A,rechtes Bit von A ins Carry
                XCH     A,R2            ;A+R2 vertauschen
                CLR     C               ;Carry l”schen
                DJNZ    R6,MESSAD3      ;nach 6 mal schieben, Division durch 64 beendet
                MOV     A,#128          ;Highbit A setzen
                ADD     A,R2            ;R2 addieren
                JNC     MESSAD4         ;wenn kein Carry weiter
                INC     B               ;sonst B+1
MESSAD4:        XCH     A,B             ;A+B vertauschen
                RET                     ;Ende der 64 Messungen


;******************************************************************************
;
;Statusmeldungen an serielle Schnittstelle
;
;Format: X10000X20000MI00YE0000YA0000CK00KP0000KI0000KD0000W10000W20000MS00ST00
;
;        Die Nullen bedeuten hierbei einmal die Anzahl der bertragenen Ziffern 
;        und stellen ausserdem einen belibigen dezimalen Wert im Bereich der 
;        vorgegeben Ziffernanzahl dar. Dabei kann es sich um HEX-Ziffern oder 
;        auch Dezimalziffern handeln. Siehe Hinweise zu den einzelnen Werten
;
;******************************************************************************

;*****************************************************************
;
;Temperatur IST-Werte bertragen  
;
;Format: X10000X20000  die Nullen stehen fr einen beliebigen Wert
;
;*****************************************************************

OUT_STATUS:     MOV     A,#'X'          ;Lade Zeichen 'X'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'1'          ;Lade Zeichen '1'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#AD_ZW_HI    ;Lade AD-Wert 1.Messung
                CALL    OUTDATA         ;Wert senden
                MOV     A,#'X'          ;Lade Zeichen 'X'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'2'          ;Lade Zeichen '2'
                CALL    OUT_CHAR        ;Zeichen aussenden
                MOV     R0,#AD_ZW_LO    ;Lade AD-Wert 2.Messung
                CALL    OUTDATA         ;Wert senden

;*****************************************
;
;IST Messbereich ausgeben
;
;Format: MI00    wobei 00 eine Hexzahl ist
;
;*****************************************

                MOV     A,#'M'          ;Lade Zeichen 'M'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'I'          ;Lade Zeichen 'I'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,AD_BEREICH    ;Lade Wert AD_Bereich 
                CALL    OUT_BYTE_ASCII  ;Zeichen als Hex-Wert ausgeben

;*****************************************************************************
;
;IST Stellgr”sse ausgeben
;
;Format: YE0000YA0000    die Nullen stehen fr einen beliebigen dezimalen Wert
;
;*****************************************************************************
                MOV     A,#'Y'          ;Lade Zeichen 'Y'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'E'          ;Lade Zeichen 'E'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#HEIZ_EIN    ;Lade Wert Heizung EIN-Zeit aktuell
                CALL    OUTDATA         ;Wert senden
                MOV     A,#'Y'          ;Lade Zeichen 'Y'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'A'          ;Lade Zeichen 'A'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#HEIZ_AUS    ;Lade Wert Heizung AUS-Zeit aktuell
                CALL    OUTDATA         ;Wert senden

;*********************************************************************
;
;Sekunden ausgeben
;
;Format: CK00    die Nullen stehen fr einen beliebigen dezimalen Wert
;
;*********************************************************************

                MOV     A,#'C'          ;Lade Zeichen 'C'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'K'          ;Lade Zeichen 'K'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#TIME-1      ;Lade Sekunden
                CALL    OUTDATA         ;Wert senden

;*********************************************************************
;
;KP-Wert ausgeben
;
;Format: KP0000  die Nullen stehen fr einen beliebigen dezimalen Wert
;
;*********************************************************************

                MOV     A,#'K'          ;Lade Zeichen 'K'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'P'          ;Lade Zeichen 'P'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#KP          ;Lade KP-Wert aktuell
                CALL    OUTDATA         ;Wert senden

;*********************************************************************
;
;KI-Wert ausgeben
;
;Format: KI0000  die Nullen stehen fr einen beliebigen dezimalen Wert
;
;*********************************************************************

                MOV     A,#'K'          ;Lade Zeichen 'K'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'I'          ;Lade Zeichen 'I'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#KI          ;Lade KI-Wert aktuell
                CALL    OUTDATA         ;Wert senden

;*********************************************************************
;
;KD-Wert ausgeben
;
;Format: KD0000  die Nullen stehen fr einen beliebigen dezimalen Wert
;
;*********************************************************************

                MOV     A,#'K'          ;Lade Zeichen 'K'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'D'          ;Lade Zeichen 'D'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#KD          ;Lade KD-Wert aktuell
                CALL    OUTDATA         ;Wert senden

;****************************************************************************
;
;Temperatur SOLL-Werte bertragen
;
;Format: W10000W20000   die Nullen stehen fr einen beliebigen dezimalen Wert 
;
;****************************************************************************

                MOV     A,#'W'          ;Lade Zeichen 'W'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'1'          ;Lade Zeichen '1'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     R0,#SOLL1       ;Lade Sollwert 1.Messung
                CALL    OUTDATA         ;Wert senden
                MOV     A,#'W'          ;Lade Zeichen 'W'                      
                CALL    OUT_CHAR        ;Zeichen senden                          
                MOV     A,#'2'          ;Lade Zeichen '2'                         
                CALL    OUT_CHAR        ;Zeichen aussenden                             
                MOV     R0,#SOLL2       ;Lade Sollwert 2.Messung             
                CALL    OUTDATA         ;Wert senden

;*****************************************
;
;Sollmessbereich ausgeben
;
;Format: MS00    wobei 00 eine Hexzahl ist
;
;*****************************************

                MOV     A,#'M'          ;Lade Zeichen 'M'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'S'          ;Lade Zeichen 'S'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,SOLLMB        ;Lade Wert Sollmessbereich
                CALL    OUT_BYTE_ASCII  ;Zeichen als Hexwert ausgeben

;*****************************************
;
;Statusmeldung ausgeben
;
;Format: ST00    wobei 00 eine Hexzahl ist
;
;*****************************************

                MOV     A,#'S'          ;Lade Zeichen 'S'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,#'T'          ;Lade Zeichen 'T'
                CALL    OUT_CHAR        ;Zeichen senden
                MOV     A,20H           ;Lade Statusbyte
                CALL    OUT_BYTE_ASCII  ;Zeichen als Hexwert ausgeben
                CALL    ENT             ;ENTER ausgeben (CR+LF)                      
                RET                     ;Ende Statusmeldungen

;***************************************
;
;Ausgabe eines Hexwertes im ASCII Format
;
;***************************************

OUTDATA:        MOV     R1,#BCD_BUF     ;Zeiger auf Anfang BCD-Puffer stellen
                CALL    BINBCD          ;geladenen Wert in Dezimalzahl umrechnen
                MOV     A,@R1           ;Highbyte aus BCD-Puffer lesen
                INC     R1              ;Zeiger inkrementieren
                MOV     B,@R1           ;Lowbyte aus BCD-Puffer lesen                                        
                CALL    OUT_WORD_ASCII  ;und ausgeben
                RET                     ;Ende Ausgabe Dezimalzahl



;**********************************************
;
;Utilitys aus MC-Tool2 Seite 58-62
;
;**********************************************



;**********************************************
;
;Interruptserviceroutine serielle Schnittstelle
;
;**********************************************




OUT_CHAR:       JNB     TI,$            ;WARTE AUF SCHNITTSTELLEN-INTERRUPT
		CLR	TI		;SENDER-FLAG LOESCHEN
		MOV	SBUF,A		;ZEICHEN AUSSENDEN
		CLR	ZNEU		;ZNEU-FLAG LOESCHEN
                CLR     A               ;Accu fr Empfang eines neuen Zeichens l”schen
		RET



IN_CHAR:        PUSH    ACC             ;Accu auf Stack retten
		JB	RI,RECEIV	;SPRUNG, FALLS EMPFAENGER-INTERRUPT
		JNB	ZNEU,AUS	;SPRUNG, FALLS SENDER WIEDER BEREIT IST
RECEIV:		MOV	A,SBUF		;EMPFANGENES ZEICHEN IN AKKU LADEN
                MOV     SIGN,A          ;UND IM EMPFANGSPUFFER SPEICHERN
    		CLR	RI		;EMPFAENGER-FLAG LOESCHEN
		SETB	ZNEU		;ZNEU-FLAG SETZEN: NEUES ZEICHEN IST
					;EMPFANGEN WORDEN
AUS:            POP     ACC             ;Accuinhalt wiederherstellen
                RETI                    ;SCHNITTSTELLENINTERRUPT BEENDEN

;**************************
;
;Ausgabe einer Zeichenkette
;
;**************************

OUT_STRING:
OS1:            JB      F0,OS2          ;Wenn F0 gesetzt Ausgabe der Zeichenkette aus externem ROM oder RAM
                CLR     A               ;Accu fr Zeiger auf Anfang
                MOVC    A,@A+DPTR       ;Zeichen aus internem ROM laden
                SJMP    OS3             ;Weiter zur šberprfung 
OS2:            MOVX    A,@DPTR         ;Zeichen aus externem ROM oder RAM laden
OS3:            CJNE    A,#03H,OS4      ;Wenn Zeichen 3 dann String Ende
                RET                     ;Ausgabe beenden
OS4:            CALL    OUT_CHAR        ;Zeichen senden
                INC     DPTR            ;Zeiger auf n„chstes Zeichen im Speicher stellen
                SJMP    OS1             ;und n„chstes Zeichen holen

;**********************************
;
;Wandlung eines Bytes in ASCII-CODE
;
;**********************************

OUT_BYTE_ASCII: MOV     B,A             ;Wert nach B retten
                SWAP    A               ;Accu High- und Lownibble vertauschen
                ANL     A,#0FH          ;High-Nibble ausmaskieren
                CALL    HEX_ASCII       ;von Hex nach ASCII wandeln
                CALL    OUT_CHAR        ;und senden
                MOV     A,B             ;Accu auffrischen
                ANL     A,#0FH          ;Low-Nibble ausmaskieren
                CALL    HEX_ASCII       ;von Hex nach ASCII wandeln
                CALL    OUT_CHAR        ;und senden
                RET                     ;Wandlung beenden

;*******************************************
;
;Ausgabe eines Doppelbytes als ASCII-Zeichen
;
;*******************************************

OUT_WORD_ASCII: PUSH    B               ;B auf Stack retten
                CALL    OUT_BYTE_ASCII  ;als ASCII-Zeichen ausgeben
                POP     ACC             ;Accu auffrischen
                CALL    OUT_BYTE_ASCII  ;als ASCII-Zeichen ausgeben
                RET                     ;Ende Ausgabe

;*****************************************
;
;Ausgabe einer Mehrbyte Zahl im ASCII-Code
;
;*****************************************

OUT_MBYTE_ASCII:MOV     A,@R0           ;Byte holen
                CALL    OUT_BYTE_ASCII  ;als ASCII-Zeichen ausgeben
                DEC     R0              ;Zeiger auf n„chstes Byte
                DJNZ    R1,OUT_MBYTE_ASCII ;wiederholen bis R1=0
                RET                     ;Ende Ausgabe


;******************************************
;
;Prfen eines Bytes auf gltigen Hexbereich
;
;******************************************

VALID_HEX:      MOV     B,A             ;Accu retten
                CLR     C               ;Carry Flag l”schen
                SUBB    A,#30H          ;wenn Zahl gr”sser 30H   >=ASCII 0
                JC      VHCL            ;dann Carry Flag l”schen
                MOV     A,B             ;Accu auffrischen
                SUBB    A,#3AH          ;wenn Zahl gr”sser 3AH   >=ASCII 9
                JC      VHS             ;dann Carry Flag setzen
                MOV     A,B             ;Accu auffrischen
                SUBB    A,#41H          ;wenn Zahl gr”sser 41H   >=ASCII A
                JC      VHCL            ;dann Carry Flag l”schen
                MOV     A,B             ;Accu auffrischen
                SUBB    A,#47H          ;wenn Zahl gr”sser 47H   >=ASCII F
                JC      VHS             ;dann Carry Flag setzen
VHCL:           CLR     C               ;Carry Flag l”schen
                RET                     ;Ende Prfung
VHS:            SETB    C               ;Carry Flag setzen
                RET                     ;Ende Prfung

;****************************************
;
;Wandlung eines ASCII-Zeichens in Hexcode
;
;****************************************

ASCII_HEX:      MOV     B,A             ;Accu retten
                ANL     A,#0F0H         ;High-Nibble ausmaskieren
                CJNE    A,#40H,AH1      ;wenn ungleich 40H Sprung nach AH1  >=ASCII A-F
                MOV     A,B             ;Accu auffrischen
                CLR     C               ;Carry Flag l”schen
                SUBB    A,#37H          ;wenn gleich 40H, 37H subtrahieren >=HEX A-F
                RET                     ;Ende Wandlung

AH1:            MOV     A,B             ;Accu auffrischen
                ANL     A,#0FH          ;Low-Nibble ausmaskieren  >=HEX 0-9
                RET                     ;Ende Wandlung


;****************************************
;
;Wandlung einer HEX Zahl in ASCII Zeichen
;
;****************************************

HEX_ASCII:      CJNE    A,#0AH,HA0      ;ist der Inhalt von A >=0AH wird das Carry Flag gesetzt
HA0:            JNC     HA1             ;im Falle des gesetzten Carry Flags wird nach HA1 verzweigt
                ORL     A,#30H          ;ansonsten wird 30H einmaskiert >=ASCII 0-9
                RET                     ;Ende Wandlung
HA1:            ADD     A,#37H          ;wenn A >=0AH dann wird 37H addiert >=ASCII A-F
                RET                     ;Ende Wandlung


;***********************
;
;Wandlung Bin„r nach BCD
;
;***********************

BINBCD:         MOV     A,@R0           ;der Inhalt der durch R0 adressierten Speicherstelle wird in den Accu geladen
                MOV     B,#100          ;in B wird der Divisor fr die Hunderter geladen
                DIV     AB              ;das durch die Division im Accu entstandene Ergebnis
                MOV     @R1,A           ;wird unter der Adresse auf die R1 zeigt gespeichert
                MOV     A,#10           ;der Accu wird mit dem Divisor fr die Zehner geladen
                XCH     A,B             ;und mit dem Rest der vorherigen Division in B getauscht
                DIV     AB              ;Das durch diese Division im Accu entstandene Ergebnis
                SWAP    A               ;wird in das h”herwertige Nibble gebracht
                ADD     A,B             ;und der Rest aus B hinzuaddiert
                INC     R1              ;der Zeiger fr das Ergebnis wird inkrementiert
                MOV     @R1,A           ;und das Ergebnis aus dem Accu dorthin abgespeichert
                DEC     R1              ;der Ursprungszustand des Zeigers wird wieder hergestellt
                RET                     ;Ende der Wandlung

;***********************
;
;Wandlung BCD nach Bin„r
;
;***********************


BCDBIN:         MOV     A,@R0           ;Der Inhalt der durch R0 adressierten Speicherstelle wird in den Accu geladen
                MOV     B,#16           ;durch 16
                DIV     AB              ;dividiert
                MOV     @R1,B           ;und der Rest in der Speicherstelle auf die R1 zeigt abgelegt
                MOV     B,#10           ;durch Multiplikation des Ergebnisses
                MUL     AB              ;im Accu mit 10
                ADD     A,@R1           ;durch Addition des Inhaltes der Speicherstelle auf die R1 zeigt zum Accu
                MOV     @R1,A           ;und Speichern des Ergebnisses in der Speicherstelle auf die R1 zeigt
                RET                     ;wird die Wandlung beendet




;****************************************
;
;Ausgabe eines Leerschrittes von 4 Blanks
;
;****************************************

TAB4:           MOV     DPTR,#LEER4     ;Zeiger auf Anfang Zeichenkette
                CLR     F0              ;Ausgabe aus internem ROM
                CALL    OUT_STRING      ;Ausgabe der Zeichenkette
                RET                     ;Ende Ausgabe TAB4

;*****************
;
;Ausgabe von ENTER
;
;*****************

ENT:            MOV     DPTR,#ENTER     ;Zeiger auf Anfang Zeichenkette
                CLR     F0              ;Ausgabe aus internem ROM
                CALL    OUT_STRING      ;Ausgabe der Zeichenkette
                RET                     ;Ende Ausgabe ENTER


;*******************
;
;Messbereichstabelle
;
;*******************

ADTAB:		DB  61,77,92,108,123,138,154,169,185,200,215,231,248

;**************************
;
;Tabelle Zeichenfolge ENTER
;
;**************************

ENTER:		DB  13,10,3


;****************************
;
;Tabelle Zeichenfolge fr TAB
;
;****************************

LEER4:		DB  '    ',3

END


