Site icon Making Easy Circuits

MPPT Solar Charger with 3-Step Charger Circuit

This MPPT solar charge controller works for 12V panels approximately 120W and 24V panels about 240W. It includes Optimum Power Point Tracking (MPPT) and 3-stage battery charging. It functions with any specific 12V panel from 40W up to 120W (3.3-10A) which enables you to also run with 24V panels in the 80W to 240W range, together with a 24V battery.

Wouldn’t it be great if you could possibly hook up a solar panel (or panels) to a battery or two and let it rest at that? Regrettably, for most except the smallest solar panels, this could be an incredibly poor strategy. The battery is going to be overcharged on summery seasons and on overcast weeks the battery may well not charge in anyway, regardless that the panel could harvest power. So you have no preference - you absolutely need a charge controller.

This Charge Controller would work for charging Flooded Lead Acid, Gel-Cell (Sealed Lead Acid or SLA) and AGM (Absorbed Glass Mat) type batteries. Essentially, any battery employed in a solar technique needs to be a “deep discharge” type. Car batteries are not deep discharge categories which makes them not best suited.

The current/voltage waveform for a standard 120W solar panel. Maximum current, with the output shorted, is Isc and maximum voltage, with the output open circuit, is Voc. For better efficiency, the solar panel is applied at its maximum power point.
For loads which must run continuously to operate a certain system, a solar panel and charge controller is the sole approach. For this usage we advise, no less than, a 12V 40W solar panel with a 12V 12Ah SLA battery.

For continuous operations, the MPPT solar charger circuit could consume approximately about 200mA. Over a 24-hour period this results to 4.8Ah or 60Wh each day from the 12V battery. This implies in case a 40W panel produces optimal power for 1.5 hours or higher every day, this could be adequate for any relevant load to operate. In spite of this, if you happen to be worried about automatic running of bilge pumps etc, a 40W panel could possibly be the ideal choice. The motive we have stipulated a larger solar panel and battery than precisely required is twofold.

While Q1 is on, current (I1) passes through inductor L1 and into capacitor C2 and the battery. In case Q1 switches off, the stored energy in L1 is appllied to the battery by means of diode D2 (current path I2).
MPPT & charge optimisation

Considering the fact that the solar panel is installed horizontally, it is reasonably crucial that you absorb the maximum electricity as possible from it so this is where the Charge Controller’s MPPT (Maximum Power Point Tracking) makes its way in.

As demonstrated in Fig.1, for a standard solar panel subjected to maximum sunlight, the output stoves from optimum current while the output leads is shorted (Isc) to highest voltage while the output is open circuit (Voc). For a regular 120W 12V panel, Isc is 7.14A and Voc is 21.8V. However the optimum power from a 120W panel is at 6.74A and 17.8V that could be barely an ideal blend for a lead-acid battery.

If we were to link up that 120W solar panel straight away to the battery, the charge current is likely to be approximately 7.1A at 12V (85.2W), 7.05A at 13V (91.7W) and 7A at 14.4V (101W), ie, significantly less than the 120W offered by the solar panel at 17.8V.

By contrast, MPPT helps keeping the solar panel current and voltage at the maximum power point while charging the battery, granting that the battery voltage is below the solar panel voltage.

This is accomplished by a smart switchmode step-down voltage converter. To learn how this implements, check with the block diagram of Fig.2 below. Current from the solar panel streams by way of diode D1 and Mosfet Q1. When Q1 is on, current (I1) runs via inductor L1 into capacitor C2 and the battery. This gathers energy within the inductor’s magnetic field.

PCB design and track layout for the proposed MPPT soar charger circuit with 3 step charger

Programming source code for the proposed 12V/24V solar MPPT circuit using PIC16F88

; 100W MPPT Solar Charger
; upgraded with a supplementary bulk charge restart feature after a 4hr break when sunlight returns to panel
; Option set when RB0 low
; Supplementary bulk restart anytime power demanded for float charging. Option set when RB1 low
; Added switch to float mode if bulk charging takes less than 60s. ie if the battery is fully charged

list P=16F88
#include p16f88.inc
ERRORLEVEL -302
ERRORLEVEL -306

;Program Configuration Register 1
__CONFIG    _CONFIG1, _CP_ALL & _CCP1_RB3 & _DEBUG_OFF & _WRT_PROTECT_OFF & _CPD_OFF & _LVP_OFF & _BODEN_ON & _MCLR_ON & _PWRTE_ON & _WDT_OFF & _INTRC_IO

;Program Configuration Register 2
__CONFIG    _CONFIG2, _IESO_OFF & _FCMEN_OFF

; Bank 0 RAM
DIGITAL            equ    H'20'    ; storage
FIRST            equ    H'21'    ; first run
CUT_M            equ    H'22'    ; cutout voltage high byte
CUT_L              equ    H'23'    ; cutout voltage low byte
FLOAT_H            equ    H'24'    ; float voltage high byte
FLOAT_L            equ    H'25'    ; float voltage low byte
COMP            equ    H'26'    ; compensation (temperature)
BATT_HI            equ    H'27'    ; battery volts high byte
BATT_LO            equ    H'28'    ; battery low byte
DELCNT            equ    H'29'    ; delay counter
FLASHER            equ    H'2A'    ; LED flasher timer
TEMPERATURE        equ    H'2B'    ; temperature reading deg C
THERMISTOR        equ    H'2C'    ; thermistor flag for LED
CUT_COMP_M        equ    H'2D'    ; cutout temp. compensated voltage high byte
CUT_COMP_L         equ    H'2E'    ; cutout temp. compensated voltage low byte
FLOAT_COMP_M    equ    H'2F'    ; float temp. compensated voltage high byte
FLOAT_COMP_L    equ    H'30'    ; float temp. compensated voltage low byte
NEGATIVE        equ    H'31'    ; subtract negative flag
VALUE_1            equ    H'32'    ; delay counter
VALUE_2            equ    H'33'    ; delay counter
HOUR0            equ    H'34'    ; hour counter
HOUR1            equ    H'35'    ; 14 seconds counter for hour counter (256 x 14s=1hour)
SENSOR_COUNT    equ    H'36'    ; sensor counter for periodic checking
SENSOR_COUNT1    equ    H'37'    ; sensor counter for periodic checking
CELL_LO            equ    H'38'    ; solar cell voltage ls byte
CELL_HI            equ    H'39'    ; solar cell voltage ms byte
CELL_I_LO        equ    H'3A'    ; solar cell current ls byte
CELL_I_HI        equ    H'3B'    ; solar cell current ms byte
CHARGE_STATE    equ    H'3C'    ; 0 charge, 1 float
CHARGE_FLAG        equ    H'3D'    ; flag for charge
CHRG_RATE        equ    H'3E'    ; charge change rate
CELL_V            equ    H'3F'    ; solar cell voltage 8-bit
CELL_I            equ    H'40'    ; solar cell current 8-bit
PERIOD            equ    H'41'    ; power calculation rate
CCPR1_STORE        equ    H'42'    ; CCPR1L storage value
POWERH            equ    H'43'    ; power ms byte
POWERL            equ    H'44'    ; power ls byte
VALUE1            equ    H'45'    ; temporary value
VALUE2            equ    H'46'    ; temporary value
VALUE3            equ    H'47'    ; temporary value
VALUE4            equ    H'48'    ; temporary value
EQ_FLAG            equ    H'49'    ; equalisation flag
EQ_LO            equ    H'4A'    ; ls byte EQ battery voltage
EQ_HI            equ    H'4B'    ; ms byte EQ battery voltage
EQ_LO_COMP        equ    H'4C'    ; ls byte temp. compensated EQ battery voltage
EQ_HI_COMP        equ    H'4D'    ; ms byte temp. compensated EQ battery voltage
EQ_LEVEL        equ    H'4E'    ; equalisation input (RB4) level store
BATT_IND        equ    H'4F'    ; battery indicator flag when error
BURST_FLG        equ    H'50'    ; burst flag
HOUR3            equ    H'51'    ; 4 hour counter
HOUR2            equ    H'52'    ; 56 seconds counter for hour counter (256 x 56s=1hour)
BULK_TIMER        equ    H'53'    ; bulk charge timer (60s)
BULK_TIMER_END    equ    H'54'    ; bulk timer ended flag

; math routines
TEMP1            equ    H'5C'
TEMPB0            equ    H'5D'
TEMPB1            equ    H'5E'
TEMPB2            equ    H'5F'
TEMP            equ H'60'
REMB3            equ H'61'
REMB2            equ    H'62'
REMB1              equ H'63'
REMB0            equ    H'64'
AARGB5            equ    H'65'
AARGB4          equ H'66'
AARGB3            equ    H'67'
AARGB2          equ H'68'
AARGB1          equ H'69'
AARGB0          equ H'6A'    ; most significant byte of argument A
BARGB3          equ H'6B'
BARGB2          equ H'6C'
BARGB1          equ H'6D'
BARGB0          equ H'6E'    ; most significant byte of argument B
LOOPCOUNT       equ H'6F'      ; division counter

; All Banks RAM
; Interrupt store registers
W_TMP            equ    H'70'    ; storage of w before interrupt
STATUS_TMP        equ    H'71'    ; status storage before interrupt

; start at memory 0
org    0
goto    SETUP
org    4
goto    INTERRUPT

; position the lookup table at start to avoid a 256 bit boundary

TEMP_CONV ; convert A/D values to deg C based on thermistor R=Ae**(B/T) where (T is in K ie deg C plus 273)
; A =0.01058 and B is 4100
addwf    PCL,f    ; add value to program counter
; 60 deg C max
retlw    D'60'    ; 60deg C for A/D D49 (8-bit)
retlw    D'59'    ; deg C for A/D D50(8-bit)
retlw    D'58'    ; deg C for A/D D51(8-bit)
retlw    D'58'    ; deg C for A/D D52(8-bit)
retlw    D'57'    ; deg C for A/D D53(8-bit)
retlw    D'57'    ; deg C for A/D D54(8-bit)
retlw    D'56'    ; deg C for A/D D55(8-bit)
retlw    D'56'    ; deg C for A/D D56(8-bit)
retlw    D'55'    ; deg C for A/D D57(8-bit)
retlw    D'54'    ; deg C for A/D D58(8-bit)
retlw    D'54'    ; 54 deg C for A/D D59(8-bit)

retlw    D'53'    ; deg C for A/D D60(8-bit)
retlw    D'53'    ; deg C for A/D D61(8-bit)
retlw    D'52'    ; deg C for A/D D62(8-bit)
retlw    D'52'    ; deg C for A/D D63(8-bit)
retlw    D'51'    ; deg C for A/D D64(8-bit)
retlw    D'50'    ; deg C for A/D D65(8-bit)
retlw    D'50'    ; deg C for A/D D66(8-bit)
retlw    D'50'    ; deg C for A/D D67(8-bit)
retlw    D'49'    ; deg C for A/D D68(8-bit)
retlw    D'49'    ; deg C for A/D D69(8-bit)

