Connect LCD with I2C in STM32

Description

As promised, today I am going to interface LCD to STM32 using an I2C device (PCF8574). PCF8574 can be used as a port extender, to which we will connect LCD. If you haven’t read my previous post about I2C go 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.

ADDRESSING of 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 upto 128 devices on the same line of I2C 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? 

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 0x4B. In this manner, we can connect upto 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.
 
Connecting 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:-
 

  Connections

1     —>   Vss
2     —>   Vcc
3     —>   Vee
4     —>   RS      —> p0
5     —>   R/W   —> p1
6     —>   En      —> p2
7     —>   DB0
8     —>   DB1
9     —>   DB2
10   —>   DB3
11   —>   DB4    —> p4
12   —>   DB5    —> p5
13   —>   DB6    —> p6
14   —>   DB7    —> p7
15   —>   LED+
16   —>   LED-

Some Insight into the Code:-

First of all we have to create functions for LCD command and data.
 
command function
void lcd_send_cmd (char cmd)
   {
        char data_u, data_l;    
	uint8_t data_t[4];
	data_u = cmd&0xf0;    // select only upper nibble
	data_l = (cmd<<4)&0xf0;    // select only lower nibble
	data_t[0] = data_u|0x04;  //en=1, rs=0
	data_t[1] = data_u;  //en=0, rs=0
	data_t[2] = data_l|0x04;  //en=1, rs=0
	data_t[3] = data_l;  //en=0, rs=0
	HAL_I2C_Master_Transmit (&hi2c1, 0x4E, (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 upper than lower. Both parts are sent along enable pin 1 and than with enable pin 0.
 
 
When Data is OR(|) with 0x04 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 alone so En, RS, R/W are all low.
 
data function
void lcd_send_data (char data)
{
	char data_u, data_l;
	uint8_t data_t[4];
	data_u = data&0xf0;    // upper data nibble
	data_l = (data<<4)&0xf0;    // lower data nibble
	data_t[0] = data_u|0x05;  //en=1, rs=0
	data_t[1] = data_u|0x01;  //en=0, rs=0
	data_t[2] = data_l|0x05;  //en=1, rs=0
	data_t[3] = data_l|0x01;  //en=0, rs=0
	HAL_I2C_Master_Transmit (&hi2c1, 0x4E,(uint8_t *) data_t, 4, 100);
}
The above functions ends the data to the device to which our LCD is connected. We have to send data in two parts first upper than lower. Both parts are sent along enable pin 1 and than with enable pin 0.
Data is OR(|) with 0x05 which implies that P2 (En) and P0 (RS) pin are high , P1(R/W) is low for the data and write operation.
In second case data is OR(|) with 0x01 to make only RS pin high and all others low.
 

LCD-COMMANDS

Result

LCD Displaying Hello world !

Video

100%
100%

DOWNLOAD

You can buy me a coffee sensor 🙂

download the CODE below

31
Leave a Reply

avatar
15 Comment threads
16 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
19 Comment authors
SattarJohnRichaplAnonymous123 Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
Ramo
Guest
Ramo

Nice.

PawelDNB
Guest
PawelDNB

I wrote to you in YT but its proper place . When you initialize LCD you must send 4-bit mode at first with delay
void lcd_init (void)
{
uint8_t i=0;
HAL_Delay(100);
for(i=0;i<3;i++)//sending 3 times: select 4-bit mode
{
lcd_send_cmd(0x03);
HAL_Delay(45);
}
lcd_send_cmd (0x02);
HAL_Delay(100);
lcd_send_cmd (0x28);
HAL_Delay(1);
lcd_send_cmd (0x0c);
HAL_Delay(1);
lcd_send_cmd (0x80);
HAL_Delay(1);
}

and some additional function to set proper location on screen :
void lcd_print_x_y(uint8_t line , uint8_t row, char *str )
{
if (line == 0 ){
uint8_t line_x_y = 0x80 + row ;
lcd_send_cmd(line_x_y);
while (*str) lcd_send_data (*str++);
}else if (line == 1 ){
uint8_t line_x_y = 0x80 |( 0x40 + row) ;
lcd_send_cmd(line_x_y);
while (*str) lcd_send_data (*str++);
}
}

Stasiu
Guest
Stasiu

I’ve got one question.
How to use it it other commands, for example command to clear screen ?
Can I find anywhere list of hex code for functions ?

Thomas Christensen
Guest
Thomas Christensen

Great tutorials/examples (not only this one). Thanks for sharing.
Can you give an estimate of the refresh rate when writing all 2×16 characters. Is it 1 sec. to write all characters or 0.1 sec.?
Do you consider such a display setup an acceptable debugging option?

JoaquinS
Guest
JoaquinS

I can’t thank you enough for this lib!
Such a great and working lib and so simple!!!

Feel free to contact me for colaboration, I’d love to!

JS

mehar
Guest
mehar

hi how to write code for scrolling display for 20*4 now i am able to write but it is improper please help me in the issuse

Marcel
Guest
Marcel

Thanks for your code it works even with a 20×4 display with some changes.
I have a question: Why does your code work without the required waitings between the init commands?
The datasheet says that you have to wait more tha 4.1 ms between the first instructions.
Thanks

Jose Ronaldo
Guest
Jose Ronaldo

I use STM32F103C8 + PCF8574A + Keil-5, does not work.
The program scans and finds the address 0x3F.
Works well with STM32F103C8 + PCF8574A + Arduino IDE using address 0x3F.
Works well STM32F103C8 + SSD1306 + Keil-5.
Does anyone have an idea what the problem is?
Regards

Vojta
Guest
Vojta

Hello, can I use your i2c-lcd library for STM32F072B-discovery? Is it ok when I replace in i2c-lcd.h #include “stm32f4xx_hal.h” for #include “stm32f0xx_hal.h”. Thank you.

Gass
Guest
Gass

it doesn’t work in my 16×4 lcd, can you help me to use 16×4 lcd?

Anonymous123
Guest
Anonymous123

i’m having problems using it, i did an exact copy of the code, activated the I2C on cubemx and the display isn’t doing anything at all besides turning on and i already adjusted brightness, i tried to measure RW pin with multimeter and i got a 5V signal everytime i did it, so i think it’s the RW but i don’t know how i change it

Ric
Guest
Ric

it’s not working yet with STM32F107VCT6, will it work with 3.3V supply and bus ? thanks

Ric
Guest
Ric

It works now, my chip is PCF8574AT, the address is 7E, thanks,
I changed on backlight side, replace the transistor with 470 ohm resistor, so it’s on always…cheers…

John
Guest
John

I’m using your code but all the i2c bus is sending are ‘Setup Write to [&] + NAK’ packets over and over and over again. I’m using salae’s logic program and an analyzer to get the data. The board is a blue pill with some chinese knockoff STM32f103c8 but it’s working as normal so far except for the code. Any ideas??

Sattar
Guest
Sattar

Hello, Thank you for this code.I use your code, when I initialize LCD my LCD’s back-light is goes off,why this happens?could you help me?

Menu