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 CCS Technical Support

Flexible LCD driver for 16x1 LCDs

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

View user's profile Send private message

Flexible LCD driver for 16x1 LCDs
PostPosted: Mon May 28, 2007 4:18 pm     Reply with quote

This is a "Flex" driver for 16x1 LCDs.

The 16x2 Flex driver is posted here:
http://www.ccsinfo.com/forum/viewtopic.php?t=24661
The 20x4 Flex driver is posted here:
http://www.ccsinfo.com/forum/viewtopic.php?t=28268


Reasons to use this driver:
This driver lets you easily specify the list of connections
between the PIC and the LCD at the start of the driver file.
Also, you can use any available i/o pins on the PIC.
They don't have to be on the same port and they don't have
to be adjacent pins.

Testing of this driver:
This driver was tested on a PicDem2-Plus board with the following
PICs at the specified oscillator frequencies, and with the specified
compiler versions. Testing was done by running the test program
posted below. A Lumex 16x1 LCD was used, p/n LCM-S01601DTR.
(Digikey p/n 67-1778). All tests were done at 5 volts.

16F877, at 4 MHz and 20 MHz
vs. 3.191
vs. 3.249
vs. 4.014
vs. 4.038

18F452, at 4 MHz, 20 MHz, and 40 MHz
vs. 3.191
vs. 3.249
vs. 4.009
vs. 4.038

18F4550 at 48 MHz
vs. 3.249
vs. 4.038

Why a special driver is required for 16x1 LCDs:
The 16x1 LCD requires a special driver because it has an
unusual memory map. It's built similar to an 8x2 LCD,
except that the 2nd line is placed to the right of the first
line instead, of being placed below it. This means that
the memory map for the 16x1 LCD has a break in the
middle. The first 8 characters have the address 0 to 7
and the last 8 characters go from 0x40 to 0x47.

The driver must be written to handle this break in the address
map. There are a few ways to do this. This driver does
it by maintaining the current x-coordinate in a global
variable. Whenever one of the LCD functions causes the
cursor to move across the two middle bytes of the LCD,
the driver detects this and it adjusts the cursor address
so it's correct.

Using the driver
This driver has the same software interface as the CCS LCD.c
driver. If you are familiar with that driver, you can use this
one in the same way. Look at the test program shown below
to see how to call the LCD functions. Most users will just need
to call lcd_init() and lcd_putc().


Restrictions
Standard i/o mode is required for use with this driver. Don't use
"fast_io" mode. Standard i/o mode is the default mode of the compiler.


Possible Problems
If your board doesn't work with this driver, check the following:

1. Make sure the voltage on the Contrast pin on the LCD is set
at about 0.4 volts. If it's set anywhere between 0v and 0.5v
you should see something on the LCD.

2. Make sure you call the lcd_init() function before you call
any other functions in the driver, such as lcd_putc().

3. On some PICs, pin A5 is input-only. You can't use it with
the LCD. Choose another pin.

4. On many PICs, pin A4 is an "open drain" pin. It requires
an external pull-up resistor on it (You can use 4.7K).

