        LIST P=12F675,R=HEX
;
;       Monitor for Li-Ion / Li-Poly Battery Packs with cell balance connector
;       SUN, PU-NENG
;       
;
;	VERSION 3.0  (DEC/12/2005)
;	BM4 
;
;	VERSION 2.0  (DEC/12/2005)
;	Add unbalance detector nominal 0.2V, actually 0.1~0.15V 
;
;	VERSION 1.0  (DEC/08/2005)
;	3cell, Cut off at 3.0 V, 
;=============================================	
;  Voltage AN0 = cell 0
;  Voltage AN1 = cell (0+1)/2
;  Voltage AN2 = cell (0+1+2)/3
;  Voltage AN3 = cell (0+1+2+3)/4
;  
;  Voltage cell 0 = AN0
;  Voltage cell 1 = (AN1*2)-AN0
;  Voltage cell 2 = (AN2*3)-(AN1*2)
;  Voltage cell 3 = (AN3*4)-(AN2*3)
;==============================================
;       
;----  SYSTEM CONFIG  ----
	__CONFIG	H'3FA4' ;_INTRC_OSC_NOCLKOUT,_MCLRE_OFF,WDT OFF,PWRTE ON
;
;-----  SYSTEM  ---------
W       EQU 0           ;d VALUE FOR INCF......
F       EQU 1           ;d VALUE
TMR0	EQU 1		;TIMER 0
PCL     EQU 2           ;PROGRAM COUNTER
STATUS  EQU 3
FSR     EQU 4           ;BUFFER OF INDIRECT ADDR
GPIO	EQU 5
OPTION_reg	EQU 0x81
TRISIO	EQU 0x85
OSCCAL  EQU 0x90	
RP0	EQU 5
CMCON	EQU 0x19	;CMCON REGISTER
ANSEL   EQU 0x9F	;ANALOG SEL
ADCON0	EQU H'1F'
ADRESH	EQU H'1E'
GO	EQU 1
C	EQU 0		;status
Z	EQU 2		;status
;=====================================
;
;----  USER registers DEFINE  ------
;
TEMP0    EQU H'20'   
TEMP1    EQU H'21'       ;
TEMP2    EQU H'22'       ;
TEMP3    EQU H'23'       ; 
SOUND_TEMP1    EQU H'24'       ;
SOUND_TEMP2    EQU H'25'       ;
SOUND_TEMP3    EQU H'26'       ;
FLAG_REG  EQU H'27'		;DEFAULT = CLEAR
; FLAG 0~3 = CELL 0~3 LOW, 5= 3 CELLS PACK, 6= UNBAL, 7= CELL LOW
;   
ACCaLO  equ     2A
ACCaHI  equ     2B
ACCbLO  equ     2C
ACCbHI  equ     2D
ACCcLO  equ     2E
ACCcHI  equ     2F
;
CELL0_V equ    	31
CELL1_V equ     32
CELL2_V equ     33
CELL3_V equ     34
;
;============================================
;----  VARIABLES DEFINE ----
;
;LOW_VOLTAGE   	EQU D'153'       ; (3.0V/5.0V)*255= 153
LOW_VOLTAGE   	EQU D'163'       ; (3.2V/5.0V)*255= 163
;LOW_VOLTAGE   	EQU D'173'       ; (3.4V/5.0V)*255= 173


;UNBAL_VOLTAGE   EQU D'10'       ; (0.2V/5.0V)*255= 10
;				;nominal 0.2V, actually 0.1~0.15V
UNBAL_VOLTAGE   EQU D'15'       ; (0.3V/5.0V)*255= 15
;UNBAL_VOLTAGE   EQU D'25'       ; (0.5V/5.0V)*255= 25

;----  HARDWARE DEFINE ----
;
AN0		EQU 0
AN1		EQU 1
AN2		EQU 2
AN3		EQU 4	;AN3 = GPIO 4
SOUND_PIN	EQU 5	;INVERSE
;
;============================================
;==============   MAIN PROGRAM   ============
;
;--- RESTORE OSC CALIBRATION ---------

	ORG     0
	BSF STATUS,RP0	;;BANK1
	CALL 3FFh
	MOVWF OSCCAL	;RESTORE OSC CALI
