|
|
View previous topic :: View next topic |
Author |
Message |
Zilog750
Joined: 19 Oct 2013 Posts: 6
|
LCD HD44780 PCF8574T i2c Driver |
Posted: Sat Oct 19, 2013 3:13 pm |
|
|
Hello,
trying to control an LCD via i2c, I have this hardware
* PIC16F877
* LCD HD44780
* Arduino-IIC-LCD GY-LCD-V1 (with PCF8574T)
also I have CCS 4.108 and this library
Code: |
//-----------------------------------------------------------------------------
// Title: lcd4_i2c.c
// Description: Driver for common LCD 4 row modules using I2C protocol.
// Date: May-2002
// Ver.Rev.: 1.1
// Author: XP8100 ([email protected]) #Based on the routines LCD.C from CCS#
// Modified: To HM-Baybus Project by Manuel Díaz
//-----------------------------------------------------------------------------
//
// lcd_init() Must be called before any other function.
//
// lcd_putc(c) Will display c on the next position of the LCD.
// The following have special meaning:
// f Clear display
//
// Go to start of second line
// Move back one position
//
// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1)
//
//-----------------------------------------------------------------------------
// LCD pins D0-D3 are not used.
//-----------------------------------------------------------------------------
//
// Commment : Control of a compatible LCD HITACHI from a bus I2C with
// an EXPANDER of I/O with connection I2C. The tests of these
// routines have been programmed using the IC PCF8574P of Phillips.
// I use 4 bits mode programming. The 8 bits mode programming
// is possible if you uses 2 x PCF8574P.
//
// As defined in the following structure the pin connection is as follows:
//
// PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
//
//
//
// THIS DOCUMENT IS PROVIDED TO THE USER "AS IS"
//-----------------------------------------------------------------------------
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
#define lcd_line_three 0x14 // LCD Ram address for the 3 line
#define lcd_line_four 0x54 // LCD Ram address for the 4 line
#define IOE_ADDR 0x40 // I2C addr for i/o expander PCF8574P
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.
byte address; // The following are used for setting the I/O port direction register.
void lcd_send_nibble( byte n, byte type ) {
switch (type) {
case "C" :
i2c_write(n << 4);
delay_cycles(1);
i2c_write(n << 4 | 0x01);
delay_us(2);
i2c_write(n << 4 & 0xFE);
break;
case "D" :
i2c_write(n << 4 | 0x02);
delay_cycles(1);
i2c_write(n << 4 | 0x03);
delay_us(2);
i2c_write(n << 4 & 0x02);
break;
}
}
void lcd_send_byte( byte n, byte type ) {
delay_ms(1);
lcd_send_nibble(n >> 4, type);
lcd_send_nibble(n & 0xf, type);
}
void lcd_init() {
byte i;
i2c_start();
i2c_write(IOE_ADDR);
lcd_send_nibble(0, "C");
delay_ms(15);
for (i=1;i<=3;++i) {
lcd_send_nibble(3, "C");
delay_ms(5);
}
lcd_send_nibble(2, "C");
delay_ms(5);
for (i=0;i<=3;++i) {
lcd_send_byte(LCD_INIT_STRING, "C");
}
i2c_stop();
}
void lcd_gotoxy( byte x, byte y) {
i2c_start();
i2c_write(IOE_ADDR);
switch(y){
case 1: address= 00; break;
case 2: address= lcd_line_two; break;
// case 3: address= lcd_line_three; break;
default: address= 00; break;
// default: address= lcd_line_four; break;
}
address+=x-1;
lcd_send_byte(0x80|address, "C");
i2c_stop();
}
void lcd_gotonl (){
/* switch(address){
case 00 : address= lcd_line_two; break;
// case lcd_line_two : address= lcd_line_three; break;
// case lcd_line_three: address= lcd_line_four; break;
default : address= 00; break;
}*/
address= lcd_line_two;
lcd_send_byte(0x80|address, "C");
}
void lcd_putc( char c) {
i2c_start();
i2c_write(IOE_ADDR);
switch (c) {
case "f" : lcd_send_byte(1, "C");
delay_ms(2);
break;
case "
" : lcd_gotonl(); break;
case "" : lcd_send_byte(0x10, "C"); break;
default : lcd_send_byte(c, "D"); break;
}
i2c_stop();
}
|
And using this main code
Code: |
/************************************************************/
/* P R E P R O C E S S O R */
/************************************************************/
#include <16F877.h>
#device ADC=10
/************************************************************/
/* F U S E S */
/************************************************************/
#fuses XT,NOWDT,NOPROTECT,NOLVP,BROWNOUT
/************************************************************/
/* O S C I L A T O R & D E L A Y */
/************************************************************/
#use delay(clock=4000000) /* one instruction=1us? */
/************************************************************/
/* S E T U P R S - 2 3 2 */
/************************************************************/
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
/************************************************************/
/* S E T U P I 2 C */
/************************************************************/
#use I2C(master, SCL=PIN_C3, SDA=PIN_C4)
/************************************************************/
/* D E F I N I T I O N S */
/************************************************************/
//
/******************************************************************/
/* I N C L U D E S */
/******************************************************************/
#include "lcd4_i2c.c"
/************************************************************/
/* G L O B A L V A R I A B L E S */
/************************************************************/
/************************************************************/
/* F U N C T I O N S */
/************************************************************/
/******************************************************************/
/* I N T E R R U P T V E C T O R S */
/******************************************************************/
// NONE
/******************************************************************/
/* M A I N P R O G R A M */
/******************************************************************/
void main()
{
delay_ms (200);
output_high (PIN_B2);
delay_ms (200);
output_low (PIN_B2);
delay_ms (200);
lcd_init ();
while (TRUE)
{
lcd_putc ("Hello");
delay_ms (100);
output_high (PIN_B1);
delay_ms (100);
output_low (PIN_B1);
delay_ms (50);
}
}
|
When compiling I have "Attempt to create a pointer to a constant" errors on lines like
Code: | lcd_send_nibble(0, "C"); |
and also
Code: | lcd_send_byte(LCD_INIT_STRING, "C"); |
I don't have experience on i2c nor CCS Compiler. Any idea what's is happening?
Regards |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sat Oct 19, 2013 5:40 pm |
|
|
The function declaration for your nibble routine looks like this:
Quote: | void lcd_send_nibble( byte n, byte type ) { |
The 2nd parameter is a byte. But below, and in several other places, you
are trying to give it a string:
Quote: | lcd_send_nibble(0, "C"); |
To give it a parameter of the letter C, then you need to put it in single
quotes. Example:
Code: | lcd_send_nibble(0, 'C'); |
Also change your switch-case statement to put the characters in single
quotes. This will give you a byte instead of a string. |
|
|
Zilog750
Joined: 19 Oct 2013 Posts: 6
|
|
Posted: Sun Oct 20, 2013 3:36 am |
|
|
Hello,
changed code as this on lib
Code: | //-----------------------------------------------------------------------------
// Title: lcd4_i2c.c
// Description: Driver for common LCD 4 row modules using I2C protocol.
// Date: May-2002
// Ver.Rev.: 1.1
// Author: XP8100 ([email protected]) #Based on the routines LCD.C from CCS#
// Modified: To HM-Baybus Project by Manuel Díaz
//-----------------------------------------------------------------------------
//
// lcd_init() Must be called before any other function.
//
// lcd_putc(c) Will display c on the next position of the LCD.
// The following have special meaning:
// f Clear display
//
// Go to start of second line
// Move back one position
//
// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1)
//
//-----------------------------------------------------------------------------
// LCD pins D0-D3 are not used.
//-----------------------------------------------------------------------------
//
// Commment : Control of a compatible LCD HITACHI from a bus I2C with
// an EXPANDER of I/O with connection I2C. The tests of these
// routines have been programmed using the IC PCF8574P of Phillips.
// I use 4 bits mode programming. The 8 bits mode programming
// is possible if you uses 2 x PCF8574P.
//
// As defined in the following structure the pin connection is as follows:
//
// PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
//
//
//
// THIS DOCUMENT IS PROVIDED TO THE USER "AS IS"
//-----------------------------------------------------------------------------
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
#define lcd_line_three 0x14 // LCD Ram address for the 3 line
#define lcd_line_four 0x54 // LCD Ram address for the 4 line
#define IOE_ADDR 0x40 // I2C addr for i/o expander PCF8574P
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.
byte address; // The following are used for setting the I/O port direction register.
void lcd_send_nibble( byte n, byte type )
{
switch (type)
{
case 'C' :
i2c_write(n << 4);
delay_cycles(1);
i2c_write(n << 4 | 0x01);
delay_us(2);
i2c_write(n << 4 & 0xFE);
break;
case 'D' :
i2c_write(n << 4 | 0x02);
delay_cycles(1);
i2c_write(n << 4 | 0x03);
delay_us(2);
i2c_write(n << 4 & 0x02);
break;
}
}
void lcd_send_byte( byte n, byte type )
{
delay_ms(1);
lcd_send_nibble(n >> 4, type);
lcd_send_nibble(n & 0xf, type);
delay_ms (1);
}
void lcd_init()
{
byte i;
i2c_start();
i2c_write(IOE_ADDR);
lcd_send_nibble(0, 'C');
delay_ms(15);
for (i=1;i<=3;++i)
{
lcd_send_nibble(3, 'C');
delay_ms(5);
}
lcd_send_nibble(2, 'C');
delay_ms(5);
for (i=0;i<=3;++i)
{
lcd_send_byte(LCD_INIT_STRING[i], 'C');
}
i2c_stop();
}
void lcd_gotoxy( byte x, byte y)
{
i2c_start();
i2c_write(IOE_ADDR);
switch(y)
{
case 1: address= 00; break;
case 2: address= lcd_line_two; break;
// case 3: address= lcd_line_three; break;
default: address= 00; break;
// default: address= lcd_line_four; break;
}
address+=x-1;
lcd_send_byte(0x80|address, 'C');
i2c_stop();
}
void lcd_gotonl ()
{
/* switch(address){
case 00 : address= lcd_line_two; break;
// case lcd_line_two : address= lcd_line_three; break;
// case lcd_line_three: address= lcd_line_four; break;
default : address= 00; break;
}*/
address= lcd_line_two;
lcd_send_byte(0x80|address, 'C');
}
void lcd_putc( char c)
{
i2c_start();
i2c_write(IOE_ADDR);
switch (c)
{
case "f" : lcd_send_byte(1, 'C');
delay_ms(2);
break;
case "
" : lcd_gotonl(); break;
case "" : lcd_send_byte(0x10, 'C'); break;
default : lcd_send_byte(c, 'D'); break;
}
i2c_stop();
} |
Intersed some wait time on
Code: | void lcd_send_byte( byte n, byte type )
{
delay_ms(1);
lcd_send_nibble(n >> 4, type);
lcd_send_nibble(n & 0xf, type);
delay_ms (1);
}
|
And some array addressing at lcd_init()
Code: | for (i=0;i<=3;++i)
{
lcd_send_byte(LCD_INIT_STRING[i], 'C');
} |
And main code is more or less equal
Code: | /************************************************************/
/* P R E P R O C E S S O R */
/************************************************************/
#include <16F877.h>
#device ADC=10
/************************************************************/
/* F U S E S */
/************************************************************/
#fuses XT,NOWDT,NOPROTECT,NOLVP,BROWNOUT
/************************************************************/
/* O S C I L A T O R & D E L A Y */
/************************************************************/
#use delay(clock=4000000) /* one instruction=1us? */
/************************************************************/
/* S E T U P R S - 2 3 2 */
/************************************************************/
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
/************************************************************/
/* S E T U P I 2 C */
/************************************************************/
#use I2C(master, SCL=PIN_C3, SDA=PIN_C4)
/************************************************************/
/* D E F I N I T I O N S */
/************************************************************/
//
/******************************************************************/
/* I N C L U D E S */
/******************************************************************/
#include "lcd4_i2c.c"
/************************************************************/
/* G L O B A L V A R I A B L E S */
/************************************************************/
/************************************************************/
/* F U N C T I O N S */
/************************************************************/
/******************************************************************/
/* I N T E R R U P T V E C T O R S */
/******************************************************************/
// NONE
/******************************************************************/
/* M A I N P R O G R A M */
/******************************************************************/
void main()
{
lcd_init ();
delay_ms (200);
output_high (PIN_B2);
delay_ms (200);
output_low (PIN_B2);
delay_ms (200);
while (TRUE)
{
lcd_putc ("Hello");
delay_ms(100);
}
} |
At this point I can see PCF8574 working with an oscilloscope but nothing happens on LCD. Any Idea or have someone a working lib for this?
Thanks |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Sun Oct 20, 2013 12:23 pm |
|
|
The driver does work....
Reason's it won't.
Using D0 to D3 instead of D4 to D7 on the LCD.
Not having a suitable Vo on the LCD.
Then just general connection problems.
You need about 500mSec delay _before_ lcd_init.
LCD's generally need about 60 to 100mSec after their supply goes 'good', before they will work. Many types don't see their supply as 'good', till a long time after the PIC. Hence 00 to 500mSec delay is needed before sending anything.
If you want an alternative look at the flex_lcd driver in the code forum, which is slightly more generic, and often works easier.
Best Wishes |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Oct 20, 2013 1:14 pm |
|
|
Post a link to the webpage for the LCD that you bought so we can look
at the specs and documentation for the LCD. |
|
|
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
|
|
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
|
Posted: Thu Nov 14, 2013 6:58 pm |
|
|
I just found out that:
For PCF8574A the addressing is:
Jp3 Jp2 Jp1
A2 A1 A0 Dec Hex
L L L 56 0x38
L L H 57 0x39
L H L 64 0x40
L H H 74 0x4A
H L L 75 0x4B
H L H 76 0x4C
H H L 77 0x4D
H H H 78 0x4E
For PCF8574 the addressing is:
Jp3 Jp2 Jp1
A2 A1 A0 Dec Hex
L L L 32 0x20
L L H 33 0x21
L H L 34 0x22
L H H 35 0x23
H L L 36 0x24
H L H 37 0x25
H H L 38 0x26
H H H 39 0x27 _________________ Hugo Silva |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
|
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
|
Posted: Thu Nov 14, 2013 10:38 pm |
|
|
Thanks PCM Programmer... The Address is 0x4e... but the driver in this thread still not working... I could make the lcd work only through another 16x4 driver...but my display is 16x2...maybe it is the reason that I got a bug in the backlight.. it turns it off when I send the command ON. I'll post the driver code below:
Code: |
//////////////////////////////////////////////////////////////////////////////
// This is the simple CCS program for I2C (PCF8574T) lcd module
// Auther: Pumrin S.
// Pin map:
// PCF8574T > 20x4 LCD
// P0 > RS
// P1 > R/W
// P2 > E
// P3 > NC
// P4 > D4
// P5 > D5
// P6 > D6
// P7 > D7
// Note: The SCL and SDA pins should be pull-up resister allway.
//////////////////////////////////////////////////////////////////////////////
#include <18f4550.h>
#device ICD = TRUE
#device ADC=10
#use delay (clock=48000000)
#use i2c(Master,Fast=100000, sda=PIN_D6, scl=PIN_D7,force_sw)
#include <lcd20x4.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,USBDIV,PLL5,CPUDIV1,DEBUG,ICSP1 // configura fuses
#define LCDADDR 0x70 // default slave address
#define ON 0x08
#define OFF 0x00
#define LCD_SET_BIT(x) fetch_data(x)
byte LCD_ADDR=0x4E; //0X4e I2C slave address for Funduino LCD module
//Transmittion data
void transceiver(unsigned char data)
{
i2c_start();
i2c_write(LCD_ADDR); //the slave addresse
i2c_write(data);
i2c_stop();
}
//Clocking the LCD's enable pin during transmit data
void fetch_data(unsigned char data)
{
data=data|0b00000100;//set pin E is a 1
transceiver(data);
delay_ms(1);
data=data-4;//toggle E back to 0
transceiver(data);
delay_ms(1);
}
void lcd_init()
{
//Request works on the command by set the RS = 0 R/W = 0 write
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x10);
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x10);
//First state in 8 bit mode
LCD_SET_BIT(0x30);
LCD_SET_BIT(0x30);
//Then set to 4-bit mode
LCD_SET_BIT(0x30);
LCD_SET_BIT(0x20);
//mode 4 bits, 2 lines, characters 5 x 7 (28 h)
LCD_SET_BIT(0x20);
LCD_SET_BIT(0x80);
//no need cursor on (0Ch)
LCD_SET_BIT(0x00);
LCD_SET_BIT(0xC0);
//the cursor moves to the left (06 h)
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x60);
//clears the display
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x10);
}
void lcd_clear()
{
LCD_SET_BIT(0x00);
LCD_SET_BIT(0x10);
}
// Need the backlight lid.
void lcd_backlight(byte state)
{
LCD_SET_BIT(0x00);
LCD_SET_BIT(state);
}
//Display the character on LCD screen.
void display(char in_data)
{
char data;
data=in_data&0xF0;
data=data+1; //set RS pin to 1
fetch_data(data);
data=in_data&0x0F;
data=data<<4;
data=data+1; //set RS pin to 1
fetch_data(data);
}
//Make the x/y pointer
void lcd_goto_xy(byte x, byte y)
{
byte ptr1, ptr2;
fetch_data(0x10);
fetch_data(0x00);
switch(y)
{
case 1:
ptr1=line_1[x]&0xF0;// stamp the high bit
ptr2=line_1[x]&0x0F;// stamp the low bit
break;
case 2:
ptr1=line_2[x]&0xF0;
ptr2=line_2[x]&0x0F;
break;
case 3:
ptr1=line_3[x]&0xF0;
ptr2=line_3[x]&0x0F;
break;
case 4:
ptr1=line_4[x]&0xF0;
ptr2=line_4[x]&0x0F;
break;
default:
fetch_data(0x80);
fetch_data(0x00);
break;
}
ptr2=ptr2<<4;
fetch_data(ptr1);
fetch_data(ptr2);
}
void main()
{
lcd_init();
while (TRUE)
{
lcd_goto_xy(0,1);
display("ABCDEFGHIJKLMNOP");
//lcd_backlight(ON);
delay_ms(1000);
lcd_goto_xy(0,2);
display("1234567890123456");
//lcd_backlight(ON); If I declare here, it clears the display
delay_ms(1000);
output_high(PIN_D2);
delay_ms(100);
output_low(PIN_D2);
delay_ms(100);
}
}
|
lcd20x4.h
Code: |
//*** START LCD Command ***
#define LCD_CLEAR_SCREEN 0B00000001
#define LCD_RETURN_HOME 0B00000010
//Entry mode set (BIT2=1)
#define LCD_ENTRY_MODE 0B00000100
#define INCREMENTS_LCD 0B00000010
#define DECREMENTS_LCD 0B00000000
#define NO_SCROLL_LCD 0B00000000
#define SCROLL_LCD 0B00000001
//Display ON/OFF (BIT3=1)
#define DISPLAY_LCD 0B00001000
#define LCD_ON 0B00000100
#define LCD_OFF 0B00000000
#define LCD_CURSOR_ON 0B00000010
#define LCD_CURSOR_OFF 0B00000000
#define LCD_BLINK_ON 0B00000001
#define LCD_BLINK_OFF 0B00000000
//Scroll Display/Shift Cursor (BIT4=1)
#define LCD_SCROLL_SHIFT 0B00010000
#define LCD_SHIFT_CURSOR 0B00000000
#define LCD_SHIFT_DISPLAY 0B00001000
#define LCD_SHIFT_LEFT 0B00000000
#define LCD_SHIFT_RIGHT 0B00000100
//Function set (BIT5=1)
#define LCD_FUNCTION_SET 0B00100000
#define LCD4BIT_MODE 0B00000000
#define LCD8BIT_MODE 0B00010000
#define LCD_LINE1 0B00000000
#define LCD_LINE2 0B00001000
#define LCD_FONT_5X7 0B00000000
#define LCD_FONT_5X10 0B00000100
//Move To CGRAM Address (BIT6=1)
#define LCD_CGRAM_ADDRESS 0B01000000
//Move To DDRAM Address (BIT7=1)
#define LCD_DDRAM_ADDRESS 0B10000000
//*** END LCD Command ***
// Prepare x,y address byte
//line1=80h, line2=C0h line3=94h, line4=D4h
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
//1 80 81 82 83 84 85 86 87 88 89 8A 8B 8C 8D 8E 8F 90 91 92 93
//2 C0 C1 C2 C3 C4 C5 C6 C7 C8 C9 CA CB CC CD CE CF D0 D1 D2 D3
//3 94 95 96 97 98 99 9A 9B 9C 9D 9E 9F A0 A1 A2 A3 A4 A5 A6 A7
//4 D4 D5 D6 D7 D8 D9 DA DB DC DE DF E0 E1 E2 E3 E4 E5 E6 E7 E8
byte line_1[20]={0x80,0x81,0x82,0x83,0x84,
0x85,0x86,0x87,0x88,0x89,
0x8A,0x8B,0x8C,0x8D,0x8E,
0x8F,0x90,0x91,0x92,0x93};
byte line_2[20]={0xC0,0xC1,0xC2,0xC3,0xC4,
0xC5,0xC6,0xC7,0xC8,0xC9,
0xCA,0xCB,0xCC,0xCD,0xCE,
0xCF,0xD0,0xD1,0xD2,0xD3};
byte line_3[20]={0x94,0x95,0x96,0x97,0x98,
0x99,0x9A,0x9B,0x9C,0x9D,
0x9E,0x9F,0xA0,0xA1,0xA2,
0xA3,0xA4,0xA5,0xA6,0xA7};
byte line_4[20]={0xD4,0xD5,0xD6,0xD7,0xD8,
0xD9,0xDA,0xDB,0xDC,0xDE,
0xDF,0xE0,0xE1,0xE2,0xE3,
0xE4,0xE5,0xE6,0xE7,0xE8};
|
_________________ Hugo Silva |
|
|
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
|
Posted: Sat Nov 16, 2013 5:39 pm |
|
|
Hi Folks Again
The issue with the driver shared by Zilog750 (in my case) was the PCF8574T i2c interface Pins, which does not match the ones declared in that driver. Can anyone explain how to change it in the driver? the code seems to be a bit complex to me,,, and would really appreciate if someone can explain how the driver is accessing these pins... is there a better way to do that?
I want to change it from:
// PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
To:
// PCF8574P LCD
// ======== ======
// P0 > RS
// P1 > R/W
// P2 > Enable
// P3 > No connect
// P4 > D4
// P5 > D5
// P6 > D6
// P7 > D7
Driver:
Code: |
//-----------------------------------------------------------------------------
// Title: lcd4_i2c.c
// Description: Driver for common LCD 4 row modules using I2C protocol.
// Date: May-2002
// Ver.Rev.: 1.1
// Author: XP8100 ([email protected]) #Based on the routines LCD.C from CCS#
// Modified: To HM-Baybus Project by Manuel Díaz
//-----------------------------------------------------------------------------
//
// lcd_init() Must be called before any other function.
//
// lcd_putc(c) Will display c on the next position of the LCD.
// The following have special meaning:
// f Clear display
//
// Go to start of second line
// Move back one position
//
// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1)
//
//-----------------------------------------------------------------------------
// LCD pins D0-D3 are not used.
//-----------------------------------------------------------------------------
//
// Comment : Control of a compatible LCD HITACHI from a bus I2C with
// an EXPANDER of I/O with connection I2C. The tests of these
// routines have been programmed using the IC PCF8574P of Phillips.
// I use 4 bits mode programming. The 8 bits mode programming
// is possible if you uses 2 x PCF8574P.
//
// As defined in the following structure the pin connection is as follows:
//
// PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
//
//
//
// THIS DOCUMENT IS PROVIDED TO THE USER "AS IS"
//-----------------------------------------------------------------------------
#define lcd_type 2 // 0=5x7, 1=5x10, 2=2 lines
#define lcd_line_two 0x40 // LCD RAM address for the second line
#define lcd_line_three 0x14 // LCD Ram address for the 3 line
#define lcd_line_four 0x54 // LCD Ram address for the 4 line
#define IOE_ADDR 0x40 // I2C addr for i/o expander PCF8574P
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.
byte address; // The following are used for setting the I/O port direction register.
void lcd_send_nibble( byte n, byte type )
{
switch (type)
{
case 'C' :
i2c_write(n << 4);
delay_cycles(1);
i2c_write(n << 4 | 0x01);
delay_us(2);
i2c_write(n << 4 & 0xFE);
break;
case 'D' :
i2c_write(n << 4 | 0x02);
delay_cycles(1);
i2c_write(n << 4 | 0x03);
delay_us(2);
i2c_write(n << 4 & 0x02);
break;
}
}
void lcd_send_byte( byte n, byte type )
{
delay_ms(1);
lcd_send_nibble(n >> 4, type);
lcd_send_nibble(n & 0xf, type);
delay_ms (1);
}
void lcd_init()
{
byte i;
i2c_start();
i2c_write(IOE_ADDR);
lcd_send_nibble(0, 'C');
delay_ms(15);
for (i=1;i<=3;++i)
{
lcd_send_nibble(3, 'C');
delay_ms(5);
}
lcd_send_nibble(2, 'C');
delay_ms(5);
for (i=0;i<=3;++i)
{
lcd_send_byte(LCD_INIT_STRING[i], 'C');
}
i2c_stop();
}
void lcd_gotoxy( byte x, byte y)
{
i2c_start();
i2c_write(IOE_ADDR);
switch(y)
{
case 1: address= 00; break;
case 2: address= lcd_line_two; break;
// case 3: address= lcd_line_three; break;
default: address= 00; break;
// default: address= lcd_line_four; break;
}
address+=x-1;
lcd_send_byte(0x80|address, 'C');
i2c_stop();
}
void lcd_gotonl ()
{
/* switch(address){
case 00 : address= lcd_line_two; break;
// case lcd_line_two : address= lcd_line_three; break;
// case lcd_line_three: address= lcd_line_four; break;
default : address= 00; break;
}*/
address= lcd_line_two;
lcd_send_byte(0x80|address, 'C');
}
void lcd_putc( char c)
{
i2c_start();
i2c_write(IOE_ADDR);
switch (c)
{
case "f" : lcd_send_byte(1, 'C');
delay_ms(2);
break;
case "
" : lcd_gotonl(); break;
case "" : lcd_send_byte(0x10, 'C'); break;
default : lcd_send_byte(c, 'D'); break;
}
i2c_stop();
}
|
Thanks!! _________________ Hugo Silva |
|
|
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Nov 17, 2013 1:27 am |
|
|
Quote: |
I would really appreciate if someone can explain how the driver is
accessing these pins.
|
It's done with the numbers shown in bold below. My advice is to change
those numbers from hex format (0x) to binary format (0b prefix). Then
compare the values in bits 0 to 2, to the pin table for pins P0, P1 and P2
given below. You will see a correspondence.
Quote: |
// PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
switch (type)
{
case 'C' :
i2c_write(n << 4);
delay_cycles(1);
i2c_write(n << 4 | 0x01);
delay_us(2);
i2c_write(n << 4 & 0xFE);
break;
case 'D' :
i2c_write(n << 4 | 0x02);
delay_cycles(1);
i2c_write(n << 4 | 0x03);
delay_us(2);
i2c_write(n << 4 & 0x02);
break;
} |
|
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Sun Nov 17, 2013 2:14 am |
|
|
Or of course, use the names...
Code: |
/ PCF8574P LCD
// ======== ======
// P0 Enable
// P1 RS
// P2 RW
// P3 No connect
// P4 D4
// P5 D5
// P6 D6
// P7 D7
#define ENABLE 1
#define RS 2
#define NOT ~
#define n_shifted n<<4
switch (type)
{
case 'C' :
i2c_write(n_shifted);
delay_cycles(1);
i2c_write(n_shifted | ENABLE);
delay_us(2);
i2c_write(n_shifted & NOT ENABLE);
break;
case 'D' :
i2c_write(n_shifted | 0xRS);
delay_cycles(1);
i2c_write(n_shifted | RS | ENABLE);
delay_us(2);
i2c_write(n_shifted & RS);
break;
}
|
This way the names reflect what pins you are operating.
Best Wishes |
|
|
younder
Joined: 24 Jan 2013 Posts: 53 Location: Brazil
|
Working Driver!!! |
Posted: Sun Nov 17, 2013 5:59 pm |
|
|
Thanks for the tips Ttelmah!!!
Both Drivers are working now... but I've modified and recompiled a third one... I will share it here for anyone who needs in future...after all.. I owe a lot to this forum...
It can control any compatible LCD HITACHI from a bus I2C with
an PCF8574T I/O expander.
The Driver worked with 2x16, 2x20 and 4x20 lcd modules.
I renamed it for "i2c_Flex_LCD.h"
Driver code:
Code: |
//-----------------------------------------------------------------------------
// Title: i2c_Flex_LCD
// Description: Driver for common LCD with 1/2/3 or 4 row modules using PCF8574T interface board with I2C protocol.
// Date: Nov-2013
// Ver.Rev.: 1.0
// Author: Hugo Silva ([email protected]) #Based on the routines of 20X4_LCD_I2C_DRIVER.h from Pumrin S.
//-----------------------------------------------------------------------------
//
// 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 dispay
// \1 Set write position on LCD Line 1
// \2 Set write position on LCD Line 2
// \3 Set write position on LCD Line 3
// \4 Set write position on LCD Line 4
//
// lcd_gotoxy(x,y) Set write position on LCD (upper left is 1,1)
//
//-----------------------------------------------------------------------------
// LCD pins D0-D3 are not used.
//-----------------------------------------------------------------------------
//
// Commment : Control of a compatible LCD HITACHI from a bus I2C with
// an EXPANDER of I/O with connection I2C. The tests of these
// routines have been programmed using the IC PCF8574T of Phillips.
// I used 4 bits mode programming. The 8 bits mode programming
// is possible if you use 2 x PCF8574T.
//
// As defined in the following structure the pin connection is as follows:
//
// PCF8574P LCD
// ======== ======
// P0 RS
// P1 RW
// 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 ON 1
#define OFF 0
#define RS 0b00000001 //P0 - PCF8574T Pin connected to RS
#define RW 0b00000010 //P1 - PCF8574T Pin connected to RW
#define EN 0b00000100 //P2 - PCF8574T Pin connected to EN
#define BACKLIGHT_LED 0b00001000 //P3 - PCF8574T Pin connected to BACKLIGHT LED
#define lcd_line_one 0x80 // LCD RAM address for line 1
#define lcd_line_two 0xC0 // LCD RAM address for line 2
#define lcd_line_three 0x94 // LCD RAM address for line 3
#define lcd_line_four 0xD4 // LCD RAM address for line 4
byte address;
int1 lcd_backlight=ON;
void i2c_send_nibble(unsigned char data)
{
i2c_start();
delay_us(20);
i2c_write(LCD_ADDR); //the slave addresse
delay_us(20);
i2c_write(data);
delay_us(20);
i2c_stop();
delay_us(20);
}
void lcd_send_byte(unsigned char data)
{
if (lcd_backlight) data=data|EN|BACKLIGHT_LED; else data=data|EN; //set pin EN
i2c_send_nibble(data);
data=data-4; //toggle EN back to 0
i2c_send_nibble(data);
}
void lcd_clear()
{
lcd_send_byte(0x00);
lcd_send_byte(0x10);
delay_ms(2);
}
void lcd_init()
{
delay_ms(200); //LCD power up delay
//Request works on the command by set the RS = 0 R/W = 0 write
lcd_send_byte(0x00);
lcd_send_byte(0x10);
lcd_send_byte(0x00);
lcd_send_byte(0x00);
lcd_send_byte(0x10);
//First state in 8 bit mode
lcd_send_byte(0x30);
lcd_send_byte(0x30);
//Then set to 4-bit mode
lcd_send_byte(0x30);
lcd_send_byte(0x20);
//mode 4 bits, 2 lines, characters 5 x 7 (28 h)
lcd_send_byte(0x20);
lcd_send_byte(0x80);
//no need cursor on (0Ch)
lcd_send_byte(0x00);
lcd_send_byte(0xC0);
//the cursor moves to the left (06 h)
lcd_send_byte(0x00);
lcd_send_byte(0x60);
//clears the display
lcd_clear();
}
void lcd_gotoxy( byte x, byte y)
{
static char data;
switch(y)
{
case 1: address= lcd_line_one; break;
case 2: address= lcd_line_two; break;
case 3: address= lcd_line_three; break;
case 4: address= lcd_line_four; break;
default: address= lcd_line_one; break;
}
address+=x-1;
data=address&0xF0;
lcd_send_byte(data);
data=address&0x0F;
data=data<<4;
lcd_send_byte(data);
}
//Display the character on LCD screen.
void LCD_PUTC(char in_data)
{
char data;
switch(in_data)
{
case '\f': lcd_clear() ; break;
case '\1': lcd_gotoxy(1,1); break;
case '\2': lcd_gotoxy(1,2); break;
case '\3': lcd_gotoxy(1,3); break;
case '\4': lcd_gotoxy(1,4); break;
default:
data=in_data&0xF0;
data=data|RS; //set RS pin to 1
lcd_send_byte(data);
data=in_data&0x0F;
data=data<<4;
data=data|RS; //set RS pin to 1
lcd_send_byte(data);
break;
}
}
|
Main code:
Code: |
#include <18f4550.h>
#device ICD = TRUE
#device ADC=10
#use delay (clock=48000000)
#use i2c(Master,Fast=100000, sda=PIN_D6, scl=PIN_D7,force_sw)
#include <i2c_Flex_LCD.h>
#fuses HSPLL,NOWDT,NOPROTECT,NOBROWNOUT,NOLVP,USBDIV,PLL5,CPUDIV1,DEBUG,ICSP1 // configura fuses
void main()
{
Char string="Test";
int16 test=1;
lcd_init();
lcd_backlight=ON;
while (TRUE)
{
test=test+1;
lcd_clear(); //Clear Display
printf(LCD_PUTC,"\1%s","LCD Line 1"); //Print on LCD line 1
delay_ms(1000);
printf(LCD_PUTC,"\2%s","LCD Line 2"); //Print on LCD line 2
delay_ms(1000);
printf(LCD_PUTC,"\f\1%s","LCD Line 1"); //Clear display, print again on Line 1
delay_ms(1000);
printf(LCD_PUTC,"\f\2%s","LCD Line 2"); //Clear display, print again on Line 2
delay_ms(1000);
lcd_backlight=OFF;
printf(LCD_PUTC,"\f\1%s\2%s","LCD BackLight"," OFF "); //Clear display, print again on Line 1
delay_ms(1000);
lcd_backlight=ON;
printf(LCD_PUTC,"\f\1%s\2%s","LCD BackLight"," ON "); //Clear display, print again on Line 2
delay_ms(1000);
}
}
|
Thanks guys for all support!
BR
Hugo _________________ Hugo Silva |
|
|
dyeatman
Joined: 06 Sep 2003 Posts: 1933 Location: Norman, OK
|
|
Posted: Sun Nov 17, 2013 7:30 pm |
|
|
Why not add it to the Code Library? _________________ Google and Forum Search are some of your best tools!!!! |
|
|
Ttelmah
Joined: 11 Mar 2010 Posts: 19496
|
|
Posted: Mon Nov 18, 2013 1:22 am |
|
|
As a minor 'consider this', why not change the init to something like:
Code: |
void lcd_init(void)
{
delay_ms(200); //LCD power up delay
cont int8 init_data[] = {0x0,0x10,0x0,0x0,0x10, \
0x30,0x30, \ //8bit mode
0x30,0x20, \ //4bit mode
0x20,0x80, \ //2lines 5*7
0x00,0xC0, \ //No cursor
0x00,0x60 };
int8 ctr;
//Request works on the command by set the RS = 0 R/W = 0 write
for (ctr=0;ctr<sizeof(init_data;ctr++)
lcd_send_byte(ctr); //send initialisation
//clears the display
lcd_clear();
}
|
When you are using the same command fifteen times, it may well be a case of 'better to loop'.
Best Wishes |
|
|
|
|
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
|