5. Verify that the list of connections (in #define statements)
at the start of the LCD driver actually matches your hardware.

Test program for the 16x1 Flex LCD driver:
(The driver is posted below the test program).
Code:

#include <18F452.H>
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use delay(clock=4000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7, ERRORS)

#include "flex_lcd_16x1.c"

// This macro converts the lower nybble of a byte to
// an ASCII hex character, so it can be displayed with
// lcd_putc().
#define tohex(x) (x & 0xF) < 10 ? x + '0' : x + '7'

//======================================
void main()
{
int8 i;
// This array is used in the test for lcd_gotoxy().
int8 x_array[16] =
{5,0xE,0xD,6,8,1,0xB,2,7,0xC,0,4,3,0x9,0xA,0xF};
int8 values_read[16];

// Always call lcd_init() once at the start of
// your program, before using the LCD.
lcd_init();

// First test the lcd_putc() function, and also the
// "clear screen" command '\f':
while(1)
  {
   printf(lcd_putc, "\f 16x1 LCD Test ");
   delay_ms(3000);
 
   printf(lcd_putc, "\fTest lcd_putc()");
   delay_ms(3000);
   printf(lcd_putc, "\f  Hello World  ");
   delay_ms(1500);
   printf(lcd_putc, "\fabcdefghijklmnop");
   delay_ms(1500);
   printf(lcd_putc, "\f0123456789ABCDEF");
   delay_ms(1500);
   printf(lcd_putc, "\f!@#$^&*(){}[]:;<");
   delay_ms(2500);


   // Test the backspace command.
   // Do a destructive backspace by moving the cursor
   // onto the previous character, overwriting it with
   // a space, and then moving the cursor back onto the
   // now erased location.
   printf(lcd_putc, "\fTest Backspacing");
   delay_ms(1000);
   
   // Backspace over the line.
   for(i = 0; i < 16; i++)
      {
       printf(lcd_putc, "\b \b");
       delay_ms(400);
      }

   delay_ms(1500);

   
   // Test that the lcd_gotoxy() function works OK.
   // Go to each x-coordinate in a semi-random order,
   // and write a character at each position.  The x-coords
   // are taken from an array declared at the start of main().
   printf(lcd_putc, "\fTest lcd_gotoxy");
   delay_ms(1500);
   printf(lcd_putc, "\frandomly:");
   delay_ms(1500);

   printf(lcd_putc, "\f");

   for(i = 0; i < 16; i++)
      {
       lcd_gotoxy(x_array[i] +1, 1);       
       printf(lcd_putc, "%c", tohex(x_array[i]));   
       delay_ms(500);
      }

   delay_ms(2000);

   // Test the ability to read characters from the LCD.
   // Read the character that was written in each position
   // in the test above, and display it. 
   //
   // Note:
   // The following test can only be done if the driver
   // can read from the LCD.  If the RW pin is not used,
   // the LCD is in write-only mode, and we can't do
   // this test.  In that case, it won't be compiled.
#ifdef  USE_LCD_RW 
 
   for(i = 0; i < 16; i++)
       values_read[i] = lcd_getc(i+1, 1);       
     
   printf(lcd_putc, "\fTest reading");
   delay_ms(2000);
   printf(lcd_putc, "\fRead these bytes");
   delay_ms(2000);

   lcd_putc('\f');       
   
   for(i = 0; i < 16; i++)
      {
       printf(lcd_putc, "%c", (values_read[i]));   
       delay_ms(400);
      }
   
   delay_ms(2000);
#endif

   // Tell the user that the tests are finished.
   printf(lcd_putc, "\f All Tests Done ");
   delay_ms(3000);
  }

}


16x1 LCD driver:
Code:

// Flex_lcd_16x1.c

// These are randomly assigned pins, used to test
// that the driver can work with any arrangement
// of i/o pins. 
#define LCD_DB4   PIN_D4
#define LCD_DB5   PIN_B1
#define LCD_DB6   PIN_C0
#define LCD_DB7   PIN_E0

#define LCD_RS    PIN_E2
#define LCD_RW    PIN_B2
#define LCD_E     PIN_D6

/*
#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
*/

// If you want to use a 6-pin interface for your LCD, then
// connect the R/W pin on the LCD to ground and comment
// out the following line.  A 6-pin interface to the LCD
// is used if only have a few free i/o pins available on
// your PIC, and you want to use the smallest possible
// number of pins for the LCD.
#define USE_LCD_RW   1       

//========================================
// Use "2 lines" as the lcd type for the 16x1 LCD.
// The LCD is the same as an 8x2 LCD, but with the
// bottom line appended on the right side of the first line.
#define LCD_TYPE  2         // 0=5x7, 1=5x10, 2=2 lines
#define LCD_2ND_HALF_ADDRESS  0x40

#define LCD_WIDTH  16
#define LCD_HALF_WIDTH  (LCD_WIDTH/2)

int8 const LCD_INIT_STRING[4] =
{
 0x20 | (LCD_TYPE << 2), // Func set: 4-bit, 2 lines, 5x8 dots
 0xc,                    // Display on
 1,                      // Clear display
 6                       // Increment cursor
 };

int8 lcd_xcoord;
                             

//-------------------------------------
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_LCD_RW
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_LCD_RW
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_LCD_RW
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_LCD_RW
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;

output_low(LCD_RS);

#ifdef USE_LCD_RW
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 60 us, so in
    // that case, lets just do a 5 ms delay
    // after all four of them.
    #ifndef USE_LCD_RW
    delay_ms(5);
    #endif
   }

lcd_xcoord = 1;
}

//----------------------------
// The x-coordinate can be 1 to 16.
// The y coordinate is ignored. 
// This x,y interface is kept in order to be
// consistent with other CCS LCD drivers.
void lcd_gotoxy(int8 x, int8 y)
{
int8 address;

// Update the global x-coordinate variable with the
// current x coordinate.
lcd_xcoord = x;

// Convert the x-coordinate from CCS format (1-16) to
// the 0-15 format used by the LCD hardware.
address = x - 1; 

// If the x-coordinate is within the 2nd half of the
// LCD line, the address must be adjusted because
// of the special architecture of the 8x2 LCD.
if(address >= LCD_HALF_WIDTH) 
  {   
   address += (LCD_2ND_HALF_ADDRESS - LCD_HALF_WIDTH);
  }

lcd_send_byte(0, 0x80 | address);
}


