LCD Display Driver
Overview
The control of the HD47780 based LCD display is handled by this module. There is a lot of information available from the internet about this type of display, and the necessary control pins etc. This code uses 4 bit mode (so each byte has to be clocked in as 2 nibbles) in order to save I/O lines.
One thing that's slightly unusual about these displays is the screen layout - I was using a 4 line display (20 chars width) and this is handled like a 2 line display, with the 1st and 3rd lines being treated as a 40 character line (and the same for line 2 being mapped to lines 2 and 4).
Code
subtitle "LCD Module" LCD_PORT_D4 EQU PORTA LCD_PORT_D5 EQU PORTA LCD_PORT_D6 EQU PORTA LCD_PORT_D7 EQU PORTA LCD_PORT_E EQU PORTE LCD_PORT_RS EQU PORTE LCD_PORT_RW EQU PORTE LCD_BIT_D4 EQU 0 LCD_BIT_D5 EQU 1 LCD_BIT_D6 EQU 2 LCD_BIT_D7 EQU 3 LCD_BIT_E EQU 0 LCD_BIT_RS EQU 1 ; 0 = Instruction, 1 = Data LCD_BIT_RW EQU 2 ; 0 = Write, 1 = Read ; ************************************************************** ; * ; * LCD_Initialise ; * ; * General Initialisation routines for the LCD display ; * ; ************************************************************** LCD_Initialise ; PORTE is exclusively for the LCD display, so ; set all these as outputs banksel TRISE clrf TRISE movlw H'06' ; Same bank as TRISE movwf ADCON1 ; Setup PORTE for Digital I/O ; PORTA set for outputs banksel TRISA movlw b'11110000' andwf TRISA, F ; Make sure the enable pin is cleared banksel PORTE bcf LCD_PORT_E, LCD_BIT_E banksel LCD_PORT_RS bsf LCD_PORT_E, LCD_BIT_E bcf LCD_PORT_RS, LCD_BIT_RS bcf LCD_PORT_RW, LCD_BIT_RW movlw 0xf0 banksel LCD_PORT_D4 andwf LCD_PORT_D4, F ; Clear the Data port ; Wait for 15mS as the Power Up delay VarDelay10mS 4 bsf LCD_PORT_D5, LCD_BIT_D5 bsf LCD_PORT_D4, LCD_BIT_D4 ; Load 0x03 ; drop the clock to latch data bcf LCD_PORT_E, LCD_BIT_E ; Wait for 4.1mS as the Power Up delay VarDelay10mS 0x2 ; Now clock the same data again bsf LCD_PORT_E, LCD_BIT_E VarDelay100uS 5 bcf LCD_PORT_E, LCD_BIT_E VarDelay100uS 5 ; And one more time bsf LCD_PORT_E, LCD_BIT_E VarDelay100uS 5 bcf LCD_PORT_E, LCD_BIT_E VarDelay100uS 5 ; Now setup the display correctly movlw 0x08 ; 4-bit interface, 2-lines call LCD_FunctionSet movlw 0x00 call LCD_DisplayOnOff ; Disp Off, Cursor Off, No blink movlw 0x04 call LCD_DisplayOnOff ; Display On, Cursor Off call LCD_ClearDisplay movlw 0x02 ; Auto increment (shift-cursor) call LCD_EntryModeSet return ; ************************************************************** ; * ; * LCD_Load8Bits ; * ; * Load 8 bits of data from W to the display ; * ; ************************************************************** LCD_Load8Bits movwf LCDTemp0 banksel TRISA ; Make sure PORTA 0-3 are set to outputs movlw H'F0' andwf TRISA, 1 banksel PORTE ; Set RW line to low (Write mode) bcf LCD_PORT_RW, LCD_BIT_RW ; Raise the Enable line ready for loading the data bsf LCD_PORT_E, LCD_BIT_E ; Now load the first (upper) nibble onto the lines bcf LCD_PORT_D4, LCD_BIT_D4 ; Clear the D4 line btfsc LCDTemp0, 4 ; If Data, bit 4 is set, execute next line: bsf LCD_PORT_D4, LCD_BIT_D4 ; Set D4 line bcf LCD_PORT_D5, LCD_BIT_D5 ; Clear the D5 line btfsc LCDTemp0, 5 ; If Data, bit 5 is set, execute next line: bsf LCD_PORT_D5, LCD_BIT_D5 ; Set D5 line bcf LCD_PORT_D6, LCD_BIT_D6 ; Clear the D6 line btfsc LCDTemp0, 6 ; If Data, bit 6 is set, execute next line: bsf LCD_PORT_D6, LCD_BIT_D6 ; Set D6 line bcf LCD_PORT_D7, LCD_BIT_D7 ; Clear the D7 line btfsc LCDTemp0, 7 ; If Data, bit 7 is set, execute next line: bsf LCD_PORT_D7, LCD_BIT_D7 ; Set D7 line ; Drop the enable line to load the data bcf LCD_PORT_E, LCD_BIT_E ; Raise the Enable line ready for loading the data bsf LCD_PORT_E, LCD_BIT_E ; Now load the second (lower) nibble onto the lines bcf LCD_PORT_D4, LCD_BIT_D4 ; Clear the D4 line btfsc LCDTemp0, 0 ; If Data, bit 0 is set, execute next line: bsf LCD_PORT_D4, LCD_BIT_D4 ; Set D4 line bcf LCD_PORT_D5, LCD_BIT_D5 ; Clear the D5 line btfsc LCDTemp0, 1 ; If Data, bit 1 is set, execute next line: bsf LCD_PORT_D5, LCD_BIT_D5 ; Set D5 line bcf LCD_PORT_D6, LCD_BIT_D6 ; Clear the D6 line btfsc LCDTemp0, 2 ; If Data, bit 2 is set, execute next line: bsf LCD_PORT_D6, LCD_BIT_D6 ; Set D6 line bcf LCD_PORT_D7, LCD_BIT_D7 ; Clear the D7 line btfsc LCDTemp0, 3 ; If Data, bit 3 is set, execute next line: bsf LCD_PORT_D7, LCD_BIT_D7 ; Set D7 line ; Drop the enable line to load the data bcf LCD_PORT_E, LCD_BIT_E VarDelay100uS 0x1 return ; ************************************************************** ; * ; * LCD_WriteInstruction ; * ; * Write an 8 bit instruction from W to the display ; * ; ************************************************************** LCD_WriteInstruction ; Clear the RS bit (INSTRUCTION mode) bcf LCD_PORT_RS, LCD_BIT_RS ; Transfer the contents of W to the display call LCD_Load8Bits return ; ************************************************************** ; * ; * LCD_WriteData ; * ; * Write 8 bit data from W to the display ; * ; ************************************************************** LCD_WriteData ; Set the RS bit (DATA mode) bsf LCD_PORT_RS, LCD_BIT_RS ; Transfer the contents of W to the display call LCD_Load8Bits ; Increment the screen locations (save having to read from display) incf ScreenLoc, F return ; ************************************************************** ; * ; * LCD_WriteData (MACRO) ; * ; * Write 8 bit data from W to the display ; * ; ************************************************************** LCD_DisplayText macro TextPointer movlw 0 ; Startindex of table message LocalLCD_DisplayMsg movwf LCDTextTableIndex ; Holds message address pagesel TextPointer call TextPointer andlw 0x0FF ; Check if at end of message btfsc STATUS, Z ; (zero returned at end) goto LocalLCD_DisplayMsgEnd call LCD_WriteData ; Display character movf LCDTextTableIndex, W ; Point to next character addlw 1 goto LocalLCD_DisplayMsg LocalLCD_DisplayMsgEnd endm ; ************************************************************** ; * ; * LCD_ClearDisplay ; * ; ************************************************************** LCD_ClearDisplay movlw H'01' call LCD_WriteInstruction VarDelay100uS 40 return ; ************************************************************** ; * ; * LCD_CursorHome ; * ; ************************************************************** LCD_CursorHome movlw H'02' call LCD_WriteInstruction VarDelay100uS 40 return ; ************************************************************** ; * ; * LCD_EntryModeSet ; * ; * W: Inc/Dec = bit 1, Shift = bit 0 ; * ; ************************************************************** LCD_EntryModeSet ; Set bit 2 and transfer bits 0 and 1 iorlw b'00000100' andlw b'00000111' call LCD_WriteInstruction return ; ************************************************************** ; * ; * LCD_DisplayOnOff ; * ; * W: I/O = bit 2, Cursor = bit 1, Blink = bit 0 ; * ; ************************************************************** LCD_DisplayOnOff ; Set bit 3 and transfer bits 0, 1 and 2 iorlw b'00001000' andlw b'00001111' call LCD_WriteInstruction return ; ************************************************************** ; * ; * LCD_CursorDisplayShift ; * ; * W: S/C = bit 3, R/L = bit 2 ; * ; ************************************************************** LCD_CursorDisplayShift ; Set bit 4 and transfer bits 2 and 3 iorlw b'00010000' andlw b'00011100' call LCD_WriteInstruction return ; ************************************************************** ; * ; * LCD_FunctionSet ; * ; * W: DL = bit 4, N = bit 3, F = bit 2 ; * ; ************************************************************** LCD_FunctionSet ; Set bit 5 and transfer bits 3 and 4 iorlw b'00100000' andlw b'00111100' call LCD_WriteInstruction return ; ************************************************************** ; * ; * LCD_SetCGRAMAddress ; * ; * W: bits 0 to 5 = CGRAM Address ; * ; ************************************************************** LCD_SetCGRAMAddress ; Set bit 6 and transfer bits 0 to 5 iorlw b'01000000' andlw b'01111111' call LCD_WriteInstruction return ; ************************************************************** ; * ; * LCD_SetDDRAMAddress ; * ; * W: bits 0 to 6 = DDRAM Address ; * ; ************************************************************** LCD_SetDDRAMAddress ; Set bit 7 and transfer bits 0 to 6 iorlw b'10000000' andlw b'11111111' call LCD_WriteInstruction return