;  ------ uP POWER CONTROLLER WITH AUTO MODE ------
;  ------    Processor AT89C2051 24PC        ------

;#DEFINE DEBUG		;Debugger on/off
;#DEFINE ASYNCEN		;enables out pin in asynchronous mode
;#DEFINE UNLIMOUT		;unlimited out pulse length (else 4 MS)

#INCLUDE "LIBREG.ASM"	;8051 SFR set
#INCLUDE "LIBMAC.ASM"	;macros set

CLK_KHZ	= 12000		;OSC frequency, KHZ
RTC_MS	= 22		;initial mains pulse wait time, MS
#INCLUDE "LIBRTC.ASM"	;calculate RTCV
INTV	.EQU RTCV		;INTV <- RTCV
RTC_MS	= 4		;indication scan rate, MS
#INCLUDE "LIBRTC.ASM"	;calculate RTCV

#IFDEF DEBUG
#INCLUDE "LIBDEF.ASM"
#ENDIF

;  ------ Constantes ------

STACK	.EQU 056H		;stack location
MAXPOW	.EQU 100		;max. power level
K	.EQU 3		;auto regulation loop gain
KEYDB	.EQU 5		;key debounce delay    (x10mS)
KEYDA	.EQU 70		;key autorepeat delay  (x10mS)
KEYARR	.EQU 10		;autorepeat rate       (x10mS)
KEYFARR	.EQU 10		;fast autorepeat rate  (x10mS)
AUDEL	.EQU 150		;auto off delay        (x10mS)
ARCOUNT	.EQU 10		;autorepeat count to change rate

;  ------ Debugger variables ------

#IFDEF DEBUG
DBGVA	.EQU STACK-1	;debugger variable address in internal memory
DBGVV	.EQU STACK	;debugger variable value   in internal memory
DBGA	.EQU 0FFH		;debugger address          in external memory
#ENDIF

;  ------ Ports ------

SEG_A	.EQU P1.0		;indicators segment A
SEG_B	.EQU P1.1		;indicators segment B
SEG_C	.EQU P1.2		;indicators segment C
SEG_D	.EQU P1.3		;indicators segment D
SEG_E	.EQU P1.4		;indicators segment E
SEG_F	.EQU P1.5		;indicators segment F
SEG_G	.EQU P1.6		;indicators segment G
SEG_H	.EQU P1.7		;indicators segment H (1XX)

SCAN0	.EQU TXD		;display scan line 1
SCAN1	.EQU RXD		;display scan line 2

RETL	.EQU P3.7		;keyboard return line

SYNC	.EQU INT0		;mains pulses
PWM	.EQU INT1		;PWM out
REF	.EQU T0		;ref. PWM out
COMP	.EQU T1		;comparator out

;  ------ Variables ------

;  Bit addressing memory

PRESS	.EQU M_20H.0	;keyboard press bit
AUTO	.EQU M_20H.1	;auto mode
REG_F	.EQU M_20H.2	;regulation fail

SIGN	.EQU 021H		;mains sign
PHASE	.EQU 022H		;PWM phase
POWER	.EQU 023H		;power level
PWOUT	.EQU 024H		;output power level
PWLOW	.EQU 025H		;output power level low byte
DELTA	.EQU 026H		;PWM routine internal variable
DEOUT	.EQU 027H		;PWM routine internal variable
KEYDBT	.EQU 028H		;key debounce timer
KEYTM	.EQU 029H		;key timer
COD	.EQU 02AH		;key code
PREV	.EQU 02BH		;previous key code
REPCNT	.EQU 02CH		;repeat counter
FLT	.EQU 02DH		;digital filter data
CNTR	.EQU 02EH		;program counter
CNTR.0	.EQU M_2EH.0	;20mS
CNTR.1	.EQU M_2EH.1	;40mS
CNTR.2	.EQU M_2EH.2	;80mS
CNTR.3	.EQU M_2EH.3	;160mS
CNTR.4	.EQU M_2EH.4	;360mS
CNTR.5	.EQU M_2EH.5	;640mS
CNTR.6	.EQU M_2EH.6	;1.28S
CNTR.7	.EQU M_2EH.7	;2.56S

