;**********************************************************************
;                                                                     
; Filename:	combiner.asm
; Version:      1.11
; Date:         03-18-2004
; Author:       Reed Bement                                           
;                                                                      
; Description:  Simple battery combiner controller.
;                                                                     
;**********************************************************************

	list      p=12c671            ; list directive to define processor
	
        #include <p12c671.inc>        ; processor specific variable definitions

	errorlevel  -302              ; suppress message 302 from list file

	__CONFIG  _CP_OFF & _WDT_OFF & _MCLRE_ON & _PWRTE_ON & _INTRC_OSC_CLKOUT 

;**********************************************************************
;
; PORT GPIO ASSIGNMENTS:
;
; bit | MCU pin |  dir   | description
; GP0 |    7    | input  | Analog input, start battery sense.
; GP1 |    6    | output | Inverter solonoid 
; GP2 |    5    | output | House solonoid 
; GP3 |    4    | input  | Master reset, JP11
; GP4 |    3    | output | Clock out, JP10
; GP5 |    2    | output | Debug output, JP9
;
;**********************************************************************

osc_cal         equ 0x94        ; Last byte of value at 0x3ff
port_dir	equ b'00001001' ; GP0 & GP3 input, all others output

sense           equ 0           ; GP0, MCU pin 7
inverter        equ 1           ; GP1, MCU pin 6
house           equ 2           ; GP2, MCU pin 5
clock           equ 3           ; GP3, MCU pin 4
reset           equ 4           ; GP4, MCU pin 3
dbg             equ 5           ; GP5, MCU pin 2

hous_inv        equ 6           ; Mask for house and inverter.

;**********************************************************************
;
; VOLTAGE THRESHOLD DEFINITIONS
;
;       VAL = 255 * (threshold_voltage / 4 )/ 5)  ->
;       VAL = (threshold_voltage * 12.75);
;                                     
;               255  - max MCU a/d (byte wide) value    
;               1/4  - voltage divider network on PCB             
;               5    - Vref tied internally to Vdd in MCU 
;
;       Example, for a threshold of 13.5 Volts:      
;            13.5 * 12.75 = 172.125 ~ 172
;
;**********************************************************************

disconn_lo      equ .165        ; Disconnect below 13.0 Volts.
connect         equ .172        ; Connect above or equal to 13.5 Volts.
disconn_hi      equ .192        ; Disconnect above or equal to 15.0 Volts.

;**********************************************************************
;
; VARIABLE DEFINITIONS
;
;**********************************************************************

w_temp          equ     0x70    ; variable used for context saving 
status_temp     equ     0x71    ; variable used for context saving

bit_count       equ     0x23    ; Used by serial out routine to count bits.
send_data       equ     0x24    ; Used by serial out rouine, value to send.

ad_data         equ     0x25    ; Holds result of AD conversion.

ascii_lo        equ     0x26    ; LSD in ASCII of voltage value.
ascii_hi        equ     0x27

chr_index       equ     0x2A    ; Character index into string.

ascii_off       equ     0x2B    ; Holds the ascii offset for '0'.

; The following are used by math utility inc's

count           equ     0x30
temp            equ     0x31
H_byte          equ     0x32      ; Hi byte of 16bit value
L_byte          equ     0x33      ; Low byte of 16bit value
R0              equ     0x34      ; BCD result MSDs       
R1              equ     0x35
R2              equ     0x36      ; BCD result LSDs
mulcnd          equ     0x37      ; 8 bit multiplicand
mulplr          equ     0x38      ; 8 bit multiplier
ACCaLO          equ     0x39
ACCaHI          equ     0x3A
ACCbLO          equ     0x3B
ACCbHI          equ     0x3C
ACCcLO          equ     0x3D
ACCcHI          equ     0x3E
ACCdLO          equ     0x3F
ACCdHI          equ     0x40

; The following used by delay.inc
sreg            equ     0x20    ; seconds delay value    
msreg           equ     0x21    ; milliseconds delay value
usreg           equ     0x22    ; 10 microsecond delay value

;**********************************************************************
;
; Main Entry Point
;        
;**********************************************************************
		
        ORG     0x000           ; processor reset vector
        goto    main            ; go to beginning of program

;**********************************************************************
;
; ISR Entry Point, interrupts not used.
;        
;**********************************************************************

        ORG     0x004           ; interrupt vector location
        retfie                  ; return from interrupt

        #include <delay.inc>
        #include <b16tobcd.inc>
        #include <mult8x8.inc>
        #include <div16.inc>
        #include <serial.inc>

;**********************************************************************
;
; Main Loop
;        
;**********************************************************************

main
        call    init            ; Do MCU setup.

        ; Send 'init' string to debug port.
        movlw   init_str
        call    send_string_w 
                
        ; Wait for 1 min to give temperature a chance to
        ; come up before putting a large load on. 
        call    wait_one_min

        ; Main loop. Get and display current battery voltage.
