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