;----------------------------------

START	
	CALL 	INIT_SYS
	call	self_test
MAIN	
	call 	output_audio	;CALL 	GET_Votlage	 
	GOTO 	MAIN

;=====================================

INIT_SYS
	bsf 	status,RP0	;bank1
	MOVLW 	H'C0'		;T0CS=0, TO ENABLE GP2
	MOVWF 	OPTION_reg 	

	MOVLW 	B'00011111'     ;GPIO 0~4 =input		
	MOVWF 	TRISIO 
	MOVLW 	B'00101111'
	MOVWF	ANSEL		;T32, AN 0~3 ARE ANALOG

	BCF     STATUS,RP0      ; BANK0
	MOVLW 	B'00000001'
	MOVWF	ADCON0		; LEFT JUSTIFIED,VDD,A/D ON
	
	BSF	GPIO,SOUND_PIN	;INVERSE
	
	CLRF	FLAG_REG	
	return
;---------------------------------------------
SELF_TEST	
	
	MOVLW 	D'20'		;20*100uS, 500Hz
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'100'		;100 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'16'		; 625Hz
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'126'		;126 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'12'		;833z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'166'		;166 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'10'		;1000z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'200'		;200 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	
	return
;---------------------------------------------
;-------- (SOUND_TEMP1*100uS)= Period, 
;-------- (PERIOD*SOUND_TEMP2)= Duration
sound	
	BCF	STATUS,C
	RRF	SOUND_TEMP1,F	;DURATION/2, 50% DUTY RATE 
SOUND_1
	BCF	GPIO,SOUND_PIN
	MOVF    SOUND_TEMP1,W 
	CALL	LOOP_S		;100uS LOOP
	BSF	GPIO,SOUND_PIN
	MOVF    SOUND_TEMP1,W 
	CALL	LOOP_S		;100uS LOOP
	DECFSZ  SOUND_TEMP2,F
	GOTO 	SOUND_1
	return

;---------------------------------------------

GET_Votlage

B0	BCF     STATUS,RP0      ; BANK0
	MOVLW 	B'00000001'	; AN0
	MOVWF	ADCON0		; LEFT JUSTIFIED,VDD,A/D ON
	CALL 	START_ADC

	CALL 	START_ADC	; BUG BUG, IF CELL3=0
COMPARE0
	MOVF	ADRESH,W
	MOVWF	CELL0_V		;SAVE CELL 0 VOLTAGE	
	MOVWF	ACCcLO		;SAVE (AN0) TO ACCc
	MOVWF	ACCbLO
	CLRF	ACCcHI
	CLRF	ACCbHI
	MOVLW 	LOW_VOLTAGE
	MOVWF	ACCaLO
	CLRF	ACCaHI
	CALL	D_SUB		; B - A
        SKPNC            	;IF f < w, C= 0,SKIP ;SKIP IF LOW VOLTAGE
	GOTO 	B1
B1_1	
	BSF	FLAG_REG,0	;Cell 0 < LOW_voltage
	BSF	FLAG_REG,7	; LOW CELL FLAG

;--------------------------------------
B1	BCF     STATUS,RP0      ; BANK0
	MOVLW 	B'00000101'	; AN1
	MOVWF	ADCON0		; LEFT JUSTIFIED,VDD,A/D ON
	CALL 	START_ADC