retlw    D'48'    ; 48 deg C for A/D D70(8-bit)
retlw    D'48'    ; deg C for A/D D71(8-bit)
retlw    D'47'    ; deg C for A/D D72(8-bit)
retlw    D'47'    ; deg C for A/D D73(8-bit)
retlw    D'46'    ; deg C for A/D D74(8-bit)
retlw    D'46'    ; deg C for A/D D75(8-bit)
retlw    D'45'    ; deg C for A/D D76(8-bit)
retlw    D'45'    ; deg C for A/D D77(8-bit)
retlw    D'44'    ; deg C for A/D D78(8-bit)
retlw    D'44'    ; deg C for A/D D79(8-bit)

retlw    D'43'    ; deg C for A/D D80(8-bit)
retlw    D'43'    ; deg C for A/D D81(8-bit)
retlw    D'43'    ; deg C for A/D D82(8-bit)
retlw    D'42'    ; deg C for A/D D83(8-bit)
retlw    D'42'    ; deg C for A/D D84(8-bit)
retlw    D'41'    ; deg C for A/D D85(8-bit)
retlw    D'41'    ; deg C for A/D D86(8-bit)
retlw    D'40'    ; deg C for A/D D87(8-bit)
retlw    D'40'    ; deg C for A/D D88(8-bit)
retlw    D'40'    ; 40 deg C for A/D D89(8-bit)

retlw    D'39'    ; deg C for A/D D90(8-bit)
retlw    D'39'    ; deg C for A/D D91(8-bit)
retlw    D'38'    ; deg C for A/D D92(8-bit)
retlw    D'38'    ; deg C for A/D D93(8-bit)
retlw    D'38'    ; deg C for A/D D94(8-bit)
retlw    D'37'    ; deg C for A/D D95(8-bit)
retlw    D'37'    ; deg C for A/D D96(8-bit)
retlw    D'36'    ; deg C for A/D D97(8-bit)
retlw    D'36'    ; deg C for A/D D98(8-bit)
retlw    D'36'    ; deg C for A/D D99(8-bit)

retlw    D'35'    ; deg C for A/D D100(8-bit)
retlw    D'35'    ; deg C for A/D D101(8-bit)
retlw    D'34'    ; deg C for A/D D102(8-bit)
retlw    D'34'    ; deg C for A/D D103(8-bit)
retlw    D'34'    ; deg C for A/D D104(8-bit)
retlw    D'33'    ; deg C for A/D D105(8-bit)
retlw    D'33'    ; deg C for A/D D106(8-bit)
retlw    D'33'    ; deg C for A/D D107(8-bit)
retlw    D'32'    ; deg C for A/D D108(8-bit)
retlw    D'32'    ; 32 deg C for A/D D109(8-bit)

retlw    D'32'    ; deg C for A/D D110(8-bit)
retlw    D'31'    ; deg C for A/D D111(8-bit)
retlw    D'31'    ; deg C for A/D D112(8-bit)
retlw    D'30'    ; deg C for A/D D113(8-bit)
retlw    D'30'    ; deg C for A/D D114(8-bit)
retlw    D'30'    ; deg C for A/D D115(8-bit)
retlw    D'29'    ; deg C for A/D D116(8-bit)
retlw    D'29'    ; deg C for A/D D117(8-bit)
retlw    D'29'    ; deg C for A/D D118(8-bit)
retlw    D'28'    ; deg C for A/D D119(8-bit)

retlw    D'28'    ; deg C for A/D D120(8-bit)
retlw    D'28'    ; deg C for A/D D121(8-bit)
retlw    D'27'    ; deg C for A/D D122(8-bit)
retlw    D'27'    ; deg C for A/D D123(8-bit)
retlw    D'27'    ; deg C for A/D D124(8-bit)
retlw    D'26'    ; deg C for A/D D125(8-bit)
retlw    D'26'    ; deg C for A/D D126(8-bit)
retlw    D'26'    ; deg C for A/D D127(8-bit)
retlw    D'25'    ; deg C for A/D D128(8-bit)
retlw    D'25'    ; deg C for A/D D129(8-bit)

retlw    D'24'    ; deg C for A/D D130(8-bit)
retlw    D'24'    ; deg C for A/D D131(8-bit)
retlw    D'24'    ; deg C for A/D D132(8-bit)
retlw    D'24'    ; deg C for A/D D133(8-bit)
retlw    D'23'    ; deg C for A/D D134(8-bit)
retlw    D'23'    ; deg C for A/D D135(8-bit)
retlw    D'23'    ; deg C for A/D D136(8-bit)
retlw    D'22'    ; deg C for A/D D137(8-bit)
retlw    D'22'    ; deg C for A/D D138(8-bit)
retlw    D'22'    ; deg C for A/D D139(8-bit)

retlw    D'21'    ; deg C for A/D D140(8-bit)
retlw    D'21'    ; deg C for A/D D141(8-bit)
retlw    D'21'    ; deg C for A/D D142(8-bit)
retlw    D'20'    ; deg C for A/D D143(8-bit)
retlw    D'20'    ; deg C for A/D D144(8-bit)
retlw    D'20'    ; deg C for A/D D145(8-bit)
retlw    D'19'    ; deg C for A/D D146(8-bit)
retlw    D'19'    ; deg C for A/D D147(8-bit)
retlw    D'19'    ; deg C for A/D D148(8-bit)
retlw    D'18'    ; deg C for A/D D149(8-bit)

retlw    D'18'    ; deg C for A/D D150(8-bit)
retlw    D'18'    ; deg C for A/D D151(8-bit)
retlw    D'17'    ; deg C for A/D D152(8-bit)
retlw    D'17'    ; deg C for A/D D153(8-bit)
retlw    D'17'    ; deg C for A/D D154(8-bit)
retlw    D'16'    ; deg C for A/D D155(8-bit)
retlw    D'16'    ; deg C for A/D D156(8-bit)
retlw    D'16'    ; deg C for A/D D157(8-bit)
retlw    D'15'    ; deg C for A/D D158(8-bit)
retlw    D'15'    ; deg C for A/D D159(8-bit)

retlw    D'15'    ; deg C for A/D D160(8-bit)
retlw    D'14'    ; deg C for A/D D161(8-bit)
retlw    D'14'    ; deg C for A/D D162(8-bit)
retlw    D'14'    ; deg C for A/D D163(8-bit)
retlw    D'13'    ; deg C for A/D D164(8-bit)
retlw    D'13'    ; deg C for A/D D165(8-bit)
retlw    D'13'    ; deg C for A/D D166(8-bit)
retlw    D'12'    ; deg C for A/D D167(8-bit)
retlw    D'12'    ; deg C for A/D D168(8-bit)
retlw    D'12'    ; deg C for A/D D169(8-bit)

retlw    D'11'    ; deg C for A/D D170(8-bit)
retlw    D'11'    ; deg C for A/D D171(8-bit)
retlw    D'11'    ; deg C for A/D D172(8-bit)
retlw    D'10'    ; deg C for A/D D173(8-bit)
retlw    D'10'    ; deg C for A/D D174(8-bit)
retlw    D'10'    ; deg C for A/D D175(8-bit)
retlw    D'9'    ; deg C for A/D D176(8-bit)
retlw    D'9'    ; deg C for A/D D177(8-bit)
retlw    D'8'    ; deg C for A/D D178(8-bit)
retlw    D'8'    ; 8 deg C for A/D D179(8-bit)

retlw    D'8'    ; deg C for A/D D180(8-bit)
retlw    D'7'    ; deg C for A/D D181(8-bit)
retlw    D'7'    ; deg C for A/D D182(8-bit)
retlw    D'7'    ; deg C for A/D D183(8-bit)
retlw    D'6'    ; deg C for A/D D184(8-bit)
retlw    D'6'    ; deg C for A/D D185(8-bit)
retlw    D'6'    ; deg C for A/D D186(8-bit)
retlw    D'5'    ; deg C for A/D D187(8-bit)
retlw    D'5'    ; deg C for A/D D188(8-bit)
retlw    D'4'    ; 4 deg C for A/D D189(8-bit)

retlw    D'4'    ; deg C for A/D D190(8-bit)
retlw    D'4'    ; deg C for A/D D191(8-bit)
retlw    D'3'    ; deg C for A/D D192(8-bit)
retlw    D'3'    ; deg C for A/D D193(8-bit)
retlw    D'2'    ; deg C for A/D D194(8-bit)
retlw    D'2'    ; deg C for A/D D195(8-bit)
retlw    D'2'    ; deg C for A/D D196(8-bit)
retlw    D'1'    ; deg C for A/D D197(8-bit)
retlw    D'1'    ; deg C for A/D D198(8-bit)
retlw    D'0'    ; deg C for A/D D199(8-bit)
; 0 deg C min
retlw    D'0'    ; 0 deg C for A/D D200(8-bit)

;**********************************************************************************************

SETUP

clrf    PORTB        ; outputs low
clrf    PORTA
; set inputs/outputs
bsf        STATUS,RP0    ; select memory bank 1
movlw    B'00000111'    ; comparators off
movwf    CMCON
movlw    B'00010011'    ; port B outputs/ inputs set
movwf    TRISB        ; port B data direction register
movlw    B'00111111'    ; outputs (0) and inputs (1)
movwf    TRISA        ; port A data direction register
movlw    B'00000111'    ; settings (pullups enabled, TMR0/256)
movwf    OPTION_REG

; analog inputs, A/D

movlw    B'00011111'    ; AN0 to AN4 are analog inputs
movwf    ANSEL
;    movlw    B'10000000'    ; * 4MHz operation right justified A/D result, Vdd to Vss A/D
movlw    B'11000000'    ; ** 8MHz operation right justified A/D result, Vdd to Vss A/D
movwf    ADCON1
bcf        STATUS,RP0    ; select memory bank 0
movlw    B'01000000'    ; Fosc, channel 0 etc
movwf    ADCON0
bsf        ADCON0,ADON    ; A/D on
bsf        STATUS,RP0    ; select memory bank 1
;    movlw    B'01101000'    ; * 4MHz operation
movlw    B'01111000'    ; ** 8MHz operation 8MHz
movwf    OSCCON        ;
bcf        STATUS,RP0    ; select memory bank 0
; timer 1
;    movlw    B'00100001'    ; * 4MHz operation timer 1 prescaler /4, fosc/4
movlw    B'00110001'    ; ** 8MHz operation timer 1 prescaler /8, fosc/4
movwf    T1CON
bsf        T1CON,0        ; timer 1 on
bsf        STATUS,RP0    ; select memory bank 1
;    movlw    H'1F'        ; * 4MHz operation 31.24kHz pwm rate 7-bit resolution
movlw    H'3F'        ; ** 8MHz operation 31.24kHz pwm rate 8-bit resolution
movwf    PR2            ; PWM period register
bcf        STATUS,RP0    ; memory bank 0

