Menu

all about electronic and microcontrollers

Monday, December 31, 2018

PCF8574 [I2C Module] & LCD Display

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)].

The device features an 8-bit quasi-bidirectional I/O port (P0–P7), including latched outputs with high-current drive capability for directly driving LEDs. Each quasi-bidirectional I/O can be used as an input or output without the use of a data-direction control signal. At power on, the I/Os are high. In this mode, only a current source to VCC is active.

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.
Applications
  • Telecom Shelters: Filter Units
  • Servers
  • Routers (Telecom Switching Equipment)
  • Personal Computers
  • Personal Electronics
  • Industrial Automation
  • Products with GPIO-Limited Processors
Diagram


Pinning


Functional Description


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 1111110x3F 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.


Interrupt output

Interrupts are used when we want to use more than one device via I2C bus.


Application Example

I2C Module for LCD 1602, 1604, 2004 Displays.


Test Example

I have a very good confident into PicKit2 Programmer, that's why often it will be seen into my photos.


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) {

    }
}