COMPARE1
	MOVF	ADRESH,W
		
	MOVWF	ACCbLO
	CLRF	ACCbHI
	CALL	D_DOUBLE	;AN1*2
	MOVF	ACCcLO,W
	MOVWF	ACCaLO
	CLRF	ACCaHI		; RESTORE AN0 FROM ACCc
	MOVF	ACCbLO,W
	MOVWF	ACCcLO
	MOVF	ACCbHI,W
	MOVWF	ACCcHI		;SAVE (AN1*2) TO ACCc
	CALL	D_sub		;(AN1*2)-AN0  B-A
	MOVF	ACCbLO,W
	MOVWF	CELL1_V		;SAVE CELL 1 VOLTAGE	
	SKPC    
	GOTO	B2_1		;;((AN1*2)-AN0)<0
	MOVLW 	LOW_VOLTAGE
	MOVwF	ACCaLO
	CLRF	ACCaHI
	CALL	D_SUB		; B - A
	SKPNC            	;IF f < w, C= 0,SKIP ;SKIP IF LOW VOLTAGE
	GOTO 	B2
B2_1	
	BSF	FLAG_REG,1
	BSF	FLAG_REG,7	; LOW CELL FLAG

;-----------------------------------------
B2	
	BCF     STATUS,RP0      ; BANK0
	MOVLW 	B'00001001'	; AN2
	MOVWF	ADCON0		; LEFT JUSTIFIED,VDD,A/D ON
	CALL 	START_ADC
COMPARE2	
	MOVF	ADRESH,W
		
	MOVWF	ACCbLO
	CLRF	ACCbHI
	CALL	D_DOUBLE
	CALL	D_DOUBLE	;AN2*4
	MOVF	ADRESH,W
	MOVWF	ACCaLO
	CLRF	ACCaHI
	CALL	D_sub		;(AN2*4)-AN2=AN2*3
	MOVF	ACCcLO,W
	MOVWF	ACCaLO
	MOVF	ACCcHI,W
	MOVWF	ACCaHI		;move (AN1*2) from ACCc TO ACCa
	MOVF	ACCbLO,W
	MOVWF	ACCcLO
	MOVF	ACCbHI,W
	MOVWF	ACCcHI		;SAVE (AN2*3) TO ACCc
	CALL	D_sub		;((AN2*3)-(AN1*2)) 
	MOVF	ACCbLO,W
	MOVWF	CELL2_V		;SAVE CELL 2 VOLTAGE	
	SKPC            	;
	GOTO	B3_1		;((AN2*3)-(AN1*2)) <0
	MOVLW 	LOW_VOLTAGE
	MOVwF	ACCaLO
	CLRF	ACCaHI
	CALL	D_SUB		; B - A
	SKPNC            	;IF f <= w, C= 0,SKIP ;SKIP IF LOW VOLTAGE
	GOTO 	B4
B3_1	
	BSF	FLAG_REG,2
	BSF	FLAG_REG,7	; LOW CELL FLAG


;----------------------------------------

B4
	BTFSC	FLAG_REG,5	; ONLY 3 CELLS 	
	GOTO	B5_1
	BCF     STATUS,RP0      ; BANK0
	MOVLW 	B'00001101'	; AN3
	MOVWF	ADCON0		; LEFT JUSTIFIED,VDD,A/D ON
	CALL 	START_ADC
COMPARE3	
	MOVF	ADRESH,W
	SKPNZ
	GOTO	B5		;NO VOLTAGE , ONLY 3 CELL
	MOVWF	ACCbLO
	CLRF	ACCbHI
	CALL	D_DOUBLE
	CALL	D_DOUBLE	;AN3*4
	
	MOVF	ACCcLO,W
	MOVWF	ACCaLO
	MOVF	ACCcHI,W
	MOVWF	ACCaHI		;move (AN2*3) from ACCc TO ACCa
	
	CALL	D_sub		;((AN3*4)-(AN2*3)) 
	MOVF	ACCbLO,W
	MOVWF	CELL3_V		;SAVE CELL 2 VOLTAGE	
	SKPC            	;
	GOTO	B4_1		;((AN2*3)-(AN1*2)) <0
	MOVLW 	LOW_VOLTAGE
	MOVwF	ACCaLO
	CLRF	ACCaHI
	CALL	D_SUB		; B - A
	SKPNC            	;IF f <= w, C= 0,SKIP ;SKIP IF LOW VOLTAGE
	GOTO 	B8