; pwm set
clrf    CCPR1L        ; duty 0% Mosfet off
bcf        CCP1CON,4
bcf        CCP1CON,5    ; clear 10-bits
clrf    T2CON
bsf        T2CON,2        ; enable timer 2
movlw    B'00001100'    ; set PWM mode
movwf    CCP1CON        ; enable PWM operation

; initial conditions
INITIAL
clrf    PORTB
clrf    PORTA
clrf    THERMISTOR        ; thermistor flags
movlw    D'1'
movwf    SENSOR_COUNT    ; counter ready to zero on next decrement
movwf    SENSOR_COUNT1
clrf    CHARGE_STATE    ; start at main Bulk ( 0 main Bulk, 1 float)
clrf    CHARGE_FLAG        ; flag for charge
movlw    D'5'            ; 5 x 0.262ms
movwf    PERIOD            ; period counter for solar cell power calculations
clrf    CCPR1_STORE        ; CCPR1L storage
clrf    FIRST            ; first run
clrf    POWERH            ; power ms byte
clrf    POWERL            ; power ls byte
clrf    FLASHER            ; flash timer
clrf    CHRG_RATE        ; charge rate flag
clrf    HOUR0            ; hour counter
clrf    HOUR1            ; 14s counter for hour0 counter
clrf    HOUR2            ; 56s counter for 4 hour counter
clrf    HOUR3            ; 4- hour timer
clrf    EQ_FLAG            ; equalisation flag
bsf        EQ_FLAG,0        ; equalisation flag.0 Does not run equalisation when set
clrf    EQ_LEVEL        ; equalisation input level store
clrf    BATT_IND        ; no battery error when clear
clrf    BURST_FLG        ; no burst
clrf    BULK_TIMER        ; bulk charge timer
clrf    BULK_TIMER_END    ; bulk timer ended flag

; load SLA preset values
; battery voltage reduced to 0.3125 so 14.4V becomes 4.50V and D920 (H398) with A/D conversion (10-bit)
movlw    H'03'
movwf    CUT_M        ; cutout SLA voltage high byte 14.4V
movlw    H'98'
movwf    CUT_L        ; cutout SLA voltage low byte
movlw    H'03'
movwf    FLOAT_H        ; float SLA voltage high byte 13.5V
movlw    H'5F'
movwf    FLOAT_L        ; float SLA voltage low byte
movlw    H'03'
movwf    EQ_HI        ; Equalsiation voltage high byte (14.4V x 10%)= 15.84V
movlw    H'F4'
movwf    EQ_LO        ; Equalisation voltage low byte

ALLOW_INTERRUPTS
; allow interrupts
bsf        STATUS,RP0        ; select memory bank 1
bsf        PIE1,TMR1IE        ; timer 1 overflow interrupt enable
bcf        STATUS,RP0        ; select memory bank 0
bcf        PIR1,TMR1IF        ; timer 1 interrupt flag
bsf        INTCON,PEIE        ; enable periperal interrupts
bsf        INTCON,GIE        ; enable global interrupts

;*******************************************************

CYCLE ; beginining of normal running loop
; sensors checked periodically
decfsz    SENSOR_COUNT,f
goto    CHARGE_MODE
decfsz    SENSOR_COUNT1,f
goto    CHARGE_MODE        ; when both zero
;    movlw    D'4'            ; * 4MHz operation
movlw    D'8'            ; ** 8MHz operation
movwf    SENSOR_COUNT1    ; set period to about 500ms

SENSORS

; equalisation input

btfss    PORTB,4        ; equalisation input selection
goto    LOW_IN
btfss    EQ_LEVEL,4    ; input store
goto    HI_IN
; if portB,4 is high and EQ_LEVEL,4 is also high set EQ_FLAG,0 (no equalisation)
clrf    EQ_FLAG
bsf        EQ_FLAG,0    ; no equalisation when flag is set (PORTB,4 is high)
goto    CH_1AD
LOW_IN
btfss    EQ_LEVEL,4
goto    CH_1AD        ; if portB,4 and EQ_LEVEL,4 both low ignore

; if portB,4 is low and EQ_LEVEL,4 is high, run delay and check again.

call     DELAY
btfsc    PORTB,4
goto    CH_1AD        ; If portB,4 high ignore

; if portB,4 remains low clear EQ_LEVEL,4 and clear EQ_FLAG (level change from h/l)
clrf    EQ_LEVEL    ; store new portB,4 level
clrf    EQ_FLAG        ; equalisation flag clear for equalisation

; level change from H/L indicates equalisation required
; flash LEDS
bcf        INTCON,GIE        ; stop interrupt
bcf        PORTA,7            ; Bulk LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTA,6            ; Absorption LED off
bsf        PORTB,6            ; equalisation LED on
call    DELAY
bcf        PORTB,6            ; Equalisation LED off
call    DELAY
bsf        PORTB,6            ; Equalisation LED on
call     DELAY
bcf        PORTB,6            ; Equalisation LED off
call    DELAY
bsf        INTCON,GIE        ; interrupt starts again
goto    CH_1AD

HI_IN
; if portB,4 is high and EQ_LEVEL,4 is low, run delay and check again.

call     DELAY
btfss    PORTB,4
goto    CH_1AD            ; portB,4 low so bypass

; If portB,4 is still high, set EQ_LEVEL,4 and set EQ_FLAG,0 (no equalisation)

bsf        EQ_LEVEL,4         ; store portB,4 level
bsf        EQ_FLAG,0        ; no equalisation

; Channel 1 A/D value(mV/deg C Compensation)
; compensation calculated as (Comp/50) x 255

CH_1AD
; set analog input address
bcf        ADCON0,5
bcf        ADCON0,4
bsf        ADCON0,3        ;
call    DEL_AD            ; convert to digital ls bits in 'DIGITAL'

; reduce from 10-bit to 8-bit
rrf        ADRESH,f        ; move right ls byte
rrf        DIGITAL,f        ; ls byte moved right
rrf        ADRESH,f        ; move right ls byte
rrf        DIGITAL,f        ; ls byte moved right

; write compensation value
movf    DIGITAL,w        ; 8-bit byte
movwf    COMP            ; mV/deg C SLA compensation

; Channel 4 A/D value (thermistor (temperature))
CH_4AD
; set analog input address
bsf        ADCON0,5
bcf        ADCON0,4
bcf        ADCON0,3        ; A/D No.4
call    DEL_AD            ; convert to digital ls bits in 'DIGITAL'

; change from 10-bit to 8-bit
rrf        ADRESH,f        ; move right ls byte
rrf        DIGITAL,f        ; ls byte moved right
rrf        ADRESH,f        ; move right ls byte
rrf        DIGITAL,f        ; ls byte moved right

; check for thermistor out of circuit
movf    DIGITAL,w
sublw    D'250'            ; >250
bcf        INTCON,GIE        ; stop interrupt
clrf    THERMISTOR        ; clear value
btfsc    STATUS,C
goto    CK_ZRO
bsf        THERMISTOR,0    ; set bit 0 when out of circuit
goto    BY_ZRO

; check for zero degrees or less
CK_ZRO
movf    DIGITAL,w
sublw    D'199'            ; >199
movlw    D'199'            ; keep at 0 degrees
btfss    STATUS,C
movwf    DIGITAL            ; keep at 0 degrees C so compensation plateaus below 0 degrees C

; check for thermistor short circuit
movlw    D'04'
subwf    DIGITAL,w        ; if less than 4
btfss    STATUS,C
bsf        THERMISTOR,1    ; set when short circuit

BY_ZRO
TEMP_CONV1
bsf        INTCON,GIE        ; allow interrupt
; convert to temperature
movf    DIGITAL,w
sublw    D'199'            ; take from 199 if negative then >200 so set at 200
movlw    D'200'            ; ready to load if >200
btfss    STATUS,C
movwf    DIGITAL
movlw    D'49'            ; minimum value
subwf    DIGITAL,f        ; take away 49 from A/D value
btfss    STATUS,C        ; if minus then >60 deg C so set at 60deg
clrf    DIGITAL            ; 0 so 60 deg C in lookup table
movf    DIGITAL,w
call    TEMP_CONV        ; convert reading to temperature in deg C
movwf    TEMPERATURE        ; store value

; find compensation requirement with temperature difference from 20 deg C
; multiplied by D100 and divided by D8000 to calculate mV/deg C for cutout and float voltages
; this is added (for <20 deg C) or subtracted (for >20deg C) from cutoff and float Voltages.

; take away 20 deg C
clrf    NEGATIVE        ; subtract flag
movlw    D'20'            ; 20 deg C
subwf    TEMPERATURE,w    ;
btfss    STATUS,C        ; if negative then less than 20 deg C
bsf        NEGATIVE,7
; (temperature - 20) x 100
movwf    AARGB0
; if negative then subtract from 20
btfss    NEGATIVE,7
goto    CONTINUE1
movf    TEMPERATURE,w
sublw    D'20'            ; temperature from 20
movwf    AARGB0

CONTINUE1
; multiply temperature difference from 20 deg C (in AARGB0) by compensation value
movf    COMP,w
movwf    BARGB0
call    EIGHTEIGHT
; result in AARGB0,AARGB1
; shift result
movf    AARGB1,w
movwf    AARGB2
movf    AARGB0,w
movwf    AARGB1
clrf    AARGB0
; Multiply by 100
movlw    D'100'
movwf    BARGB2
clrf    BARGB1
clrf    BARGB0
call    FXM2424U        ; multiply
; shift result for division
movf    AARGB2,w        ; ms of multiplication
movwf    AARGB0
movf    AARGB3,w        ; ls byte
movwf    AARGB1
movf    AARGB4,w        ; ms of multiplication
movwf    AARGB2
movf    AARGB5,w        ; ls byte
movwf    AARGB3
; Divide by D8000 = H1F40
clrf    BARGB0
clrf    BARGB1
movlw    H'1F'
movwf      BARGB2
movlw    H'40'
movwf    BARGB3
call    FXD3232U        ; divide