//-----------------------------
void lcd_putc(char c)
{
 switch(c)
   {
    case '\f':
      lcd_send_byte(0,1);
      delay_ms(2);
      lcd_xcoord = 1;
      break;
   
    case '\n':
       lcd_gotoxy(1,1);  //  Goto start of line 1
       break;
   
    case '\b':
       lcd_send_byte(0, 0x10);
       lcd_xcoord--;
       if(lcd_xcoord == LCD_HALF_WIDTH)
          lcd_gotoxy(LCD_HALF_WIDTH, 1);
       break;
   
    default:
       lcd_send_byte(1, c);
       lcd_xcoord++;
       if(lcd_xcoord == (LCD_HALF_WIDTH +1))
          lcd_gotoxy(LCD_HALF_WIDTH +1, 1);
       break;
   }
}

//------------------------------
#ifdef USE_LCD_RW
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
umka



Joined: 28 Aug 2007
Posts: 99
Location: New Zealand

View user's profile Send private message

PostPosted: Sun Mar 16, 2008 2:18 am     Reply with quote

Is there a "Flex" driver for a 8x2 LCD?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sun Mar 16, 2008 8:48 am     Reply with quote

No, but you can use the regular 16x2 "Flex" driver with an 8x2 LCD.
See this post for an example:
http://www.ccsinfo.com/forum/viewtopic.php?t=29819&start=3
OE8PCK



Joined: 19 Apr 2009
Posts: 14
Location: Klagenfurt, AUSTRIA

View user's profile Send private message

PostPosted: Tue Apr 21, 2009 12:27 pm     Reply with quote

Hi, I have now got the 16x1 LCD test program to run BUT I cannot get the text to run to a single line although the Flex_lcd_16.c has this in the program listing. I am a complete beginner can you explain how to use the "2 lines" part of the program. Thanks,
Paul OE8PCK
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Tue Apr 21, 2009 1:04 pm     Reply with quote

Quote:
I have now got the 16x1 LCD test program to run

Post the manufacturer and part number of your LCD.

Quote:
I cannot get the text to run to a single line

What do you mean ? Give an example.
OE8PCK



Joined: 19 Apr 2009
Posts: 14
Location: Klagenfurt, AUSTRIA

View user's profile Send private message

PostPosted: Tue Apr 21, 2009 11:57 pm     Reply with quote

The manufacturer of the LCD is Batron TN11605.


At the moment the output to the LCD is cut off at the middle of the LCD and written, illegible, under the first line and I would like it in one line.
I am not sure how to do this although it is defined in the Flex_lcd_16x1 driver see below. How do I set this up? Please explain simply if possible I am a COMPLETE beginner.
Thanks, Paul OE8PCK
Code:

//========================================
// Use "2 lines" as the lcd type for the 16x1 LCD.
// The LCD is the same as an 8x2 LCD, but with the
// bottom line appended on the right side of the first line.
#define LCD_TYPE  2         // 0=5x7, 1=5x10, 2=2 lines
#define LCD_2ND_HALF_ADDRESS  0x40

#define LCD_WIDTH  16
#define LCD_HALF_WIDTH  (LCD_WIDTH/2)

int8 const LCD_INIT_STRING[4] =
{
 0x20 | (LCD_TYPE << 2), // Func set: 4-bit, 2 lines, 5x8 dots
 0xc,                    // Display on
 1,                      // Clear display
 6                       // Increment cursor
 };

int8 lcd_xcoord;
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Wed Apr 22, 2009 11:01 am     Reply with quote

Quote:
The manufacturer of the LCD is Batron TN11605.

There is very little documentation available for this 16x1 LCD.
The data sheet does not show the memory map of the LCD.
The TN11605 is not shown on Batron's website:
http://www.datamodul.com/us/page/monochrome_display.htm
Batron LCDs are the least compatible. They often have some unusual
feature that must be determined, before they will work.

It's possible that your LCD is not compatible with the normal style of
16x1 LCD. I suggest that you try using the 16x2 Flex driver, and
just write to the first line on the LCD. See if that works.
OE8PCK



Joined: 19 Apr 2009
Posts: 14
Location: Klagenfurt, AUSTRIA

View user's profile Send private message

PostPosted: Thu Apr 23, 2009 4:09 am     Reply with quote

I have now got this working in one line. I changed the (LCD_width/2) to just (LCD_width) Works super with the 18F452... but I then tried the 18F4550 by just changing the include line and it does not work. That's programming i suppose! Any suggestions as to why the 18F4550 does not work? I really would like to use this chip. I finally managed to get some specs for the LCD display, Data Module sent them after a heated discussion.
Regards Paul OE8PCK
conoral11



Joined: 05 Jun 2010
Posts: 5

View user's profile Send private message

PostPosted: Sat Jun 05, 2010 4:23 pm     Reply with quote

I've got a 16x2 LCD, unknown manufacturer and model number, its recycled you see.

The LCD module works, as in it initialises. The top line fills in with squares