B4_1	
	BSF	FLAG_REG,3
	BSF	FLAG_REG,7	; LOW CELL FLAG
	GOTO	B8		;4 Cells CHECK UBL
;-----------------------------
;--- Only 3 CellS ------------- 
B5	
	call	self_test
	call	self_test
	BSF	FLAG_REG,5	; ONLY 3 CELLS 	
B5_1	
	BTFSC	FLAG_REG,7	; IF CELL LOW 7=1, DON'T DETECT UNBALANCE
	GOTO	B9
UNBAL3
	MOVF	CELL0_V,W
	SUBWF	CELL1_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL1_V,W
	SUBWF	CELL2_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL2_V,W
	SUBWF	CELL0_V,W
	CALL	CHECK_UNBAL
	GOTO	B9



;---------------------------------
;--- 4 CellS  ----------
B8
	BTFSC	FLAG_REG,7	; IF CELL LOW 7=1, DON'T DETECT UNBALANCE
	GOTO	B9

UNBAL4
	MOVF	CELL0_V,W
	SUBWF	CELL1_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL1_V,W
	SUBWF	CELL2_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL2_V,W
	SUBWF	CELL3_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL3_V,W
	SUBWF	CELL0_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL0_V,W
	SUBWF	CELL2_V,W
	CALL	CHECK_UNBAL
	MOVF	CELL1_V,W
	SUBWF	CELL3_V,W
	CALL	CHECK_UNBAL
B9
	return		

;-----------------------------------------
;
CHECK_UNBAL			;IF UNBAL, BSF FLAG_REG,6
	MOVWF	TEMP0	
	BTFSS	STATUS,C
	COMF	TEMP0,F		; NEGATIVE
	MOVLW	UNBAL_VOLTAGE
	SUBWF	TEMP0,W		;F<W, C=0 
	SKPNC
	BSF FLAG_REG,6		;(Vx-Vx) > UNBAL_VOLTAGE
	return
;------------------------------------------
;
START_ADC
	BCF     STATUS,RP0      ; BANK0
	BSF	ADCON0,GO	;START ADC
START_ADC1
	BTFSC	ADCON0,GO		
	GOTO 	START_ADC1
	return
;------------------------------------------
;
output_audio
	MOVLW 	D'40'		;cell 0
	CALL	LOOP_L		;	
	CALL 	GET_Votlage	 
	BTFSS	FLAG_REG,0
	GOTO	OA_1
	MOVLW 	D'10'		;1000z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'200'		;200 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;	
;-----------------------------------------------	
OA_1	
	MOVLW 	D'40'		;cell 1
	CALL	LOOP_L		;	
	CALL 	GET_Votlage	
	BTFSS	FLAG_REG,1
	GOTO	OA_3 
	MOVLW	2
	MOVWF   TEMP0
OA_2		
	MOVLW 	D'10'		;1000z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'200'		;200 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'10'		;100 mS
	CALL	LOOP_L		;		
	DECFSZ  TEMP0,F
        GOTO    OA_2
;----------------------------------------     
OA_3	
	MOVLW 	D'40'		;cell 2
	CALL	LOOP_L		;	
	CALL 	GET_Votlage	
	BTFSS	FLAG_REG,2
	GOTO	OA_5 
	MOVLW	3
	MOVWF   TEMP0
OA_4	
	MOVLW 	D'10'		;1000z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'200'		;200 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'10'		;100 mS
	CALL	LOOP_L		;		
	DECFSZ  TEMP0,F
        GOTO    OA_4
;----------------------------------------        
OA_5	
	BTFSC	FLAG_REG,5	; ONLY 3 CELLS, JUMP 
	GOTO	OA_7
	MOVLW 	D'40'		;cell 3
	CALL	LOOP_L		;	
	CALL 	GET_Votlage	
	BTFSS	FLAG_REG,3
	GOTO	OA_7 
	MOVLW	4
	MOVWF   TEMP0
OA_6	
	MOVLW 	D'10'		;1000z
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'200'		;200 CYCLO
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	MOVLW 	D'10'		;100 mS
	CALL	LOOP_L		;		
	DECFSZ  TEMP0,F
        GOTO    OA_6        