; cutoff calculations
btfsc    NEGATIVE,7        ; if set add value
goto    ADD_COMP1
; take compensation value from cutoff V and place in CUT_COMP_M/LS
movf    AARGB3,w        ; result
subwf    CUT_L,w
movwf    CUT_COMP_L
movf    CUT_M,w            ; ms cutout V
btfss    STATUS,C
decf    CUT_M,w            ; decrease if carry
movwf    CUT_COMP_M
btfss    CUT_COMP_M,7    ; if bit 7 set then over so clear
goto    CHECK_EQ
clrf    CUT_COMP_M
clrf    CUT_COMP_L

; take compensation value from equalisation voltage and place in EQ_LO_COMP, EQ_HI_COMP
CHECK_EQ
movf    AARGB3,w        ; result
subwf    EQ_LO,w
movwf    EQ_LO_COMP
movf    EQ_HI,w            ; ms
btfss    STATUS,C
decf    EQ_HI,w            ; decrease if carry
movwf    EQ_HI_COMP
btfss    EQ_HI_COMP,7    ; if bit 7 set then over so clear
goto    CHECK_FLOAT
clrf    EQ_HI_COMP
clrf    EQ_LO_COMP
goto    CHECK_FLOAT

ADD_COMP1
movf    CUT_M,w
movwf    CUT_COMP_M
movf    AARGB3,w        ; result
addwf    CUT_L,w
movwf    CUT_COMP_L
btfsc    STATUS,C
incf    CUT_COMP_M,f    ; increase if carry

; check if over 1024
btfss    CUT_COMP_M,2    ; if bit set over so set at 1023
goto    CHECK_EQ_PLUS
movlw    B'00000011'        ; set at 1023
movwf    CUT_COMP_M
movlw    H'FF'
movwf    CUT_COMP_L

CHECK_EQ_PLUS
movf    EQ_HI,w
movwf    EQ_HI_COMP
movf    AARGB3,w        ; result
addwf    EQ_LO,w
movwf    EQ_LO_COMP
btfsc    STATUS,C
incf    EQ_HI_COMP,f    ; increase if carry

; check if over 1024
btfss    EQ_HI_COMP,2    ; if bit set over so set at 1023
goto    CHECK_FLOAT
movlw    B'00000011'        ; set at 1023
movwf    EQ_HI_COMP
movlw    H'FF'
movwf    EQ_LO_COMP

; Float calculations
CHECK_FLOAT
btfsc    NEGATIVE,7        ; if set add value
goto    ADD_COMP2

; take compensation value from float V and place in FLOAT_COMP_M/LS
movf    AARGB3,w        ; result
subwf    FLOAT_L,w
movwf    FLOAT_COMP_L
movf    FLOAT_H,w        ; ms float V
btfss    STATUS,C
decf    FLOAT_H,w        ; decrease if carry

movwf    FLOAT_COMP_M
btfss    FLOAT_COMP_M,7    ; if bit 7 set then over so clear
goto    CH_0AD
clrf    FLOAT_COMP_M
clrf    FLOAT_COMP_L
goto    CH_0AD

ADD_COMP2
movf    FLOAT_H,w        ; ms float V to w
movwf    FLOAT_COMP_M
movf    AARGB3,w        ; result
addwf    FLOAT_L,w
movwf    FLOAT_COMP_L
btfsc    STATUS,C
incf    FLOAT_COMP_M,f        ; increase if carry

; check if over 1024
btfss    FLOAT_COMP_M,2    ; if bit set over so set at 1023
goto    CH_0AD
movlw    B'00000011'        ; set at 1023
movwf    FLOAT_COMP_M
movlw    H'FF'
movwf    FLOAT_COMP_L

; Battery voltage, Channel 0 A/D
; voltage at AN2 is 0.3125 of actual measured due to divider
; so 15V is reduced to 4.6875V and results in D960 from A/D converter (10-bit value)
; this divider value sets the other cutoff, float and compensation calculations

CH_0AD
; set analog input address
bcf        ADCON0,5
bcf        ADCON0,4
bcf        ADCON0,3        ;
call    DEL_AD
movf    DIGITAL,w
movwf    BATT_LO            ; battery voltage ls byte
movf    ADRESH,w        ; ms byte
movwf    BATT_HI            ; battery voltage ms byte

; check if below 12.45V or about 75% of capacity
; A/D is (D796, H31C). Reset to main Bulk when voltage drops to below 12.45V
movf    BATT_HI,w        ; high byte of battery voltage
sublw    H'3'            ; take from
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
sublw    H'1C'            ; 12.45V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfsc    DIGITAL,7        ; if set then >12.45V
goto    BY_BULK_SET

clrf    CHARGE_STATE    ; below 12.45V so set for bulk/main charging
clrf    BULK_TIMER
clrf    BULK_TIMER_END    ; bulk timer ended flag

; end of switched sensors
BY_BULK_SET
bsf        CHRG_RATE,0        ; charge rate flag set to show new values available

; Charge Control

CHARGE_MODE

; if THERMISTOR,1 is set (ie a short circuit)
btfss    THERMISTOR,1    ; short circuit thermistor
goto    CHK_ZRO_BIT
bsf        CHARGE_FLAG,0    ; set charge flag so no charge
goto    NO_CHARGE
CHK_ZRO_BIT
btfss    THERMISTOR,0    ; thermistor out
goto    CHK_BATT
bsf        CHARGE_FLAG,0    ; set charge flag so no charge
goto    NO_CHARGE

CHK_BATT
; battery voltage: if low then apply bursts till >10.5V (D671, H29F)

; check if below 10.5V.  10.5V = 29F

;(for testing set values to test at 33F for 13V)

movf    BATT_HI,w        ; high byte of battery voltage
sublw    H'2'            ; take from 10.5V (H29F)
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
sublw    H'9F'            ; 10.5V (H29F)
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then >10.5V
goto    BURST            ; burst charge till voltage is above 10.5V
clrf    BURST_FLG        ; burst flag off (no burst)

; if battery above 15V (usually when an O/C cell) then show Batt. LED

; first check if Equalisation running
btfss    EQ_FLAG,1        ; when set equalisation is running so bypass since >15V during eq
goto    BATT15
bsf        PORTB,2            ; equalisation and >15V output. High on equalisation
goto    MODE_CHARGE

BATT15
movf    BATT_HI,w        ; high byte of battery voltage
sublw    H'3'            ; take from 15V
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
sublw    H'BF'            ; 15V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then >15V
goto    MODE_CHARGE1    ; <15V
bsf        CHARGE_FLAG,0    ; stop charging
bsf        BATT_IND,0        ; Batt. indicator LED on (Flash Bulk LED)
bsf        PORTB,2            ; >15V output
goto    NO_CHARGE        ; battery voltage high so end charge
MODE_CHARGE1
; test for low battery

; if above 12V (3.75V after division (H2FF)) set RB2 low
BATT12
movf    BATT_HI,w        ; high byte of battery voltage
sublw    H'2'            ; take from 12V
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
sublw    H'FF'            ; 12V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then >12V
goto    BATT11.5
bcf        PORTB,2            ; output low at <15V and >12V
goto    MODE_CHARGE

; if below 11.5V (3.59V after division (H2DF)) set RB2 high
BATT11.5
movf    BATT_HI,w        ; high byte of battery voltage
sublw    H'2'            ; take from 11.5V
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
sublw    H'DF'            ; 11.5V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then >11.5V
bsf        PORTB,2            ; set when <11.5V

MODE_CHARGE
bcf        BATT_IND,0        ; Batt.indicator LED off
; charge mode
; measure solar cell voltage
; AN2
; solar cell voltage is divided by a factor of 0.176

; set analog input address
bcf        ADCON0,5
bsf        ADCON0,4
bcf        ADCON0,3        ;
call    DEL_AD
movf    DIGITAL,w
movwf    CELL_LO            ; solar cell voltage ls byte
movf    ADRESH,w        ; ms byte
movwf    CELL_HI            ; solar cell voltage ms byte

; start charge when solar cell has sufficient voltage. ie about 12V
; check if above 12V D432, H1B0 (solar cell voltage is divided by a factor of 0.176)
movf    CELL_HI,w        ; high byte of solar call voltage
sublw    H'1'            ; take from
movwf    DIGITAL            ; store
movf    CELL_LO,w        ; low byte
sublw    H'B0'            ; 12V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then >12V
goto    NO_CHARGE        ; charge off till solar cell ready
clrf    HOUR3            ; prevents return to Bulk charge if this is before the end of 4hr timer

; charge; check charge status. Bulk charge or absorption/ float (trickle)
clrf    CHARGE_FLAG        ; charging flag
movf    CHARGE_STATE,w    ; 0 Bulk, 1 float
btfsc    STATUS,Z
goto    CHARGE_FULL
goto    CHARGE_ABSORP_FLOAT

NO_CHARGE
bsf        CHARGE_FLAG,0    ; no charge flag
clrf    CCPR1L            ; charge off
clrf    FIRST
movlw    D'5'            ; 5 x 0.262ms
movwf    PERIOD

; check if 4-hour timer is set
movf    HOUR3,w            ; if zero then not yet set
btfss    STATUS,Z
goto    CYCLE
; not set
bcf        STATUS,GIE        ; stop interrupt
; set timer
movlw     H'FF'            ; 256 counts for 4 hours
; ** test timer, remark out for normal 4 hours
;    movlw    H'1'            ; ** about 56 seconds timer. For use as a shorter test period
; **
movwf    HOUR3            ; set 4 hour timer
clrf    HOUR2
bsf        STATUS,GIE        ; allow interrupt
goto    CYCLE

CHARGE_FULL; (CHARGE_STATE = 0) main bulk charge

TEST_BATT2
; compare battery voltage (BATT_HI/LO) with cutout (CUT_COMP_M/L)
movf    BATT_HI,w        ; high byte of battery voltage
subwf    CUT_COMP_M,w    ; take from compensated cutout
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
subwf    CUT_COMP_L,w    ; take from compensated cutout
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then battery is over cutout voltage
goto    FULL_POWER_RUN

; end of bulk charge

btfsc    BULK_TIMER_END,0; bulk timer ended flag. If not set, then bulk charge occurred in <60s
; so clear timer 0 so charging goes to float rather than absorption
goto    ABS_TME
clrf    HOUR0            ; stop absorption charge
clrf    BULK_TIMER_END    ; bulk timer ended flag
goto    BY_SET            ; bypass setting the timer for absorption

ABS_TME ; absorption timer set
clrf    CCPR1L
clrf    HOUR1            ; 14 seconds counter
movlw     H'FF'            ; 256 counts for 1 hour
; ** test timer, remark out for normal 1hour
;    movlw    H'4'            ; ** about 42-56 seconds timer. For use as a shorter test period
; **
movwf    HOUR0            ; set absorption period @ 1 hour
BY_SET
movlw    D'5'            ; 5 x 0.262ms
movwf    PERIOD
goto    CHARGE_ABSORP_FLOAT