run
        call    send_zerox      ; Send "0x" out the debug port
        
        call    ad_gp0          ; Get battery voltage value in ad_data.
        movwf   ad_data         ; Save the ADC result.

        call    hex_2_ascii     ; Send raw AD data out the debug port.
        movf    ascii_hi, W
        call    send_w 
        movf    ascii_lo, W
        call    send_w 
        movlw   ' '
        call    send_w 
        movlw   ' '
        call    send_w 
        movf    ad_data,W
        
        call    send_volt       ; Send voltage in decimal.

        ; Test against low voltage threshold.
        movlw   disconn_lo 
        subwf   ad_data, W        
        bnc     do_disconn       ; Disconnect if ad_data < disconn_lo.
        
        ; Above low voltage threshold if here. 
        ; Test against high voltage threshold.
        movlw   disconn_hi
        subwf   ad_data, W        
        bc      do_disconn      ; Disconnect if ad_data >= disconn_hi.

        ; Between diconnect thresholds if here.
        ; Test against connect voltage threshold.
        movlw   connect
        subwf   ad_data, W        
        skpc                    ; Continue if ad_data >= connect.
        goto    run_next

        ; Here if do connect.
        btfsc   GPIO, house     ; If house not connected, skip.
        bsf     GPIO, inverter  ; Connect the inverter battery.
        bsf     GPIO, house     ; Connect the house battery.
        goto    run_next        ; Continue main loop

do_disconn
        btfss   GPIO, inverter  ; If inverter connected, skip.
        bcf     GPIO, house     ; Disconnect the house battery.
        bcf     GPIO, inverter  ; Disconnect the inverter battery.

        ; Finish sending status then continue main loop.
run_next
        movlw   hous_inv        ; Test if either bank connected.
        andwf   GPIO, W        
        bz      send_disconn    ; Send diconnect if nothing is connected.

        movlw   house_str       ; If anything is connected, house is connected.
        call    send_string_w 
             
        btfss   GPIO, inverter  ; If inverter connected, skip.
        goto    cont_run
        
        movlw   invert_str
        call    send_string_w 
        goto    cont_run
           
send_disconn                    ; Send the disconnect string.
        movlw   discon_str
        call    send_string_w 
        
cont_run        
        call    send_crlf 
        waits   5              ; Limit update rate to .2 Hz.
        goto    run

;**********************************************************************
;
; Setup the PIC for this application
;        
;**********************************************************************

init
        clrf    GPIO            ; Clear IO register: GPIO

        ; Trim onboard RC clock.        
        call    GetOscCal       ; Retrieve factory calibration value
        bsf     STATUS,RP0      ; Set file register bank to 1 
        movwf   OSCCAL          ; Update register with factory cal value 
        
        movlw   0x80            ; Weak pullups disabled.
        movwf   OPTION_REG
        
        movlw   port_dir        ; Setup io pin directions,
        movwf   TRISIO          ; GP0 & GP3 input, all others output.
        
        ; Configure the A/D module
        movlw   B'00000110'     ; Set GP0 to analog input w/ Vdd as reference
        movwf   ADCON1          ; and GP1,GP2,GP3,GP4,GP5 as digital.
        
        bcf     STATUS, RP0     ; Return to bank0.
        
        movlw   B'11000001'     ; Selects ch0, internal RC oscillator and
        movwf   ADCON0          ; A/D operating mode.
        
        clrf    ADRES           ; clr A/D result register
        
        return 

;**********************************************************************
;
; One min. delay with debug output.
;        
;**********************************************************************

wait_one_min
        movlw   wait_1_min_str
        call    send_string_w 
        waits   60
        return
        
;**********************************************************************
;
; Send a CR LF pair out the debug port in serial format.
;        
;**********************************************************************

send_crlf 
        movlw   0xa             ; CR
        call    send_w 
        movlw   0xd             ; LF
        call    send_w 
        return

;**********************************************************************
;
; Send a "0x" pair out the debug port in serial format.
;        
;**********************************************************************

send_zerox 
        movlw   '0'            
        call    send_w 
        movlw   'x'
        call    send_w 
        return

;**********************************************************************
;
; Send a voltage value in W out the debug port in serial format.
;        
;**********************************************************************

send_volt 
        ; Convert ad value to volts = ad * 20 / 255
        movwf   mulcnd
        movlw   .20
        movwf   mulplr
        call    mpy_S           ; Value * 20

        call    send_2_dig      ; Divides by 255
        
        movlw   '.'
        call    send_w 
        
        ; Convert remainder to tenths and hunderdths.
        movf    ACCcLO, W
        movwf   mulcnd         
        movlw   .100
        movwf   mulplr
        call    mpy_S           ; Remainder * 100
        
        call    send_2_dig
        
        movlw   'V'
        call    send_w 
        return

