;----------------------------------------------------------------------; ; 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