LCD 16X2 Asm
LCD 16X2 Asm
For the first parts of this tutorial you require the Main Board and the LCD Board, the
later parts will also use the Switch Board, as written the tutorials use the LCD Board on
PortA and the Switch Board on PortB. Although the hardware diagram shows a 2x16
LCD, other sizes can be used, I've tested it with a 2x16, 2x20, and 2x40 - all worked
equally well. The intention is to develop a useful set of LCD routines, these will be used
in the later parts of the tutorials to display various information.
Download zipped tutorial files.
LCD Command Control Codes
Command
Binary
Hex
D7
D6
D5
D4
D3
D2
D1
D0
Clear Display
01
02 or 03
I/D
01 to 07
08 to 0F
Display/Cursor Shift
D/C
R/L
10 to 1F
Function Set
8/4
2/1
10/7
20 to 3F
40 to 7F
80 to FF
I/D: 1=Increment*
1=Display Shift
S:
On
D: 1=Display On
1=Cursor
U:
Underline On
1=Cursor Blink
B:
On
D/C
1=Display Shift
:
0=Decrement
0=Display Shift
off*
0=Display Off*
0=Cursor
Underline Off*
0=Cursor Blink
Off*
0=Cursor Move
0=Left Shift
0=4 bit
interface
0=1 line mode*
0=5x7 dot
format*
x=don't care
This table shows the command codes for the LCD module, it was taken from an
excellent LCD tutorial that was published in the UK magazine 'Everyday
Practical Electronics' February 1997 - it can be downloaded as a PDF file from
the EPE website. The following routines are an amalgamation of a number of
routines from various sources (including the previously mentioned tutorial), plus
various parts of my own, the result is a set of reliable, easy to use, routines
which work well (at least in my opinion!).
LIST
p=16F628
include "P16F628.inc"
ERRORLEVEL
0,
messages
__config 0x3D18
(oscillator type etc.)
registers
-302
cblock 0x20
count
count1
counta
countb
tmp1
tmp2
templcd
mode
endc
LCD_PORT
LCD_TRIS
LCD_RS
LCD_RW
LCD_E
templcd2
Equ
Equ
Equ
Equ
Equ
PORTA
TRISA
0x04
0x06
0x07
org
0x0000
movlw
0x07
movwf
CMCON
(make it like a 16F84)
Initialise
clrf
clrf
clrf
count
PORTA
PORTB
SetPorts
bsf
movlw
movwf
bcf
STATUS,
0x00
LCD_TRIS
STATUS,
call
Delay100
RP0
;select bank 1
;make all pins outputs
RP0
;select bank 0
;wait for LCD to settle
zero
Message
text table
NextMessage
column
zero
Message2
text table
call
LCD_Init
;setup LCD
clrf
count
movf
call
count, w
Text
xorlw
btfsc
goto
call
call
incf
goto
0x00
STATUS, Z
NextMessage
LCD_Char
Delay255
count, f
Message
;is it a zero?
call
LCD_Line2
clrf
count
movf
call
count, w
Text2
xorlw
btfsc
goto
call
incf
goto
0x00
STATUS, Z
EndMessage
LCD_Char
count, f
Message2
;is it a zero?
goto
Stop
;endless loop
EndMessage
Stop
0x20
LCD_Cmd
movlw
call
0x28
LCD_Cmd
movlw
0x06
call
LCD_Cmd
movlw
0x0d
call
LCD_Cmd
call
LCD_Clr
retlw
0x00
mode
cursor command
;clear display
templcd
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
movf
andlw
movwf
bcf
call
call
retlw
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
Delay5
0x00
addlw
movwf
swapf
andlw
movwf
bsf
call
0x30
templcd
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
movf
andlw
movwf
bsf
call
call
retlw
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
Delay5
0x00
movlw
0x80
call
retlw
LCD_Cmd
0x00
LCD_Line2
column
movlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_Line1W
W
addlw
0x80
call
retlw
LCD_Cmd
0x00
addlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_CharD
LCD_Char
LCD_Line1
column
LCD_Line2W
W
LCD_CurOn
movlw
cursor command
call
retlw
0x0d
LCD_CurOff
movlw
cursor command
call
0x0c
;RS line to 1
;Pulse the E line high
LCD_Cmd
0x00
LCD_Cmd
retlw
0x00
LCD_Clr
movlw
call
retlw
0x01
LCD_Cmd
0x00
LCD_HEX
movwf
swapf
andlw
call
call
movf
andlw
call
call
retlw
tmp1
tmp1,
w
0x0f
HEX_Table
LCD_Char
tmp1, w
0x0f
HEX_Table
LCD_Char
0x00
Delay255
movlw
goto
movlw
goto
movlw
goto
movlw
goto
movlw
movwf
movlw
movwf
movlw
movwf
0xff
d0
d'100'
d0
d'50'
d0
d'20'
d0
0x05
count1
0xC7
counta
0x01
countb
decfsz
goto
decfsz
goto
counta, f
$+2
countb, f
Delay_0
Delay100
Delay50
Delay20
Delay5
d0
d1
Delay_0
;Clear display
;delay 255 mS
;delay 100mS
;delay 50mS
;delay 20mS
;delay 5.000 ms (4 MHz clock)
;delay 1mS
decfsz count1 ,f
goto
d1
retlw
0x00
Pulse_e
bsf
nop
bcf
retlw
LCD_PORT, LCD_E
LCD_PORT, LCD_E
0x00
ADDWF
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
PCL
0x30
0x31
0x32
0x33
0x34
0x35
0x36
0x37
0x38
0x39
0x41
0x42
, f
RETLW
RETLW
RETLW
RETLW
0x43
0x44
0x45
0x46
Text
addwf
retlw
retlw
retlw
retlw
retlw
retlw
PCL, f
'H'
'e'
'l'
'l'
'o'
0x00
Text2
ADDWF
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
PCL, f
'R'
'e'
'a'
'd'
'y'
'.'
'.'
'.'
0x00
end
As usual, first we need to set things up, after the normal variable declarations and
port setting we reach 'call LCD_Init', this sets up the LCD module. It first waits for
100mS to give the module plenty of time to settle down, we then set it to 4 bit mode
(0x20) and set the various options how we want them - in this case, Display Shift is On
(0x28), Character Entry Mode is Increment (0x06), and Block Cursor On (0x0D). Once
the LCD is setup, we can then start to send data to it, this is read from a table, exactly
the same as the LED sequencer in the earlier tutorials - except this time we send the data
to the LCD module (using LCD_Char) and use a 0x00 to mark the end of the table, thus
removing the need to maintain a count of the characters printed. Once the first line is
displayed we then sent a command to move to the second line (using call LCD_Line2),
and then print the second line from another table. After that we enter an endless loop to
leave the display as it is.
This program introduces a new use of the 'goto' command, 'goto $+2' - '$' is an
MPASM arithmetic operator, and uses the current value of the program counter, so 'goto
$+2' means jump to the line after the next one - 'goto $+1' jumps to the next line, and
may seem pretty useless (as the program was going to be there next anyway), but it can
be extremely useful. A program branch instruction (like goto) uses two instruction
cycles, whereas other instructions only take one, so if you use a 'nop' in a program it
takes 1uS to execute, and carries on from the next line - however, if you use 'goto $+1' it
still carries on from the next line, but now takes 2uS. You'll notice more use of the 'goto
$' construction in later tutorials, if you are checking an input pin and waiting for it to
change state you can use 'goto $-1' to jump back to the previous line, this saves
allocating a label to the line that tests the condition.
This is a table of the LCD subroutines provided in these programs, you can easily
add more if you wish - for instance to set a line cursor rather than a block one, if you
find you are using a particular feature a lot you may as well make a subroutine for it.
LCD Subroutines
LCD_Init
LCD_Cmd
LCD_CharD
LCD_Char
LCD_Line1
Go to start of line 1
LCD_Line2
Go to start of line 2
LCD_HEX
LIST
p=16F628
include "P16F628.inc"
ERRORLEVEL
0,
messages
__config 0x3D18
(oscillator type etc.)
registers
cblock 0x20
count
count1
counta
countb
tmp1
tmp2
templcd
mode
templcd2
NumL
decimal convert routine
NumH
TenK
convert routine
endc
LCD_PORT
LCD_TRIS
LCD_RS
LCD_RW
LCD_E
Thou
Hund
Tens
Ones
Equ
Equ
Equ
Equ
Equ
PORTA
TRISA
0x04
0x06
0x07
org
0x0000
movlw
0x07
movwf
CMCON
(make it like a 16F84)
Initialise
clrf
clrf
clrf
clrf
clrf
count
PORTA
PORTB
NumL
NumH
SetPorts
bsf
movlw
movwf
bcf
STATUS,
0x00
LCD_TRIS
STATUS,
call
LCD_Init
;setup LCD
clrf
count
movf
call
count, w
Text
xorlw
btfsc
goto
call
incf
goto
0x00
STATUS, Z
NextMessage
LCD_Char
count, f
Message
;is it a zero?
call
LCD_Line2
call
Convert
;convert to decimal
zero
Message
text table
NextMessage
column
RP0
;select bank 1
;make all pins outputs
RP0
;select bank 0
movf
characters
call
convert to ASCII
movf
call
movf
call
movf
call
movf
call
movlw
call
movf
hexadecimal
call
movf
call
incfsz
goto
incf
Next
call
digits change
goto
TenK,
;display decimal
LCD_CharD
;using LCD_CharD to
Thou,
w
LCD_CharD
Hund,
w
LCD_CharD
Tens,
w
LCD_CharD
Ones,
w
LCD_CharD
' '
LCD_Char
NumH,
w
;display a 'space'
;and counter in
LCD_HEX
NumL,
w
LCD_HEX
NumL,
f
Next
NumH,
f
Delay255
NextMessage
mode
cursor command
Delay100
movlw
call
0x20
LCD_Cmd
movlw
call
0x28
LCD_Cmd
movlw
0x06
call
LCD_Cmd
movlw
0x0c
call
LCD_Cmd
call
LCD_Clr
;clear display
retlw
0x00
templcd
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
templcd,
andlw
movwf
bcf
call
call
retlw
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
Delay5
0x00
addlw
0x30
movwf
swapf
andlw
movwf
bsf
call
templcd
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
movf
andlw
movwf
bsf
call
call
retlw
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
Delay5
0x00
LCD_Line1
column
movlw
0x80
call
retlw
LCD_Cmd
0x00
LCD_Line2
column
movlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_Line1W
W
addlw
0x80
call
retlw
LCD_Cmd
0x00
LCD_Line2W
W
addlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_CharD
ASCII
LCD_Char
LCD_CurOn
movlw
cursor command
call
retlw
0x0d
LCD_CurOff
movlw
cursor command
call
retlw
0x0c
LCD_Clr
movlw
call
retlw
0x01
LCD_Cmd
0x00
LCD_HEX
movwf
swapf
andlw
tmp1
tmp1,
0x0f
;RS line to 0
;Pulse the E line high
;RS line to 1
;Pulse the E line high
LCD_Cmd
0x00
;Set display on/off and
LCD_Cmd
0x00
;Clear display
Delay255
Delay100
Delay50
Delay20
Delay5
d0
d1
Delay_0
call
call
movf
andlw
call
call
retlw
HEX_Table
LCD_Char
tmp1, w
0x0f
HEX_Table
LCD_Char
0x00
movlw
goto
movlw
goto
movlw
goto
movlw
goto
movlw
movwf
movlw
movwf
movlw
movwf
0xff
d0
d'100'
d0
d'50'
d0
d'20'
d0
0x05
count1
0xC7
counta
0x01
countb
decfsz
goto
decfsz
goto
counta, f
$+2
countb, f
Delay_0
;delay 255 mS
;delay 100mS
;delay 50mS
;delay 20mS
;delay 5.000 ms (4 MHz clock)
;delay 1mS
decfsz count1 ,f
goto
d1
retlw
0x00
Pulse_e
bsf
nop
bcf
retlw
LCD_PORT, LCD_E
LCD_PORT, LCD_E
0x00
ADDWF
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
PCL
0x30
0x31
0x32
0x33
0x34
0x35
0x36
0x37
0x38
0x39
0x41
0x42
0x43
0x44
0x45
0x46
Text
addwf
retlw
retlw
PCL, f
'1'
'6'
, f
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
' '
'B'
'i'
't'
' '
'C'
'o'
'u'
'n'
't'
'e'
'r'
'.'
0x00
NumH,w
0X0F
Hund,f
Hund,f
Ones,f
0XE9
Tens
Tens,f
Tens,f
swapf
andlw
addwf
addwf
NumL,w
0X0F
Tens,f
Ones,f
rlf
rlf
comf
rlf
Tens,f
Ones,f
Ones,f
Ones,f
movf
andlw
addwf
rlf
NumL,w
0X0F
Ones,f
Thou,f
movlw
movwf
0X07
TenK
;
;
;
;
;
Lb2:
Lb3:
Lb4:
movlw
0X0A
addwf
decf
btfss
goto
Ones,f
Tens,f
3,0
Lb1
addwf
decf
btfss
goto
Tens,f
Hund,f
3,0
Lb2
addwf
decf
btfss
goto
Hund,f
Thou,f
3,0
Lb3
addwf
decf
btfss
goto
Thou,f
TenK,f
3,0
Lb4
retlw
0x00
; Ten
end
p=16F628
include "P16F628.inc"
ERRORLEVEL
0,
messages
__config 0x3D18
(oscillator type etc.)
cblock 0x20
registers
count
count1
counta
countb
tmp1
tmp2
templcd
mode
templcd2
NumL
decimal convert routine
NumH
TenK
convert routine
endc
LCD_PORT
LCD_TRIS
LCD_RS
LCD_RW
LCD_E
Thou
Hund
Tens
Ones
Equ
Equ
Equ
Equ
Equ
PORTA
TRISA
0x04
0x06
0x07
org
goto
0x0000
Start
HEX_Table
ADDWF
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
RETLW
PCL
0x30
0x31
0x32
0x33
0x34
0x35
0x36
0x37
0x38
0x39
0x41
0x42
0x43
0x44
0x45
0x46
Text
addwf
retlw
retlw
retlw
retlw
retlw
retlw
PCL, f
'1'
'6'
' '
'B'
'i'
't'
, f
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
retlw
' '
'C'
'o'
'u'
'n'
't'
'e'
'r'
'.'
0x00
Start
movlw
0x07
movwf
CMCON
(make it like a 16F84)
Initialise
clrf
clrf
clrf
clrf
clrf
count
PORTA
PORTB
NumL
NumH
SetPorts
bsf
movlw
movwf
movwf
bcf
STATUS,
0x00
LCD_TRIS
TRISB
STATUS,
call
LCD_Init
;setup LCD
clrf
count
movf
call
count, w
Text
xorlw
btfsc
goto
call
incf
goto
0x00
STATUS, Z
NextMessage
LCD_Char
count, f
Message
;is it a zero?
movlw
call
d'2'
LCD_Line2W
call
movf
Convert
TenK,
w
;convert to decimal
;display decimal
call
convert to ASCII
movf
call
movf
call
movf
call
movf
call
LCD_CharD
;using LCD_CharD to
zero
Message
text table
NextMessage
column
characters
Thou,
w
LCD_CharD
Hund,
w
LCD_CharD
Tens,
w
LCD_CharD
Ones,
w
LCD_CharD
RP0
;select bank 1
;make all pins outputs
RP0
;select bank 0
hexadecimal
Next
digits change
movlw
call
movf
' '
LCD_Char
NumH,
w
call
movf
call
incfsz
goto
incf
call
LCD_HEX
NumL,
w
LCD_HEX
NumL,
f
Next
NumH,
f
Delay255
goto
NextMessage
;display a 'space'
;and counter in
mode
cursor command
LCD_Busy
movlw
call
0x20
LCD_Cmd
movlw
call
0x28
LCD_Cmd
movlw
0x06
call
LCD_Cmd
movlw
0x0c
call
LCD_Cmd
call
LCD_Clr
;clear display
retlw
0x00
LCD_CharD
ASCII
LCD_Char
templcd
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
movf
andlw
movwf
bcf
call
call
retlw
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
LCD_Busy
0x00
addlw
0x30
movwf
swapf
andlw
templcd
templcd,
0x0f
;RS line to 0
;Pulse the E line high
movwf
bsf
call
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
movf
andlw
movwf
bsf
call
call
retlw
templcd,
w
0x0f
LCD_PORT
LCD_PORT, LCD_RS
Pulse_e
LCD_Busy
0x00
LCD_Line1
column
movlw
0x80
call
retlw
LCD_Cmd
0x00
LCD_Line2
column
movlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_Line1W
W
addlw
0x80
call
retlw
LCD_Cmd
0x00
LCD_Line2W
W
addlw
0xc0
call
retlw
LCD_Cmd
0x00
LCD_CurOn
movlw
cursor command
call
retlw
0x0d
LCD_CurOff
movlw
cursor command
call
retlw
0x0c
LCD_Clr
movlw
call
retlw
0x01
LCD_Cmd
0x00
LCD_HEX
movwf
swapf
andlw
call
call
movf
andlw
call
call
retlw
tmp1
tmp1,
w
0x0f
HEX_Table
LCD_Char
tmp1, w
0x0f
HEX_Table
LCD_Char
0x00
Delay255
movlw
goto
movlw
goto
0xff
d0
d'100'
d0
Delay100
;RS line to 1
;Pulse the E line high
;RS line to 1
;Pulse the E line high
LCD_Cmd
0x00
;Set display on/off and
LCD_Cmd
0x00
;Clear display
;delay 255 mS
;delay 100mS
Delay50
Delay20
Delay5
clock)
d0
d1
Delay_0
movlw
goto
movlw
goto
movlw
d'50'
d0
d'20'
d0
0x05
movwf
movlw
movwf
movlw
movwf
count1
0xC7
counta
0x01
countb
decfsz
goto
decfsz
goto
counta, f
$+2
countb, f
Delay_0
;delay 50mS
;delay 20mS
;delay 5.000 ms (4 MHz
;delay 1mS
decfsz count1 ,f
goto
d1
retlw
0x00
Pulse_e
bsf
nop
bcf
retlw
LCD_PORT, LCD_E
bsf
movlw
movwf
bcf
bcf
bsf
bsf
swapf
STATUS, RP0
0x0f
LCD_TRIS
STATUS, RP0
LCD_PORT, LCD_RS
LCD_PORT, LCD_RW
LCD_PORT, LCD_E
LCD_PORT, w
bcf
movwf
bsf
LCD_PORT, LCD_E
templcd2
LCD_PORT, LCD_E
bcf
btfsc
LCD_PORT, LCD_E
templcd2, 7
goto
bcf
bsf
movlw
movwf
bcf
return
LCD_Busy
LCD_PORT, LCD_RW
STATUS, RP0
0x00
LCD_TRIS
STATUS, RP0
LCD_PORT, LCD_E
0x00
LCD_Busy
flag)
nibble
busy
;set bank 1
;set Port for input
;set bank 0
;set LCD for command mode
;setup to read busy flag
;read upper nibble (busy
movwf
addwf
addlw
movwf
addlw
movwf
Thou
Thou,f
0XE2
Hund
0X32
Ones
movf
andlw
addwf
addwf
addwf
addlw
movwf
addwf
addwf
NumH,w
0X0F
Hund,f
Hund,f
Ones,f
0XE9
Tens
Tens,f
Tens,f
swapf
andlw
addwf
addwf
NumL,w
0X0F
Tens,f
Ones,f
rlf
rlf
comf
rlf
Tens,f
Ones,f
Ones,f
Ones,f
movf
andlw
addwf
rlf
NumL,w
0X0F
Ones,f
Thou,f
movlw
movwf
0X07
TenK
;
;
;
;
;
;
;
;
Lb1:
Lb2:
Lb3:
Lb4:
movlw
0X0A
addwf
decf
btfss
goto
Ones,f
Tens,f
3,0
Lb1
addwf
decf
btfss
goto
Tens,f
Hund,f
3,0
Lb2
addwf
decf
btfss
goto
Hund,f
Thou,f
3,0
Lb3
addwf
decf
btfss
goto
Thou,f
TenK,f
3,0
Lb4
retlw
0x00
end