In this article, we will explore interfacing the PIC16F877 with an I2C 16×2 LCD. You’ll learn how to connect the microcontroller to the LCD for efficient data display.
Interfacing I2C LCD 16×2 With PIC16F877A Microcontroller
Microcontrollers serve as the brains of many embedded systems, handling tasks ranging from simple automation to complex control. By interfacing the PIC16F877 with an I2C 16×2 LCD, you enhance its functionality. The I2C protocol simplifies this process, enabling efficient communication between microcontrollers and peripherals. Focusing on the interfacing of the PIC16F877 with I2C 16×2 LCD modules streamlines your projects, offering convenience and flexibility.
I2C LCD IO Expander (PCF8574):
Module Board Description & Pinout
The I2C LCD IO Expander (PCF8574) module board serves as a breakout board for the PCF8574 chip, facilitating LCD interfacing through a 16-pin header. It features a jumper to toggle the LCD backlight and a potentiometer for adjusting the LCD screen contrast.
The solder pads labeled A0, A1, and A2 allow you to configure the address of the board by soldering these pads to set different address bits. This enables multiple LCD I2C expander boards on the same bus. The module board also includes a power indicator, voltage regulator, and a header for connecting the I2C 2-wire interface (SCL, SDA).
PCF8574 I2C IO Expander
The PCF8574/74A serves as a general-purpose remote I/O expansion device via the two-wire bidirectional I2C-bus (SCL, SDA) operating at 100 kHz. This device allows the system master to read from the input port or write to the output port through a single register.
The PCF8574 and PCF8574A are functionally identical, except for their fixed slave address portions. Utilizing the three hardware address pins, up to eight devices of each type can coexist on the same I2C-bus, supporting a maximum of 128 I/Os.
An active LOW open-drain interrupt output (INT) is provided, which can be connected to the microcontroller’s interrupt logic. This feature is activated when any input state differs from its corresponding input port register state, signaling to the microcontroller that an input state has changed.
Features of PCF8574:
- I2C-bus to parallel port expander
- Standard-mode I2C-bus interface operating at 100 kHz
- Operating supply voltage ranging from 2.5 V to 6 V
- 8-bit remote I/O pins defaulting to inputs at power-up
- Latched outputs capable of directly driving LEDs
- Total package sink capability of 80 mA
- Active-LOW open-drain interrupt output
- Eight programmable slave addresses using three address pins
- Low standby current consumption (2.5 µA typical)
Applications for PCF8574:
- LEDs and displays
- Servers
- Keypads
- Industrial control systems
- Programmable logic controllers (PLCs)
- Cellular phones
- Instrumentation and test measurement equipment
I2C Device Address Configuration:
The PCF8574/74A utilizes a slave address format following a START condition, where the address of the slave and the desired operation (read or write) are sent by the bus master.
The device’s address pins (A2, A1, A0) can be connected directly to VDD or VSS or through external resistors to set the address bits.
PCF8574 I2C Device Address Configuration and IO Programming
The PCF8574/74A utilizes a slave address format following a START condition, where the bus master sends the address of the slave and the desired operation (read or write). The device’s address pins (A2, A1, A0) can be connected directly to VDD or VSS, or through external resistors, to configure the address bits. This configuration allows for flexible addressing in various applications. The PCF8574 features quasi-bidirectional I/Os, functioning as input or output ports without needing a separate direction control register. Upon power-on, all ports default to a HIGH state, maintained by a weak 100 µA internal pull-up to VDD, but can be driven LOW by an internal transistor or an external signal.
In write mode, the master sends the slave address with the last bit set to logic 0, followed by the data byte for ports P7 to P0. Writing a LOW pulls the port down, while writing a HIGH activates a pull-up for half of the clock cycle before being held HIGH by a weak current source. To read from a port, the master addresses the slave device and sets the last bit of the address byte to logic 1. The slave acknowledges and sends the data byte to the master, which can then ACK or NACK and read the input register again. Reading an output pin indicates its actual state, either HIGH or LOW, providing a straightforward method for input and output operations.
Project name : I2C LCD Interface with PIC16F877A
This project demonstrates an advanced LCD interface for the PIC16F877A microcontroller using the PCF8574 I2C I/O expander. It facilitates the communication between the microcontroller and the LCD, enabling various display operations such as initialization, clearing the screen, printing messages, and controlling the cursor.
Code (in C using XC8 Compiler):
Initialization and Configuration
This part covers the necessary includes, configuration settings, and the initial setup for the PIC microcontroller. It establishes the constants for I2C communication and LCD commands, and it also defines the function prototypes.
/* File: main.c * Author: Marwen Maghrebi * Description: Advanced LCD Interface for PIC16F877A Using PCF8574 * This code demonstrates an enhanced interface for LCD (Liquid Crystal Display) with a PIC microcontroller, specifically the PIC16F877A, *utilizing the PCF8574 I2C I/O expander. It initializes the LCD, clears the screen, and prints messages. Additionally, it provides functions *to control the cursor position, send commands or data to the LCD, and manage the backlight. */ // Include necessary header files #include <xc.h> // Define constants #define _XTAL_FREQ 16000000 #define I2C_BaudRate 100000 #define SCL_D TRISC3 #define SDA_D TRISC4 #define LCD_BACKLIGHT 0x08 #define LCD_NOBACKLIGHT 0x00 #define LCD_FIRST_ROW 0x80 #define LCD_SECOND_ROW 0xC0 #define LCD_THIRD_ROW 0x94 #define LCD_FOURTH_ROW 0xD4 #define LCD_CLEAR 0x01 #define LCD_RETURN_HOME 0x02 #define LCD_ENTRY_MODE_SET 0x04 #define LCD_CURSOR_OFF 0x0C #define LCD_UNDERLINE_ON 0x0E #define LCD_BLINK_CURSOR_ON 0x0F #define LCD_MOVE_CURSOR_LEFT 0x10 #define LCD_MOVE_CURSOR_RIGHT 0x14 #define LCD_TURN_ON 0x0C #define LCD_TURN_OFF 0x08 #define LCD_SHIFT_LEFT 0x18 #define LCD_SHIFT_RIGHT 0x1E #define LCD_TYPE 2 // 0 -> 5x7 | 1 -> 5x10 | 2 -> 2 lines // Function prototypes void main(void); void I2C_Master_Init(void); void I2C_Master_Wait(void); void I2C_Master_Start(void); void I2C_Master_RepeatedStart(void); void I2C_Master_Stop(void); void I2C_ACK(void); void I2C_NACK(void); unsigned char I2C_Master_Write(unsigned char data); unsigned char I2C_Read_Byte(void);
LCD Control Functions
This part provides the functions necessary for interacting with the LCD. It includes commands for initializing the LCD, writing data and commands, controlling the cursor, and managing backlight settings.
void LCD_Init(unsigned char I2C_Add) { i2c_add = I2C_Add; IO_Expander_Write(0x00); __delay_ms(30); LCD_CMD(0x03); __delay_ms(5); LCD_CMD(0x03); __delay_ms(5); LCD_CMD(0x03); __delay_ms(5); LCD_CMD(LCD_RETURN_HOME); __delay_ms(5); LCD_CMD(0x20 | (LCD_TYPE << 2)); __delay_ms(50); LCD_CMD(LCD_TURN_ON); __delay_ms(50); LCD_CMD(LCD_CLEAR); __delay_ms(50); LCD_CMD(LCD_ENTRY_MODE_SET | LCD_RETURN_HOME); __delay_ms(50); } void IO_Expander_Write(unsigned char Data) { I2C_Master_Start(); I2C_Master_Write(i2c_add); I2C_Master_Write(Data | BackLight_State); I2C_Master_Stop(); } void LCD_Write_4Bit(unsigned char Nibble) { // Get The RS Value To LSB OF Data Nibble |= RS; IO_Expander_Write(Nibble | 0x04); IO_Expander_Write(Nibble & 0xFB); __delay_us(50); } void LCD_CMD(unsigned char CMD) { RS = 0; // Command Register Select LCD_Write_4Bit(CMD & 0xF0); LCD_Write_4Bit((CMD << 4) & 0xF0); } void LCD_Print_Char(char Data) { RS = 1; // Data Register Select LCD_Write_4Bit(Data & 0xF0); LCD_Write_4Bit((Data << 4) & 0xF0); } void LCD_Print_String(char* Str) { for(int i=0; Str[i]!='\0'; i++) LCD_Print_Char(Str[i]); } void LCD_Set_Cursor(unsigned char ROW, unsigned char COL) { switch(ROW) { case 2: LCD_CMD(0xC0 + COL-1); break; case 3: LCD_CMD(0x94 + COL-1); break; case 4: LCD_CMD(0xD4 + COL-1); break; default: LCD_CMD(0x80 + COL-1); } } void Backlight(void) { BackLight_State = LCD_BACKLIGHT; IO_Expander_Write(0); } void noBacklight(void) { BackLight_State = LCD_NOBACKLIGHT; IO_Expander_Write(0); } void LCD_SL(void) { LCD_CMD(0x18); __delay_us(40); } void LCD_SR(void) { LCD_CMD(0x1C); __delay_us(40); } void LCD_Clear(void) { LCD_CMD(0x01); __delay_us(40); }
Main Function and I2C Communication
In this section, we focus on the main function that initializes the LCD and writes strings to it. We also define the I2C communication functions, including starting, stopping, and writing data.
// PIC16F877A Configuration Bit Settings #pragma config FOSC = HS // Oscillator Selection bits (HS oscillator) #pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled) #pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled) #pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled) #pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming) #pragma config CPD = OFF // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off) #pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control) #pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off) unsigned char i2c_add, BackLight_State = LCD_BACKLIGHT; unsigned char RS; void main(void) { // Initialize I2C and LCD I2C_Master_Init(); LCD_Init(0x4E); // Initialize LCD module with I2C address = 0x4E // Write strings to LCD LCD_Set_Cursor(1, 1); LCD_Print_String(" THE EMBEDDED "); LCD_Set_Cursor(2, 1); LCD_Print_String(" THINGS "); __delay_ms(2500); // Animation loop while(1) { LCD_SR(); __delay_ms(350); LCD_SR(); __delay_ms(350); LCD_SL(); __delay_ms(350); LCD_SL(); __delay_ms(350); } return; } void I2C_Master_Init(void) { SSPCON = 0x28; SSPCON2 = 0x00; SSPSTAT = 0x00; SSPADD = ((_XTAL_FREQ/4)/I2C_BaudRate) - 1; SCL_D = 1; SDA_D = 1; } void I2C_Master_Wait(void) { while ((SSPSTAT & 0x04) || (SSPCON2 & 0x1F)); } void I2C_Master_Start(void) { I2C_Master_Wait(); SEN = 1; } void I2C_Master_Stop(void) { I2C_Master_Wait(); PEN = 1; }
Proteus Configuration :
- Open Proteus & Create New Project and click next
- Click on Pick Device
- Search for PIC16F877A & LCD 16*2 & PCF8574
- Click on Virtual Instruments Mode then choose I2C DEBUGGER
- Click on Terminal Mode then choose (DEFAULT & POWER &GROUND)
- finally make the circuit below and start the simulation
That’s all!
If you have any questions or suggestions don’t hesitate to leave a comment below