; full charge; maintain max power from solar cell during bulk charging
; periodically alter the duty cycle to find power maximum
; store CCPR1L value that provides max power

FULL_POWER_RUN
; set 60s timer for bulk charge
movf    CHARGE_STATE,w    ; check if bulk
btfss    STATUS,Z
goto    BY_BULK_TIMER     ; absorption or float so bypass bulk timer
movf    BULK_TIMER,w    ; check if timer running
btfss    STATUS,Z
goto     BY_BULK_TIMER
movlw    D'229'            ; 0.262ms x 229 = 60s
movwf    BULK_TIMER        ; decreased in interrupt to zero and sets bulk timer ended flag

BY_BULK_TIMER
movf    PERIOD,w        ; when zero check for maximum power
btfss    STATUS,Z        ;
goto    CYCLE
movf    FIRST,w            ; first run
btfss    STATUS,Z        ; when clear
goto    CH_3AD
clrf    POWERH            ; power
clrf    POWERL
clrf    CCPR1L            ; drive cleared
call    DELAY2            ; time for current reading to drop
clrf    CCPR1_STORE
movlw    H'FF'
movwf    FIRST            ; sets first run

; measure solar cell current
; AN3
CH_3AD
; solar cell current (1V=2.21A)

; set analog input address
bcf        ADCON0,5
bsf        ADCON0,4
bsf        ADCON0,3        ;
call    DEL_AD
movf    DIGITAL,w
movwf    CELL_I_LO        ; solar cell current ls byte
movf    ADRESH,w        ; ms byte
movwf    CELL_I_HI        ; solar cell current ms byte

; AN2
CH_2AD
; solar cell voltage is divided by a factor of 0.176

; set analog input address
bcf        ADCON0,5
bsf        ADCON0,4
bcf        ADCON0,3        ;
call    DEL_AD
movf    DIGITAL,w
movwf    CELL_LO            ; solar cell voltage ls byte
movf    ADRESH,w        ; ms byte
movwf    CELL_HI            ; solar cell voltage ms byte

; multiply CELL_HI, CELL_LO by CELL_I_HI, CELL_I_LO check for maximum

; set at 8-bit
; solar cell voltage
rrf        CELL_HI,w        ; shift ms byte right and store
movwf    DIGITAL
rrf        CELL_LO,w        ; shift ls byte and store
movwf    CELL_V
rrf        DIGITAL,w        ; shift stored ms byte
rrf        CELL_V,f        ; shift stored ls byte
; solar cell current
rrf        CELL_I_HI,w
movwf    DIGITAL
rrf        CELL_I_LO,w
movwf    CELL_I
rrf        DIGITAL,w
rrf        CELL_I,f

; multiply
movf    CELL_V,w
movwf    AARGB0
movf    CELL_I,w
movwf    BARGB0
call    EIGHTEIGHT

; result is in AARGB0,AARGB1
; compare with POWERH,POWERL
movf    AARGB0,w        ; high byte of power new
subwf    POWERH,w        ; take from last power value
movwf    DIGITAL            ; store
movf    AARGB1,w        ; low byte
subwf    POWERL,w        ; take from last power value
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then new power > old power
goto    INC_CHRG        ; increase PWM

TRANSFER ; transfer values
; new power is larger than last, transfer to POWERH,POWERL
; transfer CCPR1L into CCPR1_STORE
movf    AARGB0,w
movwf    POWERH
movf    AARGB1,w
movwf    POWERL
movf    CCPR1L,w
movwf    CCPR1_STORE

INC_CHRG
; increase charge
movf    CCPR1L,w        ; check if maximum
sublw    D'64'
btfss    STATUS,C        ; if maximum value end of test cycle
goto    MAX                ; bypass decrease if 0
incf    CCPR1L,f
call    DELAY3              ; allow time to change at I and V inputs
goto    CYCLE
MAX
; when max power found, set next timer for test period
movlw    D'76'            ; D76 = 20s (0.262ms per bit)
movwf    PERIOD            ; next 20s
clrf    FIRST            ; so runs with first values at start
movf    CCPR1_STORE,w
movwf    CCPR1L            ; set full charge rate at maximum power
goto    CYCLE

CHARGE_ABSORP_FLOAT; (CHARGE_STATE = 1)
movlw    D'01'
movwf    CHARGE_STATE    ; absorption/float mode set when coming from full mode
clrf    BULK_TIMER_END    ; bulk timer ended flag

; Measure battery Voltage
; set analog input address
bcf        ADCON0,5
bcf        ADCON0,4
bcf        ADCON0,3        ;
call    DEL_AD
movf    DIGITAL,w
movwf    BATT_LO            ; battery voltage ls byte
movf    ADRESH,w        ; ms byte
movwf    BATT_HI            ; battery voltage ms byte

; if battery voltage is D1023 (H3FF) then over in value
movf    BATT_HI,w        ; ms byte
xorlw    H'03'            ; 3
btfss    STATUS,Z
goto    TEST_BATT3        ; not equal
movf    BATT_LO,w
xorlw    H'FF'
btfsc    STATUS,Z
goto    NO_CHARGE        ; battery voltage high so end charge

TEST_BATT3
; alter CHARGE_CONT to adjust the drive to maintain float V

; set values to 8-bit
; battery
rrf        BATT_HI,w
movwf    VALUE1
rrf        BATT_LO,w        ; get value difference
movwf    VALUE2
rrf        VALUE1,w
rrf        VALUE2,f

; check hour timer. If still timing then absorption. if timer ended (hour0=0) then float
movf    HOUR0,w
btfsc    STATUS,Z
goto    FLOAT_VALUES

; check Equalisation or Absorption
btfsc    EQ_LEVEL,4        ; when low run equalisation unless already run (EQ_FLAG,0 set)
goto    ABSORPTION_VALUES
btfsc    EQ_FLAG,0        ; if flag is set equalisation has already run so bypass to absorption
goto    ABSORPTION_VALUES

EQUALISATION_VALUES
bsf        EQ_FLAG,1        ; equalisation started flag so LED lights
; equalisation
rrf        EQ_HI_COMP,w
movwf    VALUE3
rrf        EQ_LO_COMP,w    ; get value difference
movwf    VALUE4
rrf        VALUE3,w
rrf        VALUE4,f

; compare battery with compensated cutoff V
movf    BATT_HI,w        ; high byte of battery voltage
subwf    EQ_HI_COMP,w     ; take from compensated equalisation Voltage
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
subwf    EQ_LO_COMP,w    ; take from compensated V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then battery voltage is over cut voltage
goto    PWM_CONTROL        ; control power
goto    DECREASE_CHARGE

ABSORPTION_VALUES
bcf        EQ_FLAG,1        ; clear so equaliser LEDs off
; absorption
rrf        CUT_COMP_M,w
movwf    VALUE3
rrf        CUT_COMP_L,w    ; get value difference
movwf    VALUE4
rrf        VALUE3,w
rrf        VALUE4,f

; compare battery with compensated cutoff V
movf    BATT_HI,w        ; high byte of battery voltage
subwf    CUT_COMP_M,w     ; take from compensated cut V
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
subwf    CUT_COMP_L,w    ; take from compensated cut V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfss    DIGITAL,7        ; if set then battery voltage is over cut voltage
goto    PWM_CONTROL        ; control power
goto    DECREASE_CHARGE

FLOAT_VALUES
bsf        EQ_FLAG,0        ; equalisation no run flag set so no further equalisation occurs until reset
btfsc    EQ_FLAG,1        ; if running flag set set bit 2
bsf        EQ_FLAG,2        ; allows EQ LED to flash
bcf        EQ_FLAG,1        ; equalisation flag for eq running indicator LED
; float
rrf        FLOAT_COMP_M,w
movwf    VALUE3
rrf        FLOAT_COMP_L,w    ; get value difference
movwf    VALUE4
rrf        VALUE3,w
rrf        VALUE4,f

; compare battery with float V
movf    BATT_HI,w        ; high byte of battery voltage
subwf    FLOAT_COMP_M,w     ; take from compensated float
movwf    DIGITAL            ; store
movf    BATT_LO,w        ; low byte
subwf    FLOAT_COMP_L,w    ; take from compensated float V
btfss    STATUS,C
decf    DIGITAL,f        ; decrease if required
btfsc    DIGITAL,7        ; if set then battery voltage is over cut voltage
goto    DECREASE_CHARGE

; check RB1 for return to bulk charge after drawing current when in float
btfsc    PORTB,1
goto    PWM_CONTROL

; compare float control with previous CCPR1 storage during bulk charge

movf    CCPR1_STORE,w
subwf    CCPR1L,w
btfss    STATUS,C
goto    PWM_CONTROL        ;
clrf    CHARGE_STATE    ; set for bulk/main charging
clrf    BULK_TIMER
clrf    BULK_TIMER_END    ; bulk timer ended flag
goto    CYCLE

; control of PWM other than for full power
PWM_CONTROL
movf    CCPR1L,w
subwf    CCPR1_STORE,w
btfss    STATUS,C        ; if over max power point PWM value go to FULL POWER mode
goto    FULL_POWER_RUN
btfsc    STATUS,Z
goto    FULL_POWER_RUN
incf    CCPR1L,w
sublw    D'64'
btfss    STATUS,C
goto    FULL_POWER_RUN
incf    CCPR1L,f
goto    CYCLE

; decrease charge
DECREASE_CHARGE
movf    VALUE4,w        ; absorption/float 8-bit
subwf    VALUE2,f        ; battery 8-bit
; if >3 switch off charge
movf    VALUE2,w
sublw    D'3'
btfss    STATUS,C
goto    CLEAR_CHRG        ; over

movf    VALUE2,w        ; subtracted value
subwf    CCPR1L,w
btfss    STATUS,C        ; if negative set at 0
clrw
movwf    CCPR1L            ; reduce charge
goto    CYCLE
CLEAR_CHRG
clrf    CCPR1L
goto    CYCLE

BURST
bsf        BURST_FLG,0        ; set flag
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off

; burst pulses of current till voltage >10.5V
movf    FLASHER,w        ; flasher counter (0-255 count)
andlw    B'00001111'        ;
; only burst is on for 1 in 16 (6.25% on duty cycle) at a 4.2s rate
btfss    STATUS,Z
goto    BURST_OFF
; Burst on
bsf        PORTA,7            ; Bulk LED on
movlw    D'64'
movwf    CCPR1L            ; set full charge
goto    CYCLE

BURST_OFF
bcf        PORTA,7            ; Bulk LED off
clrf    CCPR1L
goto    CYCLE

;***************************************************

; INTERRUPT
; start interrupt by saving w and status registers

