Interface LCD 16×2 via I2C with STM32

Today I am going to interface LCD to STM32 using an I2C device (PCF8574). PCF8574 can be used as a port extender, to which LCD will be connected. If you haven’t read my previous post about I2C go check that out HERE.


As you can see above PCF8574 has 4 input pins GND, VCC, SDA, SCL and 16 output pins. We will connect our LCD to these 16 pins.

What is the ADDRESS for PCF8574

The higher nibble of PCF8574 address is 0100 and this is fixed. But lower nibble can be modified according to our convenience.  The question you must be thinking is  why we need to modify lower nibble? 

Well you generally don’t but as I mentioned in my previous article that we can connect up to 128 devices on the same I2C line and let’s say we want to connect two different LCDs on the same I2C line, than we can’t use two PCF8574 with same addresses and we need to modify one of them.

So how do we modify the address?

  • The address of the PCF8574 is 0 1 0 0 A2 A1 A0 R/W. To change the address we are provided with A0, A1 and A2 pins.
  • By default these three pins are high so the address by default is 01001110 which is 0x4E.  
  • To change the address of this device, you have to connect any/all of these three pins to ground, which is provided just above them.
  • So let’s say you connected A0 to ground, new address will be 01001100 which is 0x4C.
  • In this manner, we can connect up to 8 LCDs to the same line.
  • I want to point to one more thing here, the last bit of the address is kept 0 intentionally, because this bit is responsible for read(1)/ write(0) operation.

HOW TO Connect LCD to PCF8574

As shown in the figure above, first pin of the device is Vss which is pin 1 of LCD. So all you have to do is connect first pins of the LCD to Vss above and rest will connect accordingly.   Starting with Vss as first pin, connection is as follows:-

pcf connection to lcd
connection LCD

Some Insight into the Code

#define SLAVE_ADDRESS_LCD 0x4E // change this according to ur setup

The default slave address defined in the i2c-lcd.c is 0x4E. This is default for the PCF8574.

void lcd_send_cmd (char cmd)
  char data_u, data_l;
	uint8_t data_t[4];
	data_u = (cmd&0xf0);
	data_l = ((cmd<<4)&0xf0);
	data_t[0] = data_u|0x0C;  //en=1, rs=0
	data_t[1] = data_u|0x08;  //en=0, rs=0
	data_t[2] = data_l|0x0C;  //en=1, rs=0
	data_t[3] = data_l|0x08;  //en=0, rs=0
	HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
  • The above function will send the command to the device to which our LCD is connected.
  • As we are using 4 bit LCD mode, we have to send command in two parts.
  • First we send the upper nibble, and than the lower one. Both parts are sent along enable pin 1 and than with enable pin 0.
  • When the data_t is OR(|) with 0x0C, which implies that P2 (En) pin is high and P0 (RS), P1(R/W) are low for the command and write operation.
  • In the second case data is sent along with 0x08 only. This is to ensure that the back light is on and En, RS, R/W are all low.

void lcd_send_data (char data)
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = (data&0xf0);
	data_l = ((data<<4)&0xf0);
	data_t[0] = data_u|0x0D;  //en=1, rs=1
	data_t[1] = data_u|0x09;  //en=0, rs=1
	data_t[2] = data_l|0x0D;  //en=1, rs=1
	data_t[3] = data_l|0x09;  //en=0, rs=1
	HAL_I2C_Master_Transmit (&hi2c1, SLAVE_ADDRESS_LCD,(uint8_t *) data_t, 4, 100);
  • The above function sends the data to the device to which our LCD is connected.
  • Similarly like command, We have to send data in two parts, first upper and than lower half.
  • Both parts are sent along enable pin 1 and than with enable pin 0. Data is OR(|) with 0x0D which implies that P2 (En) and P0 (RS) pin are high , P1(R/W) is low and back light is on, for the data and write operation.
  • In second case data is OR(|) with 0x09 to make only RS pin high and for the back light, and all others low.  

void lcd_init (void)
	// 4 bit initialisation
	HAL_Delay(50);  // wait for >40ms
	lcd_send_cmd (0x30);
	HAL_Delay(5);  // wait for >4.1ms
	lcd_send_cmd (0x30);
	HAL_Delay(1);  // wait for >100us
	lcd_send_cmd (0x30);
	lcd_send_cmd (0x20);  // 4bit mode

  // dislay initialisation
	lcd_send_cmd (0x28); // Function set --> DL=0 (4 bit mode), N = 1 (2 line display) F = 0 (5x8 characters)
	lcd_send_cmd (0x08); //Display on/off control --> D=0,C=0, B=0  ---> display off
	lcd_send_cmd (0x01);  // clear display
	lcd_send_cmd (0x06); //Entry mode set --> I/D = 1 (increment cursor) & S = 0 (no shift)
	lcd_send_cmd (0x0C); //Display on/off control --> D = 1, C and B = 0. (Cursor and blink, last two bits)

As according to the datasheet of the LCD 16×2, in order to initialize the LCD, we have to use some sequence of commands. The code is commented properly, so that you can understand it better

void lcd_send_string (char *str)
	while (*str) lcd_send_data (*str++);

Above function can be Used to send the string to the LCD.



Check out the Video Below


You can help with the development by DONATING OR Just click DOWNLOAD to download the code

Notify of

Oldest Most Voted
Inline Feedbacks
View all comments

Adblocker detected! Please consider reading this notice.

We've detected that you are using AdBlock Plus or some other adblocking software which is preventing the page from fully loading.

We don't have any banner, Flash, animation, obnoxious sound, or popup ad. We do not implement these annoying types of ads!

We need money to operate the site, and almost all of it comes from our online advertising.

Please add to your ad blocking whitelist or disable your adblocking software.