;----------------------------------------------------------------------;
; WKTIM4M.ASM Pushbutton toggles clock on and off, time on LCD 4 MHz ;
;----------------------------------------------------------------------;
; Note: must add additional time digit to run over 99 hrs.
; .-----------.
; -|RA2 RA1|----[LCD E]
; -|RA3 RA0|----[LCD RS]
; -|RA4 OSC1|--|X|-||--- gnd
; V+ ---|MCLR OSC2|--|X|-||--- gnd
; gnd ---|Vss Vdd|--- V+
; [LCD D4]----|RB0 RB7|- X = 4.096 MHz xtal 20 pfd
; [LCD D5]----|RB1 RB6|- V+ = 4.5 or 5 Volts
; [LCD D6]----|RB2 RB5|-
; [LCD D7]----|RB3 RB4|---[PB]--- gnd
; '-----------'
; PIC16F84 -[PB]- pushbutton
; LCD pin connections:
; 1 - gnd ground 2 - Vcc V+ 3 - contrast 10K pot V+ & gnd
; 4 - RS RA0 PIC(17) 5 - RW gnd 6 - E RA1 PIC(18)
; 7 - D0 gnd 8 - D1 gnd 9 - D2 gnd 10 - D3 gnd
; 11 - D4 RB0 (6) 12 - D5 RB1 (7) 13 - D6 RB2 (8) 14 - D7 (9)
LIST P=16F84 ; 16F84 Runs at 4.096 MHz
INCLUDE "p16f84.inc"
__CONFIG _PWRTE_ON & _XT_OSC & _WDT_OFF
ERRORLEVEL -224 ; supress annoying message from tris
; Define Information
#DEFINE RS PORTA, 0
#DEFINE E PORTA, 1
#DEFINE TOGGLESW PORTA, 4
; Macro
EStrobe MACRO ; Strobe the "E" Bit
bsf E
bcf E
ENDM
CBLOCK 0CH
sec ; seconds digit
sec10 ; 10's of second digit
mins ; minutes digit
min10 ; 10's of minutes digit
hr ; hours digit
hr10 ; 10's of hours digit
highlim ; high limit + 1 of digit
w_temp ; holds W during interrupt
status_temp ; holds STATUS during interrupt
fsr_temp ; holds FSR during interrupt
Dlay ; 8 Bit Delay Variable
working ; working flag 0 not working, 1 working
ptr ; used in displaying message
Temp ; a temporary variable
bin ; a temporary variable
oset ; offset of time register
oldtime ; holds last value of sec
cntmsec ; used in counting milliseconds
isrcnt ; used in isr to stretch time to 1 sec
ENDC
ORG 0 ; start at location 0
goto main ; jump over to main routine
ORG 4
goto isr ; jump to interrupt routine
;----------------------------------------------------------------------;
; High limit + 1 of digits at position W ;
;----------------------------------------------------------------------;
sethi:
addwf PCL, f
dt H'A',H'6',H'A',H'6',H'A',H'A'
;----------------------------------------------------------------------;
; Data for message to be output ;
;----------------------------------------------------------------------;
shomsg ; Message to Output
addwf PCL, f ; Output the Characters
dt "Working Time:", 0
;----------------------------------------------------------------------;
; ISR, increments time by one second, (BCD), every 125th time through ;
;----------------------------------------------------------------------;
isr:
movwf w_temp ; save W
swapf STATUS,W ; save status
movwf status_temp ; without changing flags
swapf FSR,W ; save FSR
movwf fsr_temp ; without changing flags
decfsz isrcnt, f ; count down to zero before incr.
goto restore
movlw D'125' ; reset isr count
movwf isrcnt
movf working, f ; check working flag
btfsc STATUS, Z ; if not zero then increment time
goto restore ; else get out of interrupt routine
movlw sec ; point at sec register
movwf FSR
newdigit: incf INDF, f ; current digit up one
movlw sec ; get difference, sec and FSR
subwf FSR, W
call sethi ; use to get high limit + 1
subwf INDF, W ; reached that number yet?
btfss STATUS, Z ; skip over if yes
goto restore ; else exit isr
clrf INDF ; set current digit to 0
incf FSR, f ; point at next digit
goto newdigit ; no, increment the next digit
restore:
swapf status_temp,W ; get original status back
movwf STATUS ; into status register
swapf fsr_temp,W ; get original fsr back
movwf FSR ; into status register
swapf w_temp,f ; old no flags trick again
swapf w_temp,W ; to restore W
bcf INTCON,T0IF ; clear the TMR0 interrupt flag
retfie ; finished reset GIE
;----------------------------------------------------------------------;
; Initialize the ports ;
;----------------------------------------------------------------------;
init:
clrf PORTA
clrf PORTB
movlw B'00010000' ; RA4 input, others outputs
tris PORTA
movlw B'00000000' ; all outputs on port B
tris PORTB
movlw B'00000011' ; pull-ups enabled
; prescaler assigned to TMR0
; prescaler set to 1:16
; rolls over each 125th second
option
movlw 0 ; zero out all registers
movwf hr10
movwf hr
movwf min10
movwf mins
movwf sec10
movwf sec
clrf working ; working flag to zero
movlw B'10100000' ; GIE set T0IE set, T0IF cleared
movwf INTCON
return
;----------------------------------------------------------------------;
; Initialize the LCD ;
;----------------------------------------------------------------------;
initlcd:
movlw D'40'
call nmsec ; Wait 40 msecs before Reset
bcf RS ; send an 8 bit instruction
movlw 0x03 ; Reset Command
call NybbleOut ; Send the Nybble
call Dlay5 ; Wait 5 msecs before Sending Again
EStrobe
call Dlay160 ; Wait 160 usecs before Sending 2nd Time
EStrobe
call Dlay160 ; Wait 160 usecs before Sending 3rd Time
bcf RS ; send an 8 bit instruction
movlw 0x02 ; Set 4 Bit Mode
call NybbleOut
call Dlay160
movlw 0x028 ; 4 bit, 2 Line, 5x7 font
call SendINS
movlw 0x010 ; display shift off
call SendINS
movlw 0x001 ; Clear the Display RAM
call SendINS
call Dlay5 ; Note, Can take up to 4.1 msecs
movlw 0x006 ; increment cursor
call SendINS
movlw 0x00C ; display on cursor off
call SendINS
return
;----------------------------------------------------------------------;
; Send the character in W out to the LCD ;
;----------------------------------------------------------------------;
SendASCII
addlw '0' ; Send nbr as ASCII character
SendCHAR ; Send the Character to the LCD
movwf Temp ; Save the Temporary Value
swapf Temp, w ; Send the High Nybble
bsf RS ; RS = 1
call NybbleOut
movf Temp, w ; Send the Low Nybble
bsf RS
call NybbleOut
return
;----------------------------------------------------------------------;
; Send an instruction in W out to the LCD ;
;----------------------------------------------------------------------;
SendINS ; Send the Instruction to the LCD
movwf Temp ; Save the Temporary Value
swapf Temp, w ; Send the High Nybble
bcf RS ; RS = 0
call NybbleOut
movf Temp, w ; Send the Low Nybble
bcf RS
call NybbleOut
return
;----------------------------------------------------------------------;
; Send the nibble in W out to the LCD ;
;----------------------------------------------------------------------;
NybbleOut ; Send a Nybble to the LCD
movwf PORTB
EStrobe ; Strobe out the LCD Data
call Dlay160 ; delay for 160 usec
return
;----------------------------------------------------------------------;
; Output the message on the LCD ;
;----------------------------------------------------------------------;
OutMessage:
movwf FSR ; Point at first letter
OutLoop:
movf FSR, w ; Get pointer into W
incf FSR, f ; Set up for next letter
call shomsg ; Get character to output
iorlw 0 ; At the End of the Message?
btfsc STATUS, Z ; Skip if not at end
return ; Yes - Equal to Zero
call SendCHAR ; Output the ASCII Character
goto OutLoop ; Get the next character
;----------------------------------------------------------------------;
; Wait until button is released ;
;----------------------------------------------------------------------;
waitup:
btfss TOGGLESW ; test toggle switch
goto $ -1 ; ck again if pressed
movlw 20 ; wait 20 msec for debounce
call nmsec
btfss TOGGLESW ; check again, still up?
goto $ -4 ; no start over
return ; yes, finished
;----------------------------------------------------------------------;
; time delay routines ;
;----------------------------------------------------------------------;
Dlay160: movlw D'41' ; delay about 160 usec
micro4 addlw H'FF' ; subtract 1 from 'W'
btfss STATUS,Z ; skip when you reach zero
goto micro4 ; more loops
return
Dlay5: movlw 5 ; delay for 5 milliseconds
goto $ + 2
msec250: movlw D'250' ; delay for 250 milliseconds
;*** N millisecond delay routine ***
nmsec: movwf cntmsec ; delay for N (in W) millisec
msecloop: movlw D'254' ; load takes .9765625 microsec
call micro4 ; by itself CALL takes ...
; 2 + 253 X 4 + 3 + 2 = 1019
; 1019 * .977 = 995 microsec
nop ; .98 microsec
decfsz cntmsec, f ; .98 skip not taken, else 1.95
goto msecloop ; 1.95 here: total ~1000 / loop
return ; final time through ~999 to here
; overhead in and out ignored
;----------------------------------------------------------------------;
; Display the Time ;
;----------------------------------------------------------------------;
DispTime
MOVLW H'C0' ; position at beginning of second line
CALL SendINS
MOVF hr10, W ; tens of hours
CALL SendASCII
MOVF hr, W ; hours
CALL SendASCII
MOVLW ":"
CALL SendCHAR
MOVF min10, W ; tens of minutes
CALL SendASCII
MOVF mins, W ; minutes
CALL SendASCII
MOVLW ":"
CALL SendCHAR
MOVF sec10, W ; tens of seconds
CALL SendASCII
MOVF sec, W ; seconds
CALL SendASCII
RETURN
;----------------------------------------------------------------------;
; Toggle Work Flag ;
;----------------------------------------------------------------------;
togglewk:
movf working, f ; set zero flag if zero
btfss STATUS, Z ; skip if working is zero
goto turnoff
incf working, f ; set working to 1
call waitup ; wait for button release
return
turnoff:
clrf working ; working set to zero
call waitup ; wait for button to be released
return
;----------------------------------------------------------------------;
; The Main routine ;
;----------------------------------------------------------------------;
main:
call init ; initialize ports, set up timer
call initlcd ; initialize the LCD
movlw 0 ; display 'Working Time:'
call OutMessage
ckbutton: ; check for a press of the TOGGLE button
btfss TOGGLESW ; skip if not pressed
call togglewk ; else change the mode of timer
movf oldtime, W ; is oldtime the same as sec?
subwf sec, W
btfsc STATUS, Z ; if not, skip over next instruction
goto ckbutton ; else continue checking
call DispTime ; sec has changed, display the time
movf sec, W ; make sec and oldsec the same
movwf oldtime
goto ckbutton ; and continue checking
end
; Note: You could use 4 mhz crystal and change the isr count to
; 125 * 0.98 = 122 but timing would not be exact. You could also use
; a 4 mhz ceramic resonator, just to see if the program works