Maker Pro
Maker Pro

24X2 character LCD driver code

C

Chris Gentry

Jan 1, 1970
0
Hi,
I'm looking for assembly code to allow me to use my 24X2 character LCD
module with a PIC 16F873A microcontroller. I've googled on the subject, and
spent countless hours looking through webpages with no luck. I thought I
had found something, but after trying it in my project the LCD display still
does not work. I've checked the contrast, but it might be in the
initialization. I bought the
module from Crystalfontz.com, and when I contacted them, they were little
help. I do have the datasheet on this module, and I modeled my
initialization routine off of the recommended routine there. The module is
a CFAH2402A-YYB-JP.

If anyone could direct me to other assembly code to control this device, I
would appreciate it. Also I'm going to put my code below, so the experts
here can pick it apart and tell me what I've done wrong.
I tried to just cut/paste the code on here, but it didn't line up right, so
I just put it on a website.
I'm a novice at this, but I'm willing to work hard to make myself
better. -Chris

http://www.octowave.com/~lkmud/wavemaker
 
S

Spehro Pefhany

Jan 1, 1970
0
On Mon, 12 Sep 2005 22:00:16 GMT, the renowned "Chris Gentry"

That's a standard HD44780 type module.

Have you tried runing the code in MPSIM to verify the delays? Make
sure your simulation clock matches the hardware.


Best regards,
Spehro Pefhany
 
M

Martin Riddle

Jan 1, 1970
0
Also follow the startup sequence exactly as the spec says, otherwise you will get garbage.
 
Chris Gentry said:
I'm looking for assembly code to allow me to use my 24X2 character LCD
module with a PIC 16F873A microcontroller.

I don't know if it will help, but I wrote some C under Linux to control
a similar LCD module on the parallel port. It's not very beautiful -
basically it goes step-by-step through the initialization sequences -
but this might make it easier to understand. It is appended below.

Matt Roberds

---
/* lcd.c - Matt Roberds [email protected] - 8/25/99 */
/* control an LCD module over the parallel port */

#include <stdio.h>

#include <asm/io.h>
#include <sys/time.h>
#include <sys/types.h>

#define DATAPORT 0x0378
#define STATUS DATAPORT+1
#define CONTROL DATAPORT+2

void microdelay(int delay);
void strobe(int data);

/* LCD module hookup
Port addres Module PC
DATA[0-7] data0-data7 data0-data7
CONTROL[0] E (enable) strobe (pin 1; inverted logic)
CONTROL[2] RS (data/cmd) init (pin 16; standard logic)
CONTROL[3] R/W (read/write) select (pin 17; inverted logic) */

#define ENABLE 0xfe
#define DATA 0x04
#define WRITE 0xf7

void main(void)
{
int i;
char message[256];

ioperm(DATAPORT,1,1);
ioperm(STATUS,1,1);
ioperm(CONTROL,1,1);

/* printf("ioperm ok\n"); */

/* init code - parallel port */

outb(0, DATAPORT); /* zero data lines */

/*
strobe low 0x01
write 0x08
command 0x00
*/

outb(0x09, CONTROL); /* disabled (strobe low), write mode, command mode */

microdelay(50);

printf("port setup OK\n");

/* init code - LCD */

for(i=0;i<3;i++)
{
outb(0x30, DATAPORT); /* wave a chicken */
strobe(0);
}

printf("chicken waved OK\n");

outb(0x38, DATAPORT); /* 8bit, 1/16 duty, 5x8 display */

strobe(0);

outb(0x04, DATAPORT); /* Display off, cursor off, blink off */

strobe(0);

outb(0x01, DATAPORT); /* Clear screen, cursor home */

strobe(0);

microdelay(1700);

outb(0x06, DATAPORT); /* Increment cursor to right, don't shift screen */

strobe(0);

outb(0x0c, DATAPORT); /* Display ON, cursor off, blink off */

strobe(0);

outb(0x80, DATAPORT); /* Point at RAM addr 0 */

strobe(0);

printf("LCD setup OK\n");

/* try to throw a string at it */

outb(0x04, CONTROL); /* now in data mode */

strobe(1);

printf("data mode OK\n");

while(1)
{
printf("Enter a short string > ");

fgets(message, 256, stdin);

#if 0
for(i=0;i<48;i++)
{
/* message=i+160; */
message=i+228;
}

message = '\0';
#endif

outb(0x09, CONTROL); /* Strobe low, write mode, command mode */

microdelay(50);

outb(0x01, DATAPORT); /* Clear screen, cursor home */

strobe(0);

microdelay(1700);

outb(0x04, CONTROL); /* now in data mode */

microdelay(50);

i=0;

while(message)
{
if(message == '\n')
{
message = ' ';
}
i++;
}

if(!strcmp(message, "quit "))
{
exit(0);
}

i=0;

while(message && (i<24))
{
outb(message[i++], DATAPORT);
strobe(1);
}

if(message)
{
outb(0x09, CONTROL); /* Strobe low, write mode, command mode */

microdelay(50);

outb(0xc0, DATAPORT); /* Point at RAM addr 0x40, start of second line */

strobe(0);

outb(0x04, CONTROL); /* now in data mode */

microdelay(50);

while(message && (i<48))
{
outb(message[i++], DATAPORT);
strobe(1);
}
}
}
}

void microdelay(int delay)
{
struct timeval tv;

tv.tv_sec=delay / 1000000;
tv.tv_usec=delay % 1000000;

select(0,0,0,0,&tv);
}

void strobe(int data)
{
int control;

/*
strobe low 0x01, high 0x00
write 0x08
command 0x00, data 0x04

low/write/command: 0x09
low/write/data: 0x0d

high/write/command:0x08
high/write/data: 0x0c
*/

if(data)
control=0x0c;
else
control=0x08;

outb(control, CONTROL); /* enable high */

/* printf("high control: %x\n", inb(CONTROL)); */

microdelay(5);

if(data)
control=0x0d;
else
control=0x09;

outb(control, CONTROL);

/* printf("low control: %x\n", inb(CONTROL));*/

microdelay(50);
}
---
 
J

James Waldby

Jan 1, 1970
0
Chris Gentry wrote: ...
I'm looking for assembly code to allow me to use my 24X2
character LCD module with a PIC 16F873A microcontroller. ....
[The LCD] does not work. I've checked the contrast, but
it might be in the initialization. ....
[The LCD] is a CFAH2402A-YYB-JP. ....
[Code is at] http://www.octowave.com/~lkmud/wavemaker

comp.arch.embedded probably is a more-appropriate newsgroup
for your query.

You need to provide a link to a circuit diagram so people
can check if your values of LCD_D4...7, your notion of
contrast setting, etc are correct.

Be a little more precise about "does not work". Do you
get black rectangles on line 1 when you crank the contrast?
Do randomly moving blobs appear? Does it just stay plain
blank?

Looking at the __LCD_INIT routine in
http://www.octowave.com/~lkmud/wavemaker/lcd_4bit.inc
it appears that P. Pemberton has not followed the
correct sequence of LCD initialization instructions as
shown on the second page of section 13 of your reference
http://www.octowave.com/~lkmud/wavemaker/CFAH2402AYYBJP.pdf
(ie, the page labeled "4-Bit Ineterface"). The correct
sequence is: Wait > 15ms, send 0011, wait > 4.1 ms,
send 0011, wait > .1 ms, send 0011, check BF (the busy
flag), send 0010, etc. What the routine instead does is:
Wait > 15ms, send 0011, send 0011, wait > 4.1 ms,
send 0011, send 0011, wait > 1 ms, send 0011, send 0010,
etc. It may be that Pemberton's code worked ok with
some other LCD, say 16x2, or 24x1, etc, but has problems
with your 24x2. Or it may be that the above is not the
problem at all; I have used similar (wrong) code ok with
at least two different LCD's, but another one wouldn't
work until I matched the correct sequence more exactly.
-jiw
 
C

Chris Gentry

Jan 1, 1970
0
James Waldby said:
Chris Gentry wrote: ...
I'm looking for assembly code to allow me to use my 24X2
character LCD module with a PIC 16F873A microcontroller. ...
[The LCD] does not work. I've checked the contrast, but
it might be in the initialization. ...
[The LCD] is a CFAH2402A-YYB-JP. ...
[Code is at] http://www.octowave.com/~lkmud/wavemaker

comp.arch.embedded probably is a more-appropriate newsgroup
for your query.

You need to provide a link to a circuit diagram so people
can check if your values of LCD_D4...7, your notion of
contrast setting, etc are correct.

Be a little more precise about "does not work". Do you
get black rectangles on line 1 when you crank the contrast?
Do randomly moving blobs appear? Does it just stay plain
blank?

Looking at the __LCD_INIT routine in
http://www.octowave.com/~lkmud/wavemaker/lcd_4bit.inc
it appears that P. Pemberton has not followed the
correct sequence of LCD initialization instructions as
shown on the second page of section 13 of your reference
http://www.octowave.com/~lkmud/wavemaker/CFAH2402AYYBJP.pdf
(ie, the page labeled "4-Bit Ineterface"). The correct
sequence is: Wait > 15ms, send 0011, wait > 4.1 ms,
send 0011, wait > .1 ms, send 0011, check BF (the busy
flag), send 0010, etc. What the routine instead does is:
Wait > 15ms, send 0011, send 0011, wait > 4.1 ms,
send 0011, send 0011, wait > 1 ms, send 0011, send 0010,
etc. It may be that Pemberton's code worked ok with
some other LCD, say 16x2, or 24x1, etc, but has problems
with your 24x2. Or it may be that the above is not the
problem at all; I have used similar (wrong) code ok with
at least two different LCD's, but another one wouldn't
work until I matched the correct sequence more exactly.
-jiw

So in your estimation this would be good code to stick with? Someone told
me my delays might not be correct, but I'm not sure how to check them in
MPSIM. I can simulate it, but I don't know where it would show the correct
times.

As for the contrast I get dark rectangles across the first row when I turn
it up. I'm posting a little early since I haven't finished modeling the
init routine off of what is in the specs, but I'm going to do that tonight,
and get back to everyone here. Thanks for your help so far. -Chris
 
J

James Waldby

Jan 1, 1970
0
Chris said:
"James Waldby" ... wrote ...
Chris Gentry wrote: ...
I'm looking for assembly code to allow me to use my 24X2
character LCD module with a PIC 16F873A microcontroller. ...
[The LCD] does not work. I've checked the contrast, but
it might be in the initialization. ...
[The LCD] is a CFAH2402A-YYB-JP. ...
[Code is at] http://www.octowave.com/~lkmud/wavemaker

comp.arch.embedded probably is a more-appropriate newsgroup
for your query. [snip]
Looking at the __LCD_INIT routine in
http://www.octowave.com/~lkmud/wavemaker/lcd_4bit.inc
it appears that P. Pemberton has not followed the
correct sequence of LCD initialization instructions as
shown on the second page of section 13 of your reference
http://www.octowave.com/~lkmud/wavemaker/CFAH2402AYYBJP.pdf
(ie, the page labeled "4-Bit Ineterface"). The correct
sequence is: Wait > 15ms, send 0011, wait > 4.1 ms,
send 0011, wait > .1 ms, send 0011, check BF (the busy
flag), send 0010, etc. What the routine instead does is:
Wait > 15ms, send 0011, send 0011, wait > 4.1 ms,
send 0011, send 0011, wait > 1 ms, send 0011, send 0010,
etc.
[snip]

So in your estimation this would be good code to stick with?

Since I'm used to reading AVR assembly and don't know much
about pic's I thought it all looked just awful. :)
But here's an idea to try anyway - Change:
MOVLW B'00110011' ; 2x 8bit resets
CALL LCD_SENDINS
call Delay20MS
MOVLW B'00110011' ; 2x 8bit resets
CALL LCD_SENDINS
CALL Delay1MS
MOVLW B'00110010' ; 8bit reset then 4bit reset
CALL LCD_SENDINS
to:
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
call Delay20MS
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
CALL Delay1MS
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
CALL Delay1MS
MOVLW B'0010' ; 4bit reset
CALL LCD_SENDIN4
where LCD_SENDIN4 is:
LCD_SENDIN4:
MOVWF __LCD_TEMP0 ; Store byte in Temp 0
BCF LCD_RS ; RS low=instruction
CALL __LCD_SEND4 ; Send 1 nibble
CALL Delay1MS
RETURN
and __LCD_SEND4 is a label at line 43 in lcd_4bit.inc,
ie, just before the second BCF LCD_D7 of __LCD_SEND.
Note, see http://pat7.com/jp/2313s2.null.c at line 108; the
ini0 table corresponds to those first 4 instructions which
are sent to the LCD unit via its top 4 data lines; the
lower nibbles should not be sent, because the unit is not
yet in 4-bit mode. Your current code has no delay between
the third 8-bit reset and the 4-bit reset, while spec says
to check the busy flag or wait > 39 uS. (The code in
2313s2.null.c works ok for AVR AT90S2313 board like at http://pat7.com/jp/2313s2.sch.jpg .)
Someone told me my delays might not be correct, but I'm
not sure how to check them in MPSIM. I can simulate it,
but I don't know where it would show the correct
times.

What is your crystal frequency, anyway? You should check
out your delay routines by blinking an LED at 1Hz or an
easy-to-time .2Hz etc before trying to get the LCD to work.
As for the contrast I get dark rectangles across the
first row when I turn it up.

That is what you get just by applying power, which gives
you 8-bit interface and 1-line display. After software
initialization per specs you will have a blinking cursor
in first column first row, and turning up the contrast
won't show any black rectangles.
-jiw
 
C

Chris Gentry

Jan 1, 1970
0
James Waldby said:
Chris said:
"James Waldby" ... wrote ...
Chris Gentry wrote: ...
I'm looking for assembly code to allow me to use my 24X2
character LCD module with a PIC 16F873A microcontroller.
...
[The LCD] does not work. I've checked the contrast, but
it might be in the initialization.
...
[The LCD] is a CFAH2402A-YYB-JP.
...
[Code is at] http://www.octowave.com/~lkmud/wavemaker

comp.arch.embedded probably is a more-appropriate newsgroup
for your query. [snip]
Looking at the __LCD_INIT routine in
http://www.octowave.com/~lkmud/wavemaker/lcd_4bit.inc
it appears that P. Pemberton has not followed the
correct sequence of LCD initialization instructions as
shown on the second page of section 13 of your reference
http://www.octowave.com/~lkmud/wavemaker/CFAH2402AYYBJP.pdf
(ie, the page labeled "4-Bit Ineterface"). The correct
sequence is: Wait > 15ms, send 0011, wait > 4.1 ms,
send 0011, wait > .1 ms, send 0011, check BF (the busy
flag), send 0010, etc. What the routine instead does is:
Wait > 15ms, send 0011, send 0011, wait > 4.1 ms,
send 0011, send 0011, wait > 1 ms, send 0011, send 0010,
etc.
[snip]

So in your estimation this would be good code to stick with?

Since I'm used to reading AVR assembly and don't know much
about pic's I thought it all looked just awful. :)
But here's an idea to try anyway - Change:
MOVLW B'00110011' ; 2x 8bit resets
CALL LCD_SENDINS
call Delay20MS
MOVLW B'00110011' ; 2x 8bit resets
CALL LCD_SENDINS
CALL Delay1MS
MOVLW B'00110010' ; 8bit reset then 4bit reset
CALL LCD_SENDINS
to:
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
call Delay20MS
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
CALL Delay1MS
MOVLW B'0011' ; 8bit reset
CALL LCD_SENDIN4
CALL Delay1MS
MOVLW B'0010' ; 4bit reset
CALL LCD_SENDIN4
where LCD_SENDIN4 is:
LCD_SENDIN4:
MOVWF __LCD_TEMP0 ; Store byte in Temp 0
BCF LCD_RS ; RS low=instruction
CALL __LCD_SEND4 ; Send 1 nibble
CALL Delay1MS
RETURN
and __LCD_SEND4 is a label at line 43 in lcd_4bit.inc,
ie, just before the second BCF LCD_D7 of __LCD_SEND.
Note, see http://pat7.com/jp/2313s2.null.c at line 108; the
ini0 table corresponds to those first 4 instructions which
are sent to the LCD unit via its top 4 data lines; the
lower nibbles should not be sent, because the unit is not
yet in 4-bit mode. Your current code has no delay between
the third 8-bit reset and the 4-bit reset, while spec says
to check the busy flag or wait > 39 uS. (The code in
2313s2.null.c works ok for AVR AT90S2313 board like at
http://pat7.com/jp/2313s2.sch.jpg .)
Someone told me my delays might not be correct, but I'm
not sure how to check them in MPSIM. I can simulate it,
but I don't know where it would show the correct
times.

What is your crystal frequency, anyway? You should check
out your delay routines by blinking an LED at 1Hz or an
easy-to-time .2Hz etc before trying to get the LCD to work.
As for the contrast I get dark rectangles across the
first row when I turn it up.

That is what you get just by applying power, which gives
you 8-bit interface and 1-line display. After software
initialization per specs you will have a blinking cursor
in first column first row, and turning up the contrast
won't show any black rectangles.
-jiw

Ok, I've tried that and it didn't seem to work. I worked on the code, and
put everything
together. I tested my delays at 5seconds worth and they are very close. My
revised code is now
in one file and is at : http://www.octowave.com/~lkmud/wavemaker/

I think my initialization is working now, because I no longer get the solid
rectangles
all the way across the top. Now I am getting solid rectangles where the
characters are
supposed to be on line 1 and on line 2 I'm getting some weird looking
characters. They
look like a funny backwards 'e'. I did have the cursor on and blinking in
the code, but I took
that out. On the LCD the cursor was on and blinking when I had it in code.
So I think it
might have initialized correctly now, but something is still wrong. -Chris
 
J

James Waldby

Jan 1, 1970
0
Chris Gentry wrote:
....
Ok, I've tried that and it didn't seem to work. I worked
on the code, and put everything together. I tested my
delays at 5seconds worth and they are very close. My
revised code is now in one file and is at :
http://www.octowave.com/~lkmud/wavemaker/
I think my initialization is working now, because I no
longer get the solid rectangles all the way across the top.
Now I am getting solid rectangles where the characters are
supposed to be on line 1 and on line 2 I'm getting some
weird looking characters. They look like a funny backwards
'e'. I did have the cursor on and blinking in the code, but
I took that out. On the LCD the cursor was on and blinking
when I had it in code. So I think it might have initialized
correctly now, but something is still wrong.

Did you run a test with LCD_SENDIN4 being called rather
than LCD_SENDINS in the first 4 calls of LCD_INIT ?

What is your clock frequency?

Perhaps add another "GOTO $+1" or whatever the PIC code
for NOP is, between setting and clearing the LCD_E bit.

-jiw
 
C

Chris Gentry

Jan 1, 1970
0
James Waldby said:
Chris Gentry wrote:
...

Did you run a test with LCD_SENDIN4 being called rather
than LCD_SENDINS in the first 4 calls of LCD_INIT ?

Yes and it didn't seem to work. I could be wrong, but I thought that it
needed
the full eight bits on the first few calls because it was still in 8 bit
mode, and was expecting
8 bits? I'm getting ready to try some other things, so I'll try this again
with the different
code. When I tried this before my code didn't turn the display back on, so
that could be
the problem there.
What is your clock frequency?

I have a 20MHz xtal.
Perhaps add another "GOTO $+1" or whatever the PIC code
for NOP is, between setting and clearing the LCD_E bit.

I will try this. And it is 'NOP' on the PIC too.
 
C

Chris Gentry

Jan 1, 1970
0
Ok, what took days to fix, finally came down to one line that I had forgot
to add in LCD_PUTCH. My initialization has to be top notch now, thanks to
everyone who replied to this thread. The problem however wasn't with my
init routine. It was I had forgot to store the contents of the w register
into LCD_TEMP before I called LCD_SENDINS from LCD_PUTCH. My routine didn't
use 'w', but instead used 'LCD_TEMP' so when I sent the data it was just
garbage.

Thanks to everyone who helped fix this problem. -Chris
 
J

James Waldby

Jan 1, 1970
0
Chris said:
"James Waldby" ... wrote ....

Yes and it didn't seem to work. I could be wrong, but I
thought that it needed the full eight bits on the first
few calls because it was still in 8 bit mode, and was
expecting 8 bits? I'm getting ready to try some other
things, so I'll try this again with the different code.
When I tried this before my code didn't turn the display
back on, so that could be the problem there.

The display starts out in 8 bit interface mode. The 8
bit interface mode accepts bits on LCD lines D0...D7.
You don't have anything connected to D0-D3 (if you
hooked up properly). The low 4 bits of the first 4
commands are irrelevant don't-cares. If the first four
calls use LCD_SENDINS (with 2 E strobes per call) then
you are feeding the LCD 8 bytes rather than 4.
-jiw
 
C

Chris Gentry

Jan 1, 1970
0
James Waldby said:
The display starts out in 8 bit interface mode. The 8
bit interface mode accepts bits on LCD lines D0...D7.
You don't have anything connected to D0-D3 (if you
hooked up properly). The low 4 bits of the first 4
commands are irrelevant don't-cares. If the first four
calls use LCD_SENDINS (with 2 E strobes per call) then
you are feeding the LCD 8 bytes rather than 4.
-jiw

I redid the code to use LCD_SENDIN4 when I changed it last, and everything
appears to be working fine. Thanks for your suggestions. -Chris
 
Top