;  ------ Vectors Area ------

	.ORG 0000H	;reset vector

;  ------ Main Program ------

	MOV SP,#STACK	;stack init
#IFDEF DEBUG
	DEBUGINIT		;debug init
#ENDIF
	CLR PRESS		;clear keyboard press bit
	CLR AUTO
	CLR REG_F
	MOV DPTR,#FONT
	CLR A
	MOV COD,A
	MOV PREV,A
	MOV SIGN,A
	MOV FLT,A
	MOV CNTR,A
	MOV POWER,A
	MOV PWOUT,A
	MOV PWLOW,A

	MOV PHASE,#MAXPOW
	MOV DELTA,#-(MAXPOW/2)
	MOV DEOUT,#-(MAXPOW/2)

	CLR TR0
	CLR TR1
	CLR TF0
	MOV TMOD,#11H    	;timer 0 and timer 1 init
	MOV TH0,#HI(INTV)	;indication timer load
	MOV TL0,#LO(INTV)
	SETB TR0		;timer start

;======== Comparator out check ========

MAIN:
#IFDEF ASYNCEN
	JB TF0,PWM0	;jump if no mains pulses
#ELSE
	JB TF0,PWM3	;jump if no mains pulses
#ENDIF
	MOV C,SYNC	;SYNC pulse check
	RLC A
	XRL A,SIGN
	JNB ACC.0,MAIN	;loop if same mains sign

	XRL SIGN,#0FFH	;change sign

;======== PWM out control ========

PWM0:	MOV A,PWOUT
	ADD A,DEOUT
	MOV DEOUT,A
	MOV C,ACC.7
	MOV PWM,C
	JC PWM1
	SUBB A,#MAXPOW
	MOV DEOUT,A
PWM1:     MOV A,POWER
	ADD A,DELTA
	MOV DELTA,A
	MOV C,ACC.7
	CPL C
	MOV REF,C
	JNC PWM2
	SUBB A,#MAXPOW
	MOV DELTA,A

PWM2:     CLR TR0
	MOV TH0,#HI(RTCV)	;indication timer load
	MOV TL0,#LO(RTCV)
	CLR  TF0
	SETB TR0		;timer start

	DJNZ PHASE,PWM3
	MOV PHASE,#MAXPOW
	MOV DELTA,#-(MAXPOW/2)
	MOV DEOUT,#-(MAXPOW/2)
	LCALL UPDATE	;update PWOUT

;======== Indicate 1s ========

PWM3:     MOV R0,COD
	MOV A,POWER	;indicate POWER...
	CJNE R0,#4,PM3
	MOV A,PWOUT	;or PWOUT
PM3:	MOV B,#10
	DIV AB		;A=10s, B=1s
	MOV A,B
	MOVC A,@A+DPTR
	MOV C,CNTR.5	;load blink bit
	ORL C,/REG_F	;check for regulation fail
	ANL C,AUTO
	CPL C
	MOV ACC.7,C	;led AUTO on/off
	ACALL IND
	SETB SCAN1
	CLR  SCAN0	;indicate 1s

;======== Control functions processing ========

	MOV R0,COD
	MOV A,R0
	JZ W1
	CJNE R0,#2,NDN
	ACALL DO_DN	;DOWN pressed
NDN:	CJNE R0,#1,NUP
	ACALL DO_UP	;UP pressed
NUP:	CJNE R0,#3,NUD
	ACALL DO_UD	;UP+DOWN pressed
NUD:	CJNE R0,#5,NAP
	ACALL DO_AP	;AUTO+UP pressed
NAP:	CJNE R0,#6,NAN
	ACALL DO_AN	;AUTO+DOWN pressed
NAN:	SETB PRESS

W1:	JNB  TF0,$

;======== PWM out off ========

	CLR TR0
	MOV TH0,#HI(RTCV)	;indication timer load
	MOV TL0,#LO(RTCV)
	CLR  TF0
	SETB TR0		;timer start
