Description

NOTE :- The following code is written with STM32CUBEIDE. If you are using keil, or any other IDE, code will still work for you. But you have to create a project and than import those files, as shown in the video.

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.

pcf8574
pcf8574

As you can see above PCF8574 has 4 input pins GND, VCC, SDA, SCLand 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? 

The adressing of tge 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 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:-

pcf connection to lcd
pcf connection to lcd

 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-

connection 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 functions ends 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.  

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_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);
	HAL_Delay(10);
	lcd_send_cmd (0x20);  // 4bit mode
	HAL_Delay(10);

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

To send string directly, there is another function as shown below

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

YOU CAN DOWNLOAD FULL CODE AT THE END OF THIS POST

Result

Result_lcd
Result_lcd
Check out the VIDEO Below
100%
100%

DOWNLOAD

You can buy me a coffee sensor 🙂

download the CODE below

74
Leave a Reply

avatar
23 Comment threads
51 Thread replies
1 Followers
 
Most reacted comment
Hottest comment thread
27 Comment authors
adminimnFrancolakikris 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?

Pavel
Guest
Pavel

Hello!
Redid the project in STM32CUBEIDE does not work. Blue pill board.

Anh Tu Nguyen
Guest
Anh Tu Nguyen

For binary 01001100 , I think it should be 76 (decimal) and it means 0x4C instead of 0x4B

Laki
Guest
Laki

Hi Everyone, I am using with stm32f072 board and have an lcd with PCF8574 converter. The project is building successfully but for some reason I do not get anything on my lcd. What could be the reason? This is the first time I am trying to send something to my lcd.

kris
Guest

Error building…

../Core/Src/i2c-lcd.c:20: undefined reference to `hi2c1′

laki
Guest
laki

Hi, is it possible to avoid using HAL_DELAY() in void lcd_init (void)? What else can I use in the init function instead of HAL_DEALY() ?

Franco
Guest
Franco

Hello, i have problem with this project. Can anyone help me? I found the i2c addres with i2c scanner did with keil, not arduino: it’s 0x3F. The code is the same that i have downloaded from this site; maybe change some setting into frequency or clock because i use an stm32f767zi. Please help me.

Franco
Guest
Franco

If there is anyone that can help me i send him my code. The circuit that i mounted is the same of the youtube tutorial.

imn
Guest
imn

hi
how can send custom character with your library?
(is it even possible)

Menu