CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug Reports on this forum. Send them to support@ccsinfo.com

Flexible LCD driver for 20x4 LCDs
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    CCS Forum Index -> Code Library
View previous topic :: View next topic  
Author Message
PCM programmer



Joined: 06 Sep 2003
Posts: 19727

View user's profile Send private message

Flexible LCD driver for 20x4 LCDs
PostPosted: Fri Sep 22, 2006 2:07 am     Reply with quote

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

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
Neckruin



Joined: 17 Jan 2006
Posts: 66

View user's profile Send private message

PostPosted: Mon Oct 02, 2006 9:33 am     Reply with quote

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,

Juanma
PCM programmer



Joined: 06 Sep 2003
Posts: 19727

View user's profile Send private message

PostPosted: Mon Oct 02, 2006 12:53 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon Nov 20, 2006 12:22 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed Nov 22, 2006 7:13 am     Reply with quote

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???? Sad

Thanks in advance,

Juanma
Neckruin



Joined: 17 Jan 2006
Posts: 66

View user's profile Send private message

PostPosted: Wed Nov 22, 2006 10:51 am     Reply with quote

Forget it... it works perfectly! Smile
Thanks again PCM.

PD: Just for laughing... the problem was the contrast... Embarassed
ratgod



Joined: 27 Jan 2006
Posts: 69
Location: Manchester, England

View user's profile Send private message

PostPosted: Thu Nov 30, 2006 9:23 pm     Reply with quote

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 Smile

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

View user's profile Send private message

Why the read capability using an LCD
PostPosted: Tue Dec 12, 2006 4:07 pm     Reply with quote

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: 19727

View user's profile Send private message

PostPosted: Tue Dec 12, 2006 4:30 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Mon May 07, 2007 6:13 pm     Reply with quote

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: 19727

View user's profile Send private message

PostPosted: Mon May 07, 2007 7:04 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 30, 2007 10:00 am     Reply with quote

simple note:

don't work if fast_io is on
Trent___



Joined: 20 Jun 2007
Posts: 11
Location: London, UK

View user's profile Send private message MSN Messenger

Tested with Powertip 4 x 20 LCD, CCS Compiler 4.041
PostPosted: Thu Jun 21, 2007 7:54 am     Reply with quote

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

View user's profile Send private message Send e-mail

Re: Flexible LCD driver for 20x4 LCDs
PostPosted: Tue Feb 05, 2008 12:32 pm     Reply with quote

PCM programmer wrote:
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


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



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

View user's profile Send private message

PostPosted: Tue Feb 05, 2008 3:15 pm     Reply with quote

Built-in. The SPLC780A1 is compatible with the factory standard HD44780 controller
_________________
Regards,
Laurent

-----------
Here's my first visual theme for the CCS C Compiler. Enjoy!
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
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