#IFNDEF UNLIMOUT
	SETB PWM		;PWM out off
#ENDIF

;======== Comparator out digital filtering ========

	MOV C,COMP	;C <- COMP (input 1-bit data)
	CLR A		;convert 1-bit to 8-bit signed
	RLC A
	RL A
	DEC A
	ADD A,FLT		;FLT = FLT + A
	MOV FLT,A		;FLT - output of filter tap

;======== Indicate 10s ========

IND10:    MOV R0,COD
	MOV A,POWER	;indicate POWER...
	CJNE R0,#4,PM4
	MOV A,PWOUT	;or PWOUT
PM4:	MOV B,#10
	DIV AB		;A=10s, B=1s
	JNZ NZ
	MOV A,#0DH	;blank code
NZ:	MOVC A,@A+DPTR
	ACALL IND
	SETB SCAN0
	CLR  SCAN1	;indicate 10s

;======== Program timer check ========

	INC CNTR
	MOV A,KEYTM
	JZ W2
	DEC KEYTM		;advance key timer
W2:	JNB  TF0,$

;======== Indication off ========

	CLR TR0
	MOV TH0,#HI(RTCV)	;indication timer load
	MOV TL0,#LO(RTCV)
	CLR  TF0
	SETB TR0		;timer start
	SETB SCAN0
	SETB SCAN1	;indication off

;======== Keyboard check ========

	MOV P1,#11111110B	;P1.0 = 0
	MOV C,RETL	;return line check
	RLC A		;A << P1.0
	MOV P1,#11111101B	;P1.1 = 0
	MOV C,RETL	;return line check
	RLC A		;A << P1.1
	MOV P1,#11111011B	;P1.2 = 0
	MOV C,RETL	;return line check
	RLC A		;A << P1.2
	CPL A
	ANL A,#7		;ACC.2 - UP, ACC.1 - DN, ACC.0 - AUTO
	JZ  NP		;jump if no press

	MOV R0,A
	XRL A,PREV
	JNZ ND		;jump if codes are differ
	MOV A,KEYDBT	;the same code, debounce timer check
	JZ DOV
	DEC KEYDBT
	SJMP NR		;jump if debounce delay is not over

DOV:      MOV COD,R0	;COD load
	SJMP NS

ND:	MOV PREV,R0
NP:	MOV KEYDBT,#KEYDB	;debounce timer load
NR:	CLR PRESS
	MOV COD,#0	;load unused code
NS:	AJMP MAIN		;main loop

;  ------ Subroutines Area ------

;AUTO+UP key processing:

DO_AP:    JB PRESS,AP_RET	;jump if key hold
	SETB AUTO		;auto on
AP_RET:	RET

;AUTO+DOWN key processing:

DO_AN:    JB PRESS,AN_RET	;jump if key hold
	MOV PWOUT,POWER	;auto off, copy PWOUT <- POWER
	CLR AUTO
AN_RET:	RET

;UP key processing:

DO_UP:	JB PRESS,UPH	;jump if key hold
	MOV KEYTM,#KEYDA	;first press, autorepeat delay load
	MOV REPCNT,#0	;autorepeat counter clear
	SJMP UP_DO
UPH:	MOV A,KEYTM	;KEYTM check
	JNZ UP_RET
	CLR C
	MOV KEYTM,#KEYARR	;load normal repeat rate
	MOV A,REPCNT
	SUBB A,#ARCOUNT	;REPCNT check
	JC UP_DO		;jump if REPCNT < ARCOUNT
	MOV KEYTM,#KEYFARR	;load fast repeat rate
UP_DO:	MOV A,POWER
	XRL A,#MAXPOW
	JZ UP_RET		;jump if limit
	INC POWER		;power inc.
	MOV PWOUT,POWER	;initial out power: copy PWOUT <- POWER
	INC REPCNT	;autorepeat counter inc.
UP_RET:   RET

;DOWN key processing:

