Home Page . . . . . . . Email: . . . . . . . . neelandan-at-gmãil·çöm
;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
;< LCD Clock - PIC16F84A and HD44780 Liquid Crystal Display >;
;vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv28Feb05vvvvvvvv
;
;
; ''~``
; ( o o )
;+-=-=-=-=-=-=-=-=-=-=-=.oooO--(_)--Oooo.=-=-=-=-=-=-=-=-=-=-=-+
; ;
;This is not intended to be a standalone program. It is ;
;intended to be incorporated into those PIC based projects ;
;which require a realtime clock to be embedded. For this ;
;reason, there is no provision for setting the time - ;
;it is assumed that the final application will include ;
;code for that. ;
; ;
;This code assumes a 4MHz Xtal and has been tested on a ;
;PIC16f84A-04 and a HD44780 compatible 16x2 Liquid Crystal ;
;Display Module. For some reason, the second line of the ;
;display in my module was at address 40 decimal (28 hex). All ;
;data sheets I could find state this to be 64 decimal, i.e., ;
;40 hex. I might have found an oddball display, or somebody ;
;somewhere might just have become hexed and confused the dec ;
;and hex formats. ;
; ;
;Evidently, if you run into difficulties with that second ;
;line, you might have to experiment with the address of that ;
;seventeenth character. ;
; ;
; Or maybe a hex inverter might help. ;
; ;
; .oooO ;
; ( ) Oooo. ;
;+-=-=-=-=-=-=-=-=-=-=-=-=\ (----( )-=-=-=-=-=-=-=-=-=-=-=-=-+
; \_) ) /
; (_/
;
;
processor 16f84A
radix hex
__config 0x3ff1 ;WDT OFF, pwrup timer ON, XT osc
;
;---------------------------------------------------------------
; Parameter equates
POSITION equ 0x04 ;middle of first line
;
;---------------------------------------------------------------
; cpu equates (memory map)
indf equ 0x00
tmr0 equ 0x01
pcl equ 0x02
status equ 0x03
fsr equ 0x04
porta equ 0x05 ;five lines available
portb equ 0x06
portc equ 0x07 ;not available
eedata equ 0x08
eeadr equ 0x09
pclath equ 0x0a
intcon equ 0x0b
option_reg equ 0x81
trisa equ 0x85
trisb equ 0x86
trisc equ 0x87 ;not available
eecon1 equ 0x88
eecon2 equ 0x89
;
;---------------------------------------------------------------
; bit equates
c equ 0
dc equ 1
z equ 2
rp0 equ 5
gie equ 7
;
;---------------------------------------------------------------
; Variables in RAM.
;---------------------------------------------------------------
;
safe_w equ 0x0c ;w stored here during int service
safe_s equ 0x0d ;status stored here likewise
flags equ 0x0e ;array of bits as semaphores
hours equ 0x0f ;00 to 23 BCD, display format
minutes equ 0x10 ;00 to 59 BCD, display format
seconds equ 0x11 ;00 to 59 BCD, display format
timeout equ 0x12 ;notify int timeouts
tick_hi equ 0x13 ;incremented each interrupt
tick_lo equ 0x14 ;
count1 equ 0x15 ;delay counter and
count2 equ 0x16 ;temporary storage
lcdbuf equ 0x17 ;Start of Display buffer area
;
;---------------------------------------------------------------
;Port A definitions
;---------------------------------------------------------------
PORTA_CONF equ 0 ; all outputs
BLINK equ 4 ;Flashing LED for seconds - connect
;between Vcc and RA4 with a 470 ohm
;series resistor. Serves to check
;whether the PIC is running, if
;the display remains blank.
;
LCD_EN equ 1 ;Enable input of the LCD module
LCD_RS equ 0 ;Register Select of the LCD module
;---------------------------------------------------------------
; Port B definitions - keys for setting the time
;---------------------------------------------------------------
PORTB_CONF equ 0x0f
MIN_SET equ 0 ;Minutes set button----------- IN
TEN_SET equ 1 ;Set Tens of MIN-------------- IN
HR_SET equ 2 ;Hours set button------------- IN
TENH_SET equ 3 ;Set Tens of Hours------------ IN
;
; RB4 -- DB4 ;The higher nibble of Port B--OUT
; RB5 -- DB5 ;connects to the higher order-OUT
; RB6 -- DB6 ;four data lines of the-------OUT
; RB7 -- DB7 ;Liquid Crystal Display-------OUT
;===============================================================
;
; PROGRAM CODE STARTS HERE
;
;===============================================================
; Start of ROM
;---------------------------------------------------------------
org 0x00 ;Start of code space
call Ram_init ;set variables to nice values
call Timer_init ;start timer based interrupt
call Port_init ;set the ports as needed
goto Start ;the rest of the startup sequence
;---------------------------------------------------------------
; INTERRUPT SERVICE ROUTINE
;---------------------------------------------------------------
org 0x04 ;interrupt vector
Intsvc
movwf safe_w ;save w
swapf status,w ;exchange w and f
movwf safe_s ;save status with swapped nibbles
;---------------------------------------------------------------
; done saving, now start working
;---------------------------------------------------------------
movlw 0x08 ;****CHANGE FOR OTHER XTAL FREQ****
addwf tmr0,f ; 4MHz = 4 x 250 x 4000 Hz
incf tick_lo,f ; adjust timer reload value
btfsc status,z ; to make it count to 250 giving
incfsz tick_hi,f ; 4000 interrupts every second.
goto Tick_out ; Increment the tick each time.
;---------------------------------------------------------------
; here? tick has rolled over to zero and one second
; has passed. Reload tick and increment timeout counter
;---------------------------------------------------------------
movlw 0xf0 ;4000 = 0x0FA0
movwf tick_hi ;0 - 0x0FA0 = 0xF060
movlw 0x60
movwf tick_lo
incf timeout,f ;increment timeout counter
;---------------------------------------------------------------
; done working, start restoring
;---------------------------------------------------------------
Tick_out
swapf safe_s,w ;fetch status, reswap nibbles
movwf status ;restore status
swapf safe_w,f ;swap nibbles in preparation
swapf safe_w,w ;for the swap restoration of w
bcf intcon,2 ;clear interrupt flag before
retfie ;returning from interrupt
;===============================================================
; SUBROUTINES START HERE
;===============================================================
; clear important bits of ram
;---------------------------------------------------------------
Ram_init
movlw 0xf0
movwf tick_hi
movlw 0x60
movwf tick_lo
movlw 0x12 ;clocks always start
movwf hours ;at 12:00. It's official.
movlw 0x00
movwf minutes
movwf seconds
movwf flags
movwf timeout
return
;---------------------------------------------------------------
; get timer-based interrupts going
;---------------------------------------------------------------
Timer_init
bcf intcon,2 ;clear tmr0 int flag
bsf intcon,gie ;enable global interrupts
bsf intcon,5 ;enable tmr0 int
movlw 0x58 ;set up timer. No prescaler
option ;Port B pullups enabled - WARNING:
clrf tmr0 ;start timer - Use of this instruction
return ; - is not recommended
;---------------------------------------------------------------
; Port initialiser
;---------------------------------------------------------------
Port_init
bsf status,rp0 ;select correct bank
movlw PORTA_CONF
movwf trisa^80h ;flip highest bit of address
movlw PORTB_CONF ;to suppress that message about
movwf trisb^80h ;ensuring bank bits being correct
bcf status,rp0 ;back to bank zero
return
;---------------------------------------------------------------
; Initialise Liquid Crystal Display Module
;---------------------------------------------------------------
Lcd_init
bcf porta,LCD_EN ;E line low
bcf porta,LCD_RS ;RS line low, set up for control
movlw 0x3f ;8-bit, 5X7
call Pulse ;enable pulse, then wait some time
movlw 0x3f ;the previous instruction might have
call Pulse ;been ignored, so send the same thing
movlw 0x3f ;again two more times just to make
call Pulse ;sure the module gets the right idea.
movlw 0x2f ;4-bit, 5x7
call Pulse ;LCD is now in 4 bit mode. Succeeding
;instructions and data must be in
;two writes per transfer
;
movlw 0x0f
call Pulse
movlw 0xff ;display on - instruction 0F
call Pulse
;
movlw 0x0f
call Pulse
movlw 0x6f ;increment mode, no display shift - 06
call Pulse
;delay 5 milliseconds - required
goto del_5 ;before sending data
;---------------------------------------------------------------
; Fill Display Buffer area with Blanks
;---------------------------------------------------------------
Blank
movlw lcdbuf
movwf fsr
movlw 0x20 ;32 spaces to be stored starting at lcdbuf
movwf count1 ;0x20 happens to be the code for
;movlw ' ' ;ASCII space. Clever, huh?
loop
movwf indf
incf fsr,f
decfsz count1,f
goto loop
return
;===============================================================
; End of Initialise code. Main routines start here
;===============================================================
; increment seconds or minutes modulo 60 and hours modulo 24
;---------------------------------------------------------------
BCDCount
movlw 0x99
goto test ;modulo hundred for BCD Counter
Mod24
movlw 0x23
goto test
Mod60
movlw 0x59
test
xorwf indf,w
btfsc status,z
goto carry
movlw 0x07
addwf indf,w ;try adding 7
btfss status,dc ;digit carry?
addlw 0xfa ;no, take away six: 256 - 6 = 0xfa
movwf indf ;store
return ;and return - zero flag is clear
carry
clrf indf ;clear the counter
return ;and return - zero flag is set
;---------------------------------------------------------------
; increment the time based on the timeout counter
;---------------------------------------------------------------
Keep_time
movf timeout,f ;test timeout
btfsc status,z ;skip if nonzero
return ;return if zero
bsf flags,0 ;notify Display
decf timeout,f ;decrement count
;---------------------------------------------------------------
; Increment seconds
;---------------------------------------------------------------
movlw seconds ;address of seconds counter
movwf fsr ;into file select register
call Mod60 ;increment it
btfss status,z ;skip if zero
return ;if the zero flag is set then
bsf flags,1 ;notify that a minute has passed
;---------------------------------------------------------------
; Increment minutes
;---------------------------------------------------------------
decf fsr,f ;point to minutes
call Mod60 ;increment minutes
btfss status,z ;skip if zero
return ;return if nonzero
;---------------------------------------------------------------
; increment hours
;---------------------------------------------------------------
decf fsr,f ;point to hours
goto Mod24 ;a goto is worth a call-return pair
;---------------------------------------------------------------
; Convert the time from BCD to ASCII and store in
; display buffer area and write to the display module
;---------------------------------------------------------------
Display
btfss flags,0 ;Anything new? Check the seconds flag.
return ;no, so return.
bcf flags,0 ;Yes. Clear the flag
;
movlw lcdbuf + POSITION
movwf fsr
movf hours,w
call bcd2asc
movlw ':' ;colon between hours and minutes
movwf indf
;
movf minutes,w
call bcd2ascii
movlw ':' ;colon between minutes and seconds
movwf indf
;
movf seconds,w
call bcd2ascii
;
;---------------------------------------------------------------
; Get 32 characters from memory and send to LCD
;---------------------------------------------------------------
disp32
movlw lcdbuf
movwf fsr
;
bcf porta,LCD_RS ;RS line low, set up for control
;
movlw 0x8f ;control word = address first half - 80
call Pulse
movlw 0x0f
dis6
call Pulse ;pulse and delay
bsf porta,LCD_RS ;RS=1, set up for data
;
getchar
movf indf,w ;get character from display RAM
;location pointed to by file select
;register
;
iorlw 0x0f
call Pulse ;send data to display
swapf indf,w
iorlw 0x0f
call Pulse
incf fsr,f ;set up for next data byte
;
movlw lcdbuf + 0x10 ;16th character sent?
subwf fsr,w ;subtract w from fsr
btfsc status,z ;test z flag
goto half ;set up for last 16 characters
movlw lcdbuf + 0x20 ;test for 32 characters
subwf fsr,w
btfsc status,z ;test z flag
return ;32 characters sent to lcd so bye
goto getchar
half
bcf porta,LCD_RS ;RS=0, set up for control
;
; ;*****WARNING -Address might require change*****
movlw 0xaf ;control word = address second half
call Pulse
movlw 0x8f ; 80 + 28 hex
goto dis6 ;28 hex happens to be the start address
; ;of the second line in MY module.
;---------------------------------------------------------------
;
Pulse
movwf portb ;write to LC Display
bsf porta,LCD_EN ;pulse E line
nop ;delay
bcf porta,LCD_EN ;return E low, goto delay
;
;---------------------------------------------------------------
;
del_125
movlw 0x2a ;approx 42x3 cycles (decimal)
movwf count1 ;load counter
repeat
decfsz count1,f ;decrement counter
goto repeat ;not 0
return ;counter 0, ends delay
del_5
movlw 0x28 ;decimal 40
movwf count2 ;to counter
del_6
call del_125 ;delay 125 microseconds
decfsz count2,f ;do it 40 times = 5 milliseconds
goto del_6
return ;counter 0, ends delay
;---------------------------------------------------------------
; Convert Two packed BCD Digits to ASCII, store in display area
;---------------------------------------------------------------
bcd2ascii
incf fsr,f
bcd2asc
movwf count1
swapf count1,w
andlw 0x0f
iorlw 0x30
movwf indf
incf fsr,f
movf count1,w
andlw 0x0f
iorlw 0x30
movwf indf
incf fsr,f
return
;---------------------------------------------------------------
; End of subroutines
;===============================================================
;
;
;___
; ___ / || _________________________________
; _______/__ \____/ || / |
;-=| \_\_\_ ||---< Main Program continues here |
; \_________\ + \ ____| \_________________________________|
; \___\
;
;
;
;===============================================================
Start
;---------------------------------------------------------------
call Lcd_init ;make the display behave well
call Blank ;fill the display with blanks
;---------------------------------------------------------------
; Done initializing, start the endless loop.
;---------------------------------------------------------------
;
Circle ;begin the big loop
;
;
;________________________________________
LedBlink ; testing - remove when done. | \___/
btfss tick_hi,3 ;Check highest | -O O-________
bsf porta,BLINK ;bit of the 4,000| \Y/ ### |\_*
btfsc tick_hi,3 ;counter and turn| | _ ___# |
bcf porta,BLINK ;RA4 ON and OFF. | \|\| \|\|
;________________________________________| ! W W W W
; \|/ \|/ \\||// \!/
;---------------------------------------------------------------
;check timeout counter and increment time if a second has passed
;---------------------------------------------------------------
;
call Keep_time
;
;---------------------------------------------------------------
; Update display
;---------------------------------------------------------------
;
call Display
;
;
;---------------------------------------------------------------
; gentlemen, that's a clock, keep it rolling
;---------------------------------------------------------------
;
goto Circle
;
;===============================================================
;
;~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`
;~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`
end ;end of file
;~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`
;~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`'~*-,._.,-*~'`^`
Home Page . . . . . . . Email: . . . . . . . . neelandan-at-gmãil·çöm