|
|
View previous topic :: View next topic |
Author |
Message |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
Flexible LCD driver for 16x1 LCDs |
Posted: Mon May 28, 2007 4:18 pm |
|
|
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
|
|
Posted: Sun Mar 16, 2008 2:18 am |
|
|
Is there a "Flex" driver for a 8x2 LCD? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
OE8PCK
Joined: 19 Apr 2009 Posts: 14 Location: Klagenfurt, AUSTRIA
|
|
Posted: Tue Apr 21, 2009 12:27 pm |
|
|
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
|
|
Posted: Tue Apr 21, 2009 1:04 pm |
|
|
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
|
|
Posted: Tue Apr 21, 2009 11:57 pm |
|
|
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
|
|
Posted: Wed Apr 22, 2009 11:01 am |
|
|
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
|
|
Posted: Thu Apr 23, 2009 4:09 am |
|
|
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
|
|
Posted: Sat Jun 05, 2010 4:23 pm |
|
|
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
|
|
Posted: Sat Jun 05, 2010 6:35 pm |
|
|
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.
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
|
Solved |
Posted: Sun Jun 06, 2010 4:13 am |
|
|
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
|
|
Posted: Sun Jun 06, 2010 6:01 am |
|
|
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
|
pic24 support |
Posted: Thu May 17, 2012 2:31 am |
|
|
Will this library work for pic24? I'm using pcwhd v4.130.
Anyone tested this? |
|
|
the_dalga
Joined: 27 Jun 2012 Posts: 5
|
Hi |
Posted: Sat Nov 17, 2012 6:38 pm |
|
|
This is what I am looking for. I will test it, thanks a lot. |
|
|
|
|
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
|