16c84 ve diğer tümdevreler ile (74hc390, 4011, ne555) mikro denetleyici ile ölçüm cihazları tasarlamak isteyen kişilere örnek olabilir asm şema pcb dosyaları bulunmakta
Kapasitemetre Devre Şeması
Asm Yazılımı
; 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 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 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
; <>
;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)
; <>
; 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
; <>
;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
; <>
;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
Dosya indirme LINK listesi (TXT formatında) link-5418.zip şifre-pass: 320volt.com
Yayım tarihi: 2009/02/02 Etiketler: 4011 , 74hc390 , Assembly , kapasite metre , microchip pic projeleri , ne555 , PIC16C84