INTERRUPT
movwf    W_TMP            ; w to w_tmp storage
swapf    STATUS,w        ; status to w
movwf    STATUS_TMP        ; status in status_tmp
bcf        STATUS,RP0        ; bank select
bcf     STATUS,RP1        ; select memory bank 0

bcf        PIR1,TMR1IF        ; clear flag

FLASH1
; flasher rate (increases every 0.262ms)
incf    FLASHER,f        ; flasher

; rate for power calculation during charging
movf    PERIOD,w        ; period for charging power cycle
btfss    STATUS,Z
decf    PERIOD,f        ; decrease if not 0

; flash NTC Thermistor LED(PORTB,5)if required
; check thermistor
btfss    THERMISTOR,0    ; when set, thermistor is out of circuit
goto    CK_THERM1
; charge LEDs off
bcf        PORTA,7            ; Bulk LED off
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
; flash NC thermistor LED
movf    FLASHER,w
andlw    B'00001111'        ; flash 262ms on each 2 seconds
btfsc    STATUS,Z        ;
goto    SET_OUT
bcf        PORTB,5            ; NTC thermistor LED off
goto    CHECK_END
SET_OUT
bsf        PORTB,5            ; NTC thermistor LED on
goto    CHECK_END

CK_THERM1
btfss    THERMISTOR,1    ; when set, thermistor is shorted
goto    CHARGE_CONDITION
; charge LEDs off
bcf        PORTA,7            ; Bulk LED off
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
; flash thermistor LED
movf    FLASHER,w
andlw    B'00000111'        ; flash 262ms on each 1 second
btfsc    STATUS,Z
goto    SET_OUT1
bcf        PORTB,5            ; NTC thermistor LED off
goto    CHECK_END
SET_OUT1
bsf        PORTB,5            ; NTC thermistor LED on
goto    CHECK_END

CHARGE_CONDITION
; check battery error
btfss    BATT_IND,0        ; when set flash Bulk LED
goto    CHARGE_STATES
; flash Bulk LED
; charging LEDs off
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
; flash LED
movf    FLASHER,w
andlw    B'00000111'        ; flash 262ms on each 1 second
btfsc    STATUS,Z
goto    SET_OUT_CHRG
bcf        PORTA,7            ; LED off
goto    CHECK_END
SET_OUT_CHRG
bsf        PORTA,7            ; LED on
goto    CHECK_END

CHARGE_STATES
movf    CHARGE_FLAG,w    ; if zero then charging
btfss    STATUS,Z
goto    OFF_CHRG

; check burst
btfsc    BURST_FLG,0        ; if set burst mode
goto    BURST_IND

; check main or trickle
btfss    CHARGE_STATE,0    ; clear then main Bulk
goto    MAIN_LED

; if HOUR0 is clear then float. Otherwise absorption
movf    HOUR0,w
btfsc    STATUS,Z
goto    FLOAT
;
; Absorption or Equalisation
btfss    EQ_FLAG,1        ; when set Equalisation in process
goto    ABSORPTION
EQUALISATION
bcf        PORTB,5            ; NTC thermistor LED off
bcf        PORTA,7            ; Bulk LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTA,6            ; Absorption LED off
bsf        PORTB,6            ; Equalisation LED on
goto    CHECK_END
ABSORPTION
bcf        PORTB,5            ; NTC thermistor LED off
bcf        PORTA,7            ; Bulk LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
bsf        PORTA,6            ; Absorption LED on
goto    CHECK_END
;
FLOAT
bcf        PORTB,5            ; NTC thermistor LED off
bcf        PORTA,7            ; Bulk LED off
bsf        PORTB,7            ; Float LED on
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,6            ; Equalisation LED off
goto    CHECK_END
;
MAIN_LED ;(main Bulk)
bcf        PORTB,5            ; NTC thermistor LED off
bsf        PORTA,7            ; Bulk LED on
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
goto    CHECK_END
;
OFF_CHRG
; charge LEDs off
bcf        PORTA,7            ; Bulk LED off
BURST_IND ; for burst indication
bcf        PORTA,6            ; Absorption LED off
bcf        PORTB,7            ; Float LED off
bcf        PORTB,6            ; Equalisation LED off
; flash Equalisation LED once run
btfss    EQ_FLAG,2        ; equalisation run flag
goto    HOUR_TIMER
movf    FLASHER,w
andlw    B'00001111'        ; flash 262ms on each 2 seconds
btfss    STATUS,Z
goto    OFF_TIMER
bsf        PORTB,6            ; equalisation LED flashes
goto    OFF_TIMER        ; bypass hour timer when charge is off

CHECK_END

; flash Equalisation LED once run
btfss    EQ_FLAG,2        ; equalisation run flag
goto    HOUR_TIMER
movf    FLASHER,w
andlw    B'00001111'        ; flash 262ms on each 2 seconds
btfss    STATUS,Z
goto    HOUR_TIMER
bsf        PORTB,6            ; equalisation LED flashes

; hour timer
HOUR_TIMER
incf    HOUR1,f            ; seconds
movf    HOUR1,w
sublw    D'52'            ; 14 seconds
btfsc    STATUS,C
goto    BULK_TIME
clrf    HOUR1            ; cleared when 14-seconds reached

movf    HOUR0,w            ; starts at 255 so 256 x 14s = 1 hour
btfss    STATUS,Z        ; check if zero
decf    HOUR0,f            ; no decrease if already 0

