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