|
|
View previous topic :: View next topic |
Author |
Message |
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
Flexible "I2C" LCD driver (PCF8574T) by Hugo Silva |
Posted: Sun Jun 21, 2015 7:11 pm |
|
|
Hi Everyone!
I'm glad to share this I2C Driver for common LCD with 1/2/3 or 4 rows by 1...20 columns using PCF8574T interface board with I2C protocol. I've been using it for a while with no issues at all.
Some points to be considered:
1- The driver is prepared for the given PCF8574T pinout, so any changes in it will affect its properly working.
2- In order to work with a different LCD display i.e. 2x16, just set it in the parameters below:
Code: |
#define lcd_total_rows 4 //Number of rows: 1,2,3 or 4
#define lcd_total_columns 20 //Number of columns: 1...20
|
3- The Code was based in the existing "16x2 LCD driver" (Thanks to PCM Programmer!) So it will be easy to understand for those who are already using the non I2c version.
4- Any comments/updates will be appreciated!
Enjoy it...
BR,
Hugo Silva
Driver: i2c_Flex_LCD.h
Code: |
//-----------------------------------------------------------------------------
// Title: i2c_Flex_LCD
// Description: Driver for common LCD with 1/2/3 or 4 rows by 1...20 columns
// using PCF8574T interface board with I2C protocol.
// Date: Nov-2013
// Ver.Rev.: 1.1
// Author: Hugo Silva ([email protected]) #Based on the routines of
// "20X4_LCD_I2C_DRIVER.h" from Pumrin S. and "lcd4_i2c.c" from XP8100
//-----------------------------------------------------------------------------
//
// lcd_init() Must be called before any other function.
//
// lcd_putc(c) Will display c on the next position of the LCD.
//
// \f Clear LCD display
// \n Set write position on next lcd line
// \b LCD backspace
// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1)
//
// lcd_backlight_led(ON)/lcd_backlight_led(OFF) = Turn ON/OFF LCD Backlight LED
//
//-----------------------------------------------------------------------------
// LCD pins D0-D3 are not used.
//-----------------------------------------------------------------------------
//
// Comment : Control of a compatible LCD (1...4 rows by 1...4 columns) from
// a bus I2C with an EXPANDER of I/O with connection I2C.
// The tests of these routines have been programmed using the IC
// Phillips PCF8574T. I've used 4 bits mode programming.
// The 8 bits mode programming is possible if you use 2 x PCF8574T.
// RW Pin is not being used.
//
// As defined in the following structure the pin connection is as follows:
//
// PCF8574P LCD
// ======== ======
// P0 RS
// P1 RW (Not used!)
// P2 Enable
// P3 Led Backlight
// P4 D4
// P5 D5
// P6 D6
// P7 D7
//
// The SCL and SDA pins should be pull-up resistor as shown below:
//
// +5v
// |
// <
// > 4.7K
// <
//To PIC | To i2c slave
//pin xx ------------------ SDA pin
//(SDA)
// +5v
// |
// <
// > 4.7K
// <
//To PIC | To i2c slave
//pin xx ------------------ SCL pin
//(SCL)
//
//To PIC To i2c slave
//Vss pin ----------------- Vss or ground pin
// |
// -----
// --- Ground
// -
//
// THIS DOCUMENT IS PROVIDED TO THE USER "AS IS"
//-----------------------------------------------------------------------------
#define LCD_ADDR 0x4E //I2C slave address for LCD module
#define lcd_total_rows 4 //Number of rows: 1,2,3 or 4
#define lcd_total_columns 20 //Number of columns: 1...20
#define RS 0b00000001 //P0 - PCF8574T Pin connected to RS
#define RW 0b00000010 //P1 - PCF8574T Pin connected to RW
#define ENABLE 0b00000100 //P2 - PCF8574T Pin connected to EN
#define LCD_BACKLIGHT 0b00001000 //P3 - PCF8574T Pin connected to BACKLIGHT LED
#define addr_row_one 0x00 //LCD RAM address for row 1
#define addr_row_two 0x40 //LCD RAM address for row 2
#define addr_row_three 0x14 //LCD RAM address for row 3
#define addr_row_four 0x54 //LCD RAM address for row 4
#define ON 1
#define OFF 0
#define NOT ~
#define data_shifted data<<4
int8 new_row_request=1, BACKLIGHT_LED=LCD_BACKLIGHT;
void lcd_backlight_led(byte bl)
{
If (bl) BACKLIGHT_LED=LCD_BACKLIGHT; else BACKLIGHT_LED=OFF;
}
void i2c_send_nibble(byte data, byte type)
{
switch (type)
{
case 0 :
i2c_write(data_shifted | BACKLIGHT_LED);
delay_cycles(1);
i2c_write(data_shifted | ENABLE | BACKLIGHT_LED );
delay_us(2);
i2c_write(data_shifted & NOT ENABLE | BACKLIGHT_LED);
break;
case 1 :
i2c_write(data_shifted | RS | BACKLIGHT_LED);
delay_cycles(1);
i2c_write(data_shifted | RS | ENABLE | BACKLIGHT_LED );
delay_us(2);
i2c_write(data_shifted & RS | BACKLIGHT_LED);
break;
}
}
void lcd_send_byte(byte data, byte type)
{
i2c_start();
i2c_write(LCD_ADDR);
i2c_send_nibble(data >> 4 , type);
i2c_send_nibble(data & 0xf , type);
i2c_stop();
}
void lcd_clear()
{
lcd_send_byte(0x01,0);
delay_ms(2);
new_row_request=1;
}
void lcd_init(void)
{
byte i;
byte CONST lcd_type=2; // 0=5x7, 1=5x10, 2=2 lines
byte CONST LCD_INIT_STRING[4] = {0x20 | (lcd_type << 2), 0xc, 1, 6}; // These bytes need to be sent to the LCD to start it up.
disable_interrupts(GLOBAL);
delay_ms(50); //LCD power up delay
i2c_start();
i2c_write(LCD_ADDR);
i2c_send_nibble(0x00,0);
delay_ms(15);
for (i=1;i<=3;++i)
{
i2c_send_nibble(0x03,0);
delay_ms(5);
}
i2c_send_nibble(0x02,0);
delay_ms(5);
i2c_stop();
for (i=0;i<=3;++i) {
lcd_send_byte(LCD_INIT_STRING[i],0);
delay_ms(5);
}
lcd_clear(); //Clear Display
enable_interrupts(GLOBAL);
}
void lcd_gotoxy( byte x, byte y)
{
byte row,column,row_addr,lcd_address;
static char data;
if (y>lcd_total_rows) row=lcd_total_rows; else row=y;
switch(row)
{
case 1: row_addr=addr_row_one; break;
case 2: row_addr=addr_row_two; break;
case 3: row_addr=addr_row_three; break;
case 4: row_addr=addr_row_four; break;
default: row_addr=addr_row_one; break;
}
if (x>lcd_total_columns) column=lcd_total_columns; else column=x;
lcd_address=(row_addr+(column-1));
lcd_send_byte(0x80|lcd_address,0);
}
//Display the character on LCD screen.
void LCD_PUTC(char in_data)
{
switch(in_data)
{
case '\f': lcd_clear(); break;
case '\n':
new_row_request++;
if (new_row_request>lcd_total_rows) new_row_request=1;
lcd_gotoxy(1, new_row_request);
break;
case '\b': lcd_send_byte(0x10,0); break;
default: lcd_send_byte(in_data,1); break;
}
}
|
Test Code: i2c_Flex_LCD_Test.c
Code: |
//************************************************************************************
// LCD Display test code for "i2c_Flex_LCD" using Phillips PCF8574T
// By Hugo Silva - Jun 06Th 2015
// Based in the 20x4 LCD driver test code (by PCM programmer) with a few changes made
//************************************************************************************
#include <18f2550.h>
#use delay (clock=48000000)
#use i2c(Master,Fast=100000, sda=PIN_c6, scl=PIN_c7,force_sw)
#include <i2c_Flex_LCD.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,USBDIV,PLL5,CPUDIV1
void main() {
int i=0;
lcd_init();
lcd_backlight_led(ON); //Backligth ON
while (TRUE) {
lcd_clear(); //Clear Display
// 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");
delay_ms(500);
printf(lcd_putc, "\nNext is the 2nd line");
delay_ms(500);
printf(lcd_putc, "\nThis is the 3rd line");
delay_ms(500);
printf(lcd_putc, "\nFinally the 4th line");
delay_ms(1000);
// Test some additional characters.
printf(lcd_putc, "\fABCDEFGHIJKLMNOPQRST");
delay_ms(500);
printf(lcd_putc, "\nabcdefghijklmnopqrst");
delay_ms(500);
printf(lcd_putc, "\n12345678901234567890");
delay_ms(500);
printf(lcd_putc, "\n!@#$^&*(){}[]:;<>?/=");
delay_ms(500);
// 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(5, 2);
printf(lcd_putc, "Put a Nr. in");
lcd_gotoxy(5, 3);
printf(lcd_putc, "each corner");
delay_ms(500);
lcd_gotoxy(1, 1);
printf(lcd_putc, "1");
delay_ms(500);
lcd_gotoxy(20, 1);
printf(lcd_putc, "2");
delay_ms(500);
lcd_gotoxy(20, 4);
printf(lcd_putc, "3");
delay_ms(500);
lcd_gotoxy(1, 4);
printf(lcd_putc, "4");
delay_ms(1000);
// 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, "\f\nType characters and\n");
printf(lcd_putc, "backspace over them.");
delay_ms(500);
// Go to end of 3rd line.
lcd_gotoxy(20, 3);
// Backspace over 2nd line.
for(i = 0; i < 20; i++)
{
printf(lcd_putc," \b\b");
delay_ms(50);
}
// Go to end of first line.
lcd_gotoxy(20, 2);
// Backspace over first line.
for(i = 0; i < 20; i++)
{
printf(lcd_putc," \b\b");
delay_ms(50);
}
//Backlight OFF
lcd_backlight_led(OFF);
printf(LCD_PUTC,"\fLCD BackLight\n OFF ");
delay_ms(1000);
//Backlight ON
lcd_backlight_led(ON);
printf(LCD_PUTC,"\fLCD BackLight\n ON ");
delay_ms(1000);
}
}
|
_________________ Hugo Silva |
|
|
TranVhien
Joined: 12 Mar 2016 Posts: 1 Location: SaiGon city
|
|
Posted: Sat Mar 12, 2016 1:22 pm |
|
|
I have checked the program but it does not work. I find the error and fix
Code: | void i2c_send_nibble(byte data, byte type)
{
switch (type)
{
case 0 :
i2c_write(data_shifted | BACKLIGHT_LED);
delay_cycles(1);
i2c_write(data_shifted | ENABLE | BACKLIGHT_LED );
delay_us(2);
i2c_write(data_shifted & NOT ENABLE | BACKLIGHT_LED);
break;
case 1 :
i2c_write(data_shifted | RS | BACKLIGHT_LED);
delay_cycles(1);
i2c_write(data_shifted | RS | ENABLE | BACKLIGHT_LED );
delay_us(2);
i2c_write(data_shifted | RS | BACKLIGHT_LED); //fix
break;
}
} |
|
|
|
bertamf
Joined: 02 Aug 2016 Posts: 1
|
|
Posted: Tue Aug 02, 2016 8:32 am |
|
|
Thanks a lot, younder! You have been of great help |
|
|
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Mon Feb 04, 2019 4:07 am |
|
|
Can someone post a code of how custom Characters are used on i2c serial lcd using pcf8574. Please I have searched through all post but could not get a clue |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Tue Feb 05, 2019 4:16 am |
|
|
Thanks PCM programmer but i have seen the link before.
The problem am having is the code conversion. On the custom lcd code we have:
Code: |
void lcd_load_custom_chars(void)
{
int8 i;
// Set address counter pointing to CGRAM address 0.
lcd_send_byte(0, 0x40);
// Load custom lcd character data into CGRAM.
// It can only hold a maximum of 8 custom characters.
for(i = 0; i < sizeof(lcd_custom_chars); i++)
{
lcd_send_byte(1, lcd_custom_chars[i]);
}
// Set address counter pointing back to the DDRAM.
lcd_send_byte(0, 0x80);
} |
The lcd_send_byte has 2 variable values passed into it which is cgram address and data.
But on this I2c Lcd code we have
Code: |
void lcd_send_byte(byte data, byte type)
{
i2c_start();
i2c_write(LCD_ADDR);
i2c_send_nibble(data >> 4 , type);
i2c_send_nibble(data & 0xf , type);
i2c_stop();
} |
Which the lcd_send_byte has Data and type.
My question is... How do we convert the address data to data type ? |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Tue Feb 05, 2019 10:54 am |
|
|
He has reversed the order of the parameters in lcd_send_byte().
That's annoying. It makes it harder to add new features from
pre-existing code.
The modified custom chars function is below. You should now be able
to use this function with the i2c lcd code:
Code: |
void lcd_load_custom_chars(void)
{
int8 i;
// Set address counter pointing to CGRAM address 0.
// lcd_send_byte(0, 0x40);
lcd_send_byte(0x40, 0); // Reverse the parameter order
// Load custom lcd character data into CGRAM.
// It can only hold a maximum of 8 custom characters.
for(i = 0; i < sizeof(lcd_custom_chars); i++)
{
// lcd_send_byte(1, lcd_custom_chars[i]);
lcd_send_byte(lcd_custom_chars[i], 1); // Reverse the parameter order
}
// Set address counter pointing back to the DDRAM.
// lcd_send_byte(0, 0x80);
lcd_send_byte(0x80, 0); // Reverse the parameter order
} |
|
|
|
savotech
Joined: 13 Dec 2012 Posts: 12
|
|
Posted: Wed Feb 06, 2019 2:38 am |
|
|
Thanks a lot PCM programmer. It was really helpful. My code is working well and I can design any custom characters. |
|
|
bhas_r
Joined: 19 May 2009 Posts: 18
|
Unable to get custom char in i2c lcd |
Posted: Sat Aug 17, 2019 12:10 am |
|
|
Unable to get custom char in i2c lcd.
only first up arrow is displayed down arrow or degrees symbol not displayed
looking for correction _________________ Thanks and Regards
R.Bhaaskar |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Aug 17, 2019 12:54 pm |
|
|
Try to use the lcd in normal 4-bit parallel mode, if possible.
Do the custom symbols work in that mode ? |
|
|
bhas_r
Joined: 19 May 2009 Posts: 18
|
thank you PCM programmer |
Posted: Sat Aug 17, 2019 1:17 pm |
|
|
Quote: | Try to use the lcd in normal 4-bit parallel mode, if possible.
Do the custom symbols work in that mode ? |
previously, i used in 4 bit mode that was successful. now the same project i need more pins, almost i used all pins of pic16f877a. so i am looking for i2c with cgram if possible. _________________ Thanks and Regards
R.Bhaaskar |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Aug 18, 2019 2:51 am |
|
|
Then experiment. Try using a lower oscillator frequency for the PIC.
Or, put a delay of 10 msec at the end of the send_byte() function.
Assume there is something wrong, maybe in the timing, and do
experiments to see if you can find the reason. |
|
|
bhas_r
Joined: 19 May 2009 Posts: 18
|
i will try this but |
Posted: Sun Aug 18, 2019 6:19 am |
|
|
PCM programmer wrote: | Then experiment. Try using a lower oscillator frequency for the PIC.
Or, put a delay of 10 msec at the end of the send_byte() function.
Assume there is something wrong, maybe in the timing, and do
experiments to see if you can find the reason. |
but remaining normal data are displaying well.
the only cgram first char is displayed remaining not.
tonight I will try and post _________________ Thanks and Regards
R.Bhaaskar |
|
|
bhas_r
Joined: 19 May 2009 Posts: 18
|
Added 10ms |
Posted: Mon Aug 19, 2019 12:28 am |
|
|
10 ms delay added in send byte, no improvements.
i found another program from internet, i will check and post the same if works properly _________________ Thanks and Regards
R.Bhaaskar |
|
|
-Terppa-
Joined: 08 Jan 2018 Posts: 59 Location: Finland
|
Re: Unable to get custom char in i2c lcd |
Posted: Tue Nov 26, 2019 11:28 am |
|
|
bhas_r wrote: | Unable to get custom char in i2c lcd.
only first up arrow is displayed down arrow or degrees symbol not displayed
looking for correction |
I have exactly the same problem! Everything works like a charm but only one custom character shows up and it is first char. What could be wrong?
EDIT:
Nothing.. I have messed up my bitmap table. |
|
|
|
|
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
|