In this project the PIC is used to control an LCD (Liquid Crystal Display). The LCDs usually support either a serial interface or a parallel one. Serial devices are usually more expensive, since they contain a decent electronics that implement a serial interface. Concerning the parallel mode, there are two interfaces: an 8-bit mode and 4-bit mode. The difference is in the number of control pins necessary to drive LCD. In the 4-bit mode this number is 6.
The LCD I use is LCM-S01602DSR/D manufactured by Lumex. It contains 2 rows of 16 displayed characters and uses an 5x8 font. Actually, the LCD can store a line of 40 characters internally and can scroll it back and force. It is avaliable from digikey.com for about $15. The test program displays a string in both rows of the display module.
The hardware is extremely simple and contains just one element besides the PIC and LCD - the pot for adjusting the LCD contrast. To achieve the maximum contrast the Vo pin of the LCD can be just connected to the ground, thus eliminating a need in the pot. There are 6 control lines connecting the LCD with the PIC.
Schematic | Layout | |
The messages to display (msg1 and msg2) are stored as read-only arrays and put at the beginning of the code to make sure that they occur within the first address block. This is needed for a correct (and simple) implementation of the computed goto technique. The code starts with initializing the PIC and LCD according to the specification.
Two loops in the main program part are intended to output the first and the second zero-terminated strings, respectively. The LCD interface is served with 3 methods: initLCD(), writeCMD(), and writeDATA(). The first one provides necessary delays to initialize the LCD electronics right after power on and setup the 4-bit mode and other stuff. The second and the third methods are practically identical and are intended to passing a command or a data byte to the LCD, respectively. Since the 4-bit interface is used, the byte is transferred in two steps, one nibble after another. A 160μsec delay is needed after all instructions excluding clearing the display. The last instruction as well as any data transfer needs a 5 msec delay for the LCD to process it.
TITLE "lcd4.asm" ; working with an LCD in the 4-bit mode List P=16F84a, R=DEC INCLUDE "p16F84a.inc" ; data segment CBLOCK 0x00C del, cmd ; variables for passing the parameters temp, i ; local variables ENDC ; code segment PAGE __CONFIG _CP_OFF & _PWRTE_ON & _WDT_OFF & _HS_OSC org 0 ; start program at the beginning of mem goto start msg1 ; first message to display addwf PCL, f dt " Welcome to", 0 msg2 ; second message to display addwf PCL, f dt "PIC programming", 0 start bsf STATUS, RP0 ; change to BANK 1 clrf TRISB ^ 0x80 ; enable RB1-RB7 for output movlw 0x7F ; enable internal Pull-Ups movwf OPTION_REG ^ 0x80 bcf STATUS, RP0 ; back to BANK 0 call initLCD ; initialize LCD display ; output of the top row (msg1) clrf i L1 movf i, w call msg1 ; get the next char iorlw 0 ; end of string? btfsc STATUS, Z goto L2 ; YES - jump out of the loop call writeDATA ; NO - send it to LCD incf i, f ; update the char index goto L1 L2 movlw 0xC0 call writeCMD ; move the cursor to the second line clrf i ; clear the index of the second string ; output the bottom row (msg2) L3 movf i, w call msg2 ; get the next char iorlw 0 ; end of string? btfsc STATUS, Z goto L4 ; YES - jump out of the loop call writeDATA ; NO - send it to LCD incf i, f ; update the char index goto L3 L4 goto $ ; endless loop ; procedures initLCD ; initialize LCD right after power up movlw 15 movwf del call delay ; a 15 msec delay after power is on movlw 3 movwf PORTB ; start LCD init process movlw 5 movwf del call delay ; wait for 5 msec movlw 3 movwf PORTB ; start LCD init process bsf PORTB, 4 ; toggle the LCD Enable pin bcf PORTB, 4 movlw 5 movwf del call delay ; wait for 5 msec bsf PORTB, 4 ; repeat the reset command (2nd time) bcf PORTB, 4 movlw 40 ; wait for 200 usec sublw 1 sublw 0 btfss STATUS, Z goto $-3 bsf PORTB, 4 ; repeat the reset command (3rd time) bcf PORTB, 4 movlw 40 ; wait for 200 usec sublw 1 sublw 0 btfss STATUS, Z goto $-3 movlw 2 ; initialize LCD 4-bit mode movwf PORTB bsf PORTB, 4 ; toggle the LCD Enable pin bcf PORTB, 4 movlw 0x28 ; 2-line mode call writeCMD movlw 1 call writeCMD ; clear display movlw 5 ; wait for 5 msec after clearing movwf del call delay movlw 0x06 ; cursor move after each char call writeCMD movlw 0x0E ; turn on LCD and enable cursor call writeCMD return writeCMD ; write to LCD a 1-byte command in W movwf temp swapf temp, w andlw 0x0F ; leave only the high 4 bits in W movwf PORTB bsf PORTB, 4 ; toggle the E bit bcf PORTB, 4 movfw temp ; proceed with the low bit andlw 0x0F movwf PORTB bsf PORTB, 4 ; toggle the bit bcf PORTB, 4 movlw 40 ; wait for 200 usec sublw 1 sublw 0 btfss STATUS, Z goto $-3 return writeDATA ; write to LCD a 1-byte data in W movwf temp swapf temp, w andlw 0x0F ; leave only the lower 4 bits in W movwf PORTB bsf PORTB, 5 ; sending data bsf PORTB, 4 ; toggle the E bit bcf PORTB, 4 movfw temp ; proceed with the low bit andlw 0x0F movwf PORTB bsf PORTB, 5 ; sending data bsf PORTB, 4 ; toggle the bit bcf PORTB, 4 movlw 5 ; wait for 5 msec movwf del call delay return delay ; a delay for del milliseconds movlw 200 sublw 1 ; this loop takes 5us*200 = 1ms sublw 0 ; for PIC16F84A @ 4 Mhz btfss STATUS, Z goto $-3 decfsz del,f goto delay return end |
Download lcd4.asm
Last modified:Mon, Jan 23, 2023.