; decrease bulk timer (used to check if bulk charge < 1 minute
BULK_TIME
movf    BULK_TIMER,w
btfss    STATUS,Z
decfsz    BULK_TIMER,f    ; decrease til zero
goto    OFF_TIMER
bsf        BULK_TIMER_END,0; bulk timer ended flag

; solar panel 4-hour off timer
OFF_TIMER
; off timer is included if RB0 is low
btfsc    PORTB,0
goto     RECLAIM
incf    HOUR2,f            ; seconds
movf    HOUR2,w
sublw    D'208'            ; 56 seconds
btfsc    STATUS,C
goto    RECLAIM
clrf    HOUR2            ; cleared when 56-seconds reached

movf    HOUR3,w            ; starts at 255 so 256 x 56s = 4 hour
btfsc    STATUS,Z        ; check if zero
goto    RECLAIM            ; no decrease if already 0
decfsz    HOUR3,f            ;
goto    RECLAIM
clrf    CHARGE_STATE    ; set to Bulk charge after 4 hours off
clrf    BULK_TIMER
clrf    BULK_TIMER_END    ; bulk timer ended flag

RECLAIM
; end of interrupt reclaim w and status
swapf    STATUS_TMP,w; status temp storage to w
movwf    STATUS        ; w to status register
swapf    W_TMP,f        ; swap upper and lower 4-bits in w_tmp
swapf   W_TMP,w        ; swap bits and into w register
retfie                ; return from interrupt

;**************************************************************
; Subroutines

; delays

; DELAY for A/D acquisition
DEL_AD
;    movlw    D'50'            ; * 4MHz operation
movlw    D'100'            ; ** 8MHz operation
movwf    DELCNT
DEL1
decfsz    DELCNT,f
goto    DEL1

bsf        ADCON0,2        ; GO/DONE bit start conversion
WAIT_CONV1
btfsc    ADCON0,2        ; conversion complete when cleared ~11 cycles
goto    WAIT_CONV1
bsf        STATUS,RP0        ; select memory bank 1
movf    ADRESL,w        ; ls bits
bcf        STATUS,RP0        ; select memory bank 0
movwf    DIGITAL
return

; delay general purpose debounce switch 500ms
DELAY
;   movlw    D'2'        ; * 4MHz operation
movlw    D'4'        ; ** 8MHz operation number of delay cycles
DELAY_1
movwf    DELCNT

DELAY_M
movlw    D'255'        ; set delay period
movwf    VALUE_1        ; VALUE_1 = w
LP_1
movlw    D'255'        ; set delay period value 2
movwf    VALUE_2        ; VALUE_2 = w
LP_2
decfsz    VALUE_2,f    ; decrease VALUE_2, skip if zero
goto     LP_2
decfsz    VALUE_1,f    ; decrease VALUE_1, skip if zero
goto    LP_1
decfsz    DELCNT,f
goto    DELAY_M
return

DELAY2
;    movlw    D'4'        ; * 4MHz operation set delay period
movlw    D'8'        ; ** 8MHz operation set delay period
movwf    VALUE_1        ; VALUE_1 = w
LP_3
movlw    D'255'        ; set delay period value 2
movwf    VALUE_2        ; VALUE_2 = w
LP_4
decfsz    VALUE_2,f    ; decrease VALUE_2, skip if zero
goto     LP_4
decfsz    VALUE_1,f    ; decrease VALUE_1, skip if zero
goto    LP_3
return

DELAY3
;    movlw    D'116'        ; * 4MHz operation
movlw    D'58'        ; ** 8MHz operation set delay period value 2 for 50ms max power calc period
movwf    VALUE_2        ; VALUE_2 = w
LP_5
decfsz    VALUE_2,f    ; decrease VALUE_2, skip if zero
goto     LP_5
return

;*********************************
; 24x24 Bit Unsigned Fixed Point Multiply 24x24 -> 48
; Input: 24 bit unsigned fixed point multiplicand in AARGB0,1,2
; 24 bit unsigned fixed point multiplier in BARGB0,1,2
; Use: CALL FXM2424U
; Output: 48 bit unsigned fixed point product in AARGB0
; Result: AARG <-- AARG x BARG
; Max Timing: 9+501+2 = 512 clks
; Min Timing: 9+150 = 159 clks

FXM2424U
CLRF     AARGB3 ; clear partial product
CLRF     AARGB4
CLRF     AARGB5
MOVF     AARGB0,W
MOVWF     TEMPB0
MOVF     AARGB1,W
MOVWF     TEMPB1
MOVF     AARGB2,W
MOVWF     TEMPB2

MOVLW     H'08'
MOVWF     LOOPCOUNT
LOOPUM2424A
RRF     BARGB2,F
BTFSC     STATUS,C
GOTO     ALUM2424NAP
DECFSZ     LOOPCOUNT,F
GOTO    LOOPUM2424A
MOVWF     LOOPCOUNT
LOOPUM2424B
RRF     BARGB1,F
BTFSC     STATUS,C
GOTO    BLUM2424NAP
DECFSZ    LOOPCOUNT,F
GOTO    LOOPUM2424B
MOVWF    LOOPCOUNT
LOOPUM2424C
RRF        BARGB0,F
BTFSC     STATUS,C
GOTO     CLUM2424NAP
DECFSZ     LOOPCOUNT,F
GOTO     LOOPUM2424C
CLRF     AARGB0
CLRF     AARGB1
CLRF     AARGB2
RETLW     0x00
CLUM2424NAP
BCF     STATUS,C
GOTO     CLUM2424NA
BLUM2424NAP
BCF     STATUS,C
GOTO     BLUM2424NA
ALUM2424NAP
BCF     STATUS,C
GOTO     ALUM2424NA
ALOOPUM2424
RRF     BARGB2,F
BTFSS     STATUS,C
GOTO     ALUM2424NA
MOVF     TEMPB2,W
ADDWF     AARGB2,F
MOVF     TEMPB1,W
BTFSC     STATUS,C
INCFSZ     TEMPB1,W
ADDWF     AARGB1,F
MOVF     TEMPB0,W
BTFSC     STATUS,C
INCFSZ     TEMPB0,W
ADDWF    AARGB0,F
ALUM2424NA
RRF     AARGB0,F
RRF     AARGB1,F
RRF     AARGB2,F
RRF     AARGB3,F
DECFSZ     LOOPCOUNT,F
GOTO     ALOOPUM2424
MOVLW     H'08'
MOVWF     LOOPCOUNT
BLOOPUM2424
RRF     BARGB1,F
BTFSS     STATUS,C
GOTO     BLUM2424NA
MOVF     TEMPB2,W
ADDWF     AARGB2,F
MOVF     TEMPB1,W
BTFSC     STATUS,C
INCFSZ     TEMPB1,W
ADDWF     AARGB1,F
MOVF     TEMPB0,W
BTFSC     STATUS,C
INCFSZ     TEMPB0,W
ADDWF     AARGB0,F
BLUM2424NA
RRF     AARGB0,F
RRF     AARGB1,F
RRF     AARGB2,F
RRF     AARGB3,F
RRF     AARGB4,F
DECFSZ     LOOPCOUNT,F
GOTO     BLOOPUM2424
MOVLW     H'08'
MOVWF     LOOPCOUNT
CLOOPUM2424
RRF     BARGB0,F
BTFSS     STATUS,C
GOTO     CLUM2424NA
MOVF     TEMPB2,W
ADDWF     AARGB2,F
MOVF     TEMPB1,W
BTFSC     STATUS,C
INCFSZ     TEMPB1,W
ADDWF     AARGB1,F
MOVF     TEMPB0,W
BTFSC     STATUS,C
INCFSZ     TEMPB0,W
ADDWF     AARGB0,F
CLUM2424NA
RRF     AARGB0,F
RRF     AARGB1,F
RRF     AARGB2,F
RRF     AARGB3,F
RRF     AARGB4,F
RRF     AARGB5,F
DECFSZ     LOOPCOUNT,F
GOTO     CLOOPUM2424
return

;********************************
; 32/32 Bit Unsigned Fixed Point Divide 32/32 -> 32.32
; Input: 32 bit unsigned fixed point dividend in AARGB0, AARGB1,AARGB2,AARGB3
; 32 bit unsigned fixed point divisor in BARGB0, BARGB1, BARGB2, BARGB3
; Use: CALL FXD3232U
; Output: 32 bit unsigned fixed point quotient in AARGB0, AARGB1,AARGB2,AARGB3
; 32 bit unsigned fixed point remainder in REMB0, REMB1, REMB2, REMB3
; Result: AARG, REM <-- AARG / BARG
; Max Timing: 4+1025+2 = 1031 clks
; Max Timing: 4+981+2 = 987 clks
; PM: 4+359+1 = 364 DM: 13
FXD3232U
CLRF     REMB0
CLRF    REMB1
CLRF     REMB2
CLRF     REMB3
call    UDIV3232L
return

UDIV3232L
; Max Timing: 24+6*32+31+31+6*32+31+31+6*32+31+31+6*32+31+16 = 1025 clks
; Min Timing: 24+6*31+30+30+6*31+30+30+6*31+30+30+6*31+30+3 = 981 clks
; PM: 359 DM: 13
CLRF     TEMP
RLF     AARGB0,W
RLF     REMB3,F
MOVF     BARGB3,W
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS     STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
RLF     AARGB0,F
MOVLW     H'7'
MOVWF     LOOPCOUNT

LOOPU3232A
RLF     AARGB0,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS     AARGB0,0
GOTO     UADD22LA
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS    STATUS,C
INCFSZ    BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22LA

UADD22LA
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC    STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF     TEMP,F

UOK22LA
RLF        AARGB0,F
DECFSZ     LOOPCOUNT,F
GOTO     LOOPU3232A
RLF     AARGB1,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS     AARGB0,0
GOTO     UADD22L8
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS     STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22L8

UADD22L8
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC     STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF     TEMP,F

UOK22L8
RLF     AARGB1,F
MOVLW     H'7'
MOVWF     LOOPCOUNT

LOOPU3232B
RLF     AARGB1,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS     AARGB1,0
GOTO     UADD22LB
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS     STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22LB

UADD22LB
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC     STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF     TEMP,F

UOK22LB
RLF     AARGB1,F
DECFSZ     LOOPCOUNT,F
GOTO     LOOPU3232B
RLF     AARGB2,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS    AARGB1,0
GOTO     UADD22L16
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS    STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22L16

UADD22L16
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC     STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF    TEMP,F

UOK22L16
RLF        AARGB2,F
MOVLW     H'7'
MOVWF     LOOPCOUNT

LOOPU3232C
RLF     AARGB2,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS     AARGB2,0
GOTO     UADD22LC
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS    STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22LC

UADD22LC
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC     STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF     TEMP,F

UOK22LC
RLF     AARGB2,F
DECFSZ     LOOPCOUNT,F
GOTO     LOOPU3232C
RLF     AARGB3,W
RLF     REMB3,F
RLF     REMB2,F
RLF     REMB1,F
RLF     REMB0,F
RLF     TEMP,F
MOVF     BARGB3,W
BTFSS     AARGB2,0
GOTO     UADD22L24
SUBWF     REMB3,F
MOVF     BARGB2,W
BTFSS     STATUS,C
INCFSZ     BARGB2,W
SUBWF     REMB2,F
MOVF     BARGB1,W
BTFSS     STATUS,C
INCFSZ     BARGB1,W
SUBWF     REMB1,F
MOVF     BARGB0,W
BTFSS     STATUS,C
INCFSZ     BARGB0,W
SUBWF     REMB0,F
CLRW
BTFSS     STATUS,C
MOVLW     H'1'
SUBWF     TEMP,F
GOTO     UOK22L24

UADD22L24
ADDWF     REMB3,F
MOVF     BARGB2,W
BTFSC     STATUS,C
INCFSZ     BARGB2,W
ADDWF     REMB2,F
MOVF     BARGB1,W
BTFSC     STATUS,C
INCFSZ     BARGB1,W
ADDWF     REMB1,F
MOVF     BARGB0,W
BTFSC     STATUS,C
INCFSZ     BARGB0,W
ADDWF     REMB0,F
CLRW
BTFSC     STATUS,C
MOVLW     H'1'
ADDWF     TEMP,F

UOK22L24
RLF     AARGB3,F
MOVLW     H'7'
MOVWF     LOOPCOUNT

LOOPU3232D
RLF      AARGB3,W
RLF        REMB3,F
RLF        REMB2,F
RLF        REMB1,F
RLF        REMB0,F
RLF        TEMP,F
MOVF       BARGB3,W
BTFSS      AARGB3,0
GOTO       UADD22LD

SUBWF      REMB3,F
MOVF       BARGB2,W
BTFSS   STATUS,C
INCFSZ  BARGB2,W
SUBWF   REMB2,F
MOVF    BARGB1,W
BTFSS   STATUS,C
INCFSZ  BARGB1,W
SUBWF   REMB1,F
MOVF    BARGB0,W
BTFSS   STATUS,C
INCFSZ  BARGB0,W
SUBWF   REMB0,F
CLRW
BTFSS   STATUS,C
MOVLW   H'1'
SUBWF   TEMP,F
GOTO    UOK22LD

UADD22LD
ADDWF   REMB3,F
MOVF    BARGB2,W
BTFSC   STATUS,C
INCFSZ  BARGB2,W
ADDWF   REMB2,F
MOVF    BARGB1,W
BTFSC   STATUS,C
INCFSZ  BARGB1,W
ADDWF   REMB1,F
MOVF    BARGB0,W
BTFSC   STATUS,C
INCFSZ  BARGB0,W
ADDWF   REMB0,F
CLRW
BTFSC   STATUS,C
MOVLW   H'1'
ADDWF   TEMP,F

UOK22LD
RLF     AARGB3,F

DECFSZ  LOOPCOUNT, F
GOTO    LOOPU3232D

BTFSC   AARGB3,0
GOTO    UOK22L
MOVF    BARGB3,W
ADDWF   REMB3,F
MOVF    BARGB2,W
BTFSC   STATUS,C
INCFSZ  BARGB2,W
ADDWF   REMB2,F
MOVF    BARGB1,W
BTFSC   STATUS,C
INCFSZ  BARGB1,W
ADDWF   REMB1,F
MOVF    BARGB0,W
BTFSC   STATUS,C
INCFSZ  BARGB0,W
ADDWF   REMB0,F

UOK22L

RETURN

;******************
; 8 x 8 multiply

EIGHTEIGHT      CLRF    AARGB1          ; clear partial product
UMUL0808L
MOVLW   H'08'
MOVWF   LOOPCOUNT
MOVF    AARGB0,W

LOOPUM0808A
RRF     BARGB0, F
BTFSC   STATUS,C
GOTO    LUM0808NAP
DECFSZ  LOOPCOUNT, F
GOTO    LOOPUM0808A

CLRF    AARGB0
RETLW   H'00'

LUM0808NAP
BCF     STATUS,C
GOTO    LUM0808NA

LOOPUM0808
RRF     BARGB0, F
BTFSC   STATUS,C
ADDWF   AARGB0, F
LUM0808NA       RRF        AARGB0, F
RRF        AARGB1, F
DECFSZ  LOOPCOUNT,F
GOTO    LOOPUM0808

return

end

.HEX

:020000040000FA
:020000009E2838
:080008001D2B82073C343B3440
:100010003A343A343934393438343834373436347D
:100020003634353435343434343433343234323491
:10003000323431343134303430342F342F342E34A0
:100040002E342D342D342C342C342B342B342B34AF
:100050002A342A34293429342834283428342734BB
:1000600027342634263426342534253424342434C5
:1000700024342334233422342234223421342134CE
:1000800021342034203420341F341F341E341E34D5
:100090001E341D341D341D341C341C341C341B34DC
:1000A0001B341B341A341A341A34193419341834E2
:1000B00018341834183417341734173416341634E7
:1000C00016341534153415341434143414341334EC
:1000D00013341334123412341234113411341134F1
:1000E0001034103410340F340F340F340E340E34F7
:1000F0000E340D340D340D340C340C340C340B34FC
:100100000B340B340A340A340A3409340934083401
:100110000834083407340734073406340634063408
:100120000534053404340434043403340334023411
:100130000234023401340134003400348601850174
:10014000831607309C00133086003F30850007304F
:1001500081001F309B00C0309F00831240309F0001
:100160001F14831678308F008312313090001014E2
:1001700083163F3092008312950117129712920155
:1001800012150C30970086018501AC010130B600D4
:10019000B700BC01BD010530C100C201A101C3010E
:1001A000C401AA01BE01B401B501D201D101C90146
:1001B0004914CE01CF01D001D301D4010330A200F4
:1001C0009830A3000330A4005F30A5000330CB00BB
:1001D000F430CA0083160C1483120C100B178B1703
:1001E000B60BD129B70BD1290830B700061EFD2860
:1001F0004E1E1229C901491417294E1E1729B6236C
:10020000061A1729CE01C9018B138513861305130E
:100210000617B6230613B6230617B6230613B6230E
:100220008B171729B623061E17294E1649149F123D
:100230001F129F15AA239E0CA00C9E0CA00C200838
:10024000A6009F161F129F11AA239E0CA00C9E0CA5
:10025000A00C2008FA3C8B13AC01031831292C1494
:100260003A292008C73CC730031CA00004302002F4
:10027000031CAC148B172008C73CC830031CA0001B
:100280003130A002031CA00120080520AB00B10101
:1002900014302B02031CB117EA00B11F52292B089E
:1002A000143CEA002608EE00B0256908E8006A0858
:1002B000E900EA016430EC00ED01EE01D1236808A9
:1002C000EA006708E9006608E8006508E700EE0153
:1002D000ED011F30EC004030EB003824B11B8729C2
:1002E00067082302AE002208031C2203AD00AD1FE5
:1002F0007B29AD01AE0167084A02CC004B08031C04
:100300004B03CD00CD1FA129CD01CC01A12922088D
:10031000AD0067082307AE000318AD0A2D1D942910
:100320000330AD00FF30AE004B08CD0067084A0730
:10033000CC000318CD0A4D1DA1290330CD00FF309C
:10034000CC00B11BAF2967082502B0002408031CAC
:100350002403AF00AF1FBC29AF01B001BC292408A2
:10036000AF0067082507B0000318AF0A2F1DBC298E
:100370000330AF00FF30B0009F121F129F11AA235D
:100380002008A8001E08A7002708033CA000280892
:100390001C3C031CA003A01BD029BC01D301D40129
:1003A0003E14AC1CD5293D14202A2C1CD9293D14FF
:1003B000202A2708023CA00028089F3C031CA00319
:1003C000A01F0E2BD001C91CE7290615082A2708F3
:1003D000033CA0002808BF3C031CA003A01FF42975
:1003E0003D144F140615202A2708023CA0002808B7
:1003F000FF3C031CA003A01FFF290611082A2708A1
:10040000023CA0002808DF3C031CA003A01F061527
:100410004F109F121F169F11AA232008B8001E0814
:10042000B9003908013CA0003808B03C031CA00307
:10043000A01F202AD101BD013C0803192E2A952AAC
:100440003D149501A1010530C1005108031DF0289C
:100450008313FF30D100D2018317F02827082D0223
:10046000A00028082E02031CA003A01F432A541832
:100470003C2AB401D401402A9501B501FF30B400F3
:100480000530C100952A3C08031D4B2A5308031D63
:100490004B2AE530D3004108031DF0282108031D35
:1004A000582AC301C4019501C323C201FF30A10032
:1004B0009F121F169F15AA232008BA001E08BB0012
:1004C0009F121F169F11AA232008B8001E08B9000A
:1004D000390CA000380CBF00200CBF0C3B0CA00056
:1004E0003A0CC000200CC00C3F08EA004008EE00A7
:1004F000B0256A084302A00069084402031CA00357
:10050000A01F882A6A08C3006908C4001508C20031
:100510001508403C031C8F2A950ACC23F0284C3048
:10052000C100A10142089500F0280130BC00D401AF
:100530009F121F129F11AA232008A8001E08A700BF
:100540002708033A031DA82A2808FF3A0319202A7E
:10055000270CC500280CC600450CC60C340803192E
:10056000D72A4E1AC62A4918C62AC9144D0CC700E4
:100570004C0CC800470CC80C27084D02A0002808E6
:100580004C02031CA003A01FF42A002BC9102D0C41
:10059000C7002E0CC800470CC80C27082D02A0006D
:1005A00028082E02031CA003A01FF42A002B4914C4
:1005B000C9184915C9102F0CC700300CC800470CCA
:1005C000C80C27082F02A00028083002031CA00333
:1005D000A01B002B8618F42A42081502031CF42ADB
:1005E000BC01D301D401F02815084202031C432AA0
:1005F0000319432A150A403C031C432A950AF02894
:100600004808C6024608033C031C0C2B460815028A
:10061000031C03019500F0289501F02850140513E0
:10062000861306132A080F39031D1A2B851740302D
:100630009500F02885139501F028F000030EF100D5
:10064000831203130C10AA0A4108031DC1032C1CBA
:10065000352B85130513861306132A080F39031942
:10066000332B8612802B8616802BAC1C432B8513D4
:100670000513861306132A0807390319412B86121E
:10068000802B8616802B4F1C502B051386130613C8
:100690002A08073903194E2B8513802B8517802BC9
:1006A0003D08031D742B5018752B3C1C6E2B340811
:1006B0000319682BC91C622B861285138613051338
:1006C0000617802B86128513861306130517802BB9
:1006D00086128513861705130613802B861285173D
:1006E000051386130613802B851305138613061333
:1006F000491D872B2A080F39031D952B0617952BAB
:10070000491D872B2A080F39031D872B0617B50AA9
:100710003508343C0318902BB5013408031DB4038D
:100720005308031DD30B952B54140618A52BD20A7E
:100730005208D03C0318A52BD20151080319A52B50
:10074000D10BA52BBC01D301D401710E8300F00E97
:10075000700E09006430A900A90BAC2B1F151F19DE
:10076000AF2B83161E088312A00008000430A900D6
:10077000FF30B200FF30B300B30BBC2BB20BBA2B6F
:10078000A90BB82B08000830B200FF30B300B30B40
:10079000C72BB20BC52B08003A30B300B30BCE2BDE
:1007A0000800E701E601E5016A08DD006908DE00EE
:1007B0006808DF000830EF00EC0C0318F52BEF0B96
:1007C000DC2BEF00ED0C0318F32BEF0BE22BEF000B
:1007D000EE0C0318F12BEF0BE82BEA01E901E8011D
:1007E000003403102F2C0310192C0310042CEC0CD4
:1007F000031C042C5F08E8075E0803185E0FE90776
:100800005D0803185D0FEA07EA0CE90CE80CE70C39
:10081000EF0BF72B0830EF00ED0C031C192C5F08D1
:10082000E8075E0803185E0FE9075D0803185D0F0F
:10083000EA07EA0CE90CE80CE70CE60CEF0B0C2CD1
:100840000830EF00EE0C031C2F2C5F08E8075E0851
:1008500003185E0FE9075D0803185D0FEA07EA0C4D
:10086000E90CE80CE70CE60CE50CEF0B222C080079
:10087000E401E301E201E1013E240800E0016A0D28
:10088000E10D6B08E1026C08031C6C0FE2026D08BD
:10089000031C6D0FE3026E08031C6E0FE4020301DC
:1008A000031C0130E002EA0D0730EF006A0DE10D94
:1008B000E20DE30DE40DE00D6B086A1C712CE10202
:1008C0006C08031C6C0FE2026D08031C6D0FE30241
:1008D0006E08031C6E0FE4020301031C0130E002EA
:1008E000822CE1076C0803186C0FE2076D080318EF
:1008F0006D0FE3076E0803186E0FE407030103187A
:100900000130E007EA0DEF0B562C690DE10DE20D09
:10091000E30DE40DE00D6B086A1CA02CE1026C08ED
:10092000031C6C0FE2026D08031C6D0FE3026E08DE
:10093000031C6E0FE4020301031C0130E002B12C22
:10094000E1076C0803186C0FE2076D0803186D0FC0
:10095000E3076E0803186E0FE40703010318013064
:10096000E007E90D0730EF00690DE10DE20DE30D41
:10097000E40DE00D6B08691CCF2CE1026C08031C30
:100980006C0FE2026D08031C6D0FE3026E08031C7E
:100990006E0FE4020301031C0130E002E02CE107CA
:1009A0006C0803186C0FE2076D0803186D0FE3075E
:1009B0006E0803186E0FE407030103180130E00707
:1009C000E90DEF0BB42C680DE10DE20DE30DE40D24
:1009D000E00D6B08691CFE2CE1026C08031C6C0F17
:1009E000E2026D08031C6D0FE3026E08031C6E0F1C
:1009F000E4020301031C0130E0020F2DE1076C0843
:100A000003186C0FE2076D0803186D0FE3076E08FB
:100A100003186E0FE407030103180130E007E80D27
:100A20000730EF00680DE10DE20DE30DE40DE00D80
:100A30006B08681C2D2DE1026C08031C6C0FE20290
:100A40006D08031C6D0FE3026E08031C6E0FE402B9
:100A50000301031C0130E0023E2DE1076C0803187E
:100A60006C0FE2076D0803186D0FE3076E0803189B
:100A70006E0FE407030103180130E007E80DEF0BE8
:100A8000122D670DE10DE20DE30DE40DE00D6B0895
:100A9000681C5C2DE1026C08031C6C0FE2026D08FF
:100AA000031C6D0FE3026E08031C6E0FE4020301CA
:100AB000031C0130E0026D2DE1076C0803186C0F78
:100AC000E2076D0803186D0FE3076E0803186E0F39
:100AD000E407030103180130E007E70D0730EF00DA
:100AE000670DE10DE20DE30DE40DE00D6B08671CF1
:100AF0008B2DE1026C08031C6C0FE2026D08031CD5
:100B00006D0FE3026E08031C6E0FE4020301031C69
:100B10000130E0029C2DE1076C0803186C0FE2071E
:100B20006D0803186D0FE3076E0803186E0FE407D6
:100B3000030103180130E007E70DEF0B702D671874
:100B4000AF2D6B08E1076C0803186C0FE2076D0806
:100B500003186D0FE3076E0803186E0FE407080013
:100B6000E9010830EF006A08EE0C0318BB2DEF0B0B
:100B7000B42DEA0100340310C02DEE0C0318EA076F
:0A0B8000EA0CE90CEF0BBD2D080094
:02400E00700F31
:02401000FC3F73
:00000001FF

Exit mobile version