Help with a CFAH2004A-TMI-JP

Andrew

New member
Hello

I've got a 20x4 parallel character LCD (CFAH2004A-TMI-JP) that I need to interface with a PIC18F452, and it's not going so well... it seems that the busy flag is never clear (0). I've tried my own code, I've tried code that's worked with another HD44780 compatible LCD (20x2 though), code posted on the internet, modified code from the internet, etc., and it all seems to stall forever while waiting for the busy flag to clear (ie. a while(lcd_read_byte() & 0x80); loop never exits).

I've run out of ideas. Any help, suggestions, comments, code, etc. would be greatly appreciated.

I've checked and double checked and had someone else check for incorrect wiring and shorts and such, but couldn't find anything. The LCD module turns on, two white bars appear (but don't go away, is that normal?). The contrast is adjustable through a 10k potentiometer. I've tried both 4-bit and 8-bit modes. I don't know what else to try. Again, any sort of help would be greatly appreciated.

Thanks in advance,
Andrew

(I also have a 128x64 parallel graphical LCD (CFAG12864B-TMI-V) which I was going to try and use in the future, so any suggestions on that would be appreciated too)
Looking for additional LCD resources? Check out our LCD blog for the latest developments in LCD technology.
 
Last edited:
There is the presumption that the display has been initialized correctly. Try this: in place of the "busy check", just substitute a simple time delay, perhaps a few 100 microseconds, and write some text to the display that way, to see if anything actually gets written.

It would help to be able to see how your interface is designed (like a schematic), I assume you are bit-banging the E strobe from a port pin. Maybe you are "moving too fast", and need to put some NOP instructions or something to stretch the pulse widths. Although I tend to doubt that unless you have really fast cpu clock, since the write cycle time is 400nS. Perhaps you could post some snippets of your code.
 

Andrew

New member
Okay I figured out what was up with the busy flag and that's no longer a problem (I think)... unfortunately I still don't seem to be able to write characters to the LCD.

Here's the function I'm currently using to write commands to the LCD (I use CCS's compiler):

void lcd_putcmd(unsigned char v)
{
lcd_busy(); // Wait until not busy (no longer a problem)

LCD_RW = 0; // Write
LCD_RS = 0; // Instruction

LCD_DATA = v; // Set data

LCD_E = 1; // Strobe on
delay_cycles(3); // 0.6 Us (20MHz osc.)
LCD_E = 0; // Strobe off
delay_cycles(3);

return;
}

The lcd_putchar function is the same except that LCD_RS = 1. I'm assuming that the lcd_set_ddram_adr(...) and lcd_home() functions are working correctly. I'm also assuming that the display has been initialized correctly, although the two bars don't go away after the initialization...

Maybe that all helps, maybe it doesn't, or maybe I'm missing something else. Any more help would be appreciated.

Thanks again,
Andrew
 
Last edited:
The code looks OK, but if the display is not "clear" after initialization, then the init is not working. I presume you have downloaded the data sheet at:
http://www.crystalfontz.com/products/2004a-color/CFAH2004ATMIJP.pdf
there is a section in that doc that illustrates the init sequence. You can't assume that the power-up event caused the LCD controller to reset properly, so you need to go thru the multiple mode set commands, with the time delays mentioned. You might post your init routine code. Be sure to note that you can't test the busy flag for the first few commands.

Once the init results in a clear display, then you need to be sure that the character cell address is valid, by using the "home" command or an explicit address (0x0-0x27 or 0x40-0x67).
 
Last edited:

Andrew

New member
Here's the code I use to initialize the LCD. I designed it after the steps found in the CFAH2004A-TMI-JP data sheet (although I'm not entirely sure what 'shift of entire display' is, if that matters)

void Init_LCD()
{
printf("LCD Init...\n\r");

LCD_DATA_DIR = 0x00; // TRISD = 0x00, all D pins outputs
LCD_DATA = 0x00; // PORTD = 0x00, all D pins low

LCD_RS_DIR = 0; // TRISC0 = 0, pin C0 output
LCD_RW_DIR = 0; // TRISC1 = 0, pin C1 output
LCD_E_DIR = 0; // TRISC2 = 0, pin C2 output

LCD_RS = 0; // RC0 low
LCD_RW = 0; // RC1 low
LCD_E = 0; // RC2 low

DelayMs(15);
lcd_putcmd_nobusy(0x30); // Once

DelayMs(5);
lcd_putcmd_nobusy(0x30); // Twice

DelayUs(110);
lcd_putcmd_nobusy(0x30); // Thrice

lcd_putcmd(0x30); // 8-bit, 1 line, 5x8 font
lcd_putcmd(0x08); // Display off
lcd_putcmd(0x01); // Clear display
lcd_putcmd(0x07); // Entry mode set

return;
}

lcd_putcmd_nobusy(...) is the same as lcd_putcmd(...) just without the lcd_busy() line. I'm using PORTD as the data pins (pins RD0-RD7 to LCD inputs DB0-DB7, respectively), pin RC0 as RS, pin RC1 as RW, and pin RC2 as E.
For lcd_clear() I'm using lcd_putcmd(0x01); and for lcd_home() I'm using lcd_putcmd(0x02);
And if it helps... the main part of the program calls Init_LCD(), calls lcd_clear() then lcd_home(), and goes into an infinite loop, sending 'A' to Hyperterminal and to the LCD (lcd_putchar('A')) and delaying half a second (DelayMs(500)). However, the lines are still there after initialization, so...

Hopefully that gives you a little better idea of how I'm going about the LCD stuff

Thanks,
Andrew
 
Last edited:
The init code looks OK (almost), so the controller should be initialized, to where the "lines"(???) shouldn't be there. I'm not sure what you mean by "lines". An un-initialized display will usually show one or more rows where all character cells are dark.

Are you able to adjust contrast with a 10K pot between 5v and ground, wiper to pin 3?

The commands in the init sequence are setting "display off", but you are never turning it back "on". Add the command

lcd_putcmd(0x0C); // to turn display back on

also, I'm pretty sure you don't want "shift" enabled, in the Entry Mode command, just send "0x06" instead of 0x07.

You can look at my attached sample HC11 code in the "20x2" thread by atomrend, to see my init sequence. Ignore the extra garbage about loading CGRAM.

I assume your lcd_putchar() has LCD_RS output set high.
 

Andrew

New member
I used the initialization sequence from your HC11 init code and it worked ;). Apparently sending 0x38 three times instead of 0x30 made a difference. The 'lines' were the first and third rows with all characters lit up, but again, they clear after the initialization now.
Unfortunately...
I haven't been able to produce very repeatable results with the lcd_putcmd(...) and lcd_putchar(...) functions. The display seems to initialize successfully each time (ie. the two rows clear). However, if I turn the display on with no cursor (lcd_putcmd(0x0C)) or if I turn the display on with the cursor on (lcd_putcmd(0x0E)), sometimes it does what you'd expect it to do, sometimes it doesn't, sometimes it does after I restart the chip, sometimes it just doesn't do anything. The lcd_putchar('A') works every now and then, the cursor usually moves one character with every cycle of the main loop (sometimes it skips a character), but an A only shows up at random times.