;---------------------------------------
OA_7
	MOVLW 	D'40'		;UNBAL, SOUND 625Hz FOR 1 SECOND
	CALL	LOOP_L		;	
	CALL 	GET_Votlage	 
	BTFSS	FLAG_REG,6	; IF UNBAL 6=1, BEEP 
	GOTO	OA_9		; RETURN
	
	MOVLW	5		;5 
	MOVWF   TEMP0
OA_8
	MOVLW 	D'16'		;625Hz
	MOVWF   SOUND_TEMP1 
	MOVLW 	D'125'		;16mS*125*5=1 Second
	MOVWF   SOUND_TEMP2 
	CALL	sound		;
	DECFSZ  TEMP0,F
        GOTO    OA_8
	
	
OA_9
	return

;=========================================


LOOP_S				;100 uS LOOP *W
	MOVWF   TEMP1	
LOOP_S1	
	MOVLW	D'24'		;(24*4)=96 
	MOVWF   TEMP2
LOOP_S2
        NOP
	DECFSZ  TEMP2,1
        GOTO    LOOP_S2
	DECFSZ  TEMP1,1
        GOTO    LOOP_S1
	return

;=========================================


LOOP_L				;10 mS LOOP *W
	MOVWF   TEMP1	
LOOP_L1 
	MOVLW	D'10'		;10 mS
	MOVWF   TEMP2
LOOP_L2	
	MOVLW	D'249'		;(249*4)+5=1001 
	MOVWF   TEMP3
LOOP_L3
        NOP
	DECFSZ  TEMP3,1
        GOTO    LOOP_L3
	DECFSZ  TEMP2,1
        GOTO    LOOP_L2
	DECFSZ  TEMP1,1
        GOTO	LOOP_L1
	RETURN
;==============================================
;
;*******************************************************************
;                 Double Precision Double
;
;   Double : ACCb(16 bits)*2 -> ACCb(16 bits)
;      (a) Load the 1st operand in location ACCb( 16 bits )
;      (b) CALL D_Double
;      (c) The result is in location ACCbLO & ACCbHI ( 16 bits )
;     
;     
;       Revision Date:    2005-12-09     
;       By:               Philip Sun (aka: iLuFa)  
;*******************************************************************;
D_Double
	bcf	status,c	;3,0
	rlf     ACCbLO,F       
	rlf     ACCbHI,F  
	retlw   0

;****************************************************************



;*******************************************************************
;                 Double Precision Subtraction
;
;   Subtraction : ACCb(16 bits) - ACCa(16 bits) -> ACCb(16 bits)
;      (a) Load the 1st operand in location ACCaLO & ACCaHI ( 16 bits )
;      (b) Load the 2nd operand in location ACCbLO & ACCbHI ( 16 bits )
;      (c) CALL D_sub
;      (d) The result is in location ACCbLO & ACCbHI ( 16 bits )
;      (e) If ACCa > ACCb, Carry =0 (Status,C)  
;   
;       Revision Date:    2005-12-12     
;       By:               Philip Sun (aka: iLuFa)  
;*******************************************************************;

D_sub   movf    ACCaLO,W
	subwf   ACCbLO,F	;lsd
	SKPC			;f<w, c=0
	GOTO	D_SUB_1
	movf    ACCaHI,W
	subwf   ACCbHI,F       	;msb
	return
D_SUB_1
	MOVF	ACCbHI,F
	SKPNZ			;IF ACCbHI=0, ACCa > ACCb 
	GOTO	D_SUB_2
	decf    ACCbHI,F
	movf    ACCaHI,W
	subwf   ACCbHI,F       	;msb
	return			;IF ACCa > ACCb C=0
D_SUB_2
	decf    ACCbHI,F
	movf    ACCaHI,W
	subwf   ACCbHI,F       	;msb
	BCF	STATUS,C	;ACCa > ACCb 
	return
	
;****************************************************************

	END
