; ***************************************************************
; * Copyright (c) 2001, Embed Inc (http://www.embedinc.com) *
; * *
; * Permission to copy this file is granted as long as this *
; * copyright notice is included in its entirety at the *
; * beginning of the file, whether the file is copied in whole *
; * or in part and regardless of whether other information is *
; * added to the copy. *
; * *
; * The contents of this file may be used in any way, *
; * commercial or otherwise. This file is provided "as is", *
; * and Embed Inc makes no claims of suitability for a *
; * particular purpose nor assumes any liability resulting from *
; * its use. *
; ***************************************************************
;
; Module of routines that handle the A/D.
;
include "hal.inc"
extern loop_main ;main event loop, jump here after servicing an event
extern_flags ;declare global flag bits EXTERN
;
;***********************************************************************
;
; Configuration constants.
;
filtb equ 4 ;bits to shift right for filtering
save_read equ 10 ;save A/D reading every this many individual readings
n_save equ 5 ;number of previous SAVE_READ readings to save
lbank equ 0 ;register bank for the local state of this module
maxval equ 25 ;light above this level results in OFF mode, % of full
maxjump equ maxval ;max light jump to allow ON mode, % of full (disabled)
;
; Derived constants.
;
lbankadr equ bankadr(lbank) ;address within local state register bank
maxv equ maxval * 255 / 100 ;max light for ON mode, 0-255 scale
maxj equ maxjump * 255 / 100 ;max light jump for ON mode, 0-255 scale
;
;***********************************************************************
;
; Global state. All this state is assumed to be in the GBANK register
; bank by other modules.
;
.bank#v(gbank) udata
;
;***********************************************************************
;
; Local state.
;
if lbank != gbank
.bank#v(lbank) udata
endif
filt res 2 ;filtered current A/D value
cntsave res 1 ;number of readings until reading gets saved
ii set 0 ;make SAVE0 - SAVEn previous saved values
while ii < n_save
save#v(ii) res 1
ii set ii + 1
endw
.ad code
;
;***********************************************************************
;
; Subroutine AD_INIT
;
; Initialize the hardware and software state managed by this module.
;
glbsub ad_init, noregs
;
; Set up the A/D hardware.
;
dbankif adcon1
movlw b'00001110'
; 0------- result will be justified in high bits of 16 bit value
; -XXX---- unimplemented
; ----1110 single input channel, use PWR and GND as reference
movwf adcon1
dbankif adcon0
movlw b'10000001'
; 10------ conversion clock is oscillator/32
; --000--- select analog channel 0
; -----0-- don't start new conversion now
; ------X- unimplemented
; -------1 enable the A/D converter
movwf adcon0
dbankif pir1
bcf pir1, adif ;reset to A/D conversion not just completed
;
; Init local state.
;
dbankif lbankadr
movlw maxv ;init current value to max light before OFF mode
movwf filt+1
clrf filt+0
movlw n_save ;init to max readings before save and process one
movwf cntsave
movlw 255 ;init all saved readings to maximum brightness
ii set 0
while ii < n_save
movwf save#v(ii)
ii set ii + 1
endw
leaverest
;
;***********************************************************************
;
; Routine AD_START
;
; Start a new A/D conversion. This routine is jumped to from the main
; event loop, and must jump to LOOP_MAIN when done. All registers may
; be trashed.
;
glbent ad_start
dbankif gbankadr
bcf flag_adconv ;reset the event condition
dbankif adcon0
bsf adcon0, go ;start a new A/D conversion
gjump loop_main ;back to main event loop
;
;***********************************************************************
;
; Local subroutine FILTSHIFT
;
; Shift the 16 bit value in REG1,REG0 right by FILTB bits.
;
if filtb <= 0 ;nothing to shift, this version does nothing
locsub filtshift, noregs
leaverest
endif
if filtb > 0 ;must shift at least one bit
locsub filtshift, regf2
movlw filtb ;init loop counter
movwf reg2
filt_loop ;back here each new bit to shift
bcf status, c ;set bit value to shift in
rrf reg1 ;shift the 16 bit value right one bit
rrf reg0
decfsz reg2 ;count one less bit left to shift
goto filt_loop ;back to shift another bit right
leaverest
endif
;
;***********************************************************************
;
; Routine AD_READ
;
; An A/D conversion has completed. Process the result and jump back to
; LOOP_MAIN when done. All registers may be trashed.
;
glbent ad_read
dbankif pir1
bcf pir1, adif ;reset the event condition
;
; Remove the filter fraction from FILT.
;
dbankif lbankadr
movf filt+0, w ;get filtered value into REG1,REG0
movwf reg0
movf filt+1, w
movwf reg1
mcall filtshift ;shift right to make filter fraction
dbankif lbankadr
sub16 filt, reg0 ;remove the filter fraction from FILT
;
; Get the A/D result as a 16 bit value in REG1,REG0.
;
dbankif adresl
movf adresl, w ;get the low A/D result byte
movwf reg0
dbankif adresh
movf adresh, w ;get the high A/D result byte
movwf reg1
movwf reg2 ;get high 8 bits also into REG2
rrf reg2
rrf reg2, w ;get high 6 A/D bits in low 6 bits of W
andlw h'3F' ;mask in only the 6 bits
iorwf reg0 ;replicate high 6 bits as low 6 bits
;
; The A/D result is in REG1,REG0 expanded into a 16 bit unsigned value.
;
; Now make the appropriate filter fraction of it and add it into FILT.
;
mcall filtshift ;shift right to make filter fraction
dbankif lbankadr
add16 filt, reg0 ;add new value fraction into FILT
;
; FILT has been completely updated with the new reading.
;
; Now set FLAG_NEWOFF if the light level is above the max allowed before
; everything needs to be shut down.
;
dbankif lbankadr
movf filt+1, w ;get the current light level
sublw maxv ;compare to max allowed light level
skip_wgt ;too bright ?
goto done_toobr ;not too bright
dbankif gbankadr
bsf flag_newoff ;indicate to switch into OFF mode, if not already
done_toobr unbank ;done checking absolute light level
;
; Now check CNTSAVE and process and save this reading if it's time to do so.
;
dbankif lbankadr
decfsz cntsave ;count one less reading until check and save
goto done_save ;this reading doesn't get processed and saved
;
; Compare the new light level to the oldest saved, and set FLAG_NEWOFF
; if the light increased by more than MAXJ.
;
dbankif lbankadr
movf save0, w ;get oldest saved light level
subwf filt+1, w ;make light level increase from oldest saved to now
skip_wle ;light level went up or stayed the same ?
goto done_ljump ;done dealing with light level jump
sublw maxj ;compare light increase to max allowed before shutdown
skip_wgt ;light jump too much ?
goto done_ljump ;light increase is within limits
dbankif gbankadr
bsf flag_newoff ;indicate to switch into OFF mode, if not already
done_ljump unbank ;done checking for and handling light level jump
;
; Update the saved readings. The oldest reading will be lost, and the
; new value will be shifted in as the newest saved reading.
;
dbankif lbankadr
ii set 0
while ii < (n_save - 1) ;once for all but newest saved reading
movf save#v(ii+1), w ;get the next newer reading
movwf save#v(ii) ;move it into this saved reading slot
ii set ii + 1
endw
movf filt+1, w ;get the current value
movwf save#v(n_save-1) ;copy it into the newest saved reading
done_save unbank ;done checking and saving the latest value
gjump loop_main ;done handling this event, back to main loop
end