2167 lines
63 KiB
NASM
2167 lines
63 KiB
NASM
; CMETERA.ASM
|
|
; Fr. Tom McGahee's PIC CAPACITANCE METER
|
|
;
|
|
; Fr. Thomas McGahee
|
|
; Don Bosco Technical High School
|
|
; 202 Union Ave
|
|
; Paterson, NJ 07502 USA
|
|
;
|
|
; tom_mcgahee@sigmais.com
|
|
; (973)595-6655
|
|
;
|
|
; permission granted for individual use
|
|
;
|
|
; Microchip MPASM format
|
|
; Specifically designed for PIC16C84. Skeleton file.
|
|
;
|
|
|
|
; note: set assembler to case-insensitive, except within strings using /c- option
|
|
;
|
|
|
|
;OPERATIONAL DESCRIPTION OF PROJECT
|
|
|
|
|
|
;the mclear (reset) switch causes a reset and return to autorange.
|
|
;the other three switches cause an immediate move to manual mode.
|
|
;zeroswitch causes most recent displayed reading to be used as
|
|
;a zero offset. this allows the user to compare two capacitors.
|
|
;a + or - sign is displayed to indicate relative value.
|
|
;hold key down until updating stops. upon release the
|
|
;current value will be displayed.
|
|
|
|
;first line of lcd displays a + or a blinking - sign in the first
|
|
;position. This indicates whether the current displayed value is
|
|
;higher or lower than the Comparison Value (normally 0). You may
|
|
;"zero-out" any value desired by pressing the ZERO button. This will
|
|
;cause the current value to be subtracted from all future readings.
|
|
;pressing the ZERO button also forces the unit into Manual Mode.
|
|
;you can return to Auto Mode by removing the capacitor and pressing
|
|
;the reset button, or by removing the capacitor and turning the
|
|
;power off and then on again.
|
|
|
|
;manual mode can also be entered by pressing either the UP or DOWN
|
|
;buttons. (hold button down until updating stops, then release).
|
|
;UP and DOWN are used to move from one range to another in Manual Mode.
|
|
;unit starts out in Auto Mode, and will automatically switch to the
|
|
;best (most accurate) range for any given capacitor. Auto Mode has
|
|
;built-in hysteresis to prevent the capmeter from constantly
|
|
;cycling between ranges.
|
|
|
|
;the second position on the first line is sometimes occupied by a
|
|
;blinking letter "M". This indicates that you are in Manual Mode,
|
|
;and that the Auto Mode would have issued a command to go to a
|
|
;lower range. Obviously in Manual Mode you want to be able to
|
|
;change capacitances being measured, and at such a time the value
|
|
;being measured will temporarily go to zero, causing this feature
|
|
;to kick in.
|
|
|
|
;the rest of the first line is allocated to the display of the current
|
|
;count. internally a 24 bit counter allows the accumulation of counts
|
|
;up to 16,777,216. In Auto Mode the AutoRanging kicks in at around
|
|
;524,288 to range UP, and below 32,768 it shifts DOWN. There are 4
|
|
;ranges. In the lowest range it allows a display of from .00 pf to
|
|
;5,242.88 pf. range 2 ranges from 4,096 pf to 524,288 pf. range 3
|
|
;ranges from .4096 uf to 52.4288 uf. range 4 ranges from 32.768 uf
|
|
;to 16,777.216 uf.
|
|
|
|
;in Manual Mode you can generate counts from 0 to 16,777,216
|
|
;but the accuracy outside the above specified ranges can then
|
|
;have an error greater than 1%. use autoranging for maximum
|
|
;accuracy. use Manual Mode when using the Comparison feature
|
|
;or when you don't want the unit to AutoRange. AutoRanging to zero
|
|
;always causes the unit to go to range 1 for zero. then when you
|
|
;attach a new capacitor it may take a few seconds before AutoRanging
|
|
;detects an aout-of-range condition and responds. by the way, to
|
|
;speed up from such zero excursions I have implemented the UP Range
|
|
;portion of the AutoRange so that it always goes to range 4. I do
|
|
;this because it is always quicker to down range than it is to up
|
|
;range. there is method to my madness!
|
|
|
|
;values are displayed properly positioned over the value identifiers
|
|
;such as uf nf and pf. a space is provided between each set of 3
|
|
;digits to reflect standard engineering notation. partial number
|
|
;sets are padded with _ to the right. numbers have leading zeros
|
|
;suppressed up to the decimal point. commas are added where they are
|
|
;appropriate.
|
|
|
|
;note that sometimes many more digits are displayed than the accuracy
|
|
;warrants. in general you can trust the first 3 digits to be right
|
|
;on. the 4th digit is normally accurate +/- 1 count. additional
|
|
;digits are displayed not for additional accuracy, but simply because
|
|
;they are useful for watching variations due to temperature, etc.,
|
|
;and they are useful in matching two or more capacitors.
|
|
|
|
;basic accuracy is 1% or better within the AutoRanging values
|
|
;up to about 100 uf. by the time you get a measurement like
|
|
;16,000.000 uf the time to collect such a measurement has risen to
|
|
;almost 24 seconds. this causes the accumulated error to rise
|
|
;to a few percent.
|
|
|
|
;the first position on the second line will display a number from
|
|
;1 to 4. this represents the current range, where 1 is the lowest
|
|
;range. the middle section of the second line contains the uf
|
|
;nf and pf indicators, positioned directly below the numeric groups
|
|
;that they refer to. I chose to display more than one set of
|
|
;indicators so you can easily read something like:
|
|
;
|
|
;+ 12,000.___
|
|
;2 fd nf pf A**
|
|
;
|
|
;as either .012 uf 12.000 nf or 12,000 pf.
|
|
|
|
;after the uf/nf/pf indicators there is either an "A" or an "M"
|
|
;displayed as an indicator of either Manual or Auto Mode being
|
|
;active. this is followed by two locations that display an
|
|
;animated (moving) black box to indicate that a new count is
|
|
;in progress. this is replaced by ** to indicate that the
|
|
;on-screen count has just been updated. These asterisks will
|
|
;remain on-screen until the circuitry detects edge synchronization.
|
|
;then the flashing black boxes appear. to speed up the
|
|
;synchronization process I have separate routines for handling
|
|
;rising and falling edges.
|
|
|
|
;occassionaly you will see a message such as "OVER-RANGE!" when
|
|
;in the manual mode. if the number is still displayed, then this is
|
|
;just a cautionary warning that the displayed value lies outside
|
|
;the range where it can be guaranteed to be 1% accurate.
|
|
;if the message is the *only* thing displayed on the screen,
|
|
;then you have exceeded the 16 million count limit and really
|
|
;need to switch to a higher range.
|
|
;
|
|
;during AutoRanging a quick flash of the word "AUTORANGING" will
|
|
;appear, and you may hear a click from the internal relays. some
|
|
;ranges switch without the click sound because they are using
|
|
;external counters to switch ranges.
|
|
|
|
;a note of caution when using Manual Mode: if you have set the
|
|
;device to operate in Comparison Mode by pressing the ZERO
|
|
;button, this zero comparison value will be retained *for the
|
|
;current numbered range* even if you move to another scale.
|
|
;before moving to another scale it is therefore a good idea
|
|
;to remove the cap being measured and press the ZERO button to
|
|
;re-zero the scale. if you don't do this, then the next time
|
|
;you return to that range the Comparison value will again
|
|
;be in effect. there is nothing wrong with bridging a comparison
|
|
;over two different ranges. if you want to do that, you
|
|
;have to press the ZERO button at *each* range you want to
|
|
;perform the comparison on.
|
|
|
|
;because the ZERO button automatically causes entry into the
|
|
;Manual Mode, you usually can't run Comparison Mode in
|
|
;Auto Mode. however, *if* you hit the reset button and have
|
|
;a capacitor attached, the reset auto-zero feature will
|
|
;cause the current cap value to be subtracted on *every* range.
|
|
;this works with values up to about 1 uf with no problem.
|
|
;larger values will cause overflow errors on the lower ranges
|
|
;and may therefore give erroneous readings. also note that the
|
|
;time required to perfom the auto-zero function increases
|
|
;with increasing external capacitance. if the cap value is more than
|
|
;a few uf then the auto-zero logic may cause the circuitry to
|
|
;cycle endlessly in an attempt to zero the lower ranges.
|
|
;if that happens, remove the capacitor and perform a reset.
|
|
|
|
|
|
|
|
|
|
|
|
;
|
|
; directives
|
|
;
|
|
|
|
|
|
;
|
|
; note: written in all lower case so case sensitivity doesn't matter.
|
|
; however: set assembler to case-insensitive, except within strings using /c- option
|
|
;
|
|
|
|
;
|
|
; directives
|
|
;
|
|
|
|
|
|
|
|
list p=pic16f84 ;this directive must come first
|
|
|
|
; instead of using the [ include <16f84.inc> ] we have placed the contents of the
|
|
; microchip supplied include file below for documentation purposes.
|
|
;
|
|
; P16F84.INC Standard Header File, Version 2.00 Microchip Technology, Inc.
|
|
|
|
; This header file defines configurations, registers, and other useful bits of
|
|
; information for the PIC16F84 microcontroller. These names are taken to match
|
|
; the data sheets as closely as possible.
|
|
|
|
; Note that the processor must be selected before this file is
|
|
; included. The processor may be selected the following ways:
|
|
|
|
; 1. Command line switch:
|
|
; C:\ MPASM MYFILE.ASM /PIC16F84
|
|
; 2. LIST directive in the source file
|
|
; LIST P=PIC16F84
|
|
; 3. Processor Type entry in the MPASM full-screen interface
|
|
;==========================================================================
|
|
;
|
|
; Verify Processor
|
|
;
|
|
;==========================================================================
|
|
|
|
IFNDEF __16F84
|
|
MESSG "Processor-header file mismatch. Verify selected processor."
|
|
ENDIF
|
|
|
|
;==========================================================================
|
|
;
|
|
; Register Definitions
|
|
;
|
|
;==========================================================================
|
|
|
|
W EQU H'0000'
|
|
F EQU H'0001'
|
|
|
|
;----- Register Files------------------------------------------------------
|
|
|
|
INDF EQU H'0000'
|
|
TMR0 EQU H'0001'
|
|
PCL EQU H'0002'
|
|
STATUS EQU H'0003'
|
|
FSR EQU H'0004'
|
|
PORTA EQU H'0005'
|
|
PORTB EQU H'0006'
|
|
EEDATA EQU H'0008'
|
|
EEADR EQU H'0009'
|
|
PCLATH EQU H'000A'
|
|
INTCON EQU H'000B'
|
|
|
|
OPTION_REG EQU H'0081'
|
|
TRISA EQU H'0085'
|
|
TRISB EQU H'0086'
|
|
EECON1 EQU H'0088'
|
|
EECON2 EQU H'0089'
|
|
|
|
;----- STATUS Bits --------------------------------------------------------
|
|
|
|
IRP EQU H'0007'
|
|
RP1 EQU H'0006'
|
|
RP0 EQU H'0005'
|
|
NOT_TO EQU H'0004'
|
|
NOT_PD EQU H'0003'
|
|
Z EQU H'0002'
|
|
DC EQU H'0001'
|
|
C EQU H'0000'
|
|
|
|
;----- INTCON Bits --------------------------------------------------------
|
|
|
|
GIE EQU H'0007'
|
|
EEIE EQU H'0006'
|
|
T0IE EQU H'0005'
|
|
INTE EQU H'0004'
|
|
RBIE EQU H'0003'
|
|
T0IF EQU H'0002'
|
|
INTF EQU H'0001'
|
|
RBIF EQU H'0000'
|
|
|
|
;----- OPTION Bits --------------------------------------------------------
|
|
|
|
NOT_RBPU EQU H'0007'
|
|
INTEDG EQU H'0006'
|
|
T0CS EQU H'0005'
|
|
T0SE EQU H'0004'
|
|
PSA EQU H'0003'
|
|
PS2 EQU H'0002'
|
|
PS1 EQU H'0001'
|
|
PS0 EQU H'0000'
|
|
|
|
;----- EECON1 Bits --------------------------------------------------------
|
|
|
|
EEIF EQU H'0004'
|
|
WRERR EQU H'0003'
|
|
WREN EQU H'0002'
|
|
WR EQU H'0001'
|
|
RD EQU H'0000'
|
|
|
|
;==========================================================================
|
|
;
|
|
; RAM Definition
|
|
;
|
|
;==========================================================================
|
|
|
|
__MAXRAM H'CF'
|
|
__BADRAM H'07', H'50'-H'7F', H'87'
|
|
|
|
;==========================================================================
|
|
;
|
|
; Configuration Bits
|
|
;
|
|
;==========================================================================
|
|
|
|
_CP_ON EQU H'000F'
|
|
_CP_OFF EQU H'3FFF'
|
|
_PWRTE_ON EQU H'3FF7'
|
|
_PWRTE_OFF EQU H'3FFF'
|
|
_WDT_ON EQU H'3FFF'
|
|
_WDT_OFF EQU H'3FFB'
|
|
_LP_OSC EQU H'3FFC'
|
|
_XT_OSC EQU H'3FFD'
|
|
_HS_OSC EQU H'3FFE'
|
|
_RC_OSC EQU H'3FFF'
|
|
|
|
|
|
;end of <include> file stuff
|
|
|
|
;define stuff that microchip in their wisdom re-named.
|
|
; this is in case we use the identifiers in the original data sheets by accident
|
|
|
|
ind0 equ h'00' ;file address. microchip calls it indf
|
|
rtcc equ h'01' ;file address. microchip calls it tmr0
|
|
;
|
|
;<<option in mpasm is an opcode, and so not allowed as a file address name>>
|
|
;option equ h'81' ;file address. microchip calls it option_reg
|
|
;
|
|
to equ h'04' ;status. microchip calls it not_to
|
|
pd equ h'03' ;status. microchip calls it not_pd
|
|
rtie equ h'05' ;intcon. microchip calls it t0ie
|
|
rtif equ h'02' ;intcon. microchip calls it t0if
|
|
rbpu equ h'07' ;option_reg. microchip calls it not_rbpu
|
|
rts equ h'05' ;option_reg. microchip calls it t0cs
|
|
rte equ h'04' ;option_reg. microchip calls it t0se
|
|
|
|
|
|
|
|
;we have to set the configuration bits
|
|
; __config a & b & c
|
|
; _rc_osc, _xt_osc, _hs_osc, _lp_osc oscillator type
|
|
; _wdt_on, _wdt_off watchdog timer
|
|
; _cp_on, _cp_off code protect
|
|
; _pwrte_on, _pwrte_off power up timer enable
|
|
|
|
__config _xt_osc & _wdt_off & _pwrte_on & _cp_off
|
|
;configure pic as desired...
|
|
|
|
;
|
|
;constant equates
|
|
;
|
|
|
|
xtal_freq = d'4000000' ;crystal frequency
|
|
clock = xtal_freq/4 ;base operating frequency
|
|
|
|
;portb definitions (also shadowb)
|
|
|
|
db7 = h'07' ;outb lcd db7 (msb)
|
|
db6 = h'06' ;outb lcd db6
|
|
db5 = h'05' ;outb lcd db5
|
|
db4 = h'04' ;outb lcd db4
|
|
lcdrs = h'03' ;outb lcd rs line (data/!instruction)
|
|
lcde = h'02' ;outb lcd enable line
|
|
pselect = h'01' ;outb period select
|
|
; 0=period 1=px100
|
|
periodin = h'00' ;inb selected period input
|
|
|
|
;porta definitions (also acopy)
|
|
|
|
controla = h'00' ;outa 0=x1 1=rangex100
|
|
controlb = h'01' ;outa period 0=period 1=period/10
|
|
zerokey = h'02' ;[wt] causes zero compare at current range
|
|
downkey = h'03' ;[bk] causes move to next lower range
|
|
upkey = h'04' ;[gn] causes move to next higher range
|
|
|
|
;character equates
|
|
|
|
mu = h'e4' ;greek letter on lcd for uf
|
|
flipchar = ' '
|
|
flopchar = h'ff' ;big black box on lcd
|
|
|
|
|
|
;
|
|
;eeprom data area 64x8 at h'2100'
|
|
;you can store stuff like serial numbers and id codes here.
|
|
;use de directives to specify data to be stored.
|
|
|
|
;in this project eeprom is used only to hold copyright notice.
|
|
|
|
org h'2100' ;set data eeprom origin
|
|
|
|
example de "Copyright 1999. Designed and Written by Fr. Tom McGahee"
|
|
|
|
|
|
;
|
|
;define ram useage
|
|
;
|
|
|
|
|
|
;h'0c' is where general purpose sram begins, and ends at h'4f'. room for 68 bytes.
|
|
|
|
;we use cblock statements to define variable space in sram.
|
|
|
|
;ram data storage declarations
|
|
|
|
|
|
cblock h'0c'
|
|
|
|
savew ;for inthandler
|
|
savestatus ;for inthandler
|
|
savefsr ;for inthandler
|
|
temp0 ;gp: also used for mantissa
|
|
temp1 ;general purpose
|
|
temp2 ;general purpose
|
|
temp3 ;gp: also used for exponent
|
|
xmillisec ;register for timer operations
|
|
ymillisec ;2nd register for timer operations
|
|
|
|
acopy ;temp copy of "a" for input of buttons
|
|
shadowb ;port b shadow register
|
|
wlcdtemp ;lcd temp for w
|
|
nibbles ;lcd nibbles and controls are assembled here
|
|
|
|
|
|
x10 ;lsb of packed decimal digits.
|
|
x32 ; later, digits are shifted to make room for
|
|
x54 ; spaces, comma, and decimal point
|
|
x76 ; lsb of digit set is low part of byte
|
|
x98
|
|
xba
|
|
xdc
|
|
xfe ;msb of decimal digits
|
|
|
|
|
|
flags ;flags (see below for assignments)
|
|
autoflags ;autorange flags (see below)
|
|
|
|
bin24_0 ;24 bit binary version of current count
|
|
bin24_1
|
|
bin24_2
|
|
|
|
offsetx ;pointer to current offset group
|
|
|
|
offset_0 ;zero adjust and comparison register
|
|
offset_1 ; for +/- comparisons.
|
|
offset_2
|
|
|
|
offset1_0 ;zero adjust for lowest scale
|
|
offset1_1
|
|
offset1_2
|
|
|
|
offset2_0 ;zero adjust for second scale
|
|
offset2_1
|
|
offset2_2
|
|
|
|
offset3_0 ;zero adjust for third scale
|
|
offset3_1
|
|
offset3_2
|
|
|
|
offset4_0 ;zero adjust for highest scale
|
|
offset4_1
|
|
offset4_2
|
|
|
|
over_0 ;overflow binary counter
|
|
over_1 ; tells us when sync wait is too long
|
|
over_2
|
|
|
|
endc
|
|
|
|
;aliases: these allow alternate name for systems resources.
|
|
; it makes reading the program a bit easier while conserving resources
|
|
|
|
xlsb = x10
|
|
xmsb = xfe
|
|
shift = temp1
|
|
drop = wlcdtemp
|
|
mantissa = temp0
|
|
exponent = temp3
|
|
|
|
;shadowb: bit assignments
|
|
|
|
;LOW 4 BITS ARE SHADOW FOR PORTB (Some have already been defined)
|
|
|
|
;periodin = h'00' ;inb selected period input
|
|
; (not used, but still registered)
|
|
;pselect = h'01' ;outb period select
|
|
; 0=period 1=px100
|
|
;lcde = h'02' ;outb lcd enable line
|
|
;lcdrs = h'03' ;outb lcd rs line (data/!instruction)
|
|
|
|
;flags: bit assignments
|
|
|
|
intoverflow = h'0' ;1=overflow during interrupt routine
|
|
; = h'1' ;not used
|
|
; = h'2' ;not used
|
|
gotit = h'3' ;1=we have data
|
|
plusminus = h'4' ;1=plus [bin24 > offset]
|
|
flipflop = h'5' ;flipflop indicator
|
|
; = h'6' ;not used
|
|
; = h'7' ;not used
|
|
|
|
;autoflags: bit assignments
|
|
|
|
manual = h'0' ;1=manual 0=auto
|
|
stop = h'1' ;1=stop 0=continue operation
|
|
rangechange = h'2' ;1=range change
|
|
toolong = h'3' ;1=too long
|
|
|
|
;conversions used by decimal nibble output routines
|
|
|
|
n0 = h'0'
|
|
n1 = h'1'
|
|
n2 = h'2'
|
|
n3 = h'3'
|
|
n4 = h'4'
|
|
n5 = h'5'
|
|
n6 = h'6'
|
|
n7 = h'7'
|
|
n8 = h'8'
|
|
n9 = h'9'
|
|
nspace = h'a' ;after number is produced
|
|
nplus = h'b' ; we add in special characters
|
|
nminus = h'c'
|
|
ncomma = h'd'
|
|
nperiod = h'e'
|
|
nx = h'f'
|
|
|
|
|
|
;
|
|
;program 1kx14 eeprom. (h'400') can only be changed via programmer, not on-the-fly.
|
|
;
|
|
|
|
|
|
org h'0000' ;set code origin
|
|
|
|
start goto setup ;we have to get past interrupt vector at 0004
|
|
|
|
|
|
|
|
;
|
|
;interrupts
|
|
; there is a single interrupt location at 004
|
|
; we must use flags to determine which interrupt...
|
|
; this info is in intcon
|
|
;
|
|
;intcon register: byte assignments
|
|
;
|
|
;enables... 1=enable 0=disable
|
|
;<7>=gie=global_int_enable
|
|
;<6>=eeie=eeprom_int_enable
|
|
;<5>=t0ie=t0_int_enable (enables <2> t0if)
|
|
;<4>=inte=int_enable (rb0/int) (enables <1> intf)
|
|
;<3>=rbie=rb_int_enable (enables <0> rbif)
|
|
;
|
|
;flags. software reset. 0=reset 1=flagged
|
|
;<2>=t0if=t0_int_flag
|
|
;<1>=intf=int_flag (rb0/int)
|
|
;<0>=rbif=rb_int_flag (rb7-rb4)
|
|
;
|
|
;upon power up and !mclr!, intcon will contain 0000 000x
|
|
;this means that initially all interrupts are disabled.
|
|
;
|
|
;note: option_reg register is used to program use of tmr0 and wdt
|
|
;
|
|
|
|
org h'0004' ;interrupt vector location
|
|
|
|
inthandler
|
|
;global interrupts automatically disabled on entry!
|
|
;we must save context using a somewhat convoluted scheme
|
|
movwf savew ;save w register!
|
|
swapf status,w ;save status! (twisted)
|
|
movwf savestatus ;(we use swapf so as not to disturb Z!)
|
|
movf fsr,w ;save fsr!
|
|
movwf savefsr
|
|
|
|
;actual interrupt code
|
|
movf tmr0,w ;save tmr0 in case we need it
|
|
movwf bin24_0 ;save it as low byte
|
|
btfss intcon,t0if ;if not t0if it should be intf
|
|
goto intperiod
|
|
inttmr0
|
|
bcf intcon,t0if ;reset t0if
|
|
incf bin24_1,f ;update 24 bit binary count
|
|
btfss status,z ;need carry?
|
|
goto intreturn ;all done.
|
|
incf bin24_2,f ;handled carry.
|
|
btfss status,z ;overflow?
|
|
goto intreturn ;if not, almost done
|
|
call set4 ;otherwise range to top
|
|
bsf flags,intoverflow ;set overflow indicator
|
|
;now flow into intperiod to terminate
|
|
; and turn off interrupts
|
|
|
|
|
|
;intperiod occurs when rbo/int triggers on edge (intf)
|
|
; (it is also entered when there is an overflow!)
|
|
intperiod
|
|
bsf flags,gotit ;tell regular program we have data!
|
|
clrf intcon ;turn off all interrupts
|
|
;& clear all interrupt flags
|
|
|
|
intreturn
|
|
comf porta,w ;copy inverted porta to w
|
|
andlw b'00011100' ;check three bits at once
|
|
btfsc status,z
|
|
goto intfinish ;if all are low, then none pushed
|
|
movf porta,w
|
|
movwf acopy ;save copy of porta for later use
|
|
;sort out the details later...
|
|
|
|
|
|
intfinish
|
|
movf savefsr,w ;restore fsr!
|
|
movwf fsr
|
|
swapf savestatus,w ;untwist twisted saved status
|
|
movwf status ;restore normalized status!
|
|
swapf savew,f ;restore w! first twist nibbles
|
|
swapf savew,w ;then twist again and place result in w.
|
|
;(how convoluted!)
|
|
|
|
|
|
retfie ;return from interrupt!
|
|
;gie is auto-re-enabled.
|
|
|
|
|
|
;note that jump tables and decoder tables are limited to 256 bytes of program space,
|
|
;and care must be taken that tables not cross over page boundaries.
|
|
;the limitation is based on the 8 bit addressing scheme employed in tables due to
|
|
;the size of w.
|
|
|
|
;decoder tables take the form:
|
|
;label
|
|
; addwf pcl,f ;this executes an effective jump forward
|
|
; retlw 'a' ;0 decodes as 'a'
|
|
; retlw 'b' ;1 decodes as 'b'
|
|
; retlw 'c' ;2 decodes as 'c' ... and so on
|
|
;
|
|
;
|
|
|
|
|
|
;convert special packed bcd+ nibbles into one of 16 8 bit code things
|
|
|
|
convert
|
|
andlw b'00001111' ;just the right nibble, please
|
|
addwf pcl,f ;this executes an effective jump forward 0-15
|
|
retlw '0'
|
|
retlw '1'
|
|
retlw '2'
|
|
retlw '3'
|
|
retlw '4'
|
|
retlw '5'
|
|
retlw '6'
|
|
retlw '7'
|
|
retlw '8'
|
|
retlw '9'
|
|
retlw ' ' ;decode the special stuff, too
|
|
retlw '+'
|
|
retlw '-'
|
|
retlw ','
|
|
retlw '.'
|
|
retlw '_' ;underscore _ used for non-existent digits
|
|
|
|
;text for lcd messages
|
|
|
|
sometext ;routine to extract string pieces
|
|
addwf pcl,f ;this executes an effective jump forward
|
|
starttext
|
|
begin1text dt "PIC CAP METER",0 ;put *your* name or message here 16 max
|
|
begin2text dt "Fr Tom McGahee",0 ;put *your* name or message here 16 max
|
|
autotext dt "AUTORANGING ",0
|
|
overtext dt "OVER-RANGE!",0
|
|
manual2text dt "MANUAL MODE",0
|
|
fourspaces dt " "
|
|
ufnfpftext dt " ",mu,"f nf pf",0 ;that mu is code for greek letter
|
|
zerotext dt "ZERO ALL RANGES",0
|
|
|
|
|
|
commontext = overtext ;share text to conserve memory
|
|
;(every little "bit" counts!)
|
|
;if you need extra bytes you can
|
|
;reduce the size of the messages,
|
|
;or even eliminate some entirely.
|
|
;but leave fourspaces and ufnfpftext alone,
|
|
;or at least modify them with care!
|
|
|
|
|
|
|
|
; pic16c84 pinouts
|
|
;
|
|
; ra2 <1> <18> ra1
|
|
; ra3 <2> <17> ra0
|
|
; (oc) ra4/tmr0<3> <16> osc1/clkin
|
|
; !mclr! <4> <15> osc2/clkout
|
|
; gnd <5> <14> +2 to +6 volts
|
|
; rb0/int <6> <13> rb7
|
|
; rb1 <7> <12> rb6
|
|
; rb2 <8> <11> rb5
|
|
; rb3 <9> <10> rb4
|
|
;
|
|
;osc1 & osc2 allow many types of timing choices. use device command to select.
|
|
;
|
|
;!mclr! tied high via resistor. use a switch to force it low for a reset.
|
|
;
|
|
;ra4 becomes the tmr0 external input if option_reg<5>=1. then option_reg<4> selects edge.
|
|
;ra4 is not ttl. it is oc out and schmitt in. use pullup resistor if needed.
|
|
;
|
|
;ra3-ra0 are ttl level.
|
|
;
|
|
;rb7-rb0 are ttl. weak pullups can be programmed for inputs if option_reg<7>=0
|
|
;rb0/int acts as int pin if intcon<4> inte=1. intcon<1> intf is flag. software reset.
|
|
;rb7-rb4 will generate an interrupt if intcon<3> rbie=1. intcon<0> rbif is flag. software reset.
|
|
;
|
|
|
|
|
|
setup ;initialize ports and registers
|
|
|
|
;ra4/tmr0<3>, ra3<2>, ra2<1>, ra1<18>, ra0<17>
|
|
;rb7<13>, rb6<12>, rb5<11>, rb4<10>
|
|
;rb3<9>, rb2<8>, rb1<7>, rb0/int<6>
|
|
|
|
;page 1 stuff includes option_reg, trisa, trisb, eecon1, eecon2
|
|
|
|
|
|
bsf status,rp0 ;allow access to page 1 stuff!
|
|
;*************** ignore mplab message[302]
|
|
movlw b'00011100' ;set porta direction for i/o pins
|
|
movwf trisa ;0=output 1=input
|
|
|
|
movlw b'00000001' ;set portb direction for i/o pins
|
|
;using rb0 as interrupt pin.
|
|
movwf trisb ;0=output 1=input
|
|
|
|
bcf option_reg,not_rbpu ;!rbpu! rb_pullup 0=enabled 1=disabled
|
|
; enabling is based on individual port-latch values
|
|
; we have disabled rb_pullup
|
|
|
|
bcf option_reg,intedg ;intedg 0=inc on falling 1=inc on rising
|
|
; <<note: intedg and t0se use opposite definition!>>
|
|
;we are incrementing on falling edge
|
|
; because initial sync is on falling edge
|
|
; and we want full period
|
|
;
|
|
;the edge used is later changed to speed
|
|
;up sync process.
|
|
|
|
bcf option_reg,t0cs ;t0cs timer0clocksource 0=internal clkout 1=ra4/int
|
|
; (rts in some data sheets)
|
|
; we clear so we can use internal clkout
|
|
|
|
bcf option_reg,t0se ;t0se timer0signaledge 0=inc on rising 1=inc on falling
|
|
; (rte in some data sheets)
|
|
; <<note: intedg and t0se use opposite definition!>>
|
|
; in our application edge makes no difference,
|
|
; so we arbitrarily choose rising edge.
|
|
|
|
bsf option_reg,psa ;psa prescalerassignment 0=tmr0 1=wdt
|
|
;we do not use wdt, but we set prescaler
|
|
;to wdt to allow div by 1 for tmr0!
|
|
|
|
;ps2-ps0 determine prescalerrate, which is
|
|
;dependent also on whether tmr0 or wdt is selected:
|
|
;wdt from 0-7 is div by 1 2 4 8 16 32 64 128
|
|
;tmr0 from 0-7 is div by 2 4 8 16 32 64 128 256
|
|
;if wdt is assigned prescaler, then tmr0 is div by 1
|
|
; here we will set prescaler to divide by 1 for tmr0
|
|
; by assigning the prescaler to the wdt
|
|
|
|
bcf option_reg,ps2 ;ps2 set for division by 1
|
|
bcf option_reg,ps1 ;ps1
|
|
bcf option_reg,ps0 ;ps0
|
|
;***************
|
|
bcf status,rp0 ;allow access to page 0 stuff again. (back to normal)
|
|
|
|
;now use movlw/movwf and/or clrf statements to initialize any desired variables
|
|
|
|
clrf flags ;reset all flag bits
|
|
clrf autoflags
|
|
movlw b'11111111' ;ensure initial 1's to allow auto-zero!
|
|
movwf acopy
|
|
|
|
;ready now to begin main user program.
|
|
|
|
mainprog
|
|
call lcdreset ;reset lcd, set for 4 bit ops, clear, no cursor
|
|
|
|
;output opening two-line message and wait 2 seconds to allow circuitry to stabilize.
|
|
|
|
movlw begin1text-starttext
|
|
call textout
|
|
call lcdhome2
|
|
movlw begin2text-starttext
|
|
call textout
|
|
call delay1000
|
|
call delay1000
|
|
|
|
call lcdclear ;clear lcd and display "ZERO ALL" message
|
|
movlw zerotext-starttext
|
|
call textout
|
|
|
|
;set each range and perform initial Auto-Zero for each scale (click click click)
|
|
|
|
call set1
|
|
call setstuff
|
|
|
|
call set2
|
|
call setstuff
|
|
|
|
call set3
|
|
call setstuff
|
|
|
|
call set4
|
|
call setstuff
|
|
|
|
;finished with all the initialization stuff. so here we go loop de loop!
|
|
|
|
mainloop
|
|
call periodinit ;sync and acquire a count
|
|
call checkhit ;check for buttons and process
|
|
|
|
processdata
|
|
clrf intcon ;disable interrupts (we have other work to do)
|
|
btfss flags,intoverflow ;check state of interrupt overflow flag
|
|
goto convertit ;if no overflow, convert binary, etc.
|
|
overflowed
|
|
call lcdclear ;if interrupt overflow
|
|
movlw overtext-starttext ; clear display and show message
|
|
call textout
|
|
goto mainloop ;try again!
|
|
|
|
;subroutine to check for buttons being hit while interrupts are off
|
|
|
|
gethit
|
|
comf porta,w ;copy inverted porta to w
|
|
andlw b'00011100' ;check three bits at once
|
|
btfsc status,z
|
|
return ;if all are low, then none pushed
|
|
movf porta,w
|
|
movwf acopy ;save copy of original porta for later use
|
|
;sort out the details later...
|
|
goto oldhit
|
|
|
|
;subroutine to handle buttons being hit both in and out of interrupt.
|
|
;includes immediate check and check for "old" hits registered in acopy.
|
|
|
|
checkhit
|
|
;first check for new hit key
|
|
comf porta,w ;copy inverted porta to w
|
|
andlw b'00011100' ;check three bits at once
|
|
btfsc status,z
|
|
goto oldhit ;if all are low, then none pushed
|
|
movf porta,w ;otherwise we have a new hit!
|
|
movwf acopy ;save copy of porta for later use by oldhit
|
|
;liesurely flow into oldhit routine...
|
|
oldhit
|
|
comf acopy,w ;check if a key was hit (old or new)
|
|
andlw b'00011100' ;check three bits at once
|
|
btfsc status,z
|
|
return ;if all are low, then none pushed
|
|
bcf autoflags,manual ;clear manual so we can use setx routines!
|
|
btfsc acopy,upkey ;non-inverted original in acopy. is it UP key?
|
|
goto isitdown ;1 means it was NOT UP key, so check next key
|
|
itwasup ;0 means it WAS UP key
|
|
movf offsetx,w ;ummmm, where ARE we? (what range is current?)
|
|
sublw offset1_0 ;compare by subtracting one from the other
|
|
btfss status,z ;are we at range 1?
|
|
goto upto3 ;if not, check for others...
|
|
upto2
|
|
call set2 ;if it was 1, change to range 2
|
|
goto hitdone ;clean up and continue
|
|
upto3
|
|
movf offsetx,w ;it's 2/3/4. Figure out which...
|
|
sublw offset2_0 ;compare by subtracting
|
|
btfss status,z
|
|
goto upto4 ;if not a match on 2, it must be 3/4
|
|
itwas2
|
|
call set3 ;if it was 2, change range to 3
|
|
goto hitdone ;clean up and continue
|
|
upto4
|
|
|
|
call set4 ;if it was 3 go to 4.
|
|
;if it was already 4, stay at 4. (sticky button)
|
|
goto hitdone ;clean up and continue
|
|
|
|
;ahh, it wasn't the UP button. So check for DOWN or ZERO button...
|
|
|
|
isitdown
|
|
btfsc acopy,downkey ;non-inverted original in acopy
|
|
goto iszero ;if it wasn't DOWN, it was ZERO!
|
|
itwasdown
|
|
movf offsetx,w ;Down from *where*? 4/3/2/1
|
|
sublw offset4_0 ;compare by subtracting
|
|
btfss status,z
|
|
goto downto2 ;if it wasn't 4 it was 3/2/1
|
|
downto3
|
|
call set3 ;if it was 4, down range to 3
|
|
goto hitdone ;clean up and continue
|
|
downto2
|
|
movf offsetx,w ;maybe it is at 3?
|
|
sublw offset3_0
|
|
btfss status,z
|
|
goto downto1 ;if not it is 2/1
|
|
|
|
call set2 ;if it was 3, go down to 2
|
|
goto hitdone ;clean up and continue
|
|
downto1
|
|
call set1 ;2 goes down to 1. sticky button at 1
|
|
goto hitdone
|
|
iszero
|
|
call periodinit ;before we can perform a zeroing operation
|
|
; we must first acquire a clean reading!
|
|
call setzerox ;then we do the zero stuff
|
|
;and liesurely flow into the cleanup part below.
|
|
|
|
;clean up and continue
|
|
|
|
hitdone
|
|
comf porta,w ;copy inverted porta to w
|
|
andlw b'00011100' ;check three bits at once
|
|
btfss status,z
|
|
goto hitdone ;wait for key release!
|
|
movlw b'11111111'
|
|
movwf acopy ;"reset" acopy to indicate it is processed.
|
|
bsf autoflags,manual ;force manual mode
|
|
goto mainloop ;messes up stack, but who cares?
|
|
;this method allows quicker response
|
|
|
|
;exiting from a called subroutine by
|
|
;executing a goto is frowned upon by some,
|
|
;but in this case it is the simplest way
|
|
;to speed up the response. it effectively
|
|
;terminates the current loop and goes to the
|
|
;beginning of mainloop.
|
|
|
|
;following subroutine is used by initialization routine to auto-zero each range
|
|
|
|
setstuff
|
|
call xmillisecs
|
|
call periodinit
|
|
call setzerox
|
|
goto lcdclear2
|
|
|
|
;subroutines to set circuitry to handle a particular range. Range switching stuff.
|
|
|
|
set1
|
|
btfsc autoflags,manual ;normally in manual mode you don't auto-range.
|
|
return ; this is over-ridden if button is pushed.
|
|
movlw offset1_0 ;offsetx is loaded with appropriate pointer value
|
|
movwf offsetx
|
|
bsf shadowb,pselect ;ports are set to turn appropriate external
|
|
; circuitry on/off to select desired range
|
|
|
|
;some code is shared between set1 and set2
|
|
;strictly to save code space.
|
|
set1_2
|
|
movf shadowb,w
|
|
movwf portb
|
|
|
|
bcf porta,controla
|
|
bcf porta,controlb
|
|
return
|
|
|
|
set2
|
|
btfsc autoflags,manual
|
|
return
|
|
movlw offset2_0
|
|
movwf offsetx
|
|
bcf shadowb,pselect
|
|
goto set1_2
|
|
|
|
set3
|
|
btfsc autoflags,manual
|
|
return
|
|
bcf shadowb,pselect
|
|
movf shadowb,w
|
|
movwf portb
|
|
|
|
bsf porta,controla
|
|
bcf porta,controlb
|
|
movlw offset3_0
|
|
movwf offsetx
|
|
return
|
|
|
|
set4
|
|
btfsc autoflags,manual
|
|
return
|
|
bcf shadowb,pselect
|
|
movf shadowb,w
|
|
movwf portb
|
|
|
|
bsf porta,controla
|
|
bsf porta,controlb
|
|
movlw offset4_0
|
|
movwf offsetx
|
|
return
|
|
|
|
;subroutine to perform zero function by copying current contents of bin24 counter
|
|
; to local register set. Later the contents of the local set are subtracted from
|
|
; whatever the new current count is, and this effects the Zero or Comparison function.
|
|
|
|
setzerox
|
|
movf offsetx,w ;recover current offset group
|
|
movwf fsr ;use indirect addressing
|
|
movf bin24_0,w ;zero by copying bin24 to offset
|
|
movwf indf
|
|
incf fsr,f
|
|
movf bin24_1,w
|
|
movwf indf
|
|
incf fsr,f
|
|
movf bin24_2,w
|
|
movwf indf
|
|
return
|
|
|
|
;subroutine to subtract current offset from current bin24 count.
|
|
; allows Zeroing and Comparing.
|
|
;
|
|
;I do not claim that this implementation is minimal or fastest method to do this.
|
|
; I can only say that it works. (I need to get SOME sleep, guys!).
|
|
; writing code at 2 AM is not always conducive to achieving conciseness. ZZZZzzzzzz.
|
|
|
|
subtractoffset
|
|
movf offset_2,w
|
|
subwf bin24_2,w ;see which is biggest
|
|
btfsc status,z
|
|
goto equal_2 ;equal so far
|
|
notequal
|
|
btfsc status,c ;c=1 means bin24 bigger
|
|
goto bin24bigger
|
|
goto offsetbigger
|
|
equal_2
|
|
movf offset_1,w
|
|
subwf bin24_1,w ;see which is biggest
|
|
btfsc status,z
|
|
goto equal_1 ;equal so far
|
|
goto notequal ;carry bit tells all...
|
|
equal_1
|
|
|
|
movf offset_0,w
|
|
subwf bin24_0,w ;see which is biggest
|
|
btfsc status,z
|
|
goto equal_0 ;equal so far
|
|
goto notequal ;carry bit tells all...
|
|
equal_0
|
|
clrf bin24_0 ;so clear bin24
|
|
clrf bin24_1
|
|
clrf bin24_2
|
|
return
|
|
|
|
bin24bigger
|
|
bsf flags,plusminus ;bigger is +
|
|
bb0
|
|
movf offset_0,w
|
|
subwf bin24_0,f ;place result in bin24
|
|
btfsc status,z
|
|
goto bb1 ;if zero skip to next byte
|
|
btfsc status,c
|
|
goto bb1 ;if result was + skip to next byte
|
|
bb0neg
|
|
|
|
|
|
;handle negative
|
|
movlw 1 ;"borrow" one from next byte
|
|
subwf bin24_1,f
|
|
btfsc status,c ;if negative (no cy) we need another
|
|
; borrow from next byte.
|
|
goto bb1 ;if positive, do next byte
|
|
|
|
movlw 1 ;then "borrow" one from next byte
|
|
subwf bin24_2,f
|
|
|
|
|
|
bb1
|
|
|
|
movf offset_1,w ;subtract second byte set
|
|
subwf bin24_1,f ;place result in bin24
|
|
btfsc status,z
|
|
goto bb2 ;if zero skip to next byte
|
|
btfsc status,c
|
|
goto bb2 ;if result was + skip to next byte
|
|
bb1neg
|
|
;handle negative
|
|
decf bin24_2,f ;"borrow" one from next byte
|
|
;that was last byte, so no more borrows
|
|
bb2
|
|
|
|
movf offset_2,w ;subtract last byte set
|
|
subwf bin24_2,f ;place result in bin24
|
|
;this is msb so we is done
|
|
return ;result is in bin24 set
|
|
|
|
|
|
offsetbigger
|
|
|
|
;so subtract bin24 from offset
|
|
movf offset_0,w
|
|
;swap those buggers!
|
|
movwf temp0 ;use temp0
|
|
movf bin24_0,w
|
|
movwf offset_0
|
|
movf temp0,w
|
|
movwf bin24_0 ;swapped. tempset has offset.
|
|
|
|
movf offset_1,w
|
|
;swap those buggers!
|
|
movwf temp1 ;use temp1
|
|
movf bin24_1,w
|
|
movwf offset_1
|
|
movf temp1,w
|
|
movwf bin24_1 ;swapped. tempset has offset.
|
|
|
|
movf offset_2,w
|
|
;swap those buggers!
|
|
movwf temp2 ;use temp2
|
|
movf bin24_2,w
|
|
movwf offset_2
|
|
movf temp2,w
|
|
movwf bin24_2 ;swapped. temporary set has offset.
|
|
|
|
call bin24bigger ;re-use code!
|
|
bcf flags,plusminus ;except plusminus is different!
|
|
; (hey, it works!)
|
|
|
|
movf temp0,w ;recover offset set
|
|
movwf offset_0 ;0
|
|
|
|
|
|
movf temp1,w
|
|
movwf offset_1 ;1
|
|
|
|
|
|
movf temp2,w
|
|
movwf offset_2 ;2
|
|
call lcdhome1
|
|
movlw h'ff' ;flash the negative sign to attract attention
|
|
call lcdout
|
|
call xmillisecs ;just a little flash...
|
|
return
|
|
|
|
;subroutine to convert binary results into human readable format on lcd
|
|
|
|
convertit
|
|
copyzero ;set up indirect adressing to recover stuff
|
|
movf offsetx,w ;recover current offset group
|
|
movwf fsr ;use indirect addressing
|
|
movf indf,w ;get first element
|
|
movwf offset_0 ;copy it
|
|
|
|
incf fsr,f ;point to next element
|
|
movf indf,w ;get 2nd element
|
|
movwf offset_1 ;copy it
|
|
|
|
incf fsr,f ;point to next element
|
|
movf indf,w ;get 3rd element
|
|
movwf offset_2 ;copy it
|
|
|
|
call subtractoffset ;perform zero/compare
|
|
|
|
|
|
|
|
;bin2dec: converts bin24 to decimal in xlsb->xmsb registers.
|
|
;method I chose to use nibble bcd. to get speed up I use a hybrid
|
|
;approach to doing the conversions. I designed a decimal adder that
|
|
;allows me to add a decimal value by specifying its mantissa and
|
|
;exponent values. binary conversions proceed by converting binary
|
|
;bits to decimal mantissa/exponent form and then adding the
|
|
;mantissa/exponent pieces. I did not choose this method because it
|
|
;is elegant, optimal, or whatever. I chose it because it was something
|
|
;I had been playing around with as a mental exercise, and I decided
|
|
;to use the results of my investigations here in this project.
|
|
;so, if some of the code seems a bit strange to you, don't worry.
|
|
;it seems a bit strange to me, too! But, hey, it works, and I had
|
|
;some mental FUN trying this out.
|
|
|
|
bin2dec
|
|
byte0
|
|
movf bin24_0,w ;test for zero
|
|
btfsc status,z
|
|
goto byte1 ;if zero, skip & do next byte
|
|
movwf temp1 ;leave original alone
|
|
|
|
byte0loop ;first the onesies
|
|
|
|
|
|
call addtens10 ;mantissa is 1 exponent is 0. one.
|
|
decf temp1,f ;byte is finished when zero
|
|
btfss status,z
|
|
goto byte0loop
|
|
|
|
byte1 ;then groups of 256
|
|
movf bin24_1,w ;test for zero
|
|
btfsc status,z
|
|
goto byte2 ;if zero, skip & do next byte
|
|
movwf temp1 ;leave original alone
|
|
byte1loop
|
|
movlw 6
|
|
call addtensx0 ;exponent of 0 handled special
|
|
|
|
movlw 5
|
|
movwf mantissa
|
|
movlw 1
|
|
call addtensxx
|
|
|
|
movlw 2
|
|
movwf mantissa
|
|
movlw 2
|
|
call addtensxx
|
|
|
|
decf temp1,f ;byte is finished when zero
|
|
btfss status,z
|
|
goto byte1loop
|
|
|
|
|
|
byte2 ;then groups of 65,536
|
|
movf bin24_2,w ;test for zero
|
|
btfsc status,z
|
|
goto bin2decdone ;if zero, all done.
|
|
movwf temp1 ;leave original alone
|
|
byte2loop
|
|
movlw 6
|
|
call addtensx0
|
|
|
|
movlw 3
|
|
movwf mantissa
|
|
movlw 1
|
|
call addtensxx
|
|
|
|
movlw 5
|
|
movwf mantissa
|
|
movlw 2
|
|
call addtensxx
|
|
|
|
movlw 5
|
|
movwf mantissa
|
|
movlw 3
|
|
call addtensxx
|
|
|
|
movlw 6
|
|
movwf mantissa
|
|
movlw 4
|
|
call addtensxx
|
|
|
|
|
|
decf temp1,f ;byte is finished when zero
|
|
btfss status,z
|
|
goto byte2loop
|
|
;if zero, all done.
|
|
|
|
bin2decdone ;now we will take the resultant decimal number set
|
|
; and spread it out so we can add the commas and
|
|
; decimal points and other stuff.
|
|
|
|
|
|
|
|
movf offsetx,w ;determine range, because each range has a
|
|
sublw offset1_0 ; different layout for the digits.
|
|
btfss status,z
|
|
goto isit2
|
|
its1
|
|
call sd15x ;I use a shift left from here and drop
|
|
; method to get the pieces arranged and in place.
|
|
call sd12p ; for example, the sd15x routine will position
|
|
; us at digit position 15, shift all digits left
|
|
; one place, and then deposit an X at location 15
|
|
; Oh yeah, X has been replaced by an underscore instead.
|
|
; it simply looks neater.
|
|
|
|
;by the way, all this shift and drop stuff takes
|
|
;place within the bcd register set. once the register
|
|
;set is arranged as we want, then the whole set
|
|
;is read into the lcd display.
|
|
movlw d'8'
|
|
call sdxc
|
|
|
|
call sd4c
|
|
|
|
|
|
call lcdclear2
|
|
movlw '1'
|
|
call lcdout
|
|
|
|
call usual
|
|
movf bin24_2,w
|
|
sublw d'7'
|
|
btfsc status,c
|
|
goto commonstuff
|
|
call set4
|
|
goto commonstuffr
|
|
|
|
isit2
|
|
|
|
movf offsetx,w
|
|
sublw offset2_0
|
|
btfss status,z
|
|
goto isit3
|
|
its2
|
|
call sd15x
|
|
|
|
call sd15x
|
|
|
|
call sd15x
|
|
|
|
call sd12p
|
|
|
|
movlw d'8'
|
|
call sdxc
|
|
|
|
call sd4c
|
|
|
|
call lcdclear2
|
|
movlw '2'
|
|
call lcdout
|
|
|
|
call usual
|
|
movf bin24_2,w
|
|
sublw d'7'
|
|
btfsc status,c
|
|
goto check2low
|
|
call set4
|
|
goto commonstuffr
|
|
check2low
|
|
movf bin24_2,w
|
|
btfss status,z
|
|
goto commonstuff
|
|
movf bin24_1,w
|
|
sublw d'16'
|
|
btfss status,c
|
|
goto commonstuff
|
|
call set1
|
|
goto commonstuffr
|
|
|
|
|
|
isit3
|
|
|
|
movf offsetx,w
|
|
sublw offset3_0
|
|
btfss status,z
|
|
goto its4
|
|
its3
|
|
|
|
call sd15x ;sd is short for shift and drop
|
|
call sd15x ;sd is short for shift and drop
|
|
|
|
call sd12s ;sd is short for shift and drop
|
|
|
|
|
|
movlw d'8'
|
|
call sdxp ;sd is short for shift and drop
|
|
|
|
|
|
call sd4c ;sd is short for shift and drop
|
|
|
|
|
|
call lcdclear2
|
|
movlw '3'
|
|
call lcdout
|
|
|
|
call fourplus
|
|
movf bin24_2,w
|
|
sublw d'7'
|
|
btfsc status,c
|
|
goto check3low
|
|
call set4
|
|
goto commonstuffr
|
|
check3low
|
|
movf bin24_2,w
|
|
btfss status,z
|
|
goto commonstuff
|
|
movf bin24_1,w
|
|
sublw d'16'
|
|
btfss status,c
|
|
goto commonstuff
|
|
call set2
|
|
goto commonstuffr
|
|
|
|
|
|
its4
|
|
|
|
call sd15x ;sd is short for shift and drop
|
|
|
|
call sd15x
|
|
|
|
call sd15x
|
|
|
|
|
|
call sd12s
|
|
|
|
|
|
movlw d'8'
|
|
call sdxp
|
|
|
|
call sd4c
|
|
|
|
|
|
call lcdclear2
|
|
movlw '4'
|
|
call lcdout
|
|
|
|
call fourplus
|
|
|
|
check4low
|
|
movf bin24_2,w
|
|
btfss status,z
|
|
goto commonstuff
|
|
movf bin24_1,w
|
|
sublw d'128'
|
|
btfss status,c
|
|
goto commonstuff
|
|
call set3
|
|
;now some shared stuff
|
|
commonstuffr
|
|
bsf autoflags,rangechange
|
|
|
|
|
|
commonstuff
|
|
call zerooff ;suppress useless zeros, commas, etc.
|
|
call dataout ;translate & display set on line 1 of lcd
|
|
movlw 0
|
|
call lcdaddress
|
|
movlw '+' ;+/- goes in 1st position, line 1
|
|
btfss flags,plusminus
|
|
movlw '-'
|
|
call lcdout
|
|
|
|
movlw h'40'+d'13' ;lcdaddress 2nd row
|
|
call lcdaddress
|
|
btfss autoflags,manual
|
|
goto commona
|
|
movlw 'M' ;Manual Mode
|
|
goto commonstar
|
|
|
|
commona
|
|
movlw 'A' ;Auto Mode
|
|
commonstar
|
|
call lcdout
|
|
movlw '*' ;two ** indicates a new conversion is completed.
|
|
call lcdout
|
|
call lcdout
|
|
call xmillisecs
|
|
btfss autoflags,rangechange ;if range changed, say so
|
|
goto mainloop ;begin next period
|
|
bcf autoflags,rangechange ;update flag so we don't do this again.
|
|
call lcdhome2 ;2nd line of lcd
|
|
movlw autotext-starttext ;it *might* be in auto mode
|
|
btfsc autoflags,manual ;or it *could* be in manual mode
|
|
goto mstuff
|
|
call textout ;if auto mode, flash "AUTORANGING"
|
|
goto mainloop
|
|
mstuff
|
|
movlw 1
|
|
call lcdaddress
|
|
movlw 'M' ;if manual mode, just flash an "M"
|
|
call lcdout
|
|
goto mainloop
|
|
|
|
fourplus
|
|
movlw fourspaces-starttext ;some ranges have four spaces first...
|
|
call textout
|
|
usual
|
|
movlw ufnfpftext-starttext ;all have the uf nf stuff
|
|
goto textout
|
|
|
|
;zerooff: scans current ascii contents of decimal nibbles and
|
|
; replaces leftmost zeros and comma with spaces. stops at decimal point
|
|
; or first non-zero numeric. zero suppresion and some other stuff.
|
|
|
|
zerooff
|
|
bcf autoflags,stop ;start off enabled to scan
|
|
movlw xmsb+1 ;scan from xmsb to xlsb
|
|
movwf fsr ;use indirect addressing
|
|
zeroloop
|
|
decf fsr,f ;update pointer
|
|
movf fsr,w
|
|
sublw xlsb-1 ;check if past xlsb
|
|
btfsc status,z
|
|
return ;if past xlsb, we is done here
|
|
|
|
btfsc autoflags,stop
|
|
return ;done if stop is set
|
|
|
|
call leftcheck
|
|
btfsc autoflags,stop
|
|
return ;done if stop is set
|
|
call rightcheck
|
|
goto zeroloop
|
|
leftcheck
|
|
movf indf,w ;w has copy of byte.
|
|
andlw b'11110000' ;look at left nibble only
|
|
movwf temp1 ;save original...
|
|
swapf temp1,w ;check nibble in byte format
|
|
sublw n0 ;was it a leading 0?
|
|
btfsc status,z
|
|
goto spaceit ;if so, change to a space.
|
|
|
|
swapf temp1,w ;get original back into w
|
|
sublw ncomma ;was it a comma?
|
|
btfsc status,z
|
|
goto spaceit ;if so, change to a space.
|
|
|
|
|
|
swapf temp1,w ;get original back into w
|
|
sublw nspace ;was it a space?
|
|
btfsc status,z
|
|
goto spaceit ;if so, leave it a space.
|
|
|
|
bsf autoflags,stop ;if anything else, then stop
|
|
return
|
|
|
|
spaceit
|
|
swapf indf,w ;get swapped version of original
|
|
andlw b'11110000' ;preserve right nibble (in left)
|
|
iorlw nspace ;sneak in nspace
|
|
movwf indf ;save it
|
|
swapf indf,f ;swap it back to correct form
|
|
return
|
|
|
|
|
|
|
|
rightcheck
|
|
btfsc autoflags,stop
|
|
return
|
|
swapf indf,f ;swap nibbles
|
|
call leftcheck ;and share code!
|
|
swapf indf,f ;restore order.
|
|
return
|
|
|
|
; this is the main synchronization section. It is responsible for assuring that we sync in the
|
|
; shortest possible time. After syncing to the period's edge we sort of twiddle our thumbs
|
|
; and watch for the flags being set by the interrupt handler. when the flags tell us
|
|
; something, we handle it. we handle things like taking excessively long, actually getting
|
|
; a flag that says we have a bin24 count accumulated, etc. we also occasionally check
|
|
; to see if anyone has pushed any buttons. notice that pushed puttons will terminate
|
|
; regular program flow and cause (among other things) a new set of readings to be acquired.
|
|
|
|
periodinit
|
|
clrf intcon ;disable all interrupts
|
|
call clearalldata ;clear binary and decimal versions.
|
|
;also clear over_x counters.
|
|
;data on lcd remains intact.
|
|
btfss autoflags,toolong ;did interrupt routine say "toolong"?
|
|
goto periodinit2 ;if not, continue...
|
|
call lcdclear2 ;if it *was* toolong, then tell the world!
|
|
movlw autotext-starttext ; well, it might be in maual or auto modes,
|
|
btfsc autoflags,manual ; so use different messages for first line
|
|
movlw manual2text-starttext
|
|
call textout
|
|
call delay400
|
|
call lcdclear2
|
|
movlw commontext-starttext ;and use some shared text for the rest.
|
|
call textout
|
|
|
|
|
|
periodinit2
|
|
bcf autoflags,toolong ;if we got here we have handled any toolong,
|
|
bcf flags,intoverflow ; and any overflow, so clear those flags now!
|
|
btfsc portb,periodin ;read periodin to find current state of sync
|
|
goto sync1 ;highs go one place, lows another...
|
|
goto sync2
|
|
sync1
|
|
bsf status,rp0 ;allow access to page 1 stuff!
|
|
|
|
bcf option_reg,intedg ;intedg 0=inc on falling 1=inc on rising
|
|
; <<note: intedg and t0se use opposite definition!>>
|
|
;we are incrementing on falling edge
|
|
; because initial sync is on falling edge
|
|
; and we want full period
|
|
bcf status,rp0 ;allow access to page 0 stuff again. (normal)
|
|
|
|
btfsc portb,periodin ;read periodin
|
|
goto synconlow1
|
|
synconhigh1
|
|
call gethit ;allow buttons
|
|
incf over_0,f ;twiddle thumbs, but not for *too* long!
|
|
btfss status,z
|
|
goto synconhigh1b
|
|
incf over_1,f
|
|
btfss status,z
|
|
goto synconhigh1b
|
|
incf over_2,f
|
|
btfss over_2,2
|
|
goto synconhigh1b
|
|
bsf autoflags,toolong ;darn! too long! flag it!
|
|
call set4 ;attempt to autorange to highest range.
|
|
goto periodinit
|
|
synconhigh1b
|
|
btfss portb,periodin ;read periodin
|
|
goto synconhigh1 ;loop until high
|
|
synconlow1
|
|
call gethit ;allow buttons
|
|
incf over_0,f ;twiddle thumbs, but not for *too* long!
|
|
btfss status,z
|
|
goto synconlow1b
|
|
incf over_1,f
|
|
btfss status,z
|
|
goto synconlow1b
|
|
incf over_2,f
|
|
btfss over_2,2
|
|
goto synconlow1b
|
|
bsf autoflags,toolong ;darn! too long! flag it!
|
|
call set4 ;attempt to autorange to high range
|
|
goto periodinit
|
|
|
|
synconlow1b
|
|
btfsc portb,periodin ;read periodin
|
|
goto synconlow1 ;loop until low
|
|
goto gotsync
|
|
|
|
sync2
|
|
bsf status,rp0 ;allow access to page 1 stuff!
|
|
|
|
bsf option_reg,intedg ;intedg 0=inc on falling 1=inc on rising
|
|
; <<note: intedg and t0se use opposite definition!>>
|
|
;we are incrementing on rising edge
|
|
; because initial sync is on rising edge
|
|
; and we want full period
|
|
bcf status,rp0 ;allow access to page 0 stuff again. (normal)
|
|
|
|
|
|
|
|
|
|
btfsc portb,periodin ;read periodin
|
|
goto synconhigh2
|
|
synconlow2
|
|
call gethit ;allow buttons
|
|
incf over_0,f ;twiddle thumbs, but not *too* long!
|
|
btfss status,z
|
|
goto synconlow2b
|
|
incf over_1,f
|
|
btfss status,z
|
|
goto synconlow2b
|
|
incf over_2,f
|
|
btfss over_2,2
|
|
goto synconlow2b
|
|
bsf autoflags,toolong ;darn! too long! flag it!
|
|
call set4 ;attempt to autorange to high range
|
|
goto periodinit
|
|
|
|
|
|
synconlow2b
|
|
btfsc portb,periodin ;read periodin
|
|
goto synconlow2 ;loop until low
|
|
synconhigh2
|
|
call gethit ;allow buttons
|
|
incf over_0,f ;twiddle thumbs, but not for *too* long!
|
|
btfss status,z
|
|
goto synconhigh2b
|
|
incf over_1,f
|
|
btfss status,z
|
|
goto synconhigh2b
|
|
incf over_2,f
|
|
btfss over_2,2
|
|
goto synconhigh2b
|
|
bsf autoflags,toolong ;darn! too long! flag it!
|
|
call set4 ;attempt to autorange to high range
|
|
goto periodinit
|
|
|
|
|
|
synconhigh2b
|
|
btfss portb,periodin ;read periodin
|
|
goto synconhigh2 ;loop until high
|
|
goto gotsync
|
|
|
|
gotsync
|
|
clrf tmr0 ;clear timer to eliminate false triggers
|
|
;periodin just changed.
|
|
clrf intcon ;clear any pending interrupt requests
|
|
; also clears all enables including gie.
|
|
; effectively disables all interrupts
|
|
bsf intcon,inte ;enable rb0/int as int
|
|
bsf intcon,t0ie ;enable tmr0 interrupt
|
|
|
|
bcf flags,gotit ;reset indicators
|
|
bcf flags,intoverflow
|
|
bsf intcon,gie ;enable global interrupt enable for now...
|
|
clrf tmr0 ;clear timer now to reduce latency
|
|
|
|
|
|
waitloop
|
|
call gethit ;allow buttons
|
|
movlw h'40'+d'14' ;lcdaddress 2nd row
|
|
call lcdaddress
|
|
btfss flags,flipflop ;flip/flop black box
|
|
goto flop ; to indicate major thumb twiddling in progress
|
|
flip
|
|
movlw flipchar
|
|
call lcdout
|
|
movlw flopchar
|
|
call lcdout
|
|
bcf flags,flipflop
|
|
goto gotityet
|
|
|
|
flop
|
|
movlw flopchar
|
|
call lcdout
|
|
movlw flipchar
|
|
call lcdout
|
|
bsf flags,flipflop
|
|
|
|
gotityet
|
|
call xmillisecs ;delay .2 seconds
|
|
btfss flags,gotit ;is gotit flag up?
|
|
goto waitloop ;wait seemingly forever....
|
|
return ;WOW! we *finally* have a valid BIN24 ready!
|
|
|
|
|
|
|
|
;clearalldata: clears data xlsb->xmsb and bin24_0/1/2
|
|
|
|
clearalldata
|
|
movlw xlsb ;fsr will point to register set
|
|
movwf fsr
|
|
clearloop
|
|
clrf indf ;clear a set
|
|
incf fsr,f ;point to next set
|
|
movf fsr,w ;copy next set address to w
|
|
sublw xmsb+1 ;done when past xmsb
|
|
btfss status,z
|
|
goto clearloop ;clear from xlsb to xmsb
|
|
|
|
clrf bin24_2 ;clear 24 bit binary counter
|
|
clrf bin24_1
|
|
clrf bin24_0
|
|
|
|
clrf over_0 ;clear 24 bit overflow counter
|
|
clrf over_1
|
|
clrf over_2
|
|
return
|
|
|
|
;shared subroutines to add decimal numbers using mantissa/exponent information
|
|
; it may not be pretty, but it works!
|
|
|
|
addtens10
|
|
movlw 1
|
|
addtensx0
|
|
movwf mantissa
|
|
movlw 0
|
|
addtensxx
|
|
movwf exponent ;exponent
|
|
;flows into addtens
|
|
|
|
;addtens: enter with mantissa and exponent registers loaded.
|
|
;value specified gets added into decimal registers.
|
|
;w/flags/mantissa/exponent affected. Returns xlsb->xmsb updated.
|
|
|
|
addtens
|
|
movf mantissa,w ;check mantissa value
|
|
btfsc status,z
|
|
return ;return if nothing to do!
|
|
bcf status,c ;clear carry
|
|
rrf exponent,w ;divide exponent by two (nibble oriented)
|
|
addlw xlsb ;point to proper nibble set
|
|
movwf fsr ;use it for indirect addressing of nibble sets
|
|
addlw -(xmsb+1) ;don't go too far!
|
|
btfsc status,z
|
|
return ;return if all available digits done
|
|
btfsc exponent,0 ;lsb tells us even or odd exponent
|
|
goto oddstuff
|
|
evenstuff
|
|
call even ;handle rightmost nibble
|
|
incf exponent,f ;prepare for next higher digit
|
|
goto addtens ;see if more to do...
|
|
|
|
oddstuff
|
|
call odd ;handle leftmost nibble
|
|
incf exponent,f ;prepare for next higher digit
|
|
goto addtens ;see if more to do...
|
|
even
|
|
movlw b'00001111' ;just look at right nibble
|
|
andwf indf,w ; of value pointed to
|
|
addwf mantissa,f ;add mantissa+current value
|
|
; and save in mantissa
|
|
movf mantissa,w ;copy into w, too
|
|
addlw -d'10' ;same as x-10
|
|
btfsc status,z
|
|
goto setevenzero ;10-10=0
|
|
btfsc status,c
|
|
goto setevenpositive ;cy=1 if x-10 is positive
|
|
setevennegative
|
|
movlw b'11110000'
|
|
andwf indf,f ;clear out right nibble
|
|
movf mantissa,w ;copy mantissa+current value into w
|
|
; (value is less than 10)
|
|
iorwf indf,f ;now byte contains new right nibble
|
|
clrf mantissa ;clear carryout pointer
|
|
return ;all done! no carryout.
|
|
setevenzero
|
|
movlw b'11110000'
|
|
andwf indf,f ;clear out right nibble
|
|
clrf mantissa ;set mantissa to 1 for carryout.
|
|
incf mantissa,f ; 0+1=1
|
|
return ;now update next because of carryout
|
|
setevenpositive
|
|
movwf mantissa ;save positive value
|
|
movlw b'11110000'
|
|
andwf indf,f ;clear out right nibble
|
|
movf mantissa,w ;copy mantissa+current value into w
|
|
; (value is less than 10)
|
|
iorwf indf,f ;now byte contains new right nibble
|
|
clrf mantissa ;set mantissa to 1 for carryout.
|
|
incf mantissa,f ; 0+1=1
|
|
return ;now update next because of carryout
|
|
|
|
odd
|
|
swapf indf,f ;swap nibbles
|
|
call even ; and use even routines! how sneaky.
|
|
swapf indf,f ;swap nibbles back to normal
|
|
return ;return with possible carry in mantissa
|
|
|
|
|
|
|
|
|
|
|
|
;sample calling routine
|
|
; call lcdclear1
|
|
; movlw onetext-starttext ;note how we subtract starttext address
|
|
; call textout ; to make an 8 bit address PIC can handle.
|
|
; call lcdclear2 ; sometext routine and attendant data
|
|
; movlw twotext-starttext ; should be within 1st 256 bytes of program
|
|
; call textout ; to ensure addressability.
|
|
|
|
|
|
textout
|
|
movwf temp1 ;save that address!
|
|
textloop
|
|
call sometext ;retlw a byte
|
|
addlw 0 ;set z flag if we recovered terminating 0
|
|
btfsc status,z
|
|
return ;once we got 0 we are done
|
|
call lcdout ;everything else we send to lcd
|
|
incf temp1,f ;NEXT!
|
|
movf temp1,w ;need new address in both temp1 and w
|
|
goto textloop ;do a whole string of 'em
|
|
|
|
;routine to output packed bcd+ nibbles
|
|
|
|
dataout
|
|
call lcdhome1 ;data goes on 1st line of lcd
|
|
movlw 8 ;8 bytes = 16 packed bcd+ nibbles
|
|
movwf temp2 ;temp2 holds byte count
|
|
movlw xmsb
|
|
movwf fsr ;indirect addressing set to xmsb
|
|
dataloop
|
|
swapf indf,w ;get left nibble, since we are outputting
|
|
; digits etc from left to right (msb->lsb)
|
|
call convert ;convert 4 bits into full regular ascii
|
|
call lcdout ;and display it on lcd (including commas, etc.)
|
|
movf indf,w ;then right nibble
|
|
call convert
|
|
call lcdout
|
|
decf fsr,f ;point to next
|
|
decf temp2,f
|
|
btfsc status,z
|
|
return ;done when all 8 bytes done
|
|
goto dataloop ;otherwise keep going.
|
|
|
|
|
|
|
|
;these routines are used to "shift and drop" coded items such as commas and decimal
|
|
;points into the packed bcd+ decimal set.
|
|
|
|
;first we have some space-saving attempts that code some of the more popular sd constructs
|
|
sd4c
|
|
movlw d'4'
|
|
sdxc
|
|
movwf shift
|
|
movlw ncomma
|
|
goto sd
|
|
|
|
sd12s
|
|
movlw d'12'
|
|
movwf shift
|
|
movlw nspace
|
|
goto sd
|
|
sd12p
|
|
movlw d'12'
|
|
sdxp
|
|
movwf shift
|
|
movlw nperiod
|
|
goto sd
|
|
sd15x
|
|
movlw d'15'
|
|
movwf shift
|
|
movlw nx
|
|
;flows into shiftanddrop
|
|
|
|
;enter with shift position in (shift)temp1 and replacement data in w.
|
|
;use fsr method. shift left
|
|
|
|
;the method used for shifting is to shift a nibble at a time
|
|
|
|
shiftanddrop
|
|
sd ;(I use the shorter label when I have to type.)
|
|
movwf wlcdtemp ;steal the lcd's temp register to conserve resources.
|
|
movlw xmsb
|
|
movwf fsr ;fsr now points to msb
|
|
movf temp1,w ;check for 'more to do'
|
|
btfsc status,z ;if position is 0 no shift to do
|
|
goto stuffleft ; so just stuff data
|
|
nextleft
|
|
swapf indf,f ;otherwise 'shift' nibble left
|
|
movf indf,w
|
|
andlw b'11110000' ;clear right nibble
|
|
movwf indf ;of current byte
|
|
decf temp1,f ;update shift counter
|
|
btfsc status,z
|
|
goto stuffright ;if done, stuff new data
|
|
;otherwise do nextright...
|
|
nextright
|
|
decf fsr,f ;if not done, shift across bytes
|
|
swapf indf,w ; copy high nibble into low nibble and to w
|
|
andlw b'00001111' ;get nibble to be shifted...
|
|
incf fsr,f ;access 'current' location again
|
|
iorwf indf,f ;replace with shifted data.
|
|
decf fsr,f ;point to next byte!
|
|
decf temp1,f ;update shift counter
|
|
btfsc status,z
|
|
goto stuffleft ;if done, stuff new data
|
|
goto nextleft
|
|
|
|
stuffleft
|
|
movf indf,w ;recover current byte
|
|
andlw b'00001111' ;clear left nibble
|
|
movwf indf ;replace byte
|
|
swapf wlcdtemp,w ;move replacement nibble into position
|
|
andlw b'11110000' ;just replace left nibble
|
|
iorwf indf,f ;done!
|
|
return
|
|
|
|
stuffright
|
|
movf indf,w ;recover current byte
|
|
andlw b'11110000' ;clear right nibble
|
|
movwf indf ;replace byte
|
|
movf wlcdtemp,w
|
|
andlw b'00001111' ;just replace right nibble
|
|
iorwf indf,f ;done!
|
|
return
|
|
|
|
;a whole bunch of lcd routines to make life easier
|
|
|
|
initwlcd
|
|
call wmillisecs ;wait w ms (may be power up condition)
|
|
movlw b'00110000'
|
|
andlw b'11110000' ;just high bits first
|
|
movwf nibbles ;save high nibbles
|
|
movf shadowb,w ;get control bits from flags
|
|
andlw b'00001111' ; they are in lower 4 bits
|
|
iorwf nibbles,w ;save result in w
|
|
movwf portb ;output high bits and controls...
|
|
bsf portb,lcde ;begin enable strobe...
|
|
nop ;add extra 1 us delay
|
|
bcf portb,lcde ;end enable strobe
|
|
return
|
|
|
|
lcdreset
|
|
movlw d'16'
|
|
call initwlcd ;wait 16 ms (may be power up condition)
|
|
;do lcd song and dance init thing
|
|
|
|
movlw d'1'
|
|
call initwlcd ;wait 1 ms (may be power up condition)
|
|
;do lcd song and dance init thing
|
|
|
|
;most data sheets show 3 initializations,
|
|
;but I have always found 2 to be enough.
|
|
|
|
movlw d'1'
|
|
call wmillisecs ;wait 1 ms (we don't read busy flag)
|
|
movlw b'00100000'
|
|
andlw b'11110000' ;just high bits first
|
|
movwf nibbles ;save high nibbles
|
|
movf shadowb,w ;get control bits from flags
|
|
andlw b'00001111' ; they are in lower 4 bits
|
|
iorwf nibbles,w ;save result in w
|
|
movwf portb ;set for 4 bit mode
|
|
bsf portb,lcde ;begin enable strobe...
|
|
nop ;add extra 1 us delay for good luck
|
|
bcf portb,lcde ;end enable strobe
|
|
|
|
movlw d'1'
|
|
call wmillisecs ;wait 1 ms to allow it to take
|
|
|
|
;from here on out we use 4 bit interface!
|
|
;proper delays are built-in.
|
|
|
|
movlw b'00101000' ;set 4 bit, 2 lines, 5x7 font
|
|
call lcdcommand
|
|
|
|
movlw b'00001100' ;display on, cursor off,blink off
|
|
call lcdcommand
|
|
|
|
call lcdclear ;clear lcd
|
|
|
|
movlw b'00000110' ;increment cursor and no display shift
|
|
call lcdcommand
|
|
return
|
|
|
|
;lcd commands: called subroutines. each clobbers w.
|
|
|
|
lcdclear
|
|
movlw b'00000001'
|
|
goto lcdcommand
|
|
|
|
lcdclear1 call lcdhome1
|
|
call lcdlineclear
|
|
goto lcdhome1
|
|
|
|
lcdclear2 call lcdhome2
|
|
call lcdlineclear
|
|
goto lcdhome2
|
|
|
|
lcdhome1
|
|
movlw b'00000010' ;home to line 1 and set display to normal
|
|
goto lcdcommand
|
|
|
|
lcdaddress
|
|
iorlw b'10000000' ;append command to address in w.
|
|
goto lcdcommand ; sets ddram address.
|
|
|
|
lcdhome2 movlw b'11000000' ;set ddram address to h'40'
|
|
goto lcdcommand ; which is line 2
|
|
|
|
lcdlineclear movlw d'16' ;lines are 16 characters long
|
|
movwf temp1
|
|
llcloop movlw ' ' ;clear by writing 16 spaces
|
|
call lcdout
|
|
decf temp1,f ;update loop count and w
|
|
movf temp1,f
|
|
btfss status,z
|
|
goto llcloop
|
|
return
|
|
|
|
;lcdout: called subroutine. w contains data to be sent in two 4 bit nibbles.
|
|
;includes delay since we do not read the busy flag. w unaffected.
|
|
|
|
lcdout
|
|
bsf shadowb,lcdrs ;set data mode in flags register
|
|
call lcdnibbles ;do common lcd routine
|
|
return
|
|
|
|
|
|
|
|
|
|
;lcdcommand: called subroutine. w contains data to be sent in two 4 bit nibbles.
|
|
;includes delay since we do not read the busy flag. w not affected.
|
|
|
|
lcdcommand
|
|
bcf shadowb,lcdrs ;set instruction mode in flags register
|
|
call lcdnibbles ;do common lcd routine
|
|
movlw d'4'
|
|
call wmillisecs ;wait another 4 ms (we don't read busy flag)
|
|
movf wlcdtemp,w ;we have w back!
|
|
return
|
|
|
|
;NOTE! lcdnibbles is the low-level interface between data to be displayed and
|
|
; the actual hardware implementation of the 4 data bits and control stuff.
|
|
; if you implement a different port useage, then this routine would have to
|
|
; be modified. pay special attention to the necessary bit masks and port used!
|
|
|
|
lcdnibbles
|
|
movwf wlcdtemp ;save w!
|
|
movlw d'1'
|
|
call _200usecs ;wait min. .2 ms (we don't read busy flag)
|
|
bcf shadowb,lcde ;set e strobe low (default)
|
|
movf wlcdtemp,w ;recover original
|
|
andlw b'11110000' ;just high bits first
|
|
movwf nibbles ;save high nibbles
|
|
movf shadowb,w ;get control bits from flags
|
|
andlw b'00001111' ; they are in lower 4 bits
|
|
iorwf nibbles,w ;save result in w
|
|
movwf portb ;output high bits and controls...
|
|
bsf portb,lcde ;begin enable strobe...
|
|
nop ;add extra 1 us delay
|
|
bcf portb,lcde ;end enable strobe
|
|
|
|
swapf wlcdtemp,f ;swap hi and lo nibbles of original
|
|
;(leave lcdrs bit set as-is)
|
|
|
|
movf wlcdtemp,w ;recover swapped nibble
|
|
andlw b'11110000' ;now low bits are in high bits
|
|
|
|
movwf nibbles ;save this nibble (which was low bits)
|
|
movf shadowb,w ;get control bits from flags
|
|
andlw b'00001111' ; they are in lower 4 bits
|
|
iorwf nibbles,w ;save result in w
|
|
movwf portb ;output low bits...
|
|
bsf portb,lcde ;begin enable strobe...
|
|
nop ;slight delay
|
|
bcf portb,lcde ;strobe e goes low now
|
|
|
|
bsf shadowb,lcdrs ;default in flags is data mode
|
|
bcf shadowb,lcde ;default in flags is strobe enable low
|
|
swapf wlcdtemp,f ;back to normal...
|
|
movf wlcdtemp,w ;we have w back!
|
|
return
|
|
|
|
|
|
;==============================================================================
|
|
; _200usecs: (subroutine) : 200 usec delay.
|
|
;==============================================================================
|
|
|
|
|
|
_200usecs
|
|
movlw (d'200'-3)/3 ;number of loops
|
|
movwf xmillisec ;(shared register)
|
|
_200loop
|
|
decf xmillisec,f ;count down
|
|
btfss status,z
|
|
goto _200loop
|
|
return
|
|
|
|
delay1000
|
|
call xmillisecs
|
|
delay800
|
|
call xmillisecs
|
|
delay600
|
|
call xmillisecs
|
|
delay400
|
|
call xmillisecs
|
|
;flow into xmillisecs for last 200 ms delay
|
|
;==============================================================================
|
|
; xmillisecs: (subroutine) : 200 msec delay. flows into wmillisec.
|
|
;==============================================================================
|
|
|
|
xmillisecs
|
|
movlw d'200' ;delay...
|
|
|
|
;==============================================================================
|
|
; wmillisecs: (subroutine) : delay for w millisecs. tuned for 4.00 mhz xtal.
|
|
;==============================================================================
|
|
|
|
;called subroutine set to generate delays.
|
|
;enter with # of milliseconds to delay in W
|
|
|
|
wmillisecs
|
|
|
|
movwf xmillisec ;save # of millisecs in w to delay.
|
|
|
|
incf xmillisec,f ;adjust to account for initial decrement.
|
|
;first (outer) loop.
|
|
|
|
wmloop1
|
|
decfsz xmillisec,f ;update first (outer) loop
|
|
goto wmloopa ;if more to do, do it.
|
|
|
|
return ;done when xmillisec=0.
|
|
|
|
wmloopa
|
|
clrf ymillisec ;second (inner) loop
|
|
;256 loops (256+1 becomes 0)
|
|
|
|
wmloopb
|
|
decfsz ymillisec,f ;update 256 part of inner loop.
|
|
goto wmloopb ;3 usec per loop
|
|
|
|
movlw d'75'+1
|
|
movwf ymillisec ;75 loops for third loop
|
|
|
|
wmloopc
|
|
decfsz ymillisec,f ;update 75 part of inner loop.
|
|
goto wmloopc ;3 usec per loop
|
|
|
|
goto wmloop1 ;continue with next outer loop
|
|
|
|
;total for inner loop sets is
|
|
;3*(256+75)=993 usec.
|
|
;this together with the overhead
|
|
;makes the total as close to 1000 usec
|
|
;as we need.
|
|
;1000 usec = 1 millisecond
|
|
;hey, it's good enough for *me*!
|
|
|
|
;==============================================================================
|
|
; end of program (at last! Now I can finally get some much needed sleep!!)
|
|
;==============================================================================
|
|
|
|
end
|