DO_DN:    JB PRESS,DNH	;jump if key hold
	MOV KEYTM,#KEYDA	;first press, autorepeat delay load
	MOV REPCNT,#0	;autorepeat counter clear
	SJMP DN_DO
DNH:	MOV A,KEYTM	;KEYTM check
	JNZ DN_RET
	CLR C
	MOV KEYTM,#KEYARR	;load normal repeat rate
	MOV A,REPCNT
	SUBB A,#ARCOUNT	;REPCNT check
	JC DN_DO		;jump if REPCNT < ARCOUNT
	MOV KEYTM,#KEYFARR	;load fast repeat rate
DN_DO:    MOV A,POWER
	JZ DN_RET		;jump if limit
	DEC POWER		;power dec.
	MOV PWOUT,POWER	;initial out power: copy PWOUT <- POWER
	INC REPCNT	;autorepeat counter inc.
DN_RET:   RET

;UP+DOWN key processing:

DO_UD:	JB PRESS,UD_RET	;no hold mode
	MOV A,POWER
	JZ DO_MAX
	MOV POWER,#0	;load MIN power
	MOV PWOUT,#0
	SJMP UD_RET
DO_MAX:	MOV POWER,#MAXPOW	;load MAX power
	MOV PWOUT,#MAXPOW
UD_RET:   RET

;Update power
;Input: DELH,DELL, bit AUTO
;Out:   DELH,DELL=0, PWOUT,PWLOW

UPDATE:   CLR REG_F		;regulation fail clear
	MOV A,POWER
	JZ DON
	JNB AUTO,DON	;manual mode, PWOUT=POWER

	MOV A,FLT
	RLC A
	MOV F0,C		;save signum
	JNC POS		;jump if > 0
	MOV A,FLT
	CPL A
	INC A
POS:	MOV B,#K		;A = ABS(FLT), B = K
	MUL AB
	JB F0,MIN

	ADD A,PWLOW	;PWOUT,PWLOW + A x B
	MOV PWLOW,A
	MOV A,PWOUT
	ADDC A,B
	JC PL1
	CJNE A,#MAXPOW,PL2
PL2:	JC DON
PL1:	MOV A,#MAXPOW
	MOV PWLOW,#0
	SETB REG_F	;regulation fail (limit)
	SJMP DON

MIN:	CLR C		;PWOUT,PWLOW - A x B
	XCH A,PWLOW
	SUBB A,PWLOW
	MOV PWLOW,A
	MOV A,PWOUT
	SUBB A,B
	JNC DON
	CLR A
	MOV PWLOW,#0

DON:	MOV PWOUT,A	;update PWOUT
	MOV FLT,#0	;new filter cycle
	RET

;Segments on/off
;Input: A - segments copy, ACC.0 - A, ACC.1 - B, ...

IND:	RRC A
	MOV SEG_A,C
	RRC A
	MOV SEG_B,C
	RRC A
	MOV SEG_C,C
	RRC A
	MOV SEG_D,C
	RRC A
	MOV SEG_E,C
	RRC A
	MOV SEG_F,C
	RRC A
	MOV SEG_G,C
	RRC A
	MOV SEG_H,C
	RET

;Font table
;	    SGFEDCBA
FONT	.DB 11000000B	;code 00H, character 0
	.DB 11111001B	;code 01H, character 1
	.DB 10100100B	;code 02H, character 2
	.DB 10110000B	;code 03H, character 3
	.DB 10011001B	;code 04H, character 4
	.DB 10010010B	;code 05H, character 5
	.DB 10000010B	;code 06H, character 6
	.DB 11111000B	;code 07H, character 7
	.DB 10000000B	;code 08H, character 8
	.DB 10010000B	;code 09H, character 9
	.DB 01000000B	;code 0AH, character 10
	.DB 01111001B	;code 0BH, character 11
	.DB 00100100B	;code 0CH, character 12
	.DB 11111111B	;code 0DH, character blank

#IFDEF DEBUG
#INCLUDE "LIBDBG.ASM"
#ENDIF
	.END
