;************************************************* ************************************************** **
;
; Program: lcd.asm
;
; This program interfaces to an industry standard 2 line by 20 character display
; module. The program assembles for 4-bit data interface. LCD_DATA is the port which
; supplies the data tothe LCD, while LCD_CNTL is the port that has the control lines
; ( E, RS, R_W ). The data is transfer on the high nibble of the port ( PORT‹7:4› ).
;
; Target: PIC16C84 - But should work on any PIC16CXX running at 4Mhz
;
; Note: To change the clock speed, the delays at the end of the source file need to be changed.
;
; References:
; Addapted from the Microchip AN587 code.
; OPTREX data sheet.
; Also referenced the following web pages:
; http://www.iaehv.nl/users/pouweha/lcd.htm
; http://www.paranoia.com/~filipg/HTML..._LCD_tech.html
; http://hobbes.king.ac.uk/matt/pic
; Andrew Warren's QBasic program for calculating delays.
;
;
; History
; Author Date Comments
; Microchip 05-10-94 Original Code (File name was LM032L.ASM)
;
; Norm Cramer 04-22-96 Modified to only use 4 bit interface and to not destroy the
; contents of the lower portion of the output register and the
; output register TRIS. Changed the examples to include a table
; definition using the "dt" compiler directive. Added delay
; subroutines for testing. Changed Check_Busy to Wait_Busy and
; added a Check_Busy that returns 0 if not busy, ff if busy.
; Modified to meet my programming standards for easy maint.
;
;
ERRORLEVEL -302
Processor 16C84
Radix HEX
EXPAND
include ‹p16c84.inc›
include ‹lcd.h›
;
;
LCD_DATA EQU PORTB
LCD_DATA_TRIS EQU TRISB
;
;
LCD_CNTL EQU PORTA
LCD_CNTL_TRIS EQU TRISA
;
;
;
; LCD Display Commands and Control Signal names.
;
E EQU 3 ; LCD Enable control line
R_W EQU 1 ; LCD Read/Write control line
RS EQU 2 ; LCD Register Select control line
;
;
cblock 0x00c
Temp1, Temp, Char, r1, r2, r3, r4
endc
;
org _ResetVector ; RESET vector location
RESET:
goto Start
;
; This is the Periperal Interrupt routine. Should NOT get here
;
page
;
org _IntVector ; Interrupt vector location
ERROR1:
bcf STATUS, RP0 ; Bank 0
goto ERROR1
;
;
;
Start: ; POWER_ON Reset (Beginning of program)
clrf STATUS ; Do initialization (Bank 0)
clrf INTCON
bsf STATUS, RP0 ; Bank 1
clrf TRISA ; RA5 - 0 outputs
movlw 0xF0 ;
movwf TRISB ; RB7 - 4 inputs, RB3 - 0 outputs
bsf OPTION_REG, NOT_RBPU ; Disable PORTB pull-ups
bcf STATUS, RP0 ; Bank 0
clrf PORTA ; ALL PORT output should output Low.
clrf PORTB
;
page
;
call LCD_Init ; Set up the LCD Module
;Send a message the hard way
movlw 'M'
call Send_Char
movlw 'i'
call Send_Char
movlw 'c'
call Send_Char
movlw 'r'
call Send_Char
movlw 'o'
call Send_Char
movlw 'c'
call Send_Char
movlw 'h'
call Send_Char
movlw 'i'
call Send_Char
movlw 'p'
call Send_Char
movlw LINE2 ; Address DDRam first character, second line
call Send_Cmd
;Demonstration of the use of a table to output a message
movlw 0 ; Table address of start of message
dispmsg:
movwf Temp1 ; Temp1 holds start of message address
call Table
andlw 0FFh
btfsc STATUS,Z ; Check if at end of message (zero returned at end)
goto out
call Send_Char ; Display character
movf Temp1,w ; Point to next character
addlw 1
goto dispmsg
out:
loop:
goto loop ; Stay here forever
;
page
;
Table:
addwf PCL,f ; Jump to character
dt "This is a test line!" ; 20 Character string terminated with a 0
Table_End:
retlw 0
;
; Check to ensure table doesn't cross a page boundary.
if ( (Table & 0x0FF) ›= (Table_End & 0x0FF) )
MESSG "Warning - User Definded: Table crosses page boundry in computed jump"
endif
;
;************************************************* ******************
;* The LCD Module Subroutines *
;************************************************* ******************
;
;************************************************* ******************
;*SendChar - Sends character to LCD *
;*This routine splits the character into the upper and lower *
;*nibbles and sends them to the LCD, upper nibble first. *
;************************************************* ******************
;
Send_Char
movwf Char ; Character to be sent is in W
call Wait_Busy ; Wait for LCD to be ready
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
movf Char,w
andlw 0xF0 ; Get upper nibble
iorwf LCD_DATA,F ; Send data to LCD
bcf LCD_CNTL, R_W ; Set LCD to write
bsf LCD_CNTL, RS ; Set LCD to data mode
bsf LCD_CNTL, E ; toggle E for LCD
bcf LCD_CNTL, E
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
swapf Char,W
andlw 0xF0 ; Get lower nibble
iorwf LCD_DATA,F ; Send data to LCD
bsf LCD_CNTL, E ; toggle E for LCD
bcf LCD_CNTL, E
return
;
page
;
;************************************************* ******************
;* Send_Cmd - Sends command to LCD *
;* This routine splits the command into the upper and lower *
;* nibbles and sends them to the LCD, upper nibble first. *
;************************************************* ******************
Send_Cmd
movwf Char ; Character to be sent is in W
call Wait_Busy ; Wait for LCD to be ready
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
movf Char,w
andlw 0xF0 ; Get upper nibble
iorwf LCD_DATA,F ; Send data to LCD
bcf LCD_CNTL,R_W ; Set LCD to write
bcf LCD_CNTL,RS ; Set LCD to command mode
bsf LCD_CNTL,E ; toggle E for LCD
bcf LCD_CNTL,E
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
swapf Char,W
andlw 0xF0 ; Get lower nibble
iorwf LCD_DATA,F ; Send data to LCD
bsf LCD_CNTL,E ; toggle E for LCD
bcf LCD_CNTL,E
return
;
page
;
;************************************************* ******************
;* This routine checks the busy flag, returns when not busy *
;* Affects: *
;* Temp - Returned with busy/address *
;************************************************* ******************
;
Wait_Busy
bsf STATUS, RP0 ; Select Register page 1
movlw 0xf0 ; Set port to input
iorwf LCD_DATA_TRIS,W ; Only set upper half of port
movwf LCD_DATA_TRIS
bcf STATUS, RP0 ; Select Register page 0
bcf LCD_CNTL, RS ; Set LCD for Command mode
bsf LCD_CNTL, R_W ; Setup to read busy flag
bsf LCD_CNTL, E ; Set E high
bcf LCD_CNTL, E ; Set E low
movf LCD_DATA, W ; Read upper nibble busy flag, DDRam address
andlw 0xF0 ; Mask out lower nibble
movwf Temp
bsf LCD_CNTL, E ; Toggle E to get lower nibble
bcf LCD_CNTL, E
swapf LCD_DATA, w ; Read lower nibble busy flag, DDRam address
andlw 0x0F ; Mask out upper nibble
iorwf Temp,W ; Combine nibbles
btfsc Temp, 7 ; Check busy flag, high = busy
goto Wait_Busy ; If busy, check again
bcf LCD_CNTL, R_W
bsf STATUS, RP0 ; Select Register page 1
movlw 0x0F
andwf LCD_DATA_TRIS,W
movwf LCD_DATA_TRIS ; Set Port for output
bcf STATUS, RP0 ; Select Register page 0
return
;
page
;
;************************************************* ******************
;* This routine checks the busy flag, returns ff if busy 00 if not *
;* busy. Temp will contain the current address. *
;* *
;* Affects: *
;* Temp - Returned with busy/address *
;************************************************* ******************
;
Check_Busy
bsf STATUS, RP0 ; Select Register page 1
movlw 0xf0 ; Set port to input
iorwf LCD_DATA_TRIS,W ; Only set upper half of port
movwf LCD_DATA_TRIS
bcf STATUS, RP0 ; Select Register page 0
bcf LCD_CNTL, RS ; Set LCD for Command mode
bsf LCD_CNTL, R_W ; Setup to read busy flag
bsf LCD_CNTL, E ; Set E high
bcf LCD_CNTL, E ; Set E low
movf LCD_DATA, W ; Read upper nibble busy flag, DDRam address
andlw 0xF0 ; Mask out lower nibble
movwf Temp
bsf LCD_CNTL, E ; Toggle E to get lower nibble
bcf LCD_CNTL, E
swapf LCD_DATA, w ; Read lower nibble busy flag, DDRam address
andlw 0x0F ; Mask out upper nibble
iorwf Temp,W ; Combine nibbles
btfsc Temp, 7 ; Check busy flag, high = busy
goto Busy_Exit ; If busy exit with busy flag set
bcf LCD_CNTL, R_W
bsf STATUS, RP0 ; Select Register page 1
movlw 0x0F
andwf LCD_DATA_TRIS,W
movwf LCD_DATA_TRIS ; Set Port for output
bcf STATUS, RP0 ; Select Register page 0
retlw 0x00
Busy_Exit:
bcf LCD_CNTL, R_W
bsf STATUS, RP0 ; Select Register page 1
movlw 0x0F
andwf LCD_DATA_TRIS,W
movwf LCD_DATA_TRIS ; Set Port for output
bcf STATUS, RP0 ; Select Register page 0
retlw 0xff
;
page
;
; Initilize the LCD Display Module
;
LCD_Init:
bcf LCD_CNTL, E ; Clear all controll lines
bcf LCD_CNTL, RS
bcf LCD_CNTL, R_W
call Delay15000 ; Wait for 15ms for LCD to get powered up
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
movlw 0x030 ; Command for 4-bit interface high nibble
iorwf LCD_DATA,F ; Send data to LCD
bsf STATUS, RP0 ; Select Register page 1
movlw 0x0F
andwf LCD_DATA_TRIS,W
movwf LCD_DATA_TRIS ; Set Port for output
BCF STATUS, RP0 ; Select Register page 0
bsf LCD_CNTL, E ; Clock the initalize command to LCD module
bcf LCD_CNTL, E
call Delay4100 ; Delay for at least 4.1ms before continuing
bsf LCD_CNTL, E ; Clock the initalize command to LCD module
bcf LCD_CNTL, E
call Delay100 ; delay for at least 100usec before continuing
bsf LCD_CNTL, E ; Clock the initalize command to LCD module
bcf LCD_CNTL, E
call Wait_Busy ; From here on, the Busy Bit will be valid.
movlw 0x0f
andwf LCD_DATA,F ; Clear the upper nibble
movlw 0x020 ; Command for 4-bit interface high nibble (Really an 8bit command but
; lower 4 bits are don't care at this point)
iorwf LCD_DATA,F ; Send data to LCD
bsf LCD_CNTL, E ; Clock the initalize command to LCD module
bcf LCD_CNTL, E
movlw FUNCTION_SET ; Send the function set command 4-bit I/F, Font, Number of lines
call Send_Cmd ; Can now use the Send_Cmd routine since Busy Bit Valid and in 4bit mode.
movlw DISP_OFF
call Send_Cmd
movlw DISP_ON
call Send_Cmd
movlw ENTRY_INC
call Send_Cmd
; LCD Module is now initalized
return
;
; Delay routines. These routines need to be modified to work with the particular PIC configuration
; used. For test purposes, they are instruction count delays based on 4Mhz PIC. The delays are a
; little longer than advertised but close enough for testing.
;
Delay4100:
movlw d'252'
movwf r1
movlw 4
movwf r2
movlw 1
movwf r3
movlw 1
movwf r4
delay_loop:
nop
decfsz r1,f
goto delay_loop
decfsz r2,f
goto delay_loop
decfsz r3,f
goto delay_loop
decfsz r4,f
goto delay_loop
return
Delay100:
movlw d'22'
movwf r1
movlw 1
movwf r2
movlw 1
movwf r3
movlw 1
movwf r4
goto delay_loop
Delay15000:
movlw d'156'
movwf r1
movlw 15
movwf r2
movlw 1
movwf r3
movlw 1
movwf r4
goto delay_loop
end |