Overview
The PCF8574 is a silicon CMOS circuit. It provides general purpose remote I/O expansion for most microcontroller families by way of the I2C interface [serial clock (SCL), serial data (SDA)].
This 8-bit input/output (I/O) expander for the two-line bidirectional bus (I2C) is designed for 2.5-V to 6-V VCC operation.
Features
- Low Standby-Current Consumption of 10 µA Max
- I2C to Parallel-Port Expander
- Open-Drain Interrupt Output
- Compatible With Most Microcontrollers
- Latched Outputs With High-Current Drive
- Capability for Directly Driving LEDs
- Latch-Up Performance Exceeds 100 mA
- Per JESD 78, Class II
- Package: DIP16, SO16 and SSOP20.
- Telecom Shelters: Filter Units
- Servers
- Routers (Telecom Switching Equipment)
- Personal Computers
- Personal Electronics
- Industrial Automation
- Products with GPIO-Limited Processors
Diagram
Pinning
Addressing
We have to pay real attention on this section if we want to communicate with the chip without spending a lot of time to understand why it doesn't work.
In my particular case the chip address, generated by Arduino I2C Scanner, was 111111 = 0x3F and it works well with Arduino Uno board.
The things got messy when I had tried to use the chip with a PIC controller on the regular hardware. I had dug into the issue and I've discovered that string address wasn't complete because it doesn't contained the last state bit (R/W function), which in my case was 0 logic. So the complete string become 1111110 = 0x7E, and the module has revived.
Interrupts are used when we want to use more than one device via I2C bus.
I2C Module for LCD 1602, 1604, 2004 Displays.
Test Example
Code Example
Written in MikroC Pro for PIC v7.2.0
/************************************************************************
* Project name: I2C(PCF8574) & LCD1602
* Description: Communication test
* Hardware configuration is:
* IC PCF8574 is connected with our microcontroller by RC3=SCL,
* RC4=SDA (I2C Connections), +5vdc and circuit ground
* Message example written on 16x02 LCD characters:
* ------------------
* |I2C LCD 16x2 |
* |PIC16F876A 8 MHz|
* ------------------
* Adapted by:
* Aureliu Raducu Macovei, 2018.
* Test configuration:
* MCU: PIC16F876A;
* Test.Board: WB-106 Breadboard 2420 dots;
* SW: MikroC PRO for PIC 2018 (version v7.2.0);
* Configuration Word:
* Oscillator: HS (8Mhz)on pins 9 and 10;
* Watchdog Timer: OFF;
* Power up Timer: OFF;
* Browun Out Detect: ON;
* Low Voltage Program: Disabled;
* Data EE Read Protect: OFF;
* Flash Program Write: Write Protection OFF;
* Background Debug: Disabled;
* Code Protect: OFF
************************************************************************/
#define _LCD_FIRST_ROW 0x80 //Move cursor to the 1st row
#define _LCD_SECOND_ROW 0xC0 //Move cursor to the 2nd row
#define _LCD_THIRD_ROW 0x94 //Move cursor to the 3rd row
#define _LCD_FOURTH_ROW 0xD4 //Move cursor to the 4th row
#define _LCD_CLEAR 0x01 //Clear display
#define _LCD_RETURN_HOME 0x02 //Return cursor to home position, returns a shifted display to
//its original position. Display data RAM is unaffected.
#define _LCD_CURSOR_OFF 0x0C //Turn off cursor
#define _LCD_UNDERLINE_ON 0x0E //Underline cursor on
#define _LCD_BLINK_CURSOR_ON 0x0F //Blink cursor on
#define _LCD_MOVE_CURSOR_LEFT 0x10 //Move cursor left without changing display data RAM
#define _LCD_MOVE_CURSOR_RIGHT 0x14 //Move cursor right without changing display data RAM
#define _LCD_TURN_ON 0x0C //Turn Lcd display on
#define _LCD_TURN_OFF 0x08 //Turn Lcd display off
#define _LCD_SHIFT_LEFT 0x18 //Shift display left without changing display data RAM
#define _LCD_SHIFT_RIGHT 0x1E //Shift display right without changing display data RAM
// LCD Definitions
#define LCD_ADDR 0x7E // This is PCF8574 adress. On mine pcb "MH" series the adress is 0x3F or 0x7E
// Lcd constants
char txt1[] = "I2C LCD 16x2";
char txt2[] = "PIC16F876A 8 MHz";
void I2C_LCD_Cmd(char out_char) {
char hi_n, lo_n;
char rs = 0x00;
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
I2C1_stop();
if(out_char == 0x01)Delay_ms(2);
}
void I2C_LCD_Chr(char row, char column, char out_char) {
char hi_n, lo_n;
char rs = 0x01;
switch(row){
case 1:
I2C_LCD_Cmd(0x80 + (column - 1));
break;
case 2:
I2C_LCD_Cmd(0xC0 + (column - 1));
break;
case 3:
I2C_LCD_Cmd(0x94 + (column - 1));
break;
case 4:
I2C_LCD_Cmd(0xD4 + (column - 1));
break;
};
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Chr_Cp(char out_char) {
char hi_n, lo_n;
char rs = 0x01;
hi_n = out_char & 0xF0;
lo_n = (out_char << 4) & 0xF0;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
I2C1_Wr(hi_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(hi_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_us(100);
I2C1_Wr(lo_n | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(lo_n | rs | 0x00 | 0x08);
I2C1_Is_Idle();
I2C1_stop();
}
void I2C_LCD_Init() {
char rs = 0x00;
I2C1_Start();
I2C1_Is_Idle();
I2C1_Wr(LCD_ADDR);
I2C1_Is_Idle();
Delay_ms(30);
I2C1_Wr(0x30 | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(0x30 | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_ms(10);
I2C1_Wr(0x30 | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(0x30 | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_ms(10);
I2C1_Wr(0x30 | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(0x30 | rs | 0x00 | 0x08);
I2C1_Is_Idle();
Delay_ms(10);
I2C1_Wr(0x20 | rs | 0x04 | 0x08);
I2C1_Is_Idle();
Delay_us(50);
I2C1_Wr(0x20 | rs | 0x00 | 0x08);
I2C1_Is_Idle();
I2C1_Stop();
Delay_ms(10);
I2C_LCD_Cmd(0x28);
I2C_LCD_Cmd(0x06);
}
void I2C_LCD_Out(char row, char col, char *text) {
while(*text)
I2C_LCD_Chr(row, col++, *text++);
}
void I2C_LCD_Out_Cp(char *text) {
while(*text)
I2C_LCD_Chr_Cp(*text++);
}
void main() {
TRISA = 0x00;
PORTA = 0x00;
I2C1_Init(100000);
I2C_LCD_Init();
I2C_LCD_Cmd(_LCD_CURSOR_OFF);
I2C_LCD_Cmd(_LCD_CLEAR);
I2C_Lcd_Out(1,1,txt1); // Write text in first row
I2C_Lcd_Out(2,1,txt2); // Write text in second row
while(1) {
}
}