|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
Flexible LCD driver for 20x4 LCDs |
Posted: Fri Sep 22, 2006 2:07 am |
|
|
This driver is for use with 20x4 LCDs. It's similar to the
one for 16x2 LCDs, which is posted at this link:
http://www.ccsinfo.com/forum/viewtopic.php?t=24661
Note:
Ttelmah has written a version of this program that works
with dsPICs running at high speed. See the following link:
http://www.ccsinfo.com/forum/viewtopic.php?t=57617&start=6
Benefits:
The main benefit of this driver is that you can use any
selection of digital i/o pins for the signals. They don't
have to be on the same port and they don't have to be
in consecutive order. The standard CCS driver can't
do this. Another feature of this driver is that the pin
assignments can easily be changed by editing a list of
#define statements at the start of the driver file.
Make sure that the pins you assign to the LCD can do
digital i/o.
Limitations:
This driver requires "standard i/o" mode, which is the
default mode of the CCS compiler. If you want to use
fast i/o mode, then use the standard CCS driver which
is included with the compiler, called LCD420.C. It's in
this folder: c:\Program Files\PICC\Drivers
Testing:
I tested this driver on a PicDem2-Plus board with both a
16F877 and a 18F452. I tested it at 4 MHz and 20 MHz,
and also at 40 MHz with the 18F452. I test it with the PCM
and PCH compilers, versions 3.249 and 3.191, and a few
other versions as well. I didn't test it with version 4 yet.
I tested the driver with these two 20x4 LCDs:
CrystalFontz CFAH2004A-YYH-JP:
This LCD has a backlight. Basically you have to run
the backlight, otherwise it would be a little too dark.
Non-backlight models have a brighter background.
http://www.crystalfontz.com/products/2004a/index.html
It uses the Suncom SPLC780A1 controller. This controller
is used by other manufacturers such as Hantronix.
http://www.displaytech-us.com/pdf/application/Character_Module/Sunplus/splc780a1v12.pdf
Lumex LCM-S02004DSR:
This LCD doesn't have a backlight.
http://www.lumex.com/spec.asp?p_n=LCM-S02004DSR
Lumex only lists one controller chip on their website, the
Samsung S6A0069, so presumably that's the one it uses.
http://www.lumex.com/technotes/drivers/samsungdriver.pdf
Here is a test program for the 20x4 LCD driver. The driver
is posted below the test program.
Code: |
#include <16F877.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock = 4000000)
#include <Flex_LCD420.c>
//===================================
void main()
{
int8 i;
int8 b1, b2, b3, b4;
// The lcd_init() function should always be called once,
// near the start of your program.
lcd_init();
// Clear the LCD.
printf(lcd_putc, "\f");
delay_ms(500);
while(1)
{
// Test the clear screen and newline commands.
// Also test that we can write to all 4 lines.
printf(lcd_putc, "\fThis is the 1st line");
printf(lcd_putc, "\nNext is the 2nd line");
printf(lcd_putc, "\nThis is the 3rd line");
printf(lcd_putc, "\nFinally the 4th line");
delay_ms(3000);
// Test some additional characters.
printf(lcd_putc, "\fABCDEFGHIJKLMNOPQRST");
printf(lcd_putc, "\nabcdefghijklmnopqrst");
printf(lcd_putc, "\n12345678901234567890");
printf(lcd_putc, "\n!@#$^&*(){}[]:;<>?/=");
delay_ms(3000);
// Clear the LCD.
printf(lcd_putc, "\f");
delay_ms(500);
// Test that lcd_gotoxy() works. Go to each of
// the four corners and put a number in each one,
// in a clockwise direction, starting with the upper
// left corner.
lcd_gotoxy(4, 2);
printf(lcd_putc, "Put a number in");
lcd_gotoxy(4, 3);
printf(lcd_putc, "each corner.");
lcd_gotoxy(1, 1);
printf(lcd_putc, "1");
lcd_gotoxy(20, 1);
printf(lcd_putc, "2");
lcd_gotoxy(20, 4);
printf(lcd_putc, "3");
lcd_gotoxy(1, 4);
printf(lcd_putc, "4");
delay_ms(3000);
// Read the character that was written in each corner
// of the LCD and display it. This tests the lcd_getc()
// function.
// The following test can only be done if we can read
// from the LCD. If the RW pin is not used, then the
// LCD is in write-only mode, and we can't do this test.
// The #ifdef statement will prevent the code from
// being compiled, in that case.
#ifdef USE_RW_PIN
// Test if lcd_getc() can read
// a byte from each corner.
b1 = lcd_getc(1,1);
b2 = lcd_getc(20,1);
b3 = lcd_getc(20,4);
b4 = lcd_getc(1,4);
lcd_gotoxy(1, 1);
printf(lcd_putc, "\fRead these bytes\n");
printf(lcd_putc, "from the 4 corners:\n\n");
printf(lcd_putc, " %c %c %c %c", b1, b2, b3, b4);
delay_ms(3000);
#endif
// Type some characters and backspace over them.
printf(lcd_putc, "\fType characters and\n");
printf(lcd_putc, "backspace over them.");
delay_ms(2000);
// Go to end of 2nd line.
lcd_gotoxy(20, 2);
// Backspace over 2nd line.
for(i = 0; i < 20; i++)
{
printf(lcd_putc," \b\b");
delay_ms(150);
}
// Go to end of first line.
lcd_gotoxy(20, 1);
// Backspace over first line.
for(i = 0; i < 20; i++)
{
printf(lcd_putc," \b\b");
delay_ms(150);
}
}
}
|
Here is the driver:
Code: |
// Flex_LCD420.c
// These pins are for my Microchip PicDem2-Plus board,
// which I used to test this driver.
// An external 20x4 LCD is connected to these pins.
// Change these pins to match your own board's connections.
#define LCD_DB4 PIN_D4
#define LCD_DB5 PIN_D5
#define LCD_DB6 PIN_D6
#define LCD_DB7 PIN_D7
#define LCD_RS PIN_E0
#define LCD_RW PIN_E1
#define LCD_E PIN_E2
/*
// To prove that the driver can be used with random
// pins, I also tested it with these pins:
#define LCD_DB4 PIN_D4
#define LCD_DB5 PIN_B1
#define LCD_DB6 PIN_C5
#define LCD_DB7 PIN_B5
#define LCD_RS PIN_E2
#define LCD_RW PIN_B2
#define LCD_E PIN_D6
*/
// If you want only a 6-pin interface to your LCD, then
// connect the R/W pin on the LCD to ground, and comment
// out the following line. Doing so will save one PIC
// pin, but at the cost of losing the ability to read from
// the LCD. It also makes the write time a little longer
// because a static delay must be used, instead of polling
// the LCD's busy bit. Normally a 6-pin interface is only
// used if you are running out of PIC pins, and you need
// to use as few as possible for the LCD.
#define USE_RW_PIN 1
// These are the line addresses for most 4x20 LCDs.
#define LCD_LINE_1_ADDRESS 0x00
#define LCD_LINE_2_ADDRESS 0x40
#define LCD_LINE_3_ADDRESS 0x14
#define LCD_LINE_4_ADDRESS 0x54
// These are the line addresses for LCD's which use
// the Hitachi HD66712U controller chip.
/*
#define LCD_LINE_1_ADDRESS 0x00
#define LCD_LINE_2_ADDRESS 0x20
#define LCD_LINE_3_ADDRESS 0x40
#define LCD_LINE_4_ADDRESS 0x60
*/
//========================================
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines(or more)
int8 lcd_line;
int8 const LCD_INIT_STRING[4] =
{
0x20 | (lcd_type << 2), // Set mode: 4-bit, 2+ lines, 5x8 dots
0xc, // Display on
1, // Clear display
6 // Increment cursor
};
//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note: !! converts an integer expression
// to a boolean (1 or 0).
output_bit(LCD_DB4, !!(nibble & 1));
output_bit(LCD_DB5, !!(nibble & 2));
output_bit(LCD_DB6, !!(nibble & 4));
output_bit(LCD_DB7, !!(nibble & 8));
delay_cycles(1);
output_high(LCD_E);
delay_us(2);
output_low(LCD_E);
}
//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine. For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.
#ifdef USE_RW_PIN
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3
retval = 0;
output_high(LCD_E);
delay_us(1);
retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);
output_low(LCD_E);
delay_us(1);
return(retval);
}
#endif
//---------------------------------------
// Read a byte from the LCD and return it.
#ifdef USE_RW_PIN
int8 lcd_read_byte(void)
{
int8 low;
int8 high;
output_high(LCD_RW);
delay_cycles(1);
high = lcd_read_nibble();
low = lcd_read_nibble();
return( (high<<4) | low);
}
#endif
//----------------------------------------
// Send a byte to the LCD.
void lcd_send_byte(int8 address, int8 n)
{
output_low(LCD_RS);
#ifdef USE_RW_PIN
while(bit_test(lcd_read_byte(),7)) ;
#else
delay_us(60);
#endif
if(address)
output_high(LCD_RS);
else
output_low(LCD_RS);
delay_cycles(1);
#ifdef USE_RW_PIN
output_low(LCD_RW);
delay_cycles(1);
#endif
output_low(LCD_E);
lcd_send_nibble(n >> 4);
lcd_send_nibble(n & 0xf);
}
//----------------------------
void lcd_init(void)
{
int8 i;
lcd_line = 1;
output_low(LCD_RS);
#ifdef USE_RW_PIN
output_low(LCD_RW);
#endif
output_low(LCD_E);
// Some LCDs require 15 ms minimum delay after
// power-up. Others require 30 ms. I'm going
// to set it to 35 ms, so it should work with
// all of them.
delay_ms(35);
for(i=0 ;i < 3; i++)
{
lcd_send_nibble(0x03);
delay_ms(5);
}
lcd_send_nibble(0x02);
for(i=0; i < sizeof(LCD_INIT_STRING); i++)
{
lcd_send_byte(0, LCD_INIT_STRING[i]);
// If the R/W signal is not used, then
// the busy bit can't be polled. One of
// the init commands takes longer than
// the hard-coded delay of 50 us, so in
// that case, lets just do a 5 ms delay
// after all four of them.
#ifndef USE_RW_PIN
delay_ms(5);
#endif
}
}
//----------------------------
void lcd_gotoxy(int8 x, int8 y)
{
int8 address;
switch(y)
{
case 1:
address = LCD_LINE_1_ADDRESS;
break;
case 2:
address = LCD_LINE_2_ADDRESS;
break;
case 3:
address = LCD_LINE_3_ADDRESS;
break;
case 4:
address = LCD_LINE_4_ADDRESS;
break;
default:
address = LCD_LINE_1_ADDRESS;
break;
}
address += x-1;
lcd_send_byte(0, 0x80 | address);
}
//-----------------------------
void lcd_putc(char c)
{
switch(c)
{
case '\f':
lcd_send_byte(0,1);
lcd_line = 1;
delay_ms(2);
break;
case '\n':
lcd_gotoxy(1, ++lcd_line);
break;
case '\b':
lcd_send_byte(0,0x10);
break;
default:
lcd_send_byte(1,c);
break;
}
}
//------------------------------
#ifdef USE_RW_PIN
char lcd_getc(int8 x, int8 y)
{
char value;
lcd_gotoxy(x,y);
// Wait until busy flag is low.
while(bit_test(lcd_read_byte(),7));
output_high(LCD_RS);
value = lcd_read_byte();
output_low(LCD_RS);
return(value);
}
#endif
|
-----------
Edit: Added link to Ttelmah's version for high speed dsPICs.
Last edited by PCM programmer on Thu Jan 24, 2019 12:42 pm; edited 1 time in total |
|
|
Neckruin
Joined: 17 Jan 2006 Posts: 66
|
|
Posted: Mon Oct 02, 2006 9:33 am |
|
|
Hello PCM,
I'm a complete newbie in the use of LCD's and I have to use a 20x4 display for an application I'm developing.
Could you please tell me what are each function of your driver for? I mean, for example, what is a "nibble" :S
I'm really newbie, I know :(
Thanks in advance.
Last edited by Neckruin on Sun Sep 16, 2018 4:28 am; edited 1 time in total |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon Oct 02, 2006 12:53 pm |
|
|
This driver is based upon the CCS driver, so I didn't post the function
interface description. To get that information, look at the LCD420.C
file, which is in this directory: c:\Program Files\Picc\Drivers
Also some of the functions are for internal use by the driver. They're
not normally called by the user. That includes the "nibble" functions.
See the LCD420.C driver for the list of functions that are intended to
be called by the user.
If you have more newbie type questions, please ask them in the
main forum: http://www.ccsinfo.com/forum/viewforum.php?f=1 |
|
|
ratgod
Joined: 27 Jan 2006 Posts: 69 Location: Manchester, England
|
|
Posted: Mon Nov 20, 2006 12:22 am |
|
|
I tried this library and its works great, the only 4 line LCD code I could get working.
I did make a slight mod though.
in the lcd_putc() routine, I added this before the last brace:
Code: | if (lcd_line>4)
{
lcd_line=1;
} |
I found that if you kepts writting lines it would wrap back to the first line and continue overwriting the first line, with this mod it now wraps back to the first line and works its way down again. |
|
|
Neckruin
Joined: 17 Jan 2006 Posts: 66
|
|
Posted: Wed Nov 22, 2006 7:13 am |
|
|
Hi PCM
I'm trying to use your driver for a POWERTIP PC2004-A 4x20 LCD Display and... it doesn't work at all.
The electrical connections seem yo be OK and if I set to "1" the proper PIN of the PIC, the LCD light turns on.
I think there is a problem with the init sequence but I'm not sure.
I'm using the 4 bit mode of the display, as your drivers uses it.
Any suggestion????
Thanks in advance.
Last edited by Neckruin on Sun Sep 16, 2018 4:30 am; edited 1 time in total |
|
|
Neckruin
Joined: 17 Jan 2006 Posts: 66
|
|
Posted: Wed Nov 22, 2006 10:51 am |
|
|
Forget it... it works perfectly!
Thanks again PCM.
PD: Just for laughing... the problem was the contrast... |
|
|
ratgod
Joined: 27 Jan 2006 Posts: 69 Location: Manchester, England
|
|
Posted: Thu Nov 30, 2006 9:23 pm |
|
|
my contrast control is a 10K preset between Vcc and GND and the wiper going to the Vee on the LCD.
I also use a 20K preset for the brightness control (mines backlit) but I think a lower value would be more suitable, 20K was just one I had in my bits box
I also had trouble with my LCD until I tied the RW line, I think I tied it high, I cant remember. either high or low. |
|
|
JimB
Joined: 25 Aug 2005 Posts: 65 Location: Huntington Beach, CA
|
Why the read capability using an LCD |
Posted: Tue Dec 12, 2006 4:07 pm |
|
|
I'm curious as to why one would want to read from the LCD? What purpose does this have? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Dec 12, 2006 4:30 pm |
|
|
The main purpose is the read the Busy flag, to see when the current
operation is finished, so you can start another operation. |
|
|
wired57
Joined: 07 May 2007 Posts: 1
|
|
Posted: Mon May 07, 2007 6:13 pm |
|
|
I was unable to make your driver function aswell, until I modified these Two functions, so that they followed the need to hold the R line high for 220uS min and then Low for another 220uS min, with a toltal cycle time of 500uS min. for both reading and writing. this is for a 4x20 based on a Samsung KS0066 Controler. let me know if this helps others too.
Code: |
//-------------------------------------
void lcd_send_nibble(int8 nibble)
{
// Note: !! converts an integer expression
// to a boolean (1 or 0).
output_high(LCD_E);
delay_us(2);
output_bit(LCD_DB4, !!(nibble & 1));
output_bit(LCD_DB5, !!(nibble & 2));
output_bit(LCD_DB6, !!(nibble & 4));
output_bit(LCD_DB7, !!(nibble & 8));
delay_us(250);
output_low(LCD_E);
delay_us(250);
}
//-----------------------------------
// This sub-routine is only called by lcd_read_byte().
// It's not a stand-alone routine. For example, the
// R/W signal is set high by lcd_read_byte() before
// this routine is called.
#ifdef USE_RW_PIN
int8 lcd_read_nibble(void)
{
int8 retval;
// Create bit variables so that we can easily set
// individual bits in the retval variable.
#bit retval_0 = retval.0
#bit retval_1 = retval.1
#bit retval_2 = retval.2
#bit retval_3 = retval.3
retval = 0;
output_high(LCD_E);
delay_us(2);
retval_0 = input(LCD_DB4);
retval_1 = input(LCD_DB5);
retval_2 = input(LCD_DB6);
retval_3 = input(LCD_DB7);
delay_us(250);
output_low(LCD_E);
delay_us(250);
return(retval);
}
#endif
|
|
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Mon May 07, 2007 7:04 pm |
|
|
Download the KS0066u data sheet from the Hantronix website:
http://www.hantronix.com/down/ks0066u.pdf
Look at the timing diagrams and charts on pages 31 and 32.
First let's look at your modified write timing.
You have increased the positive pulse width of 'E' to at least 252 us.
The timing chart on page 31 lists Tw as 450 ns (min). You have also
added 250 us after 'E' goes low. Again, the data sheet specs the low
time at 450 ns min. The same comments apply to your modified timing
for the Read operation.
Your new timing exceeds the spec by a factor of at least 550 times.
This shouldn't be required. Either there's something wrong with your
hardware, or a small adjustment was needed somewhere else in the
driver and doing a huge increase to the 'E' pulse masks this other
problem. I wouldn't recommend your changes as a general
requirement for others to use. |
|
|
fastlink30
Joined: 14 May 2007 Posts: 33
|
|
Posted: Wed May 30, 2007 10:00 am |
|
|
simple note:
don't work if fast_io is on |
|
|
Trent___
Joined: 20 Jun 2007 Posts: 11 Location: London, UK
|
Tested with Powertip 4 x 20 LCD, CCS Compiler 4.041 |
Posted: Thu Jun 21, 2007 7:54 am |
|
|
Driver works well.
Tip for others, this should go with saying but it caught me....
Don't Multiplex your ISCP with any ports used by the LCD.
t. |
|
|
mkuang
Joined: 14 Dec 2007 Posts: 257
|
Re: Flexible LCD driver for 20x4 LCDs |
Posted: Tue Feb 05, 2008 12:32 pm |
|
|
Hi, thanks for the driver. I am a bit confused. You said the LCD uses a controller. Is the controller built-in to the LCD or do I need to purchase that separately?
Thanks. |
|
|
ELCouz
Joined: 18 Jul 2007 Posts: 427 Location: Montreal,Quebec
|
|
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|