But thats when the problem starts.

I get a constant blinking cursor, and thats it, no text or anything else

I'm using a PIC16F628a

MPLAB 8.50, A picKit2 and compiler version 4.013

I've tested all electrical connections for continuity and they are all fine.

Here is my main code:

Code:
#include "16F628a.h"
#fuses NOWDT, NOPROTECT, NOBROWNOUT, INTRC, NOLVP
#use delay(clock=8000000)
#include "LCD.c"

int main()
{
   char a = 'a';
   delay_ms(200);
   lcd_init();
   for(;;)
   {
      printf(lcd_putc, "\fHi\n");
      lcd_putc(a);
      delay_ms(100);
   }   
   return 0;
}


And my pin defines:

Code:

// flex_lcd.c

// These pins are for the Microchip PicDem2-Plus board,
// which is what I used to test the driver.  Change these
// pins to fit your own board.

#define LCD_DB4   PIN_B0
#define LCD_DB5   PIN_B1
#define LCD_DB6   PIN_B1
#define LCD_DB7   PIN_B0

#define LCD_RS    PIN_A2
#define LCD_RW    PIN_A3
#define LCD_E     PIN_A6


Is there anything obvious that I have missed?
PCM programmer



Joined: 06 Sep 2003
Posts: 21708

View user's profile Send private message

PostPosted: Sat Jun 05, 2010 6:35 pm     Reply with quote

Quote:
#include "16F628a.h"
#fuses NOWDT, NOPROTECT, NOBROWNOUT, INTRC, NOLVP

#define LCD_E PIN_A6

Is there anything obvious that I have missed?

Pin A6 is the CLKOUT pin for the oscillator. To use it as a normal i/o pin
you must specify the INTRC_IO fuse instead.

Quote:

#include "16F628a.h"
#fuses NOWDT, NOPROTECT, NOBROWNOUT, INTRC, NOLVP
#use delay(clock=8000000)

The internal oscillator doesn't run at 8 MHz. It only runs at 4 MHz.
See the following section in the PIC data sheet:
Quote:

14.2.4 PRECISION INTERNAL 4 MHZ OSCILLATOR
The internal precision oscillator provides a fixed 4 MHz
(nominal) system clock

Change the #use delay() statement to 4 MHz. For accurate operation
of the CCS delay routines, the #use delay() number must match the
actual oscillator frequency.

Quote:

#include "LCD.c"

You have given the Flex 16x1 LCD driver the same name as the CCS lcd
driver for 16x2 LCDs. For all you know, the compiler may be picking up
the CCS driver and compiling it by mistake.
Give the 16x1 driver file a better name, such as: "flex_lcd_16x1.c"
Or use something shorter such as flex16x1.c and then change the
#include statement in your program to use the same filename.

Quote:

int main()
{
char a = 'a';
delay_ms(200);
lcd_init();
for(;;)
{
printf(lcd_putc, "\fHi\n");
lcd_putc(a);
delay_ms(100);
}
return 0;
}

The main() function doesn't return to an O/S in this compiler. It's better
to declare main() as returning a 'void', and then delete the 'return 0'
statement at the end.

------
Of the 4 things that I posted above, only the first two are really important.
They must be fixed. The 3rd one should be fixed.
conoral11



Joined: 05 Jun 2010
Posts: 5

View user's profile Send private message

Solved
PostPosted: Sun Jun 06, 2010 4:13 am     Reply with quote

PCM programmer,

Thank you very much for your indepth answer.

You were correct, INTRC_IO and 4Mhz instead of 8Mhz fixed my issue.

I also noted, the code I posted
Code:

#define LCD_DB4   PIN_B0
#define LCD_DB5   PIN_B1
#define LCD_DB6   PIN_B1
#define LCD_DB7   PIN_B0

was incorrect, as I had the same pin declarations for different LCD ports thats what happens when you copy and past too often.

My circuit was using PIN_A4 for LCD_E, but it doesn't work on that pin for some reason
conoral11



Joined: 05 Jun 2010
Posts: 5

View user's profile Send private message

PostPosted: Sun Jun 06, 2010 6:01 am     Reply with quote

Worked it out,

I forgot that a pull up resistor is required for PIN_A4

Hope that helps any one else out
mustafa.sarfaraz



Joined: 17 May 2012
Posts: 1

View user's profile Send private message

pic24 support
PostPosted: Thu May 17, 2012 2:31 am     Reply with quote

Will this library work for pic24? I'm using pcwhd v4.130.

Anyone tested this?
the_dalga



Joined: 27 Jun 2012
Posts: 5

View user's profile Send private message

Hi
PostPosted: Sat Nov 17, 2012 6:38 pm     Reply with quote

This is what I am looking for. I will test it, thanks a lot.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> Code Library All times are GMT - 6 Hours
Page 1 of 1

 
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