;************************************************************
;Filename:  012300.asm                                      *
;Date:      2003/03/07                                      *
;Author:    John Taylor                                     *
;Company:   TES                                             *
;************************************************************
;Descr:     Straight code LCD interface driver for 16F877   *
;           No other code files required, just mplab proc   *
;           definitition file                               *
;************************************************************
;Revdate    Description                                     *
;2003/03/07 initial release                                 *
;************************************************************
 list p=16f877            ; list directive to define processor
 #include <p16f877.inc>   ; processor specific variable definitions
 __CONFIG _CP_OFF & _WDT_ON & _BODEN_ON & _PWRTE_ON & _RC_OSC & _WRT_ENABLE_ON & _LVP_ON & _DEBUG_OFF & _CPD_OFF 
;************************************************************




;************************************************************
            org     0x000               ; processor reset vector
reset       nop                         ;icd requires first opcode to be a nop
            nop                         ;just to fill first 4 locations
            clrf    pclath              ; ensure page bits are cleared
            goto    main                ; go to beginning of program
;************************************************************




;************************************************************
;ISR VARIABLE DEFINITIONS                                   *
;************************************************************
w_temp      EQU     0x70                ; variable used for context saving 
status_temp EQU     0x71                ; variable used for context saving
;************************************************************
            org     0x004               ; interrupt vector location
isr         movwf   w_temp              ; save off current W register contents
            movf    STATUS,w            ; move status register into W register
            movwf   status_temp         ; save off contents of STATUS register
;------------------------------------------------------------
; isr code can go here or be located as a call subroutine elsewhere
;------------------------------------------------------------
            movf    status_temp,w       ; retrieve copy of STATUS register
            movwf   STATUS              ; restore pre-isr STATUS register contents
            swapf   w_temp,f
            swapf   w_temp,w            ; restore pre-isr W register contents
            retfie                      ; return from interrupt
;************************************************************





;************************************************************
;equates could be put into an equate file                   *
;************************************************************
clock       equ     3686                ;delayms uses this to scale itself
;............................................................
#define     lcdrs   porte,0             ;setup variable names for pins
#define     lcdrw   porte,1
#define     lcden   porte,2
#define     lcdport portb
;............................................................
temp0       equ     0x50                ;setup variable names for registers
temp1       equ     0x51
;************************************************************





;************************************************************
;main code block, setup processor and then write to lcd     *
;************************************************************
main        nop
            banksel     trisb
 errorlevel -302
            movlw       0x06            ;set all i/o to digital, defaults to analog!
            movwf       adcon1
 errorlevel 0
            banksel     0
;------------------------------------------------------------
            call        lcdinit         ;initialize display
;------------------------------------------------------------
loop        movlw       '0'
            call        lcddata
            movlw       '1'
            call        lcddata
            movlw       '2'
            call        lcddata
            goto        loop
;************************************************************





;************************************************************
;this routine employs a look-ahead-busy.  when data is sent *
;here, the busy flag is tested before writing new data which*
;prevents wasting time waiting for the display to finish    *
;when the processor could be doing other things.            *
;                                                           *
;data port is bottom 4 bits of any available port, code will*
;not affect upper 4 bits of selected port                   *
;************************************************************
;********************hardware setup**************************
;#define    lcdrs       portx,bit                           *
;#define    lcdrw       portx,bit                           *
;#define    lcden       portx,bit                           *
;lcdport    equ         portx                               *
;temp0      equ         regxx       ;temp register          *
;temp1      equ         regyy       ;temp register          *
;************************************************************
;*********************sub processes**************************
;lcdinit    initialize display for 4 bit operation and clear*
;lcdcls     clear display                                   *
;lcdcrsr    set cursor to 7bit value in w, line 1   0x00    *
;                                               2   0x40    *
;lcdinst    write w to instruction register                 *
;lcddata    write w to current cursor location              *
;************************************************************
;*********************dependencies***************************
;delayms                            ;ms delay, w=#msecs     *
;************************************************************
lcdcrsr     iorlw       0x80            ;7 bit high + instruction=cursor write
            goto        lcdinst
;************************************************************
lcdinit     nop                         ;port setup
            banksel     trisb           ;picasm doesnt like banksel driective after labels
            bcf         lcdrs           ;set control lines to output
            nop
            bcf         lcdrw
            nop
            bcf         lcden
            nop
            movf        lcdport,w       ;set only low 4 bits of data port to output
            andlw       0xf0            ;does not change direction of other bits
            movwf       lcdport
            banksel     0
;------------------------------------------------------------
            bcf         lcden           ;disable
            bcf         lcdrw           ;set write+instruction mode
            bcf         lcdrs
            movlw       0x0f            ;15ms after power up to talk to display
            call        delayms
            movlw       0x03            ;write 0x3 then 5ms (spec is >4.1ms)
            call        lcdfastwr
            movlw       5
            call        delayms     
            movlw       0x03            ;write 0x3 then 1ms (spec is >100us)
            call        lcdfastwr
            movlw       1
            call        delayms
            movlw       0x03            ;write 0x3 then 1ms (spec is >40us)
            call        lcdfastwr
            movlw       1
            call        delayms
            movlw       0x02            ;write 0x2 then 1ms (spec is >40us)
            call        lcdfastwr
            movlw       1
            call        delayms         ;end of reset sequence
;------------------------------------------------------------
            movlw       0x02            ;write 0x2 then 1ms (spec is >1us)
            call        lcdfastwr           
            movlw       1
            call        delayms
            movlw       0x08            ;write 0x8 then 1ms (spec is >40us)
            call        lcdfastwr       ;busy flag can be used after this
            movlw       1
            call        delayms         ;now set for 4 bit data, 2 line, 5x7 matrix
;............................................................
            movlw       0x06            ;entry mode=increment after write, no shift
            call        lcdinst
            movlw       0x0c            ;display=on, cursor off, blink off
            call        lcdinst
;............................................................
lcdcls      movlw       0x01            ;display clear (use last return of lcdinst)
;************************************************************
lcdinst     bcf         temp1,0         ;0=instruction mode, save for after busy test
            goto        lcdidata
lcddata     bsf         temp1,0         ;1=data mode, save for after busy test
;------------------------------------------------------------
lcdidata    movwf       temp0           ;save data to write for later
            bsf         lcdrw           ;read mode/look ahead busy test
            bcf         lcdrs           ;rs=flag read mode
            banksel     trisb
            bsf         lcdport,3       ;bit is input
            banksel     0
;............................................................
lcdidatabft bsf         lcden           ;clock data out, high bit of first nibble is busy flag
            nop
            btfss       lcdport,3       ;if high then busy
            goto        lcdidatabfp
            bcf         lcden
            nop
            bsf         lcden           ;dummy clock-in of low nibble
            nop
            bcf         lcden           
;>>>>>>>>>>>   could put counter here to exit for stuck display   <<<<<<<<<<<
            goto        lcdidatabft
lcdidatabfp bcf         lcden           ;disable
            nop
            bsf         lcden           ;dummy clock-in of low nibble
            nop
            bcf         lcden
            banksel     trisb
            bcf         lcdport,3       ;bit is output
            banksel     0
;------------------------------------------------------------           
            bcf         lcdrw
            bcf         lcdrs           ;get saved inst/data mode flag
            btfsc       temp1,0
            bsf         lcdrs           ;data mode
;............................................................
            swapf       temp0,w         ;high nibble first (msb's)
            andlw       0x0f            ;chop off top
            movwf       temp1           ;save           
            movf        lcdport,w       ;get current port image
            andlw       0xf0            ;chop off bottom
            iorwf       temp1,w         ;merge
            movwf       lcdport         ;write first half
            call        lcdfasten       ;clock data
;............................................................
            movf        temp0,w         ;do bottom half (lsb's)
            andlw       0x0f            ;chop off top
            movwf       temp1           ;save           
            movf        lcdport,w       ;get current port image
            andlw       0xf0            ;chop off bottom
            iorwf       temp1,w         ;merge
            movwf       lcdport         ;write first half
            call        lcdfasten       ;clock data
            return
;************************************************************
lcdfastwr   movwf       lcdport         ;putting this just before fasten uses it's return
            nop
lcdfasten   bsf         lcden
            nop
            bcf         lcden
            nop
            return
;************************************************************




;************************************************************
;delay sent in w=milliseconds                               *
;************************************************************
 if (clock == 3686)                     ;only handles 4mhz and 3.68mhz
delaymsx    equ         0x98            ;should be replaced with calculated
 endif                                  ;delay.  these constants would serve
 if (clock == 4000)                     ;as basis for calculation
delaymsx    equ         0xa5
 endif
 ifndef delaymsx
delaymsx    equ         0xff            ;defaults to max delay if not 4 or 3.68mhz
 endif
;............................................................
delayms     movwf       temp1           ;save count of ms's to wait
delaymsbl   movlw       delaymsx        ;load main timer
            movwf       temp0
delaymsl    nop                         ;padding
            nop
            nop 
            decfsz      temp0,f
            goto        delaymsl
            decfsz      temp1,f
            goto        delaymsbl
            return
;************************************************************








 END