;**********************************************************************
;
; Send two decimal digits in out the debug port in serial format. 
; Source is 16bit value in H_byte,L_byte. Divide source by 255.
;        
;**********************************************************************

send_2_dig
        movf    H_byte, W       ; Divide by 255
        movwf   ACCbHI        
        movf    L_byte, W
        movwf   ACCbLO
        movlw   0
        movwf   ACCaHI        
        movlw   .255
        movwf   ACCaLO
        call    D_divS

        ; Convert tens and units to BCD.
        movf    ACCbHI, W
        movwf   H_byte        
        movf    ACCbLO, W
        movwf   L_byte        
        call    B2_BCD          ; Result LSDs packed in R2
        
        ; Send tens and units.
        movlw   '0'
        movwf   ascii_off
        
        swapf   R2, W          ; Send tens
        andlw   0x0F
        addwf   ascii_off, W
        call    send_w 
        
        movfw   R2             ; Send units
        andlw   0x0F
        addwf   ascii_off, W
        call    send_w 
        return
                
;**********************************************************************
;
; Convert hex value in w to ASCII in ascii_hi and ascii_lo.
;        
;**********************************************************************

hex_2_ascii
        movwf   ascii_lo        ; Copy hex value for LSN.
        movwf   ascii_hi        ; Copy hex value for MSN.
        movlw   0x0F            ; Move mask into W.
        andwf   ascii_lo, F     ; Mask to low nibble.
        swapf   ascii_hi, F     ; Swap nibbles.
        andwf   ascii_hi, F     ; Mask to high nibble.
        movlw   0x30            ; Move '0' offset into W.
        addwf   ascii_lo, F     ; Add offset to low nibble.
        addwf   ascii_hi, F     ; Add offset to high nibble.
        movlw   0x3A            ; Move test into W.
        subwf   ascii_hi, W     ; Test for nibble less than 10.
        movlw   7               ; Move 'A' offset into W.
        skpnc                   ; Skip if F - W < 0
        addwf   ascii_hi, F     ; Add 'A' offset to high nibble.
        movlw   0x3A            ; Move test into W.
        subwf   ascii_lo, W     ; Test for nibble less than 10.
        movlw   7               ; Move 'A' offset into W.
        skpnc                   ; Skip if F - W < 0
        addwf   ascii_lo, F     ; Add 'A' offset to low nibble.
        return             

;**********************************************************************
;
; This routine performs an A/D conversion on GP0.
;        
;**********************************************************************

ad_gp0
        wait10us 1              ; Wait 10 microseconds for the a/d setup.
        bsf     ADCON0, GO      ; Start ADC conversion
ad_done
        btfsc   ADCON0, NOT_DONE ;Is A/D conversion complete?
        goto    ad_done         ;No, go back and check again
        movf    ADRES, W        ;Yes, get a/d value
        return

;**********************************************************************
;
; Send a string number specified by W out the debug port in 
; serial format. Note that send_string_w and the string table must be 
; in the same 256 byte page.
;        
;**********************************************************************
        
        ORG     0x200           

send_string_w
        movwf   temp            ; Save string offset.

	movlw   2               ; Set PCLATH to match table page.
        movwf   PCLATH
        
        movf    temp, W         ; Restore string offset.
        
	call    get_str_add     ; Get base index for the defined string.
	movwf   chr_index

send_string_0
	movf    chr_index, W	; Output each character.
	call    get_char        
        addlw   0	
	btfsc   STATUS, Z
	goto    send_string_exit
        call    send_w 
	incf    chr_index, f
	goto    send_string_0
        
send_string_exit
	return

;**********************************************************************
;
; String data.
;        
;**********************************************************************

init_str        equ 0
wait_1_min_str  equ 1
house_str       equ 2
invert_str      equ 3
discon_str      equ 4

get_str_add
	addwf   PCL, F
	retlw   str0-str0	
	retlw   str1-str0
	retlw   str2-str0	
	retlw   str3-str0
	retlw   str4-str0

get_char
	addwf   PCL, F

str0    DT "Battery Combiner, Ver 1.11, Reed Bement", 0DH, 0AH, 00H
str1    DT "Waiting one minute...", 0DH, 0AH, 00H
str2    DT " House ", 00H
str3    DT " Inverter ", 00H
str4    DT " Disconnected", 00H


;**********************************************************************
;
; Return factory internal RC oscillator calibration value. This value
; needs to be read from each part before the first programing.
;        
;**********************************************************************

        ORG     0x3ff           ; Last program memory location

GetOscCal			; Return factory calibration value	
	retlw	osc_cal


	END                     ; directive 'end of program'