void lcd_putchar(unsigned char v)
{
lcd_busy(); // Wait until not busy

LCD_RW = 0; // Write
LCD_RS = 1; // Data

LCD_DATA = v; // Set data

LCD_E = 1; // Strobe on
delay_cycles(3); // 0.6 Us (20MHz osc.)
LCD_E = 0; // Strobe off
delay_cycles(3);

return;
}

I'm not sure what else to try, I'll change the delay_cycles from 3 to 4, maybe put a delay_cycle after setting LCD_DATA...

Let me know if you have any suggestions, I really appreciate your help so far

Thanks,
Andrew
 
Last edited:
Glad you got over the initialization hump. Dont know why the 0x38 matters, but thats the value I have used for many projects.

Only two things come to mind for your unreliable control:

1. Timing - and from the code I see, there shouldn't be a problem if you have 600nS delays in the code. Try sprinkling more delays, like between the LCD_RS and LCD_DATA and LCD_E. SHouldn't matter, but who knows?

2. Are you sure the bus direction is correct? I haven't seen the lcd_busy() code, but maybe there is a problem in switching the data port from input to output, and it may be left in the "input" state.

Cant think of anything else at the moment...
 

Andrew

New member
void lcd_busy()
{
LCD_DATA_DIR = 0xFF; // TRISD = 0xFF, all D pins inputs
LCD_RW = 1; // Read
LCD_RS = 1; // Data

while(1) // Loop breaks when busy flag clears
{
LCD_E = 1; // Strobe on
delay_cycles(3); // 0.6 Us

if(!(LCD_DATA & 0x80)) break; // Read data, test BF

LCD_E = 0; // Strobe off
delay_cycles(3);
}
LCD_E = 0; // Strobe off
delay_cycles(3);

LCD_RW = 0;
LCD_RS = 0;
LCD_DATA_DIR = 0x00; // TRISD = 0x00, all D pins outputs

return;
}

It all seems to work randomly, I can reset the chip, turn the power supply on and off, etc., basically just mess around with restarting things in various ways and every now and then the cursor will show up and it'll start moving, occasionally printing out an 'A', but usually it just stares blankly at me (no lines though). I'm gonna keep after it. Hopefully I'll come across something. Any ideas are still very welcome

Thanks,
Andrew
 
Last edited:
One other idea: maybe you have a wiring error.

At this point, if you have access to an oscilloscope, you should look at the signals at the module pins, to see if they are what you think they are.

If you are in the Seattle area, I could offer more direct help.
 

Andrew

New member
Low budget company, so no oscilloscope (yet)... I've checked for continuity, shorts, correct voltages, etc... I'm gonna mess around with it, make the connections a bit more solid, triple check the circuit, organize the wires, maybe even un-wire and re-wire the whole thing. It all seems really random though, and random in electronics/programming is frustrating. Grr.

Anyway, thanks for your help, especially with the initialization stuff. Feel free to e-mail me if anything else crosses your mind.

Andrew
 
One other alternative, if you have a way of debugging, is to single step thru your program, and check that the outputs are correct after each statement that does any I/O.

Try making your delays much longer, which may reveal a timing issue that is right on the edge. If that helps, then shorten them until it screws up again, then double it.
 

Andrew

New member
I did as you suggested, made the delays much longer (started by putting a getch() after every pin toggle). Apparently delaying for only a few cycles was way too fast. Right now I've got a 40 Us delay between E high and E low. I haven't tried shortening it to find out what the limit is yet. I also had to put a 1 Ms delay after the lcd_busy() command in the lcd_putcmd() function.
But it all seems to work, and now it's just program logic and stuff to start putting something sensible out to the LCD. Thanks for all your help, I really appreciate it.